Rura półsynchroniczna

11

Załóżmy, że mam następującą potok:

a | b | c | d

Jak mogę czekać na zakończenie c(lub b) w shlub bash? Oznacza to, że skrypt dmoże się uruchomić w dowolnym momencie (i nie trzeba na niego czekać), ale cdo prawidłowego działania wymaga pełnego wyjścia .

Sprawa stosowanie jest difftooldla gitże porównuje obrazy. Jest wywoływany giti musi przetworzyć dane wejściowe ( a | b | cczęść) i wyświetlić wyniki porównania ( dczęść). Dzwoniący usunie dane wymagane dla ai b. Oznacza to, że przed powrotem ze skryptu proces c(lub b) musi zostać zakończony. Z drugiej strony nie mogę się doczekać, dponieważ oznacza to, że czekam na dane wejściowe użytkownika.

Wiem, że mogę zapisać wyniki cdo pliku tymczasowego lub użyć FIFO w bash. (Nie jestem jednak pewien, czy FIFO pomoże). Czy można to osiągnąć bez tymczasowych plików sh?

EDYTOWAĆ

Być może byłoby wystarczające, gdybym mógł w wiarygodny sposób znaleźć identyfikator procesu c(lub b) procesu. Wtedy cała rura mogłaby zostać uruchomiona asynchronicznie i mogłem poczekać na identyfikator procesu. Coś w stylu

wait $(a | b | { c & [print PID of c] ; } | d)

EDYCJA ^ 2

Znalazłem rozwiązanie, komentarze (lub jeszcze lepsze rozwiązania) są mile widziane.

krlmlr
źródło
Masz na myśli, że chcesz drozpocząć przetwarzanie danych cwyjściowych dopiero po czakończeniu? Nie chcesz drozpoczynać przetwarzania każdej linii wyjściowej, jaka się pojawi?
terdon
@terdon: Nie, dmożna rozpocząć, kiedy tylko chce, ale cmusi się skończyć, zanim będę mógł przejść dalej.
krlmlr
To wydaje się być wewnętrznie sprzeczne, jeśli dmożna zacząć, kiedy chce, na co dokładnie czekasz?
terdon
@terdon: rozwinięty, aby pokazać przypadek użycia.
krlmlr
Jeśli dnie używa danych wyjściowych c, wydaje się, że nie ma sensu dwchodzić w skład potoku. Ale jeśli dkorzysta z danych wejściowych, to dpo ich przeczytaniu musi działać chwilę po ich przeczytaniu, aby Twoje podejście miało jakąkolwiek różnicę.
Hauke ​​Laging

Odpowiedzi:

6
a | b | { c; [notify];} | d

Powiadomienia można dokonać np. Przez sygnał do PID, który został przekazany w zmiennej środowiskowej ( kill -USR1 $EXTPID) lub przez utworzenie pliku ( touch /path/to/file).

Inny pomysł:

Wykonujesz następny proces (ten, aby móc rozpocząć, na który czekasz) z potoku:

a | b | { c; exec >&-; nextprocess;} | d

lub

a | b | { c; exec >&-; nextprocess &} | d
Hauke ​​Laging
źródło
Dzięki. Ale później utworzenie pliku tymczasowego dla wyniku pośredniego jest bardziej szczegółowe i łatwiejsze do przeanalizowania (dla człowieka). Ponadto, jak mogę uniknąć wyścigu z sygnałem? ... Miałem nadzieję na czystsze rozwiązanie, ale jeśli to jest właściwy sposób, niech tak będzie.
krlmlr
@krlmlr Jakie warunki wyścigu?
Hauke ​​Laging
notifymoże być wykonany zanim ustawię pułapkę sygnału. Jak mogę się upewnić, że nie przegapię tego sygnału?
krlmlr
@krlmlr Zatrzymujesz skrypt, który wywołuje potok, dopóki nie otrzyma samego sygnału, który jest wysyłany po trapzdefiniowaniu.
Hauke ​​Laging
Twoja edycja: Potok jest wywoływany w pętli, nad którą nie mam kontroli. Niestety { c; bg; }lub { c; exit 0; }nie działa.
krlmlr
5

Jeśli dobrze rozumiem twoje pytanie, powinno to działać:

a | b | c | { (exec <&3 3<&-; d) &} 3<&0

(sztuczka fd 3 jest spowodowana tym, że niektóre (większość) powłok przekierowuje standardowe wejście na / dev / null za pomocą &).

Stéphane Chazelas
źródło
4

W bash możesz użyć podstawienia procesu . Polecenie w procesie podstawiania działa asynchronicznie, nie jest czekane.

a | b | c > >(d)
Gilles „SO- przestań być zły”
źródło
Dzięki. To bashtylko prawda, brak shwsparcia?
krlmlr
@krlmlr Tylko Bash / zsh (i ksh93 z kilkoma modyfikacjami).
Gilles „SO- przestań być zły”,
3

Oto, co znalazłem metodą prób i błędów przy pomocy informacji Hauke:

a | b | { c; kill -PIPE $$; } | d

Odpowiednio:

a | b | ( c; kill -PIPE $$; ) | d

(To ostatnie jest bardziej wyraźne, ponieważ i tak {}będzie działać w podpowłoce, jeśli będzie w potoku).

Niektóre inne sygnały (włącznie QUIT, TERMi USR1) działają też jednak w tym przypadku opis sygnału jest wyświetlany na terminalu.

Zastanawiam się, czy taka jest pierwotna intencja PIPEsygnału. Zgodnie z instrukcją :

SIGPIPE: PIPESygnał jest wysyłany do procesu, gdy próbuje on zapisać do potoku bez procesu podłączonego do drugiego końca.

Oznacza to, że kiedy sztucznie wysyłam sygnał potoku do podpowłoki, po cichu kończy się, pozostawiając końcowego konsumenta ( d) samego.

Działa to zarówno shi bash.

krlmlr
źródło
0

Możesz użyć spongeprogramu z pakietu „moreutils”:

a | b | c | sponge | d

gąbka będzie na końcu cwyjścia przed podłączeniem do niego d. Mam nadzieję, że tego właśnie chciałeś.

Minijackson
źródło
0

Możesz po prostu zrobić:

a | b | c | (d ; cat > /dev/null)

Tak więc po dzakończeniu catabsorbuje resztę cmocy wyjściowej aż do jej zakończenia.

Ok. Po komentarzach myślę, że odpowiedź powinna rozpocząć się bezpośrednio dw tle.

Robić:

a | b | c | (d &)

lub skorzystaj z rozwiązania Stephane Chazelas , jeśli występują problemy z dczytaniem ze standardowego wejścia .

angus
źródło
Obawiam się, że mój przypadek użycia jest odwrotny: ckończy się wcześniej niż d, i muszę czekać do końca, cale nie przejmuję się d.
krlmlr
Przepraszam, nie rozumiem Co chcesz zrobić, gdy cskończy się i dnadal działa? Zabić d?
angus
dma GUI i może działać, dopóki użytkownik go nie zamknie.
krlmlr
OK ... ale to już się dzieje. Kiedy a, bi czakończyć swoją pracę, oni blisko stdout i wyjść; i tylko ddziała. Czy chcesz wysłać ddo tła po czakończeniu? Czy to to?
angus
Tak, dpowinien zostać wysłany do tła po czakończeniu.
krlmlr