Einfache logische Operatoren in Bash

256

Ich habe ein paar Variablen und möchte die folgende Bedingung überprüfen (in Worten geschrieben, dann mein fehlgeschlagener Versuch, Bash-Skripte zu erstellen):

if varA EQUALS 1 AND ( varB EQUALS "t1" OR varB EQUALS "t2" ) then 

do something

done.

Und bei meinem fehlgeschlagenen Versuch kam ich auf Folgendes:

if (($varA == 1)) && ( (($varB == "t1")) || (($varC == "t2")) ); 
  then
    scale=0.05
  fi
Amit
quelle

Antworten:

682

Was Sie geschrieben haben, funktioniert eigentlich fast (es würde funktionieren, wenn alle Variablen Zahlen wären), aber es ist überhaupt keine Redewendung.

  • (…)Klammern geben eine Unterschale an . Was in ihnen steckt, ist kein Ausdruck wie in vielen anderen Sprachen. Es ist eine Liste von Befehlen (genau wie außerhalb von Klammern). Diese Befehle werden in einem separaten Unterprozess ausgeführt, sodass Umleitungen, Zuweisungen usw., die in Klammern ausgeführt werden, außerhalb der Klammern keine Auswirkungen haben.
    • Mit einem führenden Dollarzeichen, $(…)ist ein Befehl Substitution : gibt es einen Befehl in den Klammern, und die Ausgabe des Befehls wird als Teil der Kommandozeile (nach Verlängerung Erweiterungen , es sei denn die Substitution zwischen doppelte Anführungszeichen, aber das ist eine andere Geschichte ) .
  • { … }Klammern sind insofern wie Klammern, als sie Befehle gruppieren, aber sie beeinflussen nur das Parsen, nicht das Gruppieren. Das Programm x=2; { x=4; }; echo $xdruckt 4, während 2 x=2; (x=4); echo $xgedruckt wird. (Auch geschweifte Klammern erfordern vor dem Schließen Leerzeichen und ein Semikolon, Klammern dagegen nicht. Das ist nur eine Syntax-Eigenart.)
    • Mit einem führenden Dollarzeichen ${VAR}wird eine Parametererweiterung auf den Wert einer Variablen mit möglichen zusätzlichen Transformationen erweitert.
  • ((…))Doppelte Klammern umgeben eine arithmetische Anweisung , dh eine Berechnung auf ganzen Zahlen, mit einer Syntax, die anderen Programmiersprachen ähnelt. Diese Syntax wird hauptsächlich für Zuweisungen und in Bedingungen verwendet.
    • Dieselbe Syntax wird in arithmetischen Ausdrücken verwendet $((…)), die auf den ganzzahligen Wert des Ausdrucks erweitert werden.
  • [[ … ]]doppelte Klammern umgeben bedingte Ausdrücke . Bedingte Ausdrücke basieren hauptsächlich auf Operatoren , um -n $variablezu testen, ob eine Variable leer ist, und um -e $filezu testen, ob eine Datei vorhanden ist. Darüber hinaus gibt es String - Gleichheitsoperator: "$string1" == "$string2"(Vorsicht , dass die rechte Seite ein Muster, zB [[ $foo == a* ]]Tests , wenn $foobeginnt mit awährend [[ $foo == "a*" ]]Tests , wenn $foogenau a*), und die vertrauten !, &&und ||Operatoren für die Negation, Konjunktion und Disjunktion sowie Klammern für die Gruppierung. Beachten Sie, dass Sie benötigen einen Raum um jeden Operator (zB [[ "$x" == "$y" ]]nicht [[ "$x"=="$y" ]]), und einen Raum oder einen Charakter wie ;sowohl innerhalb als auch außerhalb der Klammern (zB [[ -n $foo ]]nicht[[-n $foo]]).
  • [ … ]Einzelne Klammern sind eine alternative Form von bedingten Ausdrücken mit mehr Macken (aber älter und portabler). Schreibe vorerst keine; Machen Sie sich Sorgen um sie, wenn Sie Skripte finden, die sie enthalten.

Dies ist die idiomatische Art, Ihren Test in Bash zu schreiben:

if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then

Wenn Sie eine Portabilität auf andere Shells benötigen, ist dies der richtige Weg (beachten Sie die zusätzlichen Anführungszeichen und die separaten Sätze von Klammern um jeden einzelnen Test sowie die Verwendung des herkömmlichen =Operators anstelle der ksh / bash / zsh- ==Variante):

if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then
Gilles 'SO - hör auf böse zu sein'
quelle
31
Toller Beitrag, die Klammerzusammenfassung ist einfach ideal.
KomodoDave
10
Es ist besser, ==den Vergleich von der Zuweisung einer Variablen (die auch ist =) zu unterscheiden
Will Sheppard
1
Oh, ich meinte einzelne (runde) Klammern, entschuldige die Verwirrung. Diejenigen in [[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]]starten keinen Unterprozess, obwohl der erste Aufzählungspunkt ausdrücklich sagt: "Was in [Klammern] steht, ist kein Ausdruck wie in vielen anderen Sprachen" - aber es ist sicherlich hier! Das ist wahrscheinlich für den erfahrenen Bash-Zauberer offensichtlich, aber nicht einmal für mich sofort. Die Verwirrung kann entstehen , weil einzelne Klammern können in einer verwendete ifAussage, nur nicht in Ausdrücke in doppelten eckigen Klammern.
Peter - Reinstate Monica
2
@protagonist ==ist nicht wirklich "idiomatischer" als =. Sie haben die gleiche Bedeutung, sind jedoch ==eine ksh-Variante, die auch in bash und zsh verfügbar ist, während sie =portabel sind. Es gibt keinen wirklichen Vorteil bei der Verwendung ==und es funktioniert nicht in normalem sh.
Gilles 'SO - hör auf böse zu sein'
2
@ WillSheppard Es gibt bereits viele andere Unterschiede zwischen Vergleich und Zuordnung: Der Vergleich erfolgt in Klammern, die Zuordnung nicht; Der Vergleich hat Leerzeichen um den Operator, die Zuweisung nicht. Die Zuweisung hat links einen Variablennamen, der Vergleich hat nur selten etwas, das links wie ein Variablenname aussieht, und Sie können und sollten trotzdem Anführungszeichen setzen. Sie können nach ==innen schreiben [[ … ]], wenn Sie möchten, haben aber =den Vorteil , dass Sie auch darin arbeiten. [ … ]Ich empfehle daher, sich nicht daran zu gewöhnen, ==überhaupt etwas zu verwenden.
Gilles 'SO - hör auf böse zu sein'
34

sehr nah

if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]]; 
  then 
    scale=0.05
  fi

sollte arbeiten.

Brechen sie ab

[[ $varA -eq 1 ]] 

ist ein ganzzahliger Vergleich, bei dem as

$varB == 't1'

ist ein Stringvergleich. Ansonsten gruppiere ich die Vergleiche nur richtig.

Doppelte eckige Klammern begrenzen einen bedingten Ausdruck. Und ich finde, dass das Folgende eine gute Lektüre zu diesem Thema ist: "(IBM) Test entmystifizieren, [, [[, ((und wenn-dann-sonst")

Matchew
quelle
Nur um sicher zu gehen: Das Zitieren 't1'ist unnötig, oder? Denn im Gegensatz zu arithmetischen Anweisungen in doppelten Klammern, wo t1eine Variable wäre, ist t1ein bedingter Ausdruck in doppelten Klammern nur eine Literalzeichenfolge. Dh [[ $varB == 't1' ]]ist genau das gleiche wie [[ $varB == t1 ]], oder?
Peter - Stellen Sie Monica
6

Eine sehr tragbare Version (sogar für ältere Bourne-Shell):

if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then    do-something
fi

Dies hat die zusätzliche Qualität, dass höchstens ein Teilprozess ausgeführt wird (was der Prozess ist [), unabhängig vom Schalengeschmack.

Ersetzen Sie =durch, -eqwenn Variablen numerische Werte enthalten, z

  • 3 -eq 03 ist wahr, aber
  • 3 = 03ist falsch. (Stringvergleich)
JP Tosoni
quelle
3

Hier ist der Code für die Kurzversion der if-then-else-Anweisung:

( [ $a -eq 1 ] || [ $b -eq 2 ] ) && echo "ok" || echo "nok"

Beachten Sie Folgendes:

  1. ||und &&Operanden innerhalb, wenn Bedingung (dh zwischen runden Klammern) logische Operanden (oder / und) sind

  2. ||und &&Operanden außerhalb, wenn Bedingung dann / sonst bedeutet

Praktisch heißt es:

wenn (a = 1 oder b = 2) dann "ok" sonst "nok"

tlc
quelle
Klammern ( ... )erstellen eine Unterschale. Möglicherweise möchten Sie { ... }stattdessen geschweifte Klammern verwenden. Jeder in einer Subshell erstellte Status ist im Aufrufer nicht sichtbar.
Clint Pachl
0
if ([ $NUM1 == 1 ] || [ $NUM2 == 1 ]) && [ -z "$STR" ]
then
    echo STR is empty but should have a value.
fi
AlikElzin-Kilaka
quelle