Flera logiska operatorer, ((A || B) & & C) och “ syntaxfel nära oväntat token ”

Jag jobbar med Bash 3 och jag försöker att bilda en villkorad. I C / C ++ är det enkelt: ((A || B) && C). I Bash visar det sig inte så (jag tror att Git-författarna måste ha bidragit med den här koden innan de gick vidare till andra ansträngningar).

Detta fungerar inte. Observera att <0 or 1> inte är en stränglitterär; det betyder ett 0 eller 1 (kommer i allmänhet från 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 resulterar i:

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

Jag försökte sedan:

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 resulterar i:

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

En del av problemet är sökresultaten är de triviala exemplen och inte de mer komplexa exemplen med sammansatta villkor.

Hur gör jag en enkel ((A || B) && C) i Bash?


Jag är redo att bara rulla ur den och upprepa samma kommandon i flera block:

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

Syntaxen för bash är inte C-liknande, även om en liten del av den är inspirerad av C. Du kan inte helt enkelt försöka skriva C-kod och förvänta dig att den fungerar.

Huvudpunkten för ett skal är att köra kommandon. Kommandot med öppen parentes [ är ett kommando som utför ett enda test¹. Du kan även skriva det som test (utan den slutliga stängningsfästet). || och && operatörerna är skaloperatörer, de kombinerar kommandon , inte test.

Så när du skriver

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

att ”tolkas som

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

vilket är samma som

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

Lägg märke till de obalanserade parenteserna? Ja, det är inte bra. Ditt försök med parenteser har samma problem: falska parenteser.

Syntaxen för gruppkommandon tillsammans är parenteser. Sättet att parenteser analyseras kräver ett fullständigt kommando före dem, så du måste avsluta kommandot inuti parentesen med en ny linje eller semikolon.

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

Där ”ett alternativt sätt att använda dubbla parenteser. Till skillnad från enstaka parenteser är dubbla parenteser speciell skalsyntax. De avgränsar villkorliga uttryck . Inom dubbla parenteser kan du använda parenteser och operatorer som && och ||. Eftersom de dubbla parenteserna är skal-syntax, vet skalet att när dessa operatörer är inom parentes är de en del av det villkorliga uttryckssyntaxen, inte en del av den vanliga skalkommandosyntaxen.

Om alla dina tester är numeriska, finns det ännu ett sätt som avgränsar artihmetiska uttryck . Aritmetiska uttryck utför heltalberäkningar med en mycket C-liknande syntax.

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

Du kan hitta min bash-parentes primer användbart.

[ kan användas i vanlig sh. [[ och (( är specifika för bash (och ksh och zsh).

¹ Det kan också kombinera flera tester med booleska operatörer, men det är besvärligt att använda och har subtila fallgropar så jag kommer inte att förklara det.

Kommentarer

  • Tack Giles. Går det här för Bash 2 till Bash 4? Jag behöver något lätt bärbarhet, men Kusalananda nämnde några saker som jag inte gjorde är bärbara (innebär det att det jag gör är inte bärbar?). Här betyder mildt Bash 2 till Bash 4.
  • @jww [[ … ]] i bash 2.02. ((…)) lades till i bash 2.0.
  • @Giles – tack. Bash 2 är förmodligen skrattretande för de flesta. Det beror på vår styrning och ovilja att överge äldre plattformar. Bash 2 och GCC 3 dyker upp i vår Fedora 1-testning. Vi låter en äldre plattform gå ibland, men det måste finnas skäl till det. Vi gör inte ’ t prenumerera på Apple, Microsoft, Mozilla, etc policy för övergivande av programvara.
  • @jww Förstå att företräde för || och && ändra från [ till [[ och ((. Lika företräde i [ (använd parentes för att kontrollera ordningen för utvärderingen), men & & har högre företräde i [[ och (( än || (som i C).
  • @Roland Nej, parenteser i [[ … ]] eller $((…)) är bara för gruppering.

Svar

Använd [[:

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

Om du föredrar [, fungerar följande:

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

Kommentarer

  • Tack, Stephen. I mitt fall använde jag ditt första exempel thusly … regex = ’ ^ [0-9] + $ ’ length = $ {#datearg} om! [[$ datearg = ~ $ regex & & $ längd -ekv 8]]; sedan echo ” fel: Inte ett antal lenth av 8 ”; fi

Svar

Använd operatorn -o istället för din kapslad ||. Du kan också använda -a för att ersätta & & om det behövs i dina andra uttalanden .

 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

  • Tack. Jag stötte på -a och -o, men jag experimenterade inte med dem. Någon på Stack Overflow sa att han skulle undvika det. Jag gick mest med dem eftersom manuset är mindre läsbart med dem.
  • … men mer bärbart.
  • @Kusalananda – Tack för head-ups för bärbarhet. Skriptet måste köras på system med Bash 2 till Bash 4. Kommer [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]] att ha problem med dem?
  • Det kommer inte att finnas några problem så länge du håller bash eller något annat skal som förstår [[ ... ]].
  • Nedsidan av att använda -a är att den inte ’ t kortslutning om det första uttrycket är falskt. Så om du litar på kortslutning för att förhindra att ett potentiellt dödligt andra uttryck körs, kan du inte använda -a. Exempel: pastebin.com/zM77TXW1

Lämna ett svar

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