Wie man etwas bedingt macht, wenn ein Befehl erfolgreich war oder fehlgeschlagen ist


283

Wie kann ich so etwas in bash machen?

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi

Antworten:


359

Wie man etwas bedingt macht, wenn ein Befehl erfolgreich war oder fehlgeschlagen ist

Genau das macht die ifAussage von bash :

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi

Hinzufügen von Informationen aus Kommentaren: In diesem Fall müssen Sie nicht die Syntax [... verwenden ]. [ist selbst ein Befehl, fast gleichwertig mit test. Dies ist wahrscheinlich der gebräuchlichste Befehl in einem if, der zur Annahme führen kann, dass er Teil der Shell-Syntax ist. Wenn Sie jedoch testen möchten, ob ein Befehl erfolgreich war oder nicht, verwenden Sie den Befehl selbst direkt mit if, wie oben gezeigt.

Bearbeiten: Zitierte die Frage oben aus Gründen der Klarheit (diese Antwort wird nicht oben auf der Seite angezeigt).


11
Beachten Sie, dass das Semikolon wichtig ist.
Thorbjørn Ravn Andersen

21
Oder Sie können einfach theneine separate Zeile einfügen.
l0b0

36
@ Joe: Ich denke du meinst if ! command ; then ... ; fi. [ist selbst ein Befehl und wird in diesem Fall nicht benötigt.
Keith Thompson

20
@Joe: Mein Weg hat auch die Tugend, richtig zu sein. if [ ! command ]führt nicht aus command; Es wird commandals Zeichenfolge und als wahr behandelt, da es eine Länge ungleich Null hat. [ist ein Synonym für den testBefehl
Keith Thompson

5
Hoppla. Du hast Recht.
Joe

133

Für kleine Dinge, die passieren sollen, wenn ein Shell-Befehl funktioniert, können Sie das &&Konstrukt verwenden:

rm -rf somedir && trace_output "Removed the directory"

Ebenso können Sie für kleine Dinge, die passieren sollen, wenn ein Shell-Befehl fehlschlägt, Folgendes verwenden ||:

rm -rf somedir || exit_on_error "Failed to remove the directory"

Oder beides

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"

Es ist wahrscheinlich unklug, sehr viel mit diesen Konstrukten zu tun, aber sie können gelegentlich den Kontrollfluss viel klarer machen.


3
Sie sind kürzer und (zumindest bei einigen Schalen) schneller. Ich schaudere, als ich mich an ein Monster-Ultrix-Installationsskript erinnerte, das nur mit diesen bedingten Konstruktionen geschrieben wurde, die ich einmal zu entschlüsseln versuchte ...
vonbrand

101

Überprüfen Sie den Wert von $?, der das Ergebnis der Ausführung des letzten Befehls / der letzten Funktion enthält:

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi

11
Obwohl dies technisch korrekt ist (und daher keine Ablehnung rechtfertigt), wird Bashs ifRedewendung nicht verwendet. Ich bevorzuge die Antwort von Keith Thompson.
Januar,

16
Diese Redewendung hat einige Vorteile: Sie behält den Rückgabewert bei. Alles in allem finde ich dieses mächtiger, wenn auch ausführlicher. es ist auch einfacher zu lesen.
Taxilian

2
Was ist "Bash's If Idiom"?
Nowaker

3
@ Nowaker Die Tatsache, dass der einzige Zweck ifist, dies zu tun. Die Bedingungen für die Flusskontrolle in Bash werden alle $?hinter den Kulissen untersucht. das ist was sie tun. Eine explizite Prüfung des Wertes sollte in den allermeisten Fällen nicht erforderlich sein und ist normalerweise ein Anfänger-Antimuster.
Tripleee

1
@lunakid Wenn dir der Exit-Code egal if ! cmdist , ist das in Ordnung. Ansonsten ist es üblich, eine elseKlausel zu verwenden. Du kannst kein leeres haben, thenaber du siehst manchmal, then : nothing; elsewo das :No-Op von Bedeutung ist. truewürde dort auch funktionieren.
Tripleee

47

Das hat bei mir funktioniert:

command && echo "OK" || echo "NOK"

Wenn dies commanderfolgreich ist, echo "OK"wird es ausgeführt, und da es erfolgreich ist, wird die Ausführung dort gestoppt. Andernfalls &&wird übersprungen und echo "NOK"ausgeführt.


5
Wenn Sie etwas tun möchten, wenn dies fehlschlägt, und den Beendigungscode beibehalten möchten (um ihn in der Eingabeaufforderung anzuzeigen oder in einem Skript zu testen), können Sie dies tun: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
Sam Hasler,

2
@ Sam-hasler: soll das nicht sein command && echo "OK" || (c=$?; echo "NOK"; (exit $c))?
Jrw32982

9
Auch wenn das echo "OK"Teil selbst ausfallen könnte, dann ist dies besser:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
JRW32982

@ jrw32982, Schön, ich habe die erstere Konstruktion verwendet, aber nicht die letztere.
Sam Hasler

Die wirkliche Antwort ist in den Kommentaren, danke an alle!
Joshua Pinter

6

Es sollte beachtet werden, dass if...then...fiund &&/ oder der ||Typ des Ansatzes den Exit-Status behandelt, der von dem Befehl zurückgegeben wird, den wir testen möchten (0 bei Erfolg); Einige Befehle geben jedoch keinen Exit-Status ungleich Null zurück, wenn der Befehl fehlgeschlagen ist oder Eingaben nicht verarbeiten konnte. Dies bedeutet, dass die üblichen ifund &&/ ||Ansätze für diese bestimmten Befehle nicht funktionieren.

Unter Linux wird GNU beispielsweise fileimmer noch mit 0 beendet, wenn eine nicht vorhandene Datei als Argument empfangen wurde und findder angegebene Benutzer die Datei nicht finden konnte.

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

In solchen Fällen besteht eine mögliche Möglichkeit, mit der Situation umzugehen, darin, stderr/ stdinmessages zu lesen , z. B. diejenigen, die per fileBefehl zurückgegeben werden, oder die Ausgabe des Befehls wie in zu analysieren find. Zu diesem Zweck casekönnte die Anweisung verwendet werden.

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found

2

Das fehleranfälligste, das ich finden konnte, war:

  • Holen Sie sich zuerst den Wert. Angenommen, Sie machen etwas wie:

RR=$?

Betrachten Sie nun nicht nur diese Situation, sondern auch andere, denen Sie möglicherweise gegenüberstehen:

definierte Variable:

$ AA=1 ; if (( "10#0${AA}" == 1 )) ; then echo yes ; else echo no ; fi

Antwort: ja

$ AA=1 ; if (( "10#0${AA}" != 1 )) ; then echo yes ; else echo no ; fi

Antwort: nein

undefinierte Variable:

$ AA=1 ; if (( "10#0${BB}" == 1 )) ; then echo yes ; else echo no ; fi

Antwort: nein

$ AA=1 ; if (( "10#0${BB}" != 1 )) ; then echo yes ; else echo no ; fi

Antwort: ja

$ AA=1 ; if (( "10#0${BB}" == 0 )) ; then echo yes ; else echo no ; fi

Antwort: ja

Dies verhindert alle Arten von Fehlern.

Sie kennen wahrscheinlich die gesamte Syntax, aber hier einige Tipps:

  • Verwenden Sie Anführungszeichen. Vermeiden Sie ein "blank"zu sein nothing.
  • Die neue moderne Notation für Variablen ist ${variable}.
  • Wenn Sie vor Ihrer Nummer eine verkettete Null einfügen, vermeiden Sie auch "überhaupt keine Nummer".
  • Aber warten Sie, wenn Sie eine Null hinzufügen, wird die Zahl base-8. Sie erhalten eine Fehlermeldung wie:
    • value too great for base (error token is "08")für Zahlen oben 7. Dann 10#kommt ins Spiel:
    • 10#zwingt die Nummer zu sein base-10.

1

Du kannst das:

if ($( ping 4.4.4.4 -c1 > /dev/null )) ; then
  echo "ping response succsess!!!"
fi

9
Das funktioniert, ist aber verworren. Sie führen Ping in einer Subshell einer Subshell aus. Die Ausgabe von pingwird erfasst, um es als Befehl auszuführen . Da die Ausgabe jedoch nach / dev / null umgeleitet wird, ist dies immer die leere Zeichenfolge. Sie führen also nichts in einer Subshell aus, was bedeutet, dass der vorherige Exit-Status (der Ping-Subshell für die Befehlsersetzung) beibehalten wird. Offensichtlich ist der richtige Weg if ping ...; thenhier.
Stéphane Chazelas

1

Wie an anderer Stelle in diesem Thread erwähnt, beantwortet sich die ursprüngliche Frage im Wesentlichen von selbst. Die folgende Abbildung zeigt, dass ifBedingungen auch verschachtelt sein können.

In diesem Beispiel wird ifüberprüft, ob eine Datei vorhanden ist und ob es sich um eine reguläre Datei handelt. Wenn diese Bedingungen zutreffen, prüfen Sie, ob die Größe größer als 0 ist.

#!/bin/bash

echo "Which error log are you checking today? "
read answer

if [ -f /opt/logs/$answer*.errors ]
    then
        if [ -s /opt/logs/$answer*.errors ]
            then
                echo "Content is present in the $answer error log file."
            else
                echo "No errors are present in the $answer error log file."
        fi
    else
        echo "$answer does not have an error log at this time."
fi

2
Das ist, was Ihre Antwort tut, aber Ihre Antwort spricht die Frage nicht an.
Jeff Schaller

@ JeffSchaller, danke für deinen Hinweis. Ich habe meinen Beitrag so bearbeitet, dass er einen Verweis auf die Frage enthält.
Quartzinquartz,

1
#!/bin/bash

if command-1 ; then
   echo "command-1 succeeded and now running from this block command-2"
   command-2
else
   echo "command-1 failed and now running from this block command-3"
   command-3
fi

3
Dies sieht ziemlich nach der bereits akzeptierten Antwort aus . Bitte schreiben Sie die Arbeit, die Sie wiederverwenden, zu oder geben Sie Ihre eigenen Anstrengungen in Ihre Antworten ein.
Jeff Schaller

-3

Dies könnte einfach auf diese Weise geschehen, da $?Sie den Status des zuletzt ausgeführten Befehls erhalten.

So könnte es sein

#!/bin/sh

... some command ...

if [ $? == 0 ] ; then
  echo '<the output message you want to display>'
else 
  echo '<failure message>'
fi

1
Downvote: Dies umschreibt einfach eine frühere Antwort, die zu Recht als unidiomatisch kritisiert wurde.
Tripleee
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.