2017-11-26 6 views
1

Ich habe folgende Bash-Skript, das auf meinem CI läuft und will meinen Code auf einem physischen MacOS und auf mehreren Docker Linux Bilder laufen:Bash - brechen Konditionalsatz wenn eine der Anweisungen Fehlercode zurück = 0

!
if [[ "$OS_NAME" == "mac_os" ]]; then 
     make all; 
     run_test1; 
     run_test2; 
     make install; 
    else 
     docker exec -i my_docker_image bash -c "make all"; 
     docker exec -i my_docker_image bash -c "run_test1"; 
     docker exec -i my_docker_image bash -c "run_test2"; 
     docker exec -i my_docker_image bash -c "make install"; 
    fi 

Wenn die Tests fehlschlagen (run_test1 oder run_test2) sie Fehlercode zurück 1. Wenn sie sie Fehlercode passieren Rückkehr 0.

Das ganze Skript läuft mit set -e so, wenn es Exit-Code ungleich 0 sieht es aufhört und Scheitert der gesamte Build.

Das Problem ist, dass, wenn run_test1 und run_test2 innerhalb der Bedingungsklausel sind - wenn sie fehlschlagen und Fehlercode 1 zurückgeben die Bedingung Klausel nicht bricht und der Build erfolgreich obwohl Tests nicht bestanden haben.

So habe ich 2 Fragen:

  • Wie die bedingte Klausel zu brechen, wenn einer der Befehle Fehlercode ungleich 0 zurückgeben?
  • Wie wird die Bedingungsklausel so gebrochen, dass die gesamte Klausel einen Fehlercode zurückgibt (so dass der gesamte Build fehlschlägt)?
+1

Ihre Analyse ist nicht korrekt. Wenn 'set -e' in Kraft ist und' run_test1' fehlschlägt, stoppt das Skript hier. Wenn etwas anderes passiert, dann liegt das an einem Teil des Codes, den Sie nicht gezeigt haben. Poste [tatsächlicher Code, der das Problem demonstriert] (https://stackoverflow.com/help/mcve), vorzugsweise vereinfacht, damit Benutzer ihn ohne Makefiles und Docker-Images ausführen können. – Gilles

+1

http://mywiki.wooledge.org/BashFAQ/105 – pynexj

Antwort

1

Der Code sollte wie erwartet funktionieren, lassen Sie uns dies demonstrieren:

#!/usr/bin/env bash 

set -e 

var="test" 
if [[ $var = test ]]; then 
    echo "Hello" 
    cat non_existing_file &> /dev/null 
    echo "World" 
else 
    echo "Else hello" 
    cat non_existing file &> /dev/null 
fi 

echo I am done 

Dies wird nur ausgegeben, "Hallo", wie erwartet. Wenn es für Sie anders funktioniert, bedeutet dies, dass Sie nicht genügend Code zur Verfügung gestellt haben. Lassen Sie uns versuchen, den Code oben zu ändern und einige Beispiele zeigen, wenn set -e ignoriert wird, wie in Ihrem Fall:

Bash Reference manual Zitiert:

Wenn eine Verbindung Befehl oder Shell-Funktion in einem Kontext ausgeführt wird, wo -e wird ignoriert, keiner der Befehle, die in dem zusammengesetzten Befehls- oder Funktionskörper ausgeführt werden, wird von der Einstellung -e beeinflusst, selbst wenn -e gesetzt ist und ein Befehl einen Fehlerstatus zurückgibt.

Nun wollen wir das gleiche Handbuch zitieren und einige Fälle sehen, wo -e ignoriert wird:

Die Schale wird nicht beendet, wenn der Befehl, der Teil Liste der Befehl schlägt fehl, sofort nach einer Weile oder bis Schlüsselwort, Teil von der Test in einer if-Anweisung, Teil eines beliebigen Befehls in einem & & oder || ausgeführte Liste mit Ausnahme des Befehls nach dem letzten & & oder ||, einem beliebigen Befehl in einer Pipeline, aber der letzte, oder wenn der Rückgabestatus des Befehls ist invertiert mit!.

Daraus können wir, dass zum Beispiel sehen, wenn Sie den Code oben in einer Funktion haben und getestet, diese Funktion mit if würde set -e außer Acht gelassen werden:

#!/usr/bin/env bash 

set -e 

f() { 
    var="test" 
    if [[ $var = test ]]; then 
     echo "Hello" 
     cat non_existing_file &> /dev/null 
     echo "World" 
    else 
     echo "Else hello" 
     cat non_existing file &> /dev/null 
    fi 
} 

if f; then echo "Function OK!"; fi 

echo I am done 

Funktion f wird in einem ausgeführt Kontext, in dem set -e ignoriert wird (if-Anweisung), was bedeutet, dass set -e keinen der in dieser Funktion ausgeführten Befehle beeinflusst. Dieser Code gibt:

Hallo

Welt

Funktion OK!

Ich

getan

Die gleichen Regeln gelten, wenn Sie die Funktion in einem & & oder ausführen || Liste. Wenn Sie die Zeile if f; then echo "Function OK!"; fi zu f && echo "Function OK" ändern, erhalten Sie die gleiche Ausgabe. Ich glaube, Letzteres könnte Ihr Fall sein.

Trotzdem Ihre zweite Frage leicht || exit durch Zugabe gelöst werden:

run_test1 || exit 1; 
run_test2 || exit 1; 

Ihre erste Frage trivial ist, wenn Sie in einer Funktion zum Beispiel sind. Dann kannst du einfach return. Oder in einer Schleife, dann können Sie break. Wenn Sie nicht sind, ist es nicht so einfach, aus der Bedingungsklausel auszubrechen. Werfen Sie einen Blick auf diese answer.

set -e kann eine überraschende Sache sein, da es in vielen Fällen ignoriert wird. Verwenden Sie es vorsichtig und beachten Sie diese Fälle.

Verwandte Themen