Vad är skillnaden mellan Bash-operatörerna [[vs [vs (vs ((?

Jag är lite förvirrad över vad gör dessa operatörer annorlunda när används i bash (parenteser, dubbla parenteser, parenteser och dubbla parenteser).

[[ , [ , ( , (( 

Jag har sett människor använda dem på om uttalanden som denna:

if [[condition]] if [condition] if ((condition)) if (condition) 

Kommentarer

  • Relaterat: med enkel eller dubbel parentes – bash
  • Parentes och parenteser är inte ’ t som är lätt att söka efter i dokumentationen, och att ’ s allt du har om du inte ’ inte vet namnen på dessa funktioner.

Svar

I Bourne-liknande skal ser ett if -uttalande typiskt ut

if command-list1 then command-list2 else command-list3 fi 

then -klausulen körs om utgångskoden för listan med kommandon är noll. Om utgångskoden inte är noll, körs else -satsen. command-list1 kan vara enkelt eller komplicerat. Det kan till exempel vara en sekvens av en eller flera rörledningar åtskilda av en av operatörerna ;, &, &&, || eller newline. if villkoren som visas nedan är bara speciella fall av command-list1:

  1. if [ condition ]

    [ är ett annat namn för det traditionella test -kommandot. [ / test är ett standard POSIX-verktyg. Alla POSIX-skal har det inbyggt (även om det ”inte krävs av POSIX²). Kommandot test anger en utgångskod och if -uttalandet agerar i enlighet med detta. Typiska tester är om det finns en fil eller ett nummer är lika med en annan.

  2. if [[ condition ]]

    Detta är en ny uppgraderad variant på test ¹ från ksh som bash , zsh , yash , busybox sh stöder också. Denna [[ ... ]] konstruktion ställer också in en utgångskod och if uttalande fungerar i enlighet med detta. Bland dess utökade funktioner kan den testa om en sträng matchar ett jokerteckenmönster (inte i upptagen låda ).

  3. if ((condition))

    Ytterligare ett ksh -tillägg som bash och zsh stöder också. Detta utför aritmetik. Som resultat av aritmetiken ställs en utgångskod in och if uttalandet fungerar enligt rdingly. Den returnerar en utgångskod på noll (true) om resultatet av den aritmetiska beräkningen är noll. Liksom [[...]] är denna form inte POSIX och därför inte bärbar.

  4. if (command)

    Detta kör kommandot i en subshell. När kommandot är klart ställer det in en utgångskod och if -uttalandet fungerar i enlighet med detta.

    En typisk anledning till att använda en subshell som denna är att begränsa biverkningar av command om command krävs variabeltilldelningar eller andra ändringar i skalets miljö. Sådana ändringar kvarstår inte efter att subshell har slutförts.

  5. if command

    kommandot körs och if uttalande fungerar enligt dess utgångskod.


¹ men egentligen inte ett kommando utan en speciell skalkonstruktion med sin egen separata syntax från den för normalt kommando, och varierar kraftigt mellan skalimplementeringar

² POSIX kräver att det finns en fristående test och [ verktyg på systemet, men i fallet med [ har flera Linux-distributioner varit kända för att vara m utfärdar den.

Kommentarer

  • Tack för att du inkluderade det femte alternativet. Att ’ är nyckeln till att förstå hur detta faktiskt fungerar och är överraskande underutnyttjat.
  • Observera att [ faktiskt är en binär, inte ett internt kommando eller symbol. Bor vanligtvis i /bin.
  • @JulienR. faktiskt [ är en inbyggd, liksom test. Det finns binära versioner tillgängliga av kompatibilitetsskäl. Kolla in help [ och help test.
  • Värt att notera att medan ((inte POSIX, $(( dvs aritmetisk expansion är och det ’ är lätt att förvirra dem. Ofta är en lösning att använda något som [ $((2+2)) -eq 4 ] att använda aritmetik i förhållanden
  • Jag önskar att jag kunde rösta upp det här svaret mer än en gång. Perfekt förklaring.

Svar

  • (…) parenteser anger en subshell . Vad som finns inuti dem är inte ett uttryck som på många andra språk. Det är en lista med kommandon (precis som utanför parenteser). Dessa kommandon körs i ett separat delprocess, så all omdirigering, tilldelning etc. som utförs inom parentes har ingen effekt utanför parenteserna.
    • Med en ledande dollartecken, $(…) är en kommandosubstitution : det finns ett kommando inom parentes och utdata från kommandot används som en del av kommandoraden (efter extra utvidgningar såvida inte ersättningen är mellan dubbla citat, men att ”s en annan historia ).
  • { … } parenteser är som parenteser genom att de grupperar kommandon, men de påverkar bara analysering, inte gruppering. Programmet x=2; { x=4; }; echo $x skriver ut 4, medan x=2; (x=4); echo $x skriver ut 2. (Även hakparenteser som är nyckelord måste avgränsas och finns i kommandoposition (därav mellanslag efter { och ; före }) medan parenteser gör inte det. Det är bara en syntax-egendom.)
    • Med ett ledande dollartecken är ${VAR} en parameterutvidgning , expanderar till värdet på en variabel, med möjliga extra transformationer. ksh93 -skalet stöder också ${ cmd;} som en form av kommandosubstitution som inte skapar en subshell.
  • ((…)) dubbla parenteser omger en aritmetisk instruktion , det vill säga en beräkning på heltal, med en syntax som liknar andra programmeringsspråk. Denna syntax används mest för uppdrag och i villkor. Detta existerar bara i ksh / bash / zsh, inte i vanlig sh.
    • Samma syntax används i aritmetiska uttryck $((…)), som expanderar till helhetsvärdet för uttrycket.
  • [ … ] enstaka parentes omger villkorliga uttryck . Villkorliga uttryck bygger mestadels på -operatörer som -n "$variable" för att testa om en variabel är tom och -e "$file" för att testa om en fil finns. Observera att du behöver ett utrymme runt varje operatör (t.ex. [ "$x" = "$y" ], inte [ "$x"="$y" ] ), och ett mellanslag eller ett tecken som ; både inom och utanför parenteserna (t.ex. [ -n "$foo" ], inte [-n "$foo"] ).
  • [[ … ]] dubbla parenteser är en alternativ form av villkorliga uttryck i ksh / bash / zsh med några ytterligare funktioner, till exempel kan du skriva [[ -L $file && -f $file ]] för att testa om en fil är en symbolisk länk till en vanlig fil medan enstaka parentes kräver [ -L "$file" ] && [ -f "$file" ]. Se Varför fungerar parameterutvidgning med mellanslag utan citat inom dubbla parenteser [[men inte enstaka parenteser [? för mer om detta ämne.

I skalet är varje kommando ett villkorligt kommando: varje kommando har en returstatus som antingen är 0 som indikerar framgång eller ett heltal mellan 1 och 255 (och potentiellt mer i vissa skal) som indikerar fel. Kommandot [ … ] (eller [[ … ]] syntaxform) är ett särskilt kommando som också kan stavas test … och lyckas när en fil finns, eller när en sträng inte är tom, eller när ett nummer är mindre än en annan etc. Syntaxformen ((…)) lyckas när ett nummer inte är noll .Här är några exempel på villkor i ett skalskript:

  • Testa om myfile innehåller strängen hello:

    if grep -q hello myfile; then … 
  • Om mydir är en katalog, ändra till det och gör saker:

    if cd mydir; then echo "Creating mydir/myfile" echo "some content" >myfile else echo >&2 "Fatal error. This script requires mydir to exist." fi 
  • Testa om det finns en fil som heter myfile i den aktuella katalogen:

    if [ -e myfile ]; then … 
  • Samma, men inkluderar även dinglande symboliska länkar:

    if [ -e myfile ] || [ -L myfile ]; then … 
  • Testa om värdet på x (vilket antas vara numeriskt) är minst 2, portabelt:

    if [ "$x" -ge 2 ]; then … 
  • Testa om värdet för x (vilket antas vara numeriskt) är minst 2, i bash / ksh / zsh:

    if ((x >= 2)); then … 

Kommentarer

  • Observera att enstaka parentes stöder -a istället för &&, så man kan skriva: [ -L $file -a -f $file ], vilket är samma antal tecken inom parentes utan extra [ och ]
  • @AlexisWilke Operatorerna -a och -o är problematisk eftersom de kan leda till felaktiga tolkningar om några av de inblandade operanderna ser ut som operatörer. Att ’ därför jag inte ’ t nämner dem: de har noll fördel och don ’ fungerar inte alltid. Och skriv aldrig unoterade variabla utvidgningar utan goda skäl: [[ -L $file -a -f $file ]] är bra men med enstaka parentes behöver du [ -L "$file" -a -f "$file" ] (vilket är ok t.ex. om $file börjar alltid med / eller ./).
  • Observera att det ’ s [[ -L $file && -f $file ]] (ingen -a med [[...]] -variant).

Svar

[ vs [[

Detta svar täcker [ vs [[ delmängd av frågan.

Några skillnader i Bash 4.3.11:

  • POSIX vs Bash-tillägg:

  • vanligt kommando vs magi

    • [ är bara ett vanligt kommando med ett konstigt namn.

      ] är bara det sista argumentet för [.

    Ubuntu 16.04 har faktiskt en körbar för den på /usr/bin/[ tillhandahållen av coreutils , men den inbyggda bash-versionen har företräde.

    Ingenting ändras så att Bash analyserar kommandot.

    I synnerhet < är omdirigering, && och || sammanfogar flera kommandon, ( ) genererar subshells såvida inte de ryms av \ och ordutvidgning sker som vanligt.

    • [[ X ]] en enda konstruktion som gör att X tolkas magiskt. <, &&, || och () behandlas speciellt, och orduppdelningsregler är olika.

      Det finns också ytterligare skillnader som = och =~ .

    I Bashese: [ är ett inbyggt kommando och [[ är ett nyckelord: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && och ||

    • [[ a = a && b = b ]]: sant, logiskt och
    • [ a = a && b = b ]: syntaxfel, && analyseras som en AND-kommandoseparator cmd1 && cmd2
    • [ a = a ] && [ b = b ]: POSIX tillförlitlig ekvivalent
    • [ a = a -a b = b ]: nästan ekvivalent, men utfasad av POSIX eftersom den är galen och misslyckas med vissa värden på a eller b som ! eller ( vilket skulle tolkas som logiska operationer
  • (

    • [[ (a = a || a = b) && a = b ]]: falskt. Utan ( ) skulle det vara sant eftersom [[ && ]] har större företräde än [[ || ]]
    • [ ( a = a ) ]: syntaxfel, () tolkas som ett subshell
    • [ \( a = a -o a = b \) -a a = b ]: ekvivalent men (), -a och -o av POSIX. Utan \( \) skulle vara sant eftersom -a har större företräde än -o
    • { [ a = a ] || [ a = b ]; } && [ a = b ] POSIX-ekvivalent som inte är föråldrad. I detta speciella fall kunde vi dock ha skrivit bara: [ a = a ] || [ a = b ] && [ a = b ] eftersom || och && skaloperatörer har samma företräde till skillnad från [[ || ]] och [[ && ]] och -o, -a och [
  • orddelning och generering av filnamn vid utvidgningar (split + glob )

    • x="a b"; [[ $x = "a b" ]]: sant, citat behövs inte
    • x="a b"; [ $x = "a b" ]: syntax fel, expanderar till [ a b = "a b" ]
    • x="*"; [ $x = "a b" ]: syntaxfel om det finns fler än en fil i den aktuella katalogen .
    • x="a b"; [ "$x" = "a b" ]: POSIX-ekvivalent
  • =

    • [[ ab = a? ]]: true, eftersom det mönstermatchning ( * ? [ är magiska). Expanderar inte glob till f iles i aktuell katalog.
    • [ ab = a? ]: a? glob expanderar. Så kan vara sant eller falskt beroende på filerna i den aktuella katalogen.
    • [ ab = a\? ]: falsk, inte globutvidgning
    • = och == är desamma i både [ och [[, men == är en Bash-tillägg.
    • case ab in (a?) echo match; esac: POSIX-ekvivalent
    • [[ ab =~ "ab?" ]]: falsk, tappar magi med "" i Bash 3.2 och senare och förutsatt att kompatibilitet med bash 3.1 inte är aktiverad (som med BASH_COMPAT=3.1)
    • [[ ab? =~ "ab?" ]]: sant
  • =~

    • [[ ab =~ ab? ]]: true, POSIX utökat matchning med reguljärt uttryck , ? expanderar inte glob
    • [ a =~ a ]: syntaxfel. Ingen bash-ekvivalent.
    • printf "ab\n" | grep -Eq "ab?": POSIX-ekvivalent (endast data med en rad)
    • awk "BEGIN{exit !(ARGV[1] ~ ARGV[2])}" ab "ab?": POSIX-ekvivalent.

Rekommendation: använd alltid []

Det finns POSIX-ekvivalenter för varje [[ ]] konstruktion jag har sett.

Om du använder [[ ]] kommer du:

  • att förlora portabilitet
  • tvinga läsaren att lära sig svårigheterna i en annan bash-tillägg. [ är bara ett vanligt kommando med ett konstigt namn, ingen speciell semantik är inblandad.

Tack till Stéphane Chazelas för viktiga korrigeringar och tillägg.

Kommentarer

  • @St é phaneChazelas tack för informationen! Jag ’ har lagt till expr till svaret. Termen ” Bash ex spänning ” är inte avsedd att antyda att Bash var det första skalet som lade till lite syntax, att lära sig POSIX sh vs Bash är redan tillräckligt för att göra mig galen.
  • Se man test om du försökte man [ och gick vilse. Det kommer att förklara POSIX-varianten.
  • Korrigering: ] är ett argument för [ -kommandot, men det är inte ’ t förhindra att ytterligare argument används. ] måste vara sista argumentet till [, men det kan också förekomma som en del av testuttrycket. Till exempel testar if [ "$foo" = ] ]; then om variabeln foo är inställd på ”] ” (som if [ ] = "$foo" ]; then) kommer.
  • @GordonDavisson tack, jag ’ t vet det, fixat.
  • @ tgm1024 – Monicawasmbehandlades ja, det är också ett giltigt övervägande.

Svar

Från bash-dokumentation :

(list) -listan körs i en subshell-miljö (se COMMAND EXECUTION MILJÖ nedan). Variabla tilldelningar och inbyggda kommandon som påverkar skalets miljö förblir inte i kraft efter att kommandot slutförts. Returstatusen är utgångsstatus för listan.

Med andra ord ser du till att vad som än händer i ”lista” (som en cd) inte har någon effekt utanför ( och ). Det enda som kommer att läcka är utgångskoden för det sista kommandot eller med set -e det första kommandot som genererar ett fel (annat än några få som if, while, etc.)

((expression)) Uttrycket utvärderas enligt reglerna som beskrivs nedan under ARITMETISK UTVÄRDERING. Om uttryckets värde inte är noll är returstatus 0, annars är returstatus är 1. Detta motsvarar exakt ” uttryck ”.

Detta är en bash-förlängning som gör att du kan göra matte. Detta liknar något att använda expr utan alla begränsningar för expr (som att ha mellanslag överallt, undgå * osv.)

[[ expression ]] Returnera en status på 0 eller 1 beroende på utvärdering av det villkorliga uttrycksuttrycket. Uttryck består av de primärer som beskrivs nedan under FÖRHÅLLANDE UTTRYCK. Orddelning och utvidgning av sökväg utförs inte på orden mellan [[och]]; tilde-expansion, parameter- och variabel-expansion, aritmetisk expansion, kommandosubstitution, processubstitution och offertborttagning utförs. Villkorliga operatorer som -f måste inte citeras för att kännas igen som primärer.

När de används med [[, < och > operatörer sorterar lexikografiskt med den aktuella platsen.

Detta erbjuder ett avancerat test för att jämföra strängar, siffror och filer lite som test erbjudanden, men kraftfullare.

[ expr ] Returnera status på 0 (true) eller 1 (false) beroende på utvärderingen av det villkorliga uttrycket expr. Varje operatör och operatör måste vara ett separat argument. Uttryck består av de primärer som beskrivs ovan under FÖRHÅLLANDE UTTRYCK. test accepterar inga alternativ och accepterar inte heller och ignorerar ett argument av – som betyder slutet på alternativen.

[…]

Den här kallar test. Egentligen var [ i gamla tider en symbolisk länk till test. Det fungerar på samma sätt och du har samma begränsningar. Eftersom en binär känner till namnet som den startades med vet testprogrammet när den startades som [ och den kan ignorera dess sista parameter, som förväntas vara ]. Roliga Unix-tricks.

Observera att i fallet med bash, [ och test är inbyggda funktioner (som nämns i en kommentar), men i stort sett samma begränsningar gäller.

Kommentarer

  • Även om test och [ är naturligtvis inbyggda kommandon i Bash, men det ’ är troligt att extern binär finns också.
  • Den externa binära för [ är inte en symbolisk länk till test på de flesta moderna system .
  • På något sätt tycker jag att det är roligt att de bryr sig om att skapa två separata binärer, som båda har exakt vad de behöver, istället för att bara kombinera dem och lägga till ett par villkor. Även om strings /usr/bin/test faktiskt visar att den också har hjälptexten, så vet jag inte ’ vad jag ska säga.
  • @ Random832 Jag förstår din mening om GNU-motiveringen för att undvika oväntat arg0-beteende men om POSIX-krav skulle jag inte ’ inte vara så bekräftande. Även om test -kommandot uppenbarligen krävs för att existera som ett fristående filbaserat kommando enligt standarden, står inget i det för dess [ -variant. genomföras på det sättet också. Till exempel tillhandahåller Solaris 11 inte ’ t [ körbar men uppfyller ändå POSIX-standarderna
  • (utgång 1) har en effekt utanför parenteserna.

Svar

Några exempel:

Traditionellt test:

foo="some thing" # check if value of foo is not empty if [ -n "$foo" ] ; then... if test -n "$foo" ; then... 

test och [ är kommandon som alla andra, så variabeln är uppdelad i ord såvida den inte står i citattecken.

Test i ny stil

[[ ... ]] är (nyare) speciell skalkonstruktion, som fungerar lite annorlunda, det mest uppenbara är att det inte går att dela upp variabler:

if [[ -n $foo ]] ; then... 

Några dokumentation om [ och [[ här .

Aritmetiskt test:

foo=12 bar=3 if (( $foo + $bar == 15 )) ; then ... 

”Normal ”kommandon:

Alla ovanstående fungerar som vanliga kommandon, och if kan ta vilket kommando som helst:

# grep returns true if it finds something if grep pattern file ; then ... 

Flera kommandon:

Eller så kan vi använda flera kommandon. Om du slår in en uppsättning kommandon i ( ... ) kör du dem i underskal och skapar en tillfällig kopia av skalets tillstånd (arbetskatalog, variabler). för att köra något program tillfälligt i en annan katalog:

# this will move to $somedir only for the duration of the subshell if ( cd $somedir ; some_test ) ; then ... # while here, the rest of the script will see the new working # directory, even after the test if cd $somedir ; some_test ; then ... 

Svar

Grupperingskommandon

Bash ger två sätt att gruppera en lista med kommandon som ska köras som en enhet.

( list ) Att placera en lista med kommandon mellan parenteser gör att en subshell-miljö skapas och att alla kommandon i listan körs i den subshell. Eftersom listan är körs i en subshell, variabla tilldelningar förblir inte i kraft efter att subshell har slutförts.

$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a" inside: a=2 outside: a=1 

{ list; } Placera en lista med kommandon mellan lockiga parenteser gör att listan körs i nuvarande skalkontext . Ingen subshell skapas. Följande lista med semikolon (eller ny linje) krävs. Källa

${} Parameter expansion Ex: ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s $() Command substitution Ex: result=$(COMMAND) $(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

Villkorliga konstruktioner

Enstaka fäste dvs. []
För jämförelse ==, !=, <, och > och ska användas och för numerisk jämförelse eq, ne,lt och gt ska användas.

Förbättrade parenteser dvs. [[]]

I alla ovanstående exempel använde vi endast enstaka parentes för att stänga det villkorliga uttrycket, men bash tillåter dubbla parenteser som fungerar som en förbättrad version av syntaxen för enstaka parentes.

För jämförelse kan ==, !=, <, och > använda bokstavligt.

  • [ är en synonym för testkommando. Även om den är inbyggd i skalet skapar den en ny process.
  • [[ är en ny förbättrad version av den, som är ett nyckelord, inte ett program .
  • [[ förstås av Korn och Bash.

Källa

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *