Tak, bash
podobnie jak w ksh
(skąd pochodzi funkcja), procesy wewnątrz substytucji procesu nie są czekane (przed uruchomieniem następnego polecenia w skrypcie).
dla <(...)
jednego, to zwykle w porządku, jak w:
cmd1 <(cmd2)
powłoka będzie na nią czekać cmd1
i cmd1
zwykle będzie na nią czekać cmd2
, czytając ją do końca pliku na potoku, który jest podstawiany, a ten koniec pliku zwykle ma miejsce, gdy cmd2
umiera. To z tego samego powodu kilka muszli (nie bash
) nie przeszkadza czeka na cmd2
w cmd2 | cmd1
.
Na cmd1 >(cmd2)
ogół jednak tak nie jest, ponieważ cmd2
zwykle więcej czeka na cmd1
niego, więc zazwyczaj kończy się później.
Zostało to naprawione, zsh
ponieważ czeka na cmd2
to (ale nie, jeśli napiszesz je jako cmd1 > >(cmd2)
i cmd1
nie jest ono wbudowane, użyj {cmd1} > >(cmd2)
zamiast tego jak udokumentowano ).
ksh
nie czeka domyślnie, ale pozwala poczekać na to z wait
wbudowanym (udostępnia również pid $!
, choć to nie pomaga, jeśli tak zrobisz cmd1 >(cmd2) >(cmd3)
)
rc
(ze cmd1 >{cmd2}
składnią), tak samo jak ksh
z tym wyjątkiem, że można uzyskać pids wszystkich procesów w tle za pomocą $apids
.
es
(również z cmd1 >{cmd2}
) czeka na cmd2
podobne w zsh
, a także czeka na przekierowania cmd2
w <{cmd2}
procesie.
bash
udostępnia pid cmd2
(lub dokładniej podpowłoki, ponieważ działa cmd2
w procesie potomnym tej podpowłoki, mimo że jest to ostatnia tam dostępna komenda) $!
, ale nie pozwala na to czekać.
Jeśli musisz użyć bash
, możesz obejść problem za pomocą polecenia, które będzie czekać na oba polecenia za pomocą:
{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1
To sprawia, że zarówno cmd1
i cmd2
mają fd 3 otwarte na rurze. cat
będzie czekać na EOF na drugim końcu, więc zazwyczaj tylko wyjść, gdy oba cmd1
i cmd2
są martwe. I powłoka będzie czekać na to cat
polecenie. Można to zobaczyć jako sieć przechwytującą zakończenie wszystkich procesów w tle (możesz użyć jej do innych rzeczy rozpoczętych w tle, takich jak &
, coprocs, a nawet polecenia, które same w tle, pod warunkiem, że nie zamykają wszystkich swoich deskryptorów plików, jak zwykle demony ).
Zauważ, że dzięki wspomnianemu wyżej procesowi zmarnowanej podpowłoki działa, nawet jeśli cmd2
zamyka swój fd 3 (polecenia zwykle tego nie robią, ale niektóre lubią sudo
lub ssh
robią). Przyszłe wersje bash
mogą ostatecznie przeprowadzić tam optymalizację, tak jak w innych powłokach. Wtedy potrzebujesz czegoś takiego:
{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1
Aby upewnić się, że jest jeszcze dodatkowy proces powłoki z otwartym fd 3 czekającym na to sudo
polecenie.
Zauważ, że cat
nic nie przeczyta (ponieważ procesy nie piszą na swoim fd 3). Jest tylko do synchronizacji. Wykona tylko jedno read()
wywołanie systemowe, które na końcu nie wróci.
Można faktycznie uniknąć uruchamiania cat
, używając synchronizacji komend do synchronizacji potoku:
{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1
Tym razem to powłoka zamiast cat
tego czyta z potoku, którego drugi koniec jest otwarty na fd 3 z cmd1
i cmd2
. Używamy przypisania zmiennej, więc status wyjścia cmd1
jest dostępny w $?
.
Lub możesz ręcznie podstawić proces, a następnie możesz nawet użyć systemu, sh
ponieważ stałoby się to standardową składnią powłoki:
{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1
zauważ jednak, jak wspomniano wcześniej, że nie wszystkie sh
implementacje będą czekać cmd1
po cmd2
zakończeniu (choć jest to lepsze niż na odwrót). Ten czas $?
zawiera status wyjścia cmd2
; Choć bash
and zsh
make cmd1
„s status wyjścia dostępne ${PIPESTATUS[0]}
i $pipestatus[1]
odpowiednio (patrz również pipefail
opcję w kilku pocisków więc $?
może zgłosić awarię elementów rurowych inne niż ostatnio)
Pamiętaj, że yash
ma podobne problemy z funkcją przekierowywania procesów . cmd1 >(cmd2)
zostanie cmd1 /dev/fd/3 3>(cmd2)
tam napisane . Ale cmd2
nie jest czekany i nie można też wait
na niego czekać, a jego pid również nie jest dostępny w $!
zmiennej. Używałbyś tych samych obejść, co dla bash
.