2014-04-23 20 views
6

Sto lavorando su una pipeline che ha alcuni punti di ramificazione che in seguito merge-- sembrano qualcosa di simile:Come posso dividere e ricongiungere STDOUT da più processi?

  command2 
     /  \ 
command1   command4 
     \  /
     command3 

Ogni comando scrive su STDOUT e accetta l'input via STDIN. STDOUT dal comando 1 deve essere passato a sia a sia a comando2 e comando3, che vengono eseguiti in sequenza e il loro output deve essere effettivamente concatenato e passato a command4. Inizialmente ho pensato che qualcosa di simile potrebbe funzionare:

$ command1 | (command2; command3) | command4 

che non funziona, però, come solo STDOUT da comando2 viene passato al comando 4, e quando rimuovo Command4 è evidente che Command3 non viene superato il flusso appropriato da command1 - in altre parole, è come se command2 stia esaurendo o consumando il flusso. Ottengo lo stesso risultato con {command2; comando3; } nel mezzo pure. Così ho pensato che dovrei usare 'tee' with process substitution, e provato questo:

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

Ma sorprendentemente che non ha funzionato sia - sembra che l'uscita del command1 e l'output di comando2 sono convogliato in Command3, che genera errori e solo l'output di command3 viene convogliato in command4. Ho trovato che il seguente ottiene l'input e l'output appropriato da e per command2 e Command3:

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

Tuttavia, questo torrenti l'uscita di Command1 Command4 pure, che porta a problemi come command2 e Command3 produrre una diversa specifica di comando1. La soluzione che ho arrivato sembra hacky, ma funziona:

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

che sopprime command1 passando la sua uscita a Command4, mentre la raccolta STDOUT da command2 e Command3. Funziona, ma mi sento come se mi mancasse una soluzione più ovvia. Sono io? Ho letto dozzine di thread e non ho trovato una soluzione a questo problema che funzioni nel mio caso d'uso, né ho visto un'elaborazione del problema esatto di divisione e ri-unione di stream (anche se non posso essere il primo uno per affrontare questo). Dovrei usare solo pipe con nome? Ho provato ma ho avuto difficoltà a farlo funzionare, quindi forse questa è un'altra storia per un'altra discussione. Sto usando bash in RHEL5.8.

+0

Sembra che la tua domanda abbia una soluzione _che funziona_ - quindi stai chiedendo una soluzione diversa? In genere quel tipo di suddivisione non appare frequentemente negli script di shell, ma lo fa spesso in strumenti specializzati come Hadoop-MapReduce - Non credo che troveremo qualcosa di meglio come pipeline bash. – Soren

+0

@ Soren - sì, mi chiedo se c'è una soluzione migliore. Non sto perdendo il sonno perché la mia soluzione sembra funzionare, ma mi aspetto che ci sia una soluzione che non implichi il reindirizzamento dello stdout a/dev/null e sono curioso di sapere dove ho sbagliato perché potrebbe essere informativo per me (o altri) mentre continuo a svilupparmi. –

risposta

4

È possibile giocare con descrittori di file come questo;

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

o

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

Per spiegare, cioè tee >(wc >&3) stamperà i dati originali sul stdout, e l'interno uscita wc volontà il risultato sul FD 3. L'esterno 3> & 1) sarà quindi unire l'output FD3 in STDOUT in modo che l'output di entrambi i wc venga inviato al comando tailing.

TUTTAVIA, non c'è nulla in questa pipeline (o quella nella propria soluzione) che garantirà che l'output non verrà alterato.Quelle linee incomplete da comando2 non saranno confuse con le righe di comando3 - se questa è una preoccupazione, dovrai fare una delle due cose;

  1. Scrivi la tua tee programma che usa internamente popen e leggere ogni linea di fondo prima di inviare linee complete per stdout per Command4 leggere
  2. scrivere l'output da command2 e Command3 in un file e utilizzare cat per unire i dati come input per il comando4
+0

Grazie - questo è esattamente quello che sto cercando. Apprezzo anche la nota riguardante la potenziale intercalazione dei flussi di output. Mi chiedo se c'è una soluzione più elegante di riscrivere 'tee' o usare un file. Forse potrei usare 'wait' per mantenere il comando 3 fino a quando il comando2 non sarà finito? –

+0

Questa soluzione sembra funzionare su bash/ksh/zsh. Qualcuno sa come farlo funzionare con/bin/static-sh (cioè busybox)? –

0

Vedere anche https://unix.stackexchange.com/questions/28503/how-can-i-send-stdout-to-multiple-commands. Tra tutte le risposte, ho trovato this answer particolarmente adatto alle mie esigenze.

espandere un po '@ risposta di Soren,

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

Si può fare senza l'utilizzo di tee, ma una variabile d'ambiente,

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

Nel mio caso, ho applicato questa tecnica e ha scritto una più complessa script che gira sotto busybox.