2012-01-23 9 views
22

Używam potoku kilku poleceń w bash. Czy istnieje sposób na skonfigurowanie bash, aby natychmiast zakończyć wszystkie polecenia w całym potoku w przypadku niepowodzenia jednego z poleceń?Rurka powłoki: zamknij się natychmiast, jeś li nie powiedzie się jedna komenda.

W moim przypadku pierwsze polecenie, na przykład command1, działa przez chwilę, dopóki nie wygeneruje jakiegoś wyjścia. Możesz na przykład wstawić command1 przez (sleep 5 && echo "Hello").

Teraz, command1 | false kończy się niepowodzeniem po 5 sekundach, ale nie natychmiast.

To zachowanie wydaje się mieć związek z ilością danych wyjściowych generowanych przez polecenie. Na przykład: find/| false natychmiast zwraca.

Ogólnie zastanawiam się, dlaczego bash zachowuje się w ten sposób. Czy ktokolwiek może sobie wyobrazić jakąkolwiek sytuację, w której użyteczne jest, aby kod taki jak command1 | non-existing-command nie wychodził jednocześnie?

PS: Używanie plików tymczasowych nie jest dla mnie opcją, ponieważ wyniki pośrednie, które przeglądam, są duże, aby je przechowywać.

PPS: Ani set -e ani set -o pipefail wydają się mieć wpływ na to zjawisko.

+1

To pytanie jest lepiej dostosowane do http://unix.stackexchange.com. Prawdopodobnie dostaniesz tam dobrą odpowiedź. – dogbane

Odpowiedz

16

Dokumentacja bash mówi w jego section about pipelines:

Każda komenda w rurociągu jest wykonywana we własnym podpowłoce [...]

„W swojej własnej podpowłoce” oznacza, że ​​nowa proces bash jest tworzony, a następnie wykonuje rzeczywiste polecenie. Każda podpowłokę rozpoczyna się pomyślnie, nawet jeśli natychmiast stwierdza, że ​​polecenie, które ma wykonać, nie istnieje.

To wyjaśnia, dlaczego cała rura może zostać pomyślnie skonfigurowana, nawet jeśli jedno z poleceń jest nonsensem. Bash nie sprawdza, czy każde polecenie może zostać uruchomione, przekazuje je do podpowłok. To wyjaśnia również, dlaczego na przykład polecenie nonexisting-command | touch hello wygeneruje błąd "nie znaleziono polecenia", ale plik hello zostanie utworzony.

W tej samej sekcji, mówi także:

The Shell czeka na wszystkich poleceń w rurociągu, aby zakończyć przed zwróceniem wartości.

W sleep 5 | nonexisting-command jak A. H. zauważono, sleep 5 kończy się po 5 sekundach, nie od razu, a tym samym również płaszcz odczekać 5   sekund.

Nie wiem, dlaczego wdrożenie zostało wykonane w ten sposób. W przypadkach takich jak twoje zachowanie z pewnością nie jest takie, jak można się spodziewać.

Zresztą jeden nieco brzydki obejście jest użycie FIFO:

mkfifo myfifo 
./long-running-script.sh > myfifo & 
whoops-a-typo < myfifo 

Tutaj long-running-script.sh jest uruchamiany, a następnie skrypty nie od razu w następnym wierszu. Używając wielu FIFO, można rozszerzyć to na potoki z więcej niż dwoma poleceniami.

4

sleep 5 nie generuje żadnego wyjścia, dopóki się nie zakończy, podczas gdy find / natychmiast produkuje dane wyjściowe, których bash próbuje wykonać potok do false.

+0

To prawda, pytanie jest dobre, ale ma źle dobrane przykłady. –

+0

@Jaypal: Zgadzam się, że przykład może wprowadzać w błąd. Zmieniłem stanowisko i mam nadzieję, że teraz jest jaśniejsze. – Tobi

+0

@Dan: Tak, ale to tak naprawdę nie odpowiada na moje pytanie. Chcę wiedzieć, czy mogę spowodować, że bash zakończy wszystkie polecenia w potoku, gdy jedno polecenie się nie powiedzie. – Tobi

2

find/|false nie szybciej, ponieważ pierwsze wywołanie systemowe write(2) z find nie powiedzie się z powodu błędu EPIPE (Broken pipe). Jest tak, ponieważ false został już zakończony, a zatem rura między tymi dwoma poleceniami została już zamknięta po jednej stronie.

Gdyby find zignorowałby ten błąd (mógł to zrobić w teorii), byłby on również "fail slow".

(sleep 5 && echo "Hello") | false to błąd "powolny", ponieważ pierwsza część, sleep, nie "testuje" rury, pisząc do niej. Po 5 sekundach echo również otrzymuje błąd EPIPE. To, czy ten błąd kończy pierwszą część w tym przypadku, czy nie, nie jest ważne dla pytania.

3

Pierwszy program nie wie, czy drugie jest zakończone, czy nie, dopóki nie spróbuje wpisać daty do potoku. W przypadku zakończenia drugiego, pierwszy otrzymuje SIGPIPE, co zwykle powoduje natychmiastowe wyjście.

można zmusić pierwszą linię wyjścia zostać wyprowadzony natychmiast po wpatrując się w następujący sposób:

(sleep 0.1; echo; command1) | command2 

100ms ten sen ma czekać, aż możliwym wyjściem command2 tuż po starcie. Oczywiście, jeśli polecenie 2 zakończy się po 2 sekundach, a polecenie1 będzie milczeć przez 60 sekund, całe polecenie powłoki powróci dopiero po 60.1 sekundach.

Powiązane problemy