Pipefail für einen einzelnen Pipe-Befehl einstellen

18

Ich muss eine Reihe von Pipe-Shell-Befehlen von einem Nicht-BASH-Skript (nämlich PHP-Skript) wie folgt ausführen:

command1 | command2 | command3

Wenn also ein command1Fehler mit einem Exit-Code ungleich Null auftritt, schlägt auch jeder andere Befehl fehl. Bisher habe ich mir Folgendes ausgedacht:

set -o pipefail && command1 | command2 | command3

Aber obwohl es vom Terminal aus gut läuft, erzeugt es dies, wenn es vom Skript ausgeführt wird:

sh: 1: set: Unzulässige Option -o pipefail

Desmond Hume
quelle
Es scheint, dass /bin/shnicht gefällt set -o pipefail. Ist es tatsächlich bashverschleiert oder ist es eine andere Hülle? Wann bashwird ausgeführt als /bin/sh, akzeptiert es set -o pipefail?
Jonathan Leffler
@ JonathanLeffler Wenn ich es laufen lasse, /bin/sh set -o pipefailheißt es /bin/sh: 0: Can't open set(dasselbe mit sudo). Hoffe ich habe es richtig getestet. Das System ist Ubuntu 12.
Desmond Hume
Sie müssten es versuchen /bin/sh -c "set -o pipefail"; Die Shell hat versucht, ein Skript im aktuellen Verzeichnis auszuführen, setund es wurde nicht gefunden.
Jonathan Leffler
@ JonathanLeffler /bin/sh -c "set -o pipefail"funktioniert jedoch bash -c "set -o pipefail"nicht.
Desmond Hume
Ihr Problem ist also, dass das Skript ausgeführt wird, von /bin/shdem es nicht erkannt wird set -o pipefail. Folglich müssen Sie sicherstellen, dass das Skript /bin/bashvon ausgeführt wird /bin/sh. Oder, wenn Sie zuversichtlich sind, mutig - und wahrscheinlich tollkühn - ändern /bin/sh, um eine Verknüpfung zu oder eine Kopie von zu sein, /bin/bashanstatt der Shell, mit der sie derzeit verknüpft ist, oder einer Kopie von. Wenn Sie sicher sind, dass dies der Fall /bin/shist bash, verwenden Sie ein Verhalten, das bashbeim Ausführen als nicht verfügbar gemacht wird /bin/sh. Verwenden Sie bashals bash.
Jonathan Leffler

Antworten:

18

Über die Bash-Befehlszeile müssten Sie eine Subshell aufrufen, um zu vermeiden pipefail, dass sie später festgelegt wird:

$ (set -o pipefail && command1 | command2 | command3)

Dies würde den Effekt der pipefailOption auf die durch die Klammern erzeugte Unterschale begrenzen (...).

Ein echtes Beispiel:

$ (set -o pipefail && false | true) && echo pipefail inactive || echo pipefail active
pipefail active

Wenn Sie einen expliziten Shell-Aufruf mit der -cOption verwenden, benötigen Sie keine Subshell, weder mit bashnoch mit einem shAlias ​​für bash:

$ bash -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active
$ sh -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active

Da Ihr shdie pipefailOption nicht akzeptiert , müsste ich davon ausgehen, dass es sich entweder um eine ältere oder modifizierte Version handelt bash- oder dass es sich tatsächlich um eine andere Shell handelt.

Thkala
quelle
1
Ist $im ersten Teil des Befehls nur eine Shell-Eingabeaufforderung enthalten? In beide Richtungen versucht, hat nicht wirklich funktioniert. Der bash -cWeg funktioniert immer, aber nicht zu elegant.
Desmond Hume
5

Ich bin mir nicht sicher, warum es oben nicht erwähnt wurde, aber es ist möglich, es explizit zu deaktivieren pipefail, indem ich benutze set +o pipefail.

set -o pipefail
command1 | command2 | command3
set +o pipefail

Wenn Sie ein Fragment ausführen und sich über die pipefailbereits festgelegte Menge nicht sicher sind , können Sie dies mit der Subshell wie oben vorgeschlagen verwenden:

# explicitly execute with pipefail set
(set -o pipefail ; command1 | command2 | command3 )
# explicitly execute with pipefail unset
(set +o pipefail ; command1 | command2 | command3 )
robbat2
quelle
0

Sie können $ (command1) in Kombination mit $? :

a=$(echo "a")
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

Druckt "abc"

a=$(ls unexitingDir)
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

Druckt "".

"[$? -eq 0] &&" bedeutet, dass der folgende Befehl nur ausgeführt wird, wenn der vorherige erfolgreich war.

Das beantwortet Ihre Frage nicht, aber es ist eine Lösung für Ihr Problem.

cglacet
quelle
Bedeutet das, dass ich die Ausgabe jedes Befehls immer in einer Variablen speichern müsste?
Desmond Hume
Ich weiß es immer nicht, aber mit meiner Lösung werden Sie es tun (wenn Sie es für Ihren nächsten Befehl brauchen). Es sei denn, Sie können nur schreibencommand1 && command2 && command3
cglacet