409 Stimmen

Wie lassen sich mehrere Bedingungen in einer Shell-If-Anweisung darstellen?

Ich möchte mehrere Bedingungen wie folgt darstellen:

if [ ( $g -eq 1 -a "$c" = "123" ) -o ( $g -eq 2 -a "$c" = "456" ) ]   
then  
    echo abc;  
else  
    echo efg;   
fi  

aber wenn ich das Skript ausführe, zeigt es

Syntaxfehler in Zeile 15: `[' unerwartet,

wobei Zeile 15 diejenige ist, die das "if ...." anzeigt.

Was ist falsch an dieser Bedingung? Ich denke, dass etwas mit den () nicht stimmt.

9 Stimmen

Sie fragen nicht nach Shell-Bedingungen, sondern nach Test-Bedingungen. Der gesamte Ausdruck in Ihrem Beispiel wird von test ([) und nicht von der Shell ausgewertet. Die Shell bewertet nur den Exit-Status von [.

0 Stimmen

0 Stimmen

482voto

Jonathan Leffler Punkte 694013

Klassische Technik (Metazeichen escapen):

if [ \( "$g" -eq 1 -a "$c" = "123" \) -o \( "$g" -eq 2 -a "$c" = "456" \) ]
then echo abc
else echo efg
fi

Ich habe die Verweise auf $g in doppelte Anführungszeichen gesetzt; das ist im Allgemeinen eine gute Praxis. Streng genommen sind die Klammern nicht erforderlich, da die Priorität von -a und -o auch ohne sie korrekt ist.

Beachten Sie, dass die Operatoren -a und -o Teil der POSIX-Spezifikation für test sind, auch bekannt als [, hauptsächlich aus Gründen der Abwärtskompatibilität (da sie beispielsweise ein Teil von test in der 7. Edition von UNIX waren), aber von POSIX ausdrücklich als 'veraltet' gekennzeichnet sind. Bash (siehe bedingte Ausdrücke) scheint die klassische und POSIX-Bedeutung von -a und -o


Mit etwas Vorsicht können Sie den moderneren [[-Operator verwenden, aber beachten Sie, dass die Versionen in Bash und Korn-Shell (beispielsweise) nicht unbedingt identisch sein müssen.

for g in 1 2 3
do
    for c in 123 456 789
    do
        if [[ ( "$g" -eq 1 && "$c" = "123" ) || ( "$g" -eq 2 && "$c" = "456" ) ]]
        then echo "g = $g; c = $c; true"
        else echo "g = $g; c = $c; false"
        fi
    done
done

Beispielablauf unter Verwendung von Bash 3.2.57 auf Mac OS X:

g = 1; c = 123; true
g = 1; c = 456; false
g = 1; c = 789; false
g = 2; c = 123; false
g = 2; c = 456; true
g = 2; c = 789; false
g = 3; c = 123; false
g = 3; c = 456; false
g = 3; c = 789; false

Sie müssen die Variablen in [[ nicht in Anführungszeichen setzen, wie bei [, da es kein separates Kommando ist, so wie es [ ist.


Ist es nicht eine klassische Frage?

Hätte ich gedacht. Es gibt jedoch eine weitere Alternative, nämlich:

if [ "$g" -eq 1 -a "$c" = "123" ] || [ "$g" -eq 2 -a "$c" = "456" ]
then echo abc
else echo efg
fi

Tatsächlich empfehlen die 'portable shell'-Richtlinien für das Werkzeug autoconf oder verwandte Pakete diese Notation - mit '||' und '&&'. Ich denke, man könnte sogar so weit gehen:

if [ "$g" -eq 1 ] && [ "$c" = "123" ]
then echo abc
elif [ "$g" -eq 2 ] && [ "$c" = "456" ]
then echo abc
else echo efg
fi

Bei so trivialen Aktionen wie dem Echo ist das nicht schlecht. Wenn der Aktionsblock, der wiederholt werden soll, mehrere Zeilen lang ist, ist die Wiederholung zu schmerzhaft und eine der früheren Versionen ist vorzuziehen - oder Sie müssen die Aktionen in eine Funktion einbinden, die in den verschiedenen then-Blöcken aufgerufen wird.

13 Stimmen

Es ist gut zu wissen, dass maskierte Klammern funktionieren; als Randnotiz: In diesem speziellen Fall werden die Klammern nicht einmal benötigt, weil -a tatsächlich eine höhere Präzedenz hat als -o (im Gegensatz zu && und || in der Shell - jedoch hat innerhalb von bash [[ ... ]] Bedingungen hat && auch eine höhere Präzedenz als ||). Wenn Sie -a und -o zur maximalen Robustheit und Portabilität vermeiden möchten - wie die POSIX-Man-Seite selbst vorschlägt - könnten Sie auch Unterprozesse zum Gruppieren verwenden: if ([ $g -eq 1 ] && [ "$c" = "123" ]) || ([ $g -eq 2 ] && [ "$c" = "456" ])

0 Stimmen

Gute Lösung, aber ich mag das Formular ohne alle Klammern und Klammern: if test "$g" -eq 1 -a "$c" = "123" || test "$g" -eq 2 -a "$c" = "456"; then ...

0 Stimmen

Ich bin etwas spät dran, aber es ist immer noch ein Top-Suchergebnis, also möchte ich nur darauf hinweisen, dass die Verwendung von && oder || auch bevorzugt wird, da es sich wie Bedingungen in anderen Sprachen verhält und es Ihnen ermöglicht, Bedingungen zu umgehen. Zum Beispiel: if [ -n "${check_inodes}" ] && [ "$(stat -f %i "/foo/bar")" = "$(stat -f %i "/foo/baz")" ]. Wenn Sie es auf diese Weise machen, vermeiden Sie zwei Aufrufe von stat, wenn check_inodes leer ist, während eine größere, komplexe Testbedingung alle Argumente verarbeiten muss, bevor sie ausgeführt wird (was auch zu Fehlern führen kann, wenn Sie dieses Verhalten vergessen).

230voto

Dennis Williamson Punkte 322329

In Bash:

if [[ ( $g == 1 && $c == 123 ) || ( $g == 2 && $c == 456 ) ]]

12 Stimmen

Definitiv der beste Ansatz in bash. Nebenbei bemerkt: In diesem speziellen Fall sind die Klammern nicht einmal erforderlich, weil innerhalb von [[ ... ]] Bedingungen && tatsächlich eine höhere Priorität als || hat - im Gegensatz zu außerhalb solcher Bedingungen.

0 Stimmen

Gibt es eine Möglichkeit herauszufinden, welche Bedingung hier übereinstimmte? War es die in der ersten Klammer oder die andere?

1 Stimmen

@Firelord: Sie müssten die Bedingungen in eine if / else Anweisung trennen und den Code zwischen then und fi in eine Funktion setzen, um Wiederholungen zu vermeiden. In sehr einfachen Fällen könnten Sie eine case Anweisung mit ;& Fallthrough verwenden (in Bash 4).

54voto

sunitha Punkte 1338

Verwenden von /bin/bash funktioniert folgendermaßen:

if [ "$option" = "Y" ] || [ "$option" = "y" ]; then
    echo "Eingegeben: $option"
fi

11voto

orkoden Punkte 17328

Seien Sie vorsichtig, wenn Sie Leerzeichen in Ihren Zeichenfolgenvariablen haben und auf deren Existenz überprüfen. Stellen Sie sicher, dass Sie sie ordnungsgemäß in Anführungszeichen setzen.

if [ ! "${somepath}" ] || [ ! "${otherstring}" ] || [ ! "${barstring}" ] ; then

2 Stimmen

Wenn wir die geschweiften Klammern nach dem Dollarzeichen verwenden!!

9voto

In Bash kannst du die folgende Technik zur Zeichenkettenvergleich verwenden

if [ $var OP "val" ]; then
    echo "Anweisungen"
fi

Beispiel:

var="etwas"
if [ $var != "anderes" ] && [ $var != "alles" ] && [ $var != "allendinge" ]; then
    echo "das wird gedruckt"
else
    echo "das wird nicht gedruckt"
fi

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X