Ustawienie pipefail dla pojedynczego polecenia potokowego

19

Muszę wykonać szereg potokowych poleceń powłoki ze skryptu innego niż BASH (a mianowicie skryptu PHP), takiego jak te:

command1 | command2 | command3

tak, że jeśli command1nie powiedzie się niezerowy kod wyjścia, każde inne polecenie również zawiedzie. Do tej pory wymyśliłem:

set -o pipefail && command1 | command2 | command3

Ale mimo że działa poprawnie z terminala, generuje to, jeśli jest wykonywany ze skryptu:

sh: 1: set: Niedozwolona opcja -o pipefail

Desmond Hume
źródło
Wygląda na to, że /bin/shsię nie podoba set -o pipefail. Czy to rzeczywiście bashw przebraniu, czy może to inna skorupa? Kiedy bashjest uruchamiany jako /bin/sh, czy akceptuje set -o pipefail?
Jonathan Leffler
@JathanathanLeffler Kiedy uruchamiam /bin/sh set -o pipefail, mówi /bin/sh: 0: Can't open set(to samo z sudo). Mam nadzieję, że dobrze to przetestowałem. System to Ubuntu 12.
Desmond Hume
Musisz spróbować /bin/sh -c "set -o pipefail"; tak jak było, powłoka próbowała wykonać skrypt w bieżącym katalogu o nazwie seti nie znalazł go.
Jonathan Leffler
@JathanathanLeffler /bin/sh -c "set -o pipefail"nie działa, jednak bash -c "set -o pipefail"działa.
Desmond Hume
Problem polega na tym, że skrypt jest uruchamiany przez program, /bin/shktóry nie rozpoznaje set -o pipefail. W związku z tym musisz upewnić się, że skrypt jest uruchamiany /bin/bashzamiast /bin/sh. Lub, jeśli jesteś pewny siebie, odważny - i prawdopodobnie nierozsądny - zmień /bin/shsię na link do lub kopię /bin/bashzamiast na jakąkolwiek powłokę, z którą jest obecnie połączona lub kopię. Jeśli masz pewność, że /bin/shjest bash, to używasz zachowania, które bashnie narażają gdy uruchamiane jako /bin/sh; użyj bashjako bash.
Jonathan Leffler

Odpowiedzi:

18

Z linii poleceń Bash trzeba wywołać podpowłokę, aby uniknąć pipefailpóźniejszego ustawienia:

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

Ograniczyłoby to efekt tej pipefailopcji do podpowłoki utworzonej przez nawiasy (...).

Prawdziwy przykład:

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

Jeśli używasz jawnego wywołania powłoki z -copcją, nie potrzebujesz podpowłoki, bashz shaliasem lub z aliasem, aby 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

Ponieważ shnie akceptujesz tej pipefailopcji, musiałbym założyć, że jest to jakaś starsza lub zmodyfikowana wersja bash- lub że w rzeczywistości jest to zupełnie inna powłoka.

thkala
źródło
1
Czy $w pierwszym kawałku jest częścią polecenia, czy to tylko zachęta do powłoki? Próbowałem w obie strony, tak naprawdę nie działało. bash -cSposób zawsze działa, nie jest zbyt elegancki tho ..
Desmond Hume
5

Nie jestem pewien, dlaczego nie wspomniano powyżej, ale możliwe jest jawne rozbrojenie pipefailprzy użyciu set +o pipefail.

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

Jeśli wykonujesz fragment i nie pipefailmasz pewności co do już ustawionego, możesz użyć tego w podpowłoce, jak wcześniej sugerowano:

# explicitly execute with pipefail set
(set -o pipefail ; command1 | command2 | command3 )
# explicitly execute with pipefail unset
(set +o pipefail ; command1 | command2 | command3 )
robbat2
źródło
0

Możesz użyć $ (polecenie1) w połączeniu z $? :

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

Drukuje „abc”

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

Drukuje „”.

„[$? -eq 0] &&” oznacza, że ​​następujące polecenie jest wykonywane tylko wtedy, gdy poprzednie się powiodło.

Ta dawka nie odpowiada na twoje pytanie, ale jest rozwiązaniem twojego problemu.

cglacet
źródło
Czy to oznacza, że ​​zawsze musiałbym przechowywać dane wyjściowe każdego polecenia w zmiennej?
Desmond Hume
Zawsze nie wiem, ale z moim rozwiązaniem tak, będziesz (jeśli będziesz go potrzebować do następnego polecenia). Chyba że możesz po prostu pisaćcommand1 && command2 && command3
cglacet