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
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
[[ … ]]
i bash 2.02.((…))
lades till i bash 2.0.||
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).[[ … ]]
eller$((…))
är bara för gruppering.