Flere logiske operatorer, ((A || B) & & C), og “ syntaksfeil nær uventet token ”

Jeg jobber med Bash 3, og jeg prøver å danne en betinget. I C / C ++ er det dødt enkelt: ((A || B) && C). I Bash viste det seg ikke slik (jeg tror Git-forfatterne må ha bidratt med denne koden før de gikk videre til andre anstrengelser).

Dette fungerer ikke. Merk at <0 or 1> ikke er en streng bokstavelig; det betyr 0 eller 1 (kommer vanligvis fra grep -i).

A=<0 or 1> B=<0 or 1> C=<0 or 1> if [ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eqe "0" ]; then ... fi 

Det resulterer i:

line 322: syntax error near unexpected token `[[" 

Jeg prøvde da:

A=<0 or 1> B=<0 or 1> C=<0 or 1> if [ ([ "$A" -eq "0" ]) || ([ "$B" -ne "0" ]) ] && [ "$C" -eq "0" ]; then ... fi 

det resulterer i:

line 322: syntax error near unexpected token `[[" 

En del av problemet er søkeresultatene er de trivielle eksemplene, og ikke de mer komplekse eksemplene med sammensatte betingelser.

Hvordan utfører jeg en enkel ((A || B) && C) i Bash?


Jeg er klar til å bare rulle den ut og gjenta de samme kommandoene i flere blokker:

A=<0 or 1> B=<0 or 1> C=<0 or 1> if [ "$A" -eq "0" ] && [ "$C" -eq "0" ]; then ... elif [ "$B" -ne "0" ] && [ "$C" -eq "0" ]; then ... fi 

Svar

Syntaksen til bash er ikke C-lignende, selv om en liten del av den er inspirert av C. Du kan ikke bare prøve å skrive C-kode og forvente at den skal fungere.

Hovedpoenget med et skall er å kjøre kommandoer. Kommandoen med åpen brakett [ er en kommando som utfører en enkelt test¹. Du kan til og med skrive det som test (uten den endelige lukkebraketten). || og && operatørene er skalloperatorer, de kombinerer kommandoer , ikke tester.

Så når du skriver

[ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eq "0" ] 

det blir analysert som

[ [ "$A" -eq "0" ] || [ "$B" -ne "0" ] ] && [ "$C" -eq "0" ] 

som er det samme som

test [ "$A" -eq "0" || test "$B" -ne "0" ] && test "$C" -eq "0" 

Legg merke til de ubalanserte parentesene? Ja, det er ikke bra. Forsøket ditt med parentes har det samme problemet: falske parenteser.

Syntaksen til gruppekommandoer sammen er parenteser. Måten klammeparenteser blir analysert på, krever en fullstendig kommando foran dem, så du må avslutte kommandoen i klammeparentesene med en ny linje eller semikolon.

if { [ "$A" -eq "0" ] || [ "$B" -ne "0" ]; } && [ "$C" -eq "0" ]; then … 

Der «er en alternativ måte å bruke doble parenteser. I motsetning til enkelt parenteser, er dobbel parentes spesiell skallsyntaks. De avgrenser betingede uttrykk . Inne i doble parenteser kan du bruke parenteser og operatorer som && og ||. Siden de dobbelte parentesene er skallsyntaks, vet skallet at når disse operatørene er innenfor parentes, er de «en del av den betingede uttrykkssyntaksen, ikke en del av den vanlige skallkommandosyntaks.»

Hvis alle testene dine er numeriske, er det enda en måte som avgrenser artihmetiske uttrykk . Aritmetiske uttrykk utfører heltallberegninger med en veldig C-lignende syntaks.

if (((A == 0 || B != 0) && C == 0)); then … 

Du kan finne min bash-brakett primer nyttig.

[ kan brukes i vanlig sh. [[ og (( er spesifikke for bash (og ksh og zsh).

¹ Det kan også kombinere flere tester med boolske operatører, men dette er tungvint å bruke og har subtile fallgruver, så jeg vil ikke forklare det.

Kommentarer

  • Takk Giles.Holder dette for Bash 2 til Bash 4? Jeg trenger noe mild portabilitet, men Kusalananda nevnte noen ting jeg ikke gjorde er bærbare (betyr det at det jeg gjør er ikke bærbar?). Her betyr mildt Bash 2 til Bash 4.
  • @jww [[ … ]] i bash 2.02. ((…)) ble lagt til i bash 2.0.
  • @Giles – takk. Bash 2 er sannsynligvis latterlig for de fleste. Det er på grunn av vår styring og uvillighet å forlate eldre plattformer. Bash 2 og GCC 3 dukker opp på Fedora 1-testing. Vi vil la en eldre plattform gå av og til, men det må være grunner til det. Vi gjør ikke ‘ ikke abonner på retningslinjene for forlatelse av Apple, Microsoft, Mozilla osv.
  • @jww Vennligst forstå at forrang for || og && endre fra [ til [[ og ((. Lik forrang i [ (bruk parentes for å kontrollere rekkefølgen på evalueringen), men & & har høyere prioritet i [[ og (( enn || (som i C).
  • @Roland Nei, parentes i [[ … ]] eller $((…)) er bare for gruppering.

Svar

Bruk [[:

if [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]; then ... 

Hvis du foretrekker [, fungerer følgende:

if [ \( "$A" -eq "0" -o "$B" -ne "0" \) -a "$C" -eq "0" ]; then ... 

Kommentarer

  • Takk, Stephen. I mitt tilfelle brukte jeg ditt første eksempel thusly … regex = ‘ ^ [0-9] + $ ‘ length = $ {#datearg} hvis! [[$ datearg = ~ $ regex & & $ lengde -ekv 8]]; deretter ekko » feil: Ikke et antall lenth av 8 «; fi

Svar

Bruk -o -operatøren i stedet for nestet ||. Du kan også bruke -a for å erstatte & & om nødvendig i dine andre uttalelser .

 EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true if [ "$A" -eq "0" -o "$B" -ne "0" ] && [ "$C" -eq "0" ]; then echo success;fi 

Kommentarer

  • Takk. Jeg kom over -a og -o, men jeg eksperimenterte ikke med dem. Noen på Stack Overflow sa at de skulle unngå det. Jeg var for det meste enig med dem siden skriptet er mindre lesbart å bruke dem.
  • … men mer bærbart.
  • @Kusalananda – Takk for head-ups for bærbarhet. Skriptet må kjøre på systemer med Bash 2 til Bash 4. Vil [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]] ha problemer med dem?
  • Det vil ikke være noen problemer så lenge du holder deg til bash eller et annet skall som forstår [[ ... ]].
  • Undersiden av å bruke -a er at den ikke ‘ t kortslutning hvis det første uttrykket er feil. Så hvis du stoler på kortslutning for å forhindre at et potensielt dødelig 2. uttrykk kjører, kan du ikke bruke -a. Eksempel: pastebin.com/zM77TXW1

Legg igjen en kommentar

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