Hva er forskjellen mellom Bash-operatorene [[vs [vs (vs ((?

Jeg er litt forvirret på hva gjør disse operatørene annerledes når brukt i bash (parentes, dobbel parentes, parentes og dobbel parentes).

[[ , [ , ( , (( 

Jeg har sett folk bruke dem på hvis uttalelser som dette:

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

Kommentarer

Svar

I Bourne-lignende skjell ser en if -uttalelse vanligvis ut

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

then -klausulen utføres hvis utgangskoden til listen over kommandoer er null. Hvis utgangskoden ikke er null, blir else -klausulen utført. command-list1 kan være enkel eller kompleks. Det kan for eksempel være en sekvens av en eller flere rørledninger adskilt av en av operatørene ;, &, &&, || eller ny linje. if forholdene vist nedenfor er bare spesielle tilfeller av command-list1:

  1. if [ condition ]

    [ er et annet navn for den tradisjonelle test -kommandoen. [ / test er et standard POSIX-verktøy. Alle POSIX-skjell har innebygd (skjønt det er ikke påkrevd av POSIX²). test -kommandoen setter en utgangskode og if -uttalelsen opptrer deretter. Typiske tester er om en fil eksisterer eller ett tall er lik et annet.

  2. if [[ condition ]]

    Dette er en ny oppgradert variant på test ¹ fra ksh som bash , zsh , yash , busybox sh støtter også. Denne [[ ... ]] -konstruksjonen setter også en utgangskode og if uttalelse fungerer tilsvarende. Blant de utvidede funksjonene kan den teste om en streng samsvarer med et jokertegnmønster (ikke i busybox sh ).

  3. if ((condition))

    En annen ksh utvidelse som bash og zsh også støtter. Dette utfører aritmetikk. Som et resultat av aritmetikken settes en utgangskode og if uttalelsen fungerer i samsvar med rdingly. Den returnerer en utgangskode på null (sann) hvis resultatet av den aritmetiske beregningen ikke er null. I likhet med [[...]], er dette skjemaet ikke POSIX og derfor ikke bærbart.

  4. if (command)

    Dette kjører kommandoen i en subshell. Når kommandoen er fullført, setter den en utgangskode og if -uttalelsen fungerer deretter.

    En typisk årsak til å bruke et subshell som dette er å begrense bivirkninger av command hvis command krevde variable tilordninger eller andre endringer i skallets miljø. Slike endringer forblir ikke etter at subshell er fullført.

  5. if command

    kommandoen kjøres og if -uttalelsen i henhold til utgangskoden.


¹ skjønt egentlig ikke en kommando, men en spesiell skallkonstruksjon med sin egen separate syntaks fra den for normal kommando, og varierer betydelig mellom skallimplementeringer

² POSIX krever at det er en frittstående test og [ verktøy på systemet, men i tilfellet [ har flere Linux-distribusjoner vært kjent for å være m utsteder den.

Kommentarer

  • Takk for at du inkluderer det femte alternativet. At ‘ er nøkkelen til å forstå hvordan dette faktisk fungerer og er overraskende underutnyttet.
  • Merk at [ faktisk er binær, ikke en intern kommando eller et symbol. Bor generelt i /bin.
  • @JulienR. faktisk er [ en innebygd, som test. Det er binære versjoner tilgjengelig av kompatibilitetsårsaker. Ta en titt på help [ og help test.
  • Verdt å merke seg at mens ((ikke POSIX, $(( dvs. aritmetisk utvidelse er og det ‘ er lett å forvirre dem. Ofte er en løsning å bruke noe sånt som [ $((2+2)) -eq 4 ] å bruke aritmetikk i betingelsesuttalelser
  • Jeg skulle ønske jeg kunne stemme på dette svaret mer enn en gang. Perfekt forklaring.

Svar

  • (…) parenteser angir en subshell . Det som er inni dem, er ikke et uttrykk som på mange andre språk. Det er en liste over kommandoer (akkurat som utenfor parentes). Disse kommandoene utføres i en egen underprosess, så enhver omdirigering, tildeling osv. Utført i parentes har ingen effekt utenfor parentesene.
    • Med en ledende dollartegn, $(…) er en kommandosubstitusjon : det er en kommando i parentes, og utdata fra kommandoen brukes som en del av kommandolinjen (etter ekstra utvidelser med mindre erstatningen er mellom doble anførselstegn, men at «s en annen historie ).
  • { … } parenteser er som parenteser ved at de grupperer kommandoer, men de påvirker bare parsing, ikke gruppering. Programmet x=2; { x=4; }; echo $x skriver ut 4, mens x=2; (x=4); echo $x skriver ut 2. (Også parenteser som er søkeord må avgrenses og funnet i kommandoposisjon (derav mellomrom etter { og ; før }) mens parenteser gjør ikke det. Det er bare en syntaks-sære.)
    • Med et ledende dollartegn er ${VAR} et parameterutvidelse , utvider til verdien av en variabel, med mulige ekstra transformasjoner. ksh93 skallet støtter også ${ cmd;} som en form for kommandosubstitusjon som ikke gyter et subshell.
  • ((…)) doble parenteser omgir en aritmetisk instruksjon , det vil si en beregning på heltall, med en syntaks som ligner på andre programmeringsspråk. Denne syntaksen brukes mest til oppgaver og i betingede forhold. Denne eksisterer bare i ksh / bash / zsh, ikke i vanlig sh.
    • Den samme syntaksen brukes i aritmetiske uttrykk $((…)), som utvides til heltallverdien til uttrykket.
  • [ … ] enkelt parentes omgir betingede uttrykk . Betingede uttrykk er for det meste bygget på operatorer slik som -n "$variable" for å teste om en variabel er tom og -e "$file" for å teste om en fil eksisterer. Merk at du trenger et mellomrom rundt hver operatør (f.eks. [ "$x" = "$y" ], ikke [ "$x"="$y" ] ), og et mellomrom eller et tegn som ; både innenfor og utenfor parentesene (f.eks. [ -n "$foo" ], ikke [-n "$foo"] ).
  • [[ … ]] doble parenteser er en alternativ form for betingede uttrykk i ksh / bash / zsh med noen få ekstra funksjoner, for eksempel kan du skrive [[ -L $file && -f $file ]] for å teste om en fil er en symbolsk lenke til en vanlig fil, mens enkelte parenteser krever [ -L "$file" ] && [ -f "$file" ]. Se Hvorfor fungerer parameterutvidelse med mellomrom uten anførselstegn i doble parentes [[men ikke enkelt parentes [? for mer om dette emnet.

I skallet er hver kommando en betinget kommando: hver kommando har en returstatus som enten er 0 som indikerer suksess eller et heltall mellom 1 og 255 (og potensielt mer i noen skall) som indikerer feil. [ … ] -kommandoen (eller [[ … ]] syntaksform) er en bestemt kommando som også kan staves test … og lykkes når en fil eksisterer, eller når en streng ikke er tom, eller når et tall er mindre enn en annen, osv. ((…)) syntaksformularen lykkes når et tall ikke er null .Her er noen eksempler på betingelser i et skallskript:

  • Test om myfile inneholder strengen hello:

    if grep -q hello myfile; then … 
  • Hvis mydir er en katalog, kan du endre til det og gjør ting:

    if cd mydir; then echo "Creating mydir/myfile" echo "some content" >myfile else echo >&2 "Fatal error. This script requires mydir to exist." fi 
  • Test om det er en fil som heter myfile i gjeldende katalog:

    if [ -e myfile ]; then … 
  • Det samme, men inkluderer også dinglende symbolske lenker:

    if [ -e myfile ] || [ -L myfile ]; then … 
  • Test om verdien av x (som antas å være numerisk) er minst 2, bærbart:

    if [ "$x" -ge 2 ]; then … 
  • Test om verdien til x (som antas å være numerisk) er minst 2, i bash / ksh / zsh:

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

Kommentarer

  • Merk at enkelt brakett støtter -a i stedet for &&, slik at man kan skrive: [ -L $file -a -f $file ], som er det samme antall tegn i parentes uten ekstra [ og ]
  • @AlexisWilke Operatørene -a og -o er problematisk fordi de kan føre til feil parser hvis noen av operandene som er involvert ser ut som operatører. At ‘ derfor jeg ikke ‘ ikke nevner dem: de har ingen fordel og don ‘ fungerer ikke alltid. Og skriv aldri unoterte variabelutvidelser uten god grunn: [[ -L $file -a -f $file ]] er greit, men med enkelt parentes trenger du [ -L "$file" -a -f "$file" ] (som er ok, f.eks. Hvis $file starter alltid med / eller ./).
  • Merk at det ‘ s [[ -L $file && -f $file ]] (ingen -a med [[...]] variant).

Svar

[ vs [[

Dette svaret dekker [ vs [[ delsett av spørsmålet.

Noen forskjeller på Bash 4.3.11:

  • POSIX vs Bash-utvidelse:

  • vanlig kommando vs magi

    • [ er bare en vanlig kommando med et merkelig navn.

      ] er bare det siste argumentet til [.

    Ubuntu 16.04 har faktisk en kjørbar fil for /usr/bin/[ levert av coreutils , men den innebygde bash-versjonen har forrang.

    Ingenting endres slik Bash analyserer kommandoen.

    Spesielt < er omdirigering, && og || sammenkobler flere kommandoer, ( ) genererer subshells med mindre de slippes unna \, og ordutvidelse skjer som vanlig.

    • [[ X ]] er en enkelt konstruksjon som gjør at X kan analyseres magisk. <, &&, || og () behandles spesielt, og orddelingsregler er forskjellige.

      Det er også ytterligere forskjeller som = og =~ .

    I Bashese: [ er en innebygd kommando, og [[ er et nøkkelord: https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • && og ||

    • [[ a = a && b = b ]]: sann, logisk og
    • [ a = a && b = b ]: syntaksfeil, && analysert som AND-kommandoseparator cmd1 && cmd2
    • [ a = a ] && [ b = b ]: POSIX pålitelig ekvivalent
    • [ a = a -a b = b ]: nesten ekvivalent, men avskrevet av POSIX fordi den er sinnssyk og mislykkes for noen verdier av a eller b som ! eller ( som vil bli tolket som logiske operasjoner
  • (

    • [[ (a = a || a = b) && a = b ]]: falsk. Uten ( ), ville det være sant fordi [[ && ]] har større forrang enn [[ || ]]
    • [ ( a = a ) ]: syntaksfeil, () tolkes som et subshell
    • [ \( a = a -o a = b \) -a a = b ]: ekvivalent, men (), -a og -o av POSIX. Uten \( \) ville være sant fordi -a har større forrang enn -o
    • { [ a = a ] || [ a = b ]; } && [ a = b ] POSIX-ekvivalent som ikke er utdatert. I dette spesielle tilfellet kunne vi imidlertid ha skrevet bare: [ a = a ] || [ a = b ] && [ a = b ] fordi || og && skalloperatører har samme forrang i motsetning til [[ || ]] og [[ && ]] og -o, -a og [
  • orddeling og generering av filnavn ved utvidelser (delt + glob )

    • x="a b"; [[ $x = "a b" ]]: sant, anførselstegn ikke nødvendig
    • x="a b"; [ $x = "a b" ]: syntaks feil, utvides til [ a b = "a b" ]
    • x="*"; [ $x = "a b" ]: syntaksfeil hvis det er mer enn én fil i den aktuelle katalogen .
    • x="a b"; [ "$x" = "a b" ]: POSIX-ekvivalent
  • =

    • [[ ab = a? ]]: sant, fordi det gjør mønstermatching ( * ? [ er magiske). utvides ikke til f iles i gjeldende katalog.
    • [ ab = a? ]: a? glob utvides. Det kan være sant eller usant, avhengig av filene i den gjeldende katalogen.
    • [ ab = a\? ]: false, ikke glob utvidelse
    • = og == er de samme i både [ og [[, men == er en Bash-utvidelse.
    • case ab in (a?) echo match; esac: POSIX-ekvivalent
    • [[ ab =~ "ab?" ]]: false, mister magi med "" i Bash 3.2 og over, og forutsatt at kompatibilitet med bash 3.1 ikke er aktivert (som med BASH_COMPAT=3.1)
    • [[ ab? =~ "ab?" ]]: sant
  • =~

    • [[ ab =~ ab? ]]: true, POSIX utvidet regulært uttrykk samsvar, ? utvides ikke globalt
    • [ a =~ a ]: syntaksfeil. Ingen bash-ekvivalent.
    • printf "ab\n" | grep -Eq "ab?": POSIX-ekvivalent (kun enkeltlinjedata)
    • awk "BEGIN{exit !(ARGV[1] ~ ARGV[2])}" ab "ab?": POSIX-ekvivalent.

Anbefaling: bruk alltid []

Det er POSIX-ekvivalenter for hver [[ ]] konstruksjon jeg har sett.

Hvis du bruker [[ ]], må du:

  • miste bærbarhet
  • tving leseren til å lære seg komplikasjonene til en annen bash-utvidelse. [ er bare en vanlig kommando med et merkelig navn, ingen spesiell semantikk er involvert.

Takk til Stéphane Chazelas for viktige rettelser og tillegg.

Kommentarer

  • @St é phaneChazelas takk for informasjonen! Jeg ‘ har lagt til expr til svaret. Begrepet » Bash ex spenning » er ikke ment å antyde at Bash var det første skallet som la til litt syntaks. Å lære POSIX sh vs Bash er allerede nok til å gjøre meg gal.
  • Se man test hvis du prøvde man [ og gikk deg vill. Det vil forklare POSIX-varianten.
  • Retting: ] er et argument for [ -kommandoen, men det

t forhindre at flere argumenter blir brukt.]må være siste argument til[, men det kan også forekomme som en del av testuttrykket. For eksempel vilif [ "$foo" = ] ]; thenteste om variabelenfooer satt til «] » (somif [ ] = "$foo" ]; then).

  • @GordonDavisson takk, jeg gjorde ikke ‘ t vet det, løst.
  • @ tgm1024 – Monicawasmbehandlet ja, det er også en gyldig vurdering.
  • Svar

    Fra bash-dokumentasjonen :

    (list) -listen kjøres i et subshell-miljø (se KOMMANDOUTFØRINGSMILJØ nedenfor). Variable tilordninger og innebygde kommandoer som påvirker skallets miljø, forblir ikke i kraft etter at kommandoen er fullført. Returstatusen er utgangsstatus for listen.

    Med andre ord sørger du for at det som skjer i «liste» (som en cd) ikke har noen effekt utenfor ( og ). Det eneste som vil lekke er utgangskoden til den siste kommandoen eller med set -e den første kommandoen som genererer en feil (annet enn noen få som if, while osv.)

    ((expression)) Uttrykket evalueres i henhold til reglene beskrevet nedenfor under ARITMETISK EVALUERING. Hvis verdien av uttrykket ikke er null, er returstatusen 0; ellers er returstatusen er 1. Dette tilsvarer nøyaktig å la » uttrykk «.

    Dette er en bash-utvidelse som lar deg gjøre matte. Dette ligner litt på å bruke expr uten alle begrensningene for expr (for eksempel å ha mellomrom overalt, unnslippe * osv.)

    [[ expression ]] Returner statusen 0 eller 1, avhengig av evaluering av betinget uttrykk. Uttrykk er sammensatt av primærfagene beskrevet nedenfor under BETINGELSESUTTRYKK. Orddeling og stienavnutvidelse utføres ikke på ordene mellom [[og]]; tilde-utvidelse, parameter- og variabel utvidelse, aritmetisk utvidelse, kommandosubstitusjon, prosessubstitusjon og fjerning av sitater utføres. Betingede operatører som -f må ikke siteres for å bli gjenkjent som primær.

    Når den brukes med [[, < og > operatører sorterer leksikografisk ved hjelp av gjeldende sted.

    Dette gir en avansert test for å sammenligne strenger, tall og filer litt som test tilbud, men kraftigere.

    [ expr ] Returner statusen til 0 (true) eller 1 (false), avhengig av evalueringen av det betingede uttrykket ekspr. Hver operatør og operatør må være et eget argument. Uttrykk er sammensatt av primærfagene beskrevet ovenfor under BETINGELSESUTTRYKK. test godtar ikke noen alternativer, og godtar heller ikke og ignorerer et argument om – som betegner slutten på alternativene.

    […]

    Denne kaller test. I gamle dager var [ faktisk en symbolsk lenke til test. Det fungerer på samme måte, og du har de samme begrensningene. Siden en binær kjenner navnet den ble startet med, vet testprogrammet når den ble startet som [, og den kan ignorere den siste parameteren, som forventes å være ]. Morsomme Unix-triks.

    Merk at i tilfelle bash, [ og test er innebygde funksjoner (som nevnt i en kommentar), men likevel gjelder de samme begrensningene.

    Kommentarer

    • Skjønt test og [ er selvfølgelig innebygde kommandoer i Bash, men det ‘ er sannsynlig at en Ekstern binær eksisterer også.
    • Ekstern binær for [ er ikke en symbolsk lenke til test på de fleste moderne systemer .
    • På en eller annen måte synes jeg det er morsomt at de gidder å lage to separate binærfiler, som begge har akkurat det de trenger, i stedet for bare å kombinere dem og legge til et par betingede betingelser. Selv om strings /usr/bin/test faktisk viser at den også har hjelpeteksten, så vet jeg ikke ‘ hva jeg skal si.
    • @ Random832 Jeg forstår poenget ditt om GNU-begrunnelsen for å unngå uventet arg0-oppførsel, men om POSIX-krav, jeg vil ikke ‘ ikke være så bekreftende. Selv om test -kommandoen åpenbart kreves for å eksistere som en frittstående filbasert kommando av standarden, er det ingenting i den som sier at [ -varianten trenger å bli implementert på den måten også. For eksempel gir Solaris 11 ikke ‘ t [ kjørbar, men er likevel fullt i samsvar med POSIX-standardene
    • (avkjørsel 1) har en effekt utenfor parentesene.

    Svar

    Noen eksempler:

    Tradisjonell test:

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

    test og [ er kommandoer som alle andre, så variabelen er delt i ord med mindre den er i anførselstegn.

    Test i ny stil

    [[ ... ]] er (nyere) spesiell skallkonstruksjon, som fungerer litt annerledes, den mest åpenbare tingen er at den ikke deler opp orddeler:

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

    Noen dokumentasjon på [ og [[ her .

    Aritmetisk test:

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

    «Normal «kommandoer:

    Alt det ovennevnte fungerer som vanlige kommandoer, og if kan ta en hvilken som helst kommando:

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

    Flere kommandoer:

    Eller vi kan bruke flere kommandoer. Innpakking av et sett med kommandoer i ( ... ) kjører dem i subshell, og skaper en midlertidig kopi av skallets tilstand (arbeidskatalog, variabler). for å kjøre noe program midlertidig i en annen 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

    Grupperingskommandoer

    Bash gir to måter å gruppere en liste over kommandoer som skal utføres som en enhet.

    ( list ) Hvis du plasserer en liste med kommandoer mellom parenteser, blir det opprettet et subshell-miljø, og hver av kommandoene i listen blir utført i det subshell. utført i en subshell, forblir variable tilordninger ikke gjeldende etter at subshell er fullført.

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

    { list; } Plassere en liste over kommandoer mellom krøllete parenteser fører til at listen kjøres i gjeldende skallkontekst . Ingen subshell opprettes. Semikolon (eller ny linje) følgende liste er obligatorisk. Kilde

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

    Betingede konstruksjoner

    Enkelt brakett dvs. []
    Til sammenligning ==, !=, <, og > og skal brukes og for numerisk sammenligning eq, ne,lt og gt bør brukes.

    Forbedrede parenteser dvs. [[]]

    I alle eksemplene ovenfor brukte vi bare enkelt parenteser for å legge inn det betingede uttrykket, men bash tillater doble parenteser som fungerer som en forbedret versjon av syntaksen med enkelt parentes.

    Til sammenligning kan ==, !=, <, og > bruke bokstavelig.

    • [ er et synonym for testkommando. Selv om den er innebygd i skallet, skaper den en ny prosess.
    • [[ er en ny forbedret versjon av den, som er et nøkkelord, ikke et program .
    • [[ forstås av Korn og Bash.

    Kilde

    Legg igjen en kommentar

    Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *