Définition de pipefail pour une seule commande canalisée

18

J'ai besoin d'exécuter un certain nombre de commandes shell canalisées à partir d'un script non BASH (à savoir un script PHP) comme ceux-ci:

command1 | command2 | command3

de sorte qu'en cas d' command1échec avec un code de sortie différent de zéro, chaque autre commande échoue également. Jusqu'à présent, ce que j'ai trouvé est:

set -o pipefail && command1 | command2 | command3

Mais même s'il fonctionne correctement depuis le terminal, il produit ceci s'il est exécuté à partir du script:

sh: 1: set: option illégale -o pipefail

Desmond Hume
la source
Il semble que ça /bin/shn'aime pas set -o pipefail. Est-ce en fait bashdéguisé ou s'agit-il d'une coquille différente? Quand bashest-il exécuté comme /bin/sh, accepte- set -o pipefailt- il ?
Jonathan Leffler
@JonathanLeffler Quand je cours, /bin/sh set -o pipefailil dit /bin/sh: 0: Can't open set(même chose avec sudo). J'espère que je l'ai testé correctement. Le système est Ubuntu 12.
Desmond Hume
Vous auriez besoin d'essayer /bin/sh -c "set -o pipefail"; tel qu'il était, le shell essayait d'exécuter un script dans le répertoire courant appelé setet il ne l'a pas trouvé.
Jonathan Leffler
@JonathanLeffler /bin/sh -c "set -o pipefail"ne fonctionne pas, cependant bash -c "set -o pipefail".
Desmond Hume
Donc, votre problème est que le script est exécuté par /bin/shqui ne reconnaît pas set -o pipefail. Par conséquent, vous devrez vous assurer que le script est exécuté par /bin/bashau lieu de /bin/sh. Ou, si vous êtes confiant, courageux - et probablement imprudent - changez /bin/shpour être un lien ou une copie de, /bin/bashau lieu du shell auquel il est actuellement lié ou d'une copie. Si vous êtes sûr que /bin/shest bash, alors vous utilisez un comportement qui bashn'expose pas lorsqu'il est exécuté comme /bin/sh; utiliser bashcomme bash.
Jonathan Leffler

Réponses:

18

À partir de la ligne de commande Bash, vous devrez appeler un sous-shell pour éviter d' pipefailêtre défini par la suite:

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

Cela limiterait l'effet de l' pipefailoption au sous-shell créé par les parenthèses (...).

Un vrai exemple:

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

Si vous utilisez un appel shell explicite avec l' -coption, vous n'avez pas besoin d'un sous-shell, avec bashou avec un shalias pour 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

Puisque votre shn'accepte pas l' pipefailoption, je devrais supposer qu'il s'agit d'une version plus ancienne ou modifiée de bash- ou qu'il s'agit en fait d'un autre shell complètement.

thkala
la source
1
Est-ce que $dans la première partie une partie de la commande est-ce juste une invite shell? Essayé dans les deux sens, n'a pas vraiment fonctionné. La bash -cmanière fonctionne toujours, pas trop élégante cependant.
Desmond Hume
5

Je ne sais pas pourquoi cela n'a pas été mentionné ci-dessus, mais il est possible de désactiver explicitement l' pipefailutilisation set +o pipefail.

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

Si vous exécutez un fragment et n'êtes pas sûr de ce qui est pipefaildéjà défini, vous pouvez l'utiliser avec le sous-shell comme suggéré précédemment:

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

Vous pouvez utiliser $ (command1) combiné avec $? :

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

Imprime "abc"

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

Imprime "".

"[$? -eq 0] &&" signifie que la commande suivante n'est exécutée que si la précédente a réussi.

Cela ne répond pas à votre question mais c'est une solution à votre problème.

cglacet
la source
Cela signifie-t-il que j'aurais toujours besoin de stocker la sortie de chacune des commandes dans une variable?
Desmond Hume
Toujours je ne sais pas mais avec ma solution oui vous le ferez (si vous en avez besoin pour votre prochaine commande). Sauf si vous pouvez simplement écrirecommand1 && command2 && command3
cglacet