Gibt es in bash eine Möglichkeit, kürzere (if oder) Aussagen zu machen?

7
if [ foo = bar -o foo = car -o foo = jar -o foo = foo ]
  then
    echo yes
fi

Um die Organisation zu verbessern, möchte ich versuchen, sie einer Liste zuzuordnen, z

if [ foo = {bar,car,jar,foo} ] ; then

Offensichtlich hat die Methode zur Erweiterung der geschweiften Klammer nicht funktioniert, sonst wäre ich nicht hier! Aber ich würde gerne wissen, ob so etwas überhaupt möglich ist.

TuxForLife
quelle

Antworten:

9

Ich schlage vor, Fallstruktur zu verwenden. Etwas wie:

case $foo in
    foo|car|bar|jar) echo yes ;;
    *) ;;
esac

Wenn Sie möchten, können Sie einen Befehl hinzufügen , zwischen *)und ;;ausgeführt werden , wenn $foonicht übereinstimmt foo, car, bar, oder jar. Zum Beispiel könnten Sie eine Nachricht mit echo 'wrong input'oder so etwas drucken .

Sergiy Kolodyazhnyy
quelle
Ich sehe, wo die Verwirrung war, da es meine Art war, sie zu formulieren, seit ich "foo eintippen" sagte. Ich habe meinen Wortlaut etwas geändert. Ich möchte nicht buchstäblich "foo" als Eingabe eingeben, sondern meinen Code ein bisschen besser organisieren. Tut mir leid, aber gut gedacht
TuxForLife
OK, ich habe es endlich bearbeitet, damit es beim Schießen funktioniert. Aus irgendeinem Grund habe ich Ihre Frage mehrmals falsch verstanden. -_- Jetzt wird die Variable foo getestet, wenn sie gleich foo oder Auto oder Bar oder Glas ist, und es wird ja wiedergegeben, wenn dies zutrifft. Sie können die Groß- / Kleinschreibung durch einen beliebigen Platz ersetzen, anstatt zu brechen, z. B. "falsche Eingabe" oder
ähnliches
Oh ok, ich dachte, Fall war nur für manuelle Eingaben, aber ich verstehe es jetzt, danke!
TuxForLife
1
@TuxForLife selectist (normalerweise) für die manuelle Eingabe gedacht , also haben Sie vielleicht darüber nachgedacht. Übrigens, wenn Sie keine Operation ausführen müssen, wenn keine Übereinstimmung vorliegt, können Sie die *)Zeile vollständig entfernen . dann könnten Sie das Ganze in eine einzige Zeile setzen, ohne die Lesbarkeit wirklich zu beeinträchtigen.
Eliah Kagan
1
@EliahKagan nah, in meinem früheren Beitrag hatte ich case $input inLeitung, deshalb denkt OP, dass es nur für Eingaben ist.
Sergiy Kolodyazhnyy
14

Es sieht aus wie ein Fehler in Ihrem Code, aber wir können ihn sehr klein umgestalten ...

Wenn Sie Ihren Ausdruck wirklich mit geschrieben hätten [ foo = bar -o foo = car -o foo = jar -o foo = foo ], hätte er vereinfacht werden können zu:

echo yes

Dieser Test ist immer wahr, weil er foo = fooimmer wahr ist, was wiederum darauf zurückzuführen ist, dass die Literalzeichenfolge fooimmer dieselbe ist wie sie selbst.

Obwohl Sie klargestellt haben, dass diese Syntax nicht in dem Code enthalten ist, den Sie wirklich verwendet haben, habe ich diesen einleitenden Abschnitt beibehalten, da dies ein häufiger Fehler ist, insbesondere bei Anfängern. Der Versuch, den Wert auf foodiese Weise zu überprüfen, funktioniert in keiner Bourne-Shell - um den Wert einer Variablen mit zu untersuchen [, müssen Sie eine Parametererweiterung mit durchführen $(siehe unten). Dieser spezielle Aspekt des Problems ist nicht Shell-spezifisch. Hier ist zum Beispiel ein Test in 7 Bourne-Muscheln .

Korrigieren des Originalcodes

Vielleicht wollten Sie stattdessen den Wert der fooVariablen (dh ihren Inhalt und nicht ihren Namen) anhand dieser Zeichenfolgen überprüfen . In diesem Fall würde Ihr ursprünglicher Code wie folgt aussehen:

if [ "$foo" = bar -o "$foo" = car -o "$foo" = jar -o "$foo" = foo ]; then
    echo yes
fi

Dies kann mit dem Kurzschlussbediener leicht verkürzt &&werden:

[ "$foo" = bar -o "$foo" = car -o "$foo" = jar -o "$foo" = foo ] && echo yes

Aber es ist immer noch länger und komplizierter und wiederholt sich, als Sie wahrscheinlich bevorzugen. Also weiter ...

Kürzere Formulare über Pattern Matching

Sie können den in der [[Einrichtung integrierten Operator für den Abgleich regulärer Ausdrücke verwenden:

[[ $foo =~ ^(bar|car|jar|foo)$ ]] && echo yes

Während die Verwendung casewahrscheinlich der häufigste Weg ist, ist [[mit =~:

  • die gleiche Länge wie eine kompakte Verwendung von case(siehe unten)
  • Wahrscheinlich wahrer als die Logik Ihres ursprünglichen Codes, indem er verwendet [[, der ähnlich wie ein Testausdruck funktioniert.

Vielleicht bevorzugen Sie das also. Es ist die Methode, die ich wahrscheinlich verwenden würde, um dies in Bash zu tun.

Wenn Sie so etwas tun wollten, aber mit grepstatt [[mit übereinstimmen =~, ist dies ähnlich:

grep -Eq '^(bar|car|jar|foo)$' <<< "$foo" && echo yes

Oder dieses:

grep -Fqe{bar,car,jar,foo} <<< "$foo" && echo yes

Ein Einzeiler mit case

Wie Serg sagt , kann unter Verwendung vereinfacht werden casestatt ifund [. Das ist der klassische und wahrscheinlich häufigste Weg.

Die Methode in Sergs Antwort funktioniert einwandfrei, aber für den bestimmten Code, den Sie umgestalten, können wir etwas Einfacheres schreiben, indem wir nur ein Muster für verwenden case. Am Ende muss kein Allround- *)Match stattfinden. Wenn die Zeichenfolge keinem der Muster entspricht, durchläuft die Steuerung einfach das case-Konstrukt und es werden keine Befehle ausgeführt, was Ihrem ifKonstrukt mit no entspricht else.

case $foo in bar|car|jar|foo) echo yes;; esac

Das macht es kurz und einfach genug, um leicht in eine einzelne Zeile zu passen - und lesbar zu sein (was genau das zu sein scheint, wonach Sie suchen). Diese Methode kann auch auf andere Bourne-Shells als Bash übertragen werden.

Eliah Kagan
quelle
Wow beeindruckende Antwort. Ich mag das, [[ $foo =~ ^(bar|car|jar|foo)$ ]] && echo yesaber so wie ich es verstehe und wie Sie es formuliert haben, wäre es nicht für andere Muscheln tragbar? Glaubst du wirklich, dass es ein Fehler ist? Es kann einfach kein Merkmal der if-Aussage sein, die ich denke. Ich mag die Fallmethode, die Serg angesprochen hat, sehr. Wäre ich ein Idiot, wenn ich Ihre in die akzeptierte Antwort ändern würde? Ich bin sicher, wir können uns alle einig sein, dass Sie mit mehr Details und Methoden ein bisschen mehr darüber nachdenken, aber ich würde mich schrecklich schlecht fühlen, wenn ich ihn "nicht belohne". Serg, wenn du das liest, gib mir bitte deine 2 ¢
TuxForLife
1
Kein Fehler, aber wahrscheinlich ein Bashismus. Fühlen Sie sich frei, die akzeptierte Antwort auf Eliahs zu ändern, da ich zustimmen würde, dass seine wahrscheinlich prägnanter ist
Sergiy Kolodyazhnyy
@TuxForLife Der Fehler befindet sich im Originalcode, den Sie geschrieben haben, nicht in der Shell (ich werde ihn zur Verdeutlichung bearbeiten). Sie möchten den Wert von überprüfen foo, haben jedoch foo = foodie Literalzeichenfolge foound nicht fooden Wert auf beiden Seiten von =. • Sie haben Recht, dass [[es nicht auf alle Bourne-Shells portierbar ist (dh es sollte nicht in einem #!/bin/shSkript verwendet werden), obwohl einige andere es gerne kshhaben. • Es ist zwar in Ordnung, eine Antwort zu akzeptieren und eine andere zu akzeptieren, aber ich glaube nicht, dass Sie dies in diesem Fall tun müssen. Sergs Antwort ist auch richtig und gut. Vielleicht möchten Sie auch abwarten, welche anderen Antworten veröffentlicht werden.
Eliah Kagan
Ja, der wörtliche String Foo sollte die Dinge einfacher machen, aber ich denke, er hat ihn verwirrender gemacht. Großartig, ich werde es bei Sergs Antwort belassen. Prost!
TuxForLife
1
@TuxForLife Ah, ich verstehe. Macht Sinn. Ich habe den einleitenden Abschnitt gehalten , da das Schreiben [Vergleiche mit unbeabsichtigt nicht expandierten Variablen ist eigentlich eine etwas gemeinsam Shell - Scripting Fehler (obwohl, wie Sie geklärt haben, waren Sie eigentlich nicht , dass Fehler im Code , dass Sie wurden ausgeführt wird ). Aber ich habe es umformuliert, um dies widerzuspiegeln.
Eliah Kagan
3

Noch kürzer:

[[ $foo =~ ^([bcj]ar|foo)$ ]] && echo yes

Auf diese Weise $foowird abgeglichen gegen einen regulären Ausdruck entweder beginnend mit b, coder jgefolgt von aroder genau passend foo.

Dies verwendet den &&Operator als Kurzschluss, um echo yesnur ausgeführt zu werden, wenn der [[ $foo =~ ^([bcj]ar|foo)$ ]]Ausdruck als wahr ausgewertet wird.

Wenn dies zu kompakt ist, um problemlos gelesen zu werden, können Sie immer noch das vollständige if-Konstrukt verwenden, das genau das Gleiche tut, aber besser lesbar ist:

if [[ $foo =~ ^([bcj]ar|foo)$ ]]
then
    echo yes
fi
kos
quelle
@EliahKagan Ich habe Ihren vollständigen Kommentar nicht erhalten, aber die Nachricht trotzdem verstanden. Sie haben in der Tat Recht, danke.
Kos
@EliahKagan Du hast wieder Recht, die Verwendung von Ankern hätte es richtig machen sollen. Vielen Dank auch für den Kurzschlussvorschlag, es ist in der Tat eine große Verbesserung, da faule Bewertungen, obwohl nicht gut lesbar, meiner Meinung nach immer zu fördern sind. Ich wusste nicht, dass ich hier einen hätte verwenden können.
Kos
Sehr beeindruckend, ich mag es wirklich, wenn der [bcj] Teil den ähnlichen "ar" Teil nutzt.
TuxForLife
1
@TuxForLife Altoughit, es ist in realen Fällen vielleicht nicht sehr nützlich, aber wenn Sie zufällig gegen einen regulären Ausdruck antreten können, ist dies der richtige Weg.
Kos