Hebben dubbele vierkante haken [[ ]] de voorkeur boven enkele vierkante haken [ ] in Bash?

Een collega beweerde onlangs in een codebeoordeling dat de constructie [[ ]]de voorkeur verdient boven [ ]in constructies zoals

if [ "`id -nu`" = "$someuser" ] ; then 
     echo "I love you madly, $someuser"
fi

Hij kon geen reden geven. Is er een?


Antwoord 1, autoriteit 100%

[[heeft minder verrassingen en is over het algemeen veiliger in gebruik. Maar het is niet draagbaar – POSIX specificeert niet wat het doet en slechts enkele shells ondersteunen het (naast bash hoorde ik dat ksh het ook ondersteunt). U kunt bijvoorbeeld

[[ -e $b ]]

om te testen of een bestand bestaat. Maar met [moet je $bciteren, omdat het het argument splitst en zaken als "a*"uitbreidt (waarbij [[neemt het letterlijk). Dat heeft ook te maken met hoe [een extern programma kan zijn en zijn argument ontvangt zoals elk ander programma (hoewel het ook een ingebouwd programma kan zijn, maar dan nog niet deze speciale behandeling heeft).

[[heeft ook een aantal andere leuke functies, zoals het matchen van reguliere expressies met =~samen met operators zoals ze bekend zijn in C-achtige talen. Hier is een goede pagina over: Wat is het verschil tussen test, [en [[?en Bash-tests


Antwoord 2, autoriteit 35%

Gedragsverschillen

Enkele verschillen op Bash 4.3.11:

  • POSIX vs Bash-extensie:

  • gewone opdracht versus magie

    • [is gewoon een gewoon commando met een rare naam.

      ]is slechts het laatste argument van [.

      Ubuntu 16.04 heeft er zelfs een uitvoerbaar bestand voor op /usr/bin/[geleverd door coreutils, maar de ingebouwde bash-versie heeft voorrang.

      Er is niets veranderd aan de manier waarop Bash het commando ontleedt.

      In het bijzonder <is omleiding, &&en ||voegen meerdere commando’s samen, ( )genereert subshells tenzij ontsnapt door \, en woorduitbreiding gebeurt zoals gewoonlijk.

    • [[ X ]]is een enkele constructie waarmee Xop magische wijze kan worden geparseerd. <, &&, ||en ()worden speciaal behandeld, en regels voor het splitsen van woorden zijn anders.

      Er zijn ook andere verschillen, zoals =en =~.

    In Bashese: [is een ingebouwde opdracht en [[is een sleutelwoord: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • &&en ||

    • [[ a = a && b = b ]]: waar, logisch en
    • [ a = a && b = b ]: syntaxisfout, &&geparseerd als een AND-opdrachtscheidingsteken cmd1 && cmd2
    • [ a = a ] && [ b = b ]: POSIX betrouwbaar equivalent
    • [ a = a -a b = b ]: bijna gelijkwaardig, maar afgekeurd door POSIX omdat het krankzinnig is en faalt voor sommige waarden van aof bzoals !of (wat zou worden geïnterpreteerd als logische bewerkingen
  • (

    • [[ (a = a || a = b) && a = b ]]: onwaar. Zonder ( )zou het waar zijn omdat [[ && ]]heeft een grotere prioriteit dan [[ || ]]
    • [ ( a = a ) ]: syntaxisfout, ()wordt geïnterpreteerd als een subshell
    • [ \( a = a -o a = b \) -a a = b ]: equivalent, maar (), -a, en -ozijn verouderd door POSIX. Zonder \( \)zou waar zijn omdat -aeen grotere prioriteit heeft dan -o
    • { [ a = a ] || [ a = b ]; } && [ a = b ]niet-verouderd POSIX-equivalent. In dit specifieke geval hadden we echter gewoon kunnen schrijven: [ a = a ] || [ a = b ] && [ a = b ]omdat de shell-operators ||en &&dezelfde prioriteit hebben in tegenstelling tot [[ || ]]en [[ && ]]en -o, -aen [
  • woord splitsen en bestandsnaam genereren bij uitbreidingen (split+glob)

    • x='a b'; [[ $x = 'a b' ]]: waar, aanhalingstekens niet nodig
    • x='a b'; [ $x = 'a b' ]: syntaxisfout, breidt uit naar [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]: syntaxisfout als er meer dan één bestand in de huidige map is.
    • x='a b'; [ "$x" = 'a b' ]: POSIX-equivalent
  • =

    • [[ ab = a? ]]: waar, omdat het patroonovereenkomst(* ? [zijn magisch). Breidt glob niet uit naar bestanden in de huidige map.
    • [ ab = a? ]: a?glob wordt uitgevouwen. Het kan dus waar of onwaar zijn, afhankelijk van de bestanden in de huidige map.
    • [ ab = a\? ]: false, geen globale uitbreiding
    • =en ==zijn hetzelfde in zowel [als [[), maar ==is een Bash-extensie.
    • case ab in (a?) echo match; esac: POSIX-equivalent
    • [[ ab =~ 'ab?' ]]: false, verliest magie met ''in Bash 3.2 en hoger en op voorwaarde dat compatibiliteit met bash 3.1 niet is ingeschakeld (zoals met BASH_COMPAT=3.1)
    • [[ ab? =~ 'ab?' ]]: waar
  • =~

    • [[ ab =~ ab? ]]: true, POSIX uitgebreide reguliere expressiekomt overeen, ?wordt niet glob uitgevouwen
    • [ a =~ a ]: syntaxisfout. Geen bash-equivalent.
    • printf 'ab\n' | grep -Eq 'ab?': POSIX-equivalent (alleen gegevens van één regel)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX-equivalent.

Aanbeveling: gebruik altijd []

Er zijn POSIX-equivalenten voor elke [[ ]]-constructie die ik heb gezien.

Als je [[ ]]gebruikt, dan:

  • draagbaarheid verliezen
  • dwing de lezer om de fijne kneepjes van een andere bash-extensie te leren. [is gewoon een gewoon commando met een rare naam, er komt geen speciale semantiek bij kijken.

Met dank aan Stéphane Chazelasvoor belangrijke correcties en toevoegingen.


Antwoord 3, autoriteit 9%

[[ ]]heeft meer functies – ik raad u aan een kijkje te nemen op de Advanced Bash Scripting Guidevoor meer informatie, met name de uitgebreide testopdrachtsectie in Hoofdstuk 7. Tests.

Overigens, zoals de gids opmerkt, werd [[ ]]geïntroduceerd in ksh88 (de 1988-versie van de Korn-shell).


Antwoord 4, autoriteit 2%

Van Welke vergelijker, test, beugel of dubbele beugel is het snelst?(http://bashcurescancer .com)

De dubbele haak is een “compound”
commando” waar als test en de single
beugel zijn ingebouwde shell-ins (en in
werkelijkheid zijn hetzelfde commando). Dus,
de enkele beugel en dubbele beugel;
andere code uitvoeren.

De test en enkele beugel zijn de
meest draagbare zoals ze bestaan als
afzonderlijke en externe opdrachten.
Als u echter een op afstand gebruikt
moderne versie van BASH, de dubbele
beugel wordt ondersteund.


Antwoord 5

In een vraag met de tag ‘bash’ die expliciet “in Bash” in de titel heeft, ben ik een beetje verrast door alle antwoorden die zeggen dat je [[]]omdat het alleen in bash werkt!

Het is waar dat draagbaarheid het belangrijkste bezwaar is: als je een shellscript wilt schrijven dat in Bourne-compatibele shells werkt, zelfs als ze geen bash zijn, moet je [[]]. (En als je je shellscripts in een striktere POSIX-shell wilt testen, raad ik dashaan; hoewel het een onvolledige POSIX-implementatie is omdat het de internationaliseringsondersteuning mist die door de standaard wordt vereist, ontbreekt het ook aan ondersteuning voor de vele niet-POSIX constructies gevonden in bash, ksh, zsh, etc.)

Het andere bezwaar dat ik zie is in ieder geval van toepassing binnen de aanname van bash: dat [[]]zijn eigen speciale regels heeft die je moet leren , terwijl []zich gedraagt als een gewoon commando. Dat is weer waar (en meneer Santilli bracht de bonnetjes met alle verschillen mee), maar het is nogal subjectief of de verschillen goed of slecht zijn. Persoonlijk vind ik het bevrijdend dat ik met de constructie met dubbele haakjes ()kan gebruiken voor groepering, &&en ||voor Booleaanse logica, <en >voor vergelijking, en niet-geciteerde parameteruitbreidingen. Het is als zijn eigen kleine afgesloten wereld waar uitdrukkingen meer werken zoals in traditionele programmeertalen zonder commando-shell.

Een punt dat ik nog niet heb opgemerkt, is dat dit gedrag van [[]]volledig consistent is met dat van de rekenkundige uitbreidingsconstructie $(()), die isgespecificeerd door POSIX, en staat ook haakjes zonder aanhalingstekens en Booleaanse en ongelijkheidsoperatoren toe (die hier numerieke in plaats van lexicale vergelijkingen). Elke keer dat u de dubbele haakjes ziet, krijgt u in wezen hetzelfde afschermingseffect voor aanhalingstekens.

(Bash en zijn moderne verwanten gebruiken ook (())– zonder de leidende $– als ofwel een C- style forloop header of een omgeving voor het uitvoeren van rekenkundige bewerkingen; geen van beide syntaxis maakt deel uit van POSIX.)

Er zijn dus enkele goede redenen om de voorkeur te geven aan [[]]; er zijn ook redenen om het te vermijden, al dan niet van toepassing in uw omgeving. Wat uw collega betreft, “onze stijlgids zegt het” is een geldige rechtvaardiging, voor zover het gaat, maar ik zou ook het achtergrondverhaal opzoeken van iemand die begrijpt waarom de stijlgids aanbeveelt wat het doet.


Antwoord 6

Als je Google’s stijlgids:

Test, [en [[

[[ ... ]]vermindert fouten omdat er geen padnaamuitbreiding of woordsplitsing plaatsvindt tussen [[en ]], en [[ ... ]]maakt het mogelijk om reguliere expressies te matchen waar [ ... ]dat niet doet.

# This ensures the string on the left is made up of characters in the
# alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
# For the gory details, see
# E14 at https://tiswww.case.edu/php/chet/bash/FAQ
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
  echo "Match"
fi
# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
  echo "Match"
fi
# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
  echo "Match"
fi

7

[[]] Dubbele beugels worden niet ondergeschikt onder bepaalde versie van SunOS en volledig niet-ondergeschreven binnenfunctieverklaringen door:
GNU Bash, versie 2.02.0 (1) -Release (SPARC-SUN-SOLARIS2.6)


8

in een notendop, [[is beter omdat het geen ander proces vult. Geen beugels of een enkele beugel is langzamer dan een dubbele beugel omdat het een ander proces vorken.

Other episodes