2014-04-23 15 views
6

pracuję nad rurociągiem, który ma kilka punktów branżowych, które następnie merge-- one wyglądać tak:Jak mogę podzielić i ponownie połączyć STDOUT z wielu procesów?

  command2 
     /  \ 
command1   command4 
     \  /
     command3 

Każde polecenie zapisuje na standardowe wyjście i wejście akceptuje pośrednictwem standardowego wejścia. STDOUT z komendy1 musi być przekazane do zarówno command2 i command3, które są uruchamiane kolejno, a ich wyjście musi być skutecznie połączone i przekazane do command4. Początkowo myślałem, że coś jak to będzie działać:

$ command1 | (command2; command3) | command4 

to nie zadziała jednak, jak tylko standardowe wyjście z command2 jest przekazywana do polecenia 4, a kiedy usunąć command4 to oczywiste, że nie jest command3 przeszły odpowiedni strumień z polecenia 1 - innymi słowy, jest tak, jakby polecenie 2 wyczerpało lub pochłonęło strumień. Otrzymuję taki sam wynik z {command2; command3; } również w środku. Więc pomyślałem, że należy używać 'tee' with process substitution i próbowała to:

$ command1 | tee >(command2) | command3 | command4 

Ale niespodziewanie, że nie działało - wydaje się, że wyjście komendzie command1 i wyjście command2 są rurami do command3, która powoduje błędy i tylko wyjście polecenia command3 jest podłączone do command4. I nie okaże się, że dodaje się dostaje odpowiednie wejście i wyjście do iz command2 i command3:

$ command1 | tee >(command2) >(command3) | command4 

Jednak ta strumieni wyjście Command1, aby command4 jak również, co prowadzi do problemów jak command2 i command3 produkować inny specyfikacja niż command1. Rozwiązanie Mam przybył wydaje hacky, ale to działa:

$ command1 | tee >(command2) >(command3) > /dev/null | command4 

To tłumi Command1 przechodzącej swoje wyjście do command4, podczas zbierania STDOUT z command2 i command3. Działa, ale czuję, że brakuje mi bardziej oczywistego rozwiązania. Czy ja jestem? Przeczytałem dziesiątki wątków i nie znalazłem rozwiązania tego problemu, który działa w moim przypadku użycia, ani nie widziałem opracowania dokładnego problemu dzielenia i ponownego łączenia strumieni (choć nie mogę być pierwszym jeden do poradzenia sobie z tym). Czy powinienem używać tylko nazwanych potoków? Próbowałem, ale miałem problemy z tym, żeby to działało, więc może to inna historia dla innego wątku. Używam bash w RHEL5.8.

+0

Wygląda na to, że twoje pytanie ma rozwiązanie, które działa - więc czy prosisz o inne rozwiązanie? Zazwyczaj ten rodzaj podziału nie pojawia się często w skryptach powłoki, ale często występuje w specjalistycznym narzędziu, takim jak Hadoop-MapReduce - nie sądzę, że znajdziesz coś lepszego niż potok bash. – Soren

+0

@Soren - tak, zastanawiam się, czy istnieje lepsze rozwiązanie. Nie tracę z tym snu, ponieważ moje rozwiązanie wydaje się działać, ale spodziewam się, że istnieje rozwiązanie, które nie wiąże się z przekierowywaniem stdout do/dev/null i jestem ciekawy, gdzie popełniłem błąd, ponieważ może to być pouczające dla mnie (lub innych) w miarę rozwoju. –

Odpowiedz

4

Można odtwarzać takie deskryptory plików;

((date | tee >(wc >&3) | wc) 3>&1) | wc 

lub

((command1 | tee >(command2 >&3) | command3) 3>&1) | command4 

Aby wyjaśnić to tee >(wc >&3) wyjście będzie oryginalne dane na standardowe wyjście, a wyjście wewnętrzną wc Czy wynik na FD 3. Zewnętrzna 3> & 1) będą następnie scal FD3 wyjście z powrotem do STDOUT, więc wyjście z obu wc jest wysyłane do polecenia ogonowania.

JEDNAKŻE nie ma nic w tym potoku (lub w swoim własnym rozwiązaniu), które zagwarantuje, że dane wyjściowe nie zostaną zmanipulowane.To niecałkowite linie z command2 nie zostaną pomieszane z liniami dowodzenia3 - jeśli to jest problem, musisz zrobić jedną z dwóch rzeczy;

  1. Napisz tee program, który wewnętrznie używa popen i czytać każdą linię z powrotem przed wysłaniem kompletnych linii do stdout command4 czytać
  2. Napisz wyjście z command2 i command3 do pliku i wykorzystać cat scalić dane jako wejście do polecenia 4
+0

Dziękuję - właśnie tego szukam. Doceniam również notatkę dotyczącą potencjalnej interkalacji strumieni wyjściowych. Zastanawiam się, czy istnieje bardziej eleganckie rozwiązanie, niż przepisanie 'tee' lub użycie pliku. Może mógłbym użyć 'wait' do przytrzymania polecenia command3 do momentu zakończenia komendy2? –

+0

To rozwiązanie wydaje się działać na bash/ksh/zsh. Czy ktokolwiek wie, jak sprawić, by działał z/bin/static-sh (np. Busybox)? –

0

Zobacz także https://unix.stackexchange.com/questions/28503/how-can-i-send-stdout-to-multiple-commands. Wśród wszystkich odpowiedzi, znalazłem this answer szczególnie pasuje do mojej potrzeby.

Rozwiń trochę @ odpowiedź Soren, w

$ ((date | tee >(wc >&3) | wc) 3>&1) | cat -n 
    1   1  6  29 
    2   1  6  29 

Można to zrobić bez używania tee ale zmienną środowiskową,

$ (z=$(date); (echo "$z"| wc); (echo "$z"| wc)) | cat -n 
    1   1  6  29 
    2   1  6  29 

W moim przypadku zastosowano tę technikę i napisał wiele kompleks skrypt uruchamiany w ramach busybox.

Powiązane problemy