Mehrere logische Operatoren ((A || B) && C) und "Syntaxfehler in der Nähe von unerwartetem Token"

24

Ich arbeite mit Bash 3 und versuche, eine Bedingung zu bilden. In C / C ++, ihre Toten einfach: ((A || B) && C). In Bash ist das nicht der Fall (ich denke, die Git-Autoren müssen diesen Code beigesteuert haben, bevor sie sich anderen Bestrebungen widmen).

Das funktioniert nicht. Beachten Sie, dass dies <0 or 1>kein String-Literal ist. es bedeutet eine 0 oder 1 (kommt im Allgemeinen von 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

Es fuehrt zu:

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

Ich habe dann versucht:

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

es fuehrt zu:

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

Ein Teil des Problems besteht darin, dass die Suchergebnisse die trivialen Beispiele und nicht die komplexeren Beispiele mit zusammengesetzten Bedingungen sind.

Wie führe ich eine einfache ((A || B) && C)in Bash?


Ich bin bereit, es einfach abzuwickeln und die gleichen Befehle in mehreren Blöcken zu wiederholen:

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

quelle

Antworten:

42

Die Syntax von bash ist nicht C-artig, auch wenn ein kleiner Teil davon von C inspiriert ist. Sie können nicht einfach versuchen, C-Code zu schreiben und erwarten, dass er funktioniert.

Der Hauptpunkt einer Shell ist die Ausführung von Befehlen. Der Befehl mit offener Klammer [ist ein Befehl, der einen einzelnen Test¹ ausführt. Sie können es sogar als test(ohne die letzte schließende Klammer) schreiben . Die Operatoren ||und &&sind Shell-Operatoren, sie kombinieren Befehle , keine Tests.

Also wenn du schreibst

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

das wird analysiert als

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

das ist das gleiche wie

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

Beachten Sie die unsymmetrischen Klammern? Ja, das ist nicht gut. Ihr Versuch mit Klammern hat das gleiche Problem: falsche Klammern.

Die Syntax zum Gruppieren von Befehlen ist geschweifte Klammern. Die Art und Weise, wie geschweifte Klammern analysiert werden, erfordert einen vollständigen Befehl, sodass Sie den Befehl in geschweiften Klammern mit einem Zeilenumbruch oder einem Semikolon beenden müssen.

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

Alternativ können Sie auch doppelte Klammern verwenden. Im Gegensatz zu einfachen Klammern handelt es sich bei doppelten Klammern um eine spezielle Shell-Syntax. Sie begrenzen bedingte Ausdrücke . In doppelten Klammern können Sie Klammern und Operatoren wie &&und verwenden ||. Da die doppelten Klammern Shell-Syntax sind, weiß die Shell, dass diese Operatoren, wenn sie sich in Klammern befinden, Teil der Bedingungsausdruckssyntax sind und nicht Teil der normalen Shell-Befehlssyntax.

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

Wenn alle Ihre Tests numerisch sind, gibt es noch einen anderen Weg, der artihmetische Ausdrücke abgrenzt . Arithmetische Ausdrücke führen Ganzzahlberechnungen mit einer sehr C-ähnlichen Syntax durch.

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

Möglicherweise ist mein Bash Bracket Primer hilfreich.

[kann in normalem sh verwendet werden. [[und ((sind spezifisch für bash (und ksh und zsh).

¹ Es kann auch mehrere Tests mit Booleschen Operatoren kombinieren, aber die Verwendung ist umständlich und birgt subtile Fallstricke, sodass ich es nicht erläutern werde.

Gilles 'SO - hör auf böse zu sein'
quelle
Danke Giles. Gilt dies für Bash 2 bis Bash 4? Ich brauche eine gewisse Portabilität, aber Kusalananda erwähnte, dass einige Dinge, die ich nicht getan habe, portabel sind (bedeutet das, dass das, was ich tue, nicht portabel ist?). Hier bedeutet mild Bash 2 bis Bash 4.
@jww [[ … ]]wurde in Bash 2.02 hinzugefügt. ((…))wurde in Bash 2.0 hinzugefügt.
Gilles 'SO- hör auf böse zu sein'
@ Giles - danke. Bash 2 ist für die meisten wahrscheinlich lächerlich. Dies liegt an unserer Governance und an der mangelnden Bereitschaft, ältere Plattformen aufzugeben. Bash 2 und GCC 3 tauchen bei unseren Fedora 1-Tests auf. Wir werden gelegentlich eine ältere Plattform freigeben, aber es muss Gründe dafür geben. Wir abonnieren keine Apple-, Microsoft-, Mozilla- usw. Richtlinie für Abbruchprodukte.
@jww Bitte haben Sie Verständnis dafür, dass sich der Vorrang von ||und &&von [zu [[und ändert ((. Gleiche Priorität in [(verwenden Sie Klammern, um die Reihenfolge der Auswertung zu steuern), aber && hat eine höhere Priorität in [[und ((als ||(wie in C).
6

Verwendung [[:

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

Wenn Sie es vorziehen [, funktioniert Folgendes:

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

Verwenden Sie den -oOperator anstelle Ihres verschachtelten ||. Sie können auch -a&& ersetzen, falls dies in Ihren anderen Anweisungen erforderlich ist.

   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
Zachary Brady
quelle
Vielen Dank. Ich bin auf das -aund gestoßen -o, aber ich habe nicht mit ihnen experimentiert. Jemand auf Stack Overflow sagte, es zu vermeiden. Ich stimmte ihnen größtenteils zu, da das Skript mit ihnen weniger lesbar ist.
... aber tragbarer.
Kusalananda
@ Kusalananda - Vielen Dank für die Hinweise zur Portabilität. Das Skript muss auf Systemen mit Bash 2 bis Bash 4 ausgeführt werden. [[ ( "$A" -eq "0" || "$B" -ne "0" ) && "$C" -eq "0" ]]Haben Sie Probleme damit?
Es wird keine Probleme geben, solange Sie sich an basheine andere Shell halten, die es versteht [[ ... ]].
Kusalananda