Esegui il pipe su più file nella shell


29

Ho un'applicazione che produrrà una grande quantità di dati che non desidero archiviare sul disco. L'applicazione produce principalmente dati che non desidero utilizzare, ma una serie di informazioni utili che devono essere suddivise in file separati. Ad esempio, dato il seguente output:

JUNK
JUNK
JUNK
JUNK
A 1
JUNK
B 5
C 1
JUNK

Potrei eseguire l'applicazione tre volte in questo modo:

./app | grep A > A.out
./app | grep B > B.out
./app | grep C > C.out

Questo mi procurerebbe quello che voglio, ma richiederebbe troppo tempo. Inoltre, non voglio scaricare tutti gli output in un singolo file e analizzarlo.

Esiste un modo per combinare le tre operazioni sopra indicate in modo tale che ho solo bisogno di eseguire l'applicazione una volta e ottenere ancora tre file di output separati?

Risposte:


78

Se hai il tee

./app | tee >(grep A > A.out) >(grep B > B.out) >(grep C > C.out) > /dev/null

(da qui )

( sulla sostituzione del processo )


4
Fantastico, questo potrebbe anche essere reso come:./app | tee >(grep A > A.out) >(grep B > B.out) | grep C > C.out
evilsoup

7
Questa risposta è attualmente l'unica accurata, dato il titolo originale della domanda "pipe to multiple processi".
acelent,

3
+1. Questa è la risposta più generalmente applicabile, poiché non dipende dal fatto che fosse il comando di filtro specifico grep.
Ruakh,

1
Concordo sul fatto che questa è la migliore risposta alla domanda posta e che dovrebbe essere contrassegnata così. Parallelo è un'altra soluzione (come pubblicata) ma dopo aver fatto alcuni confronti a tempo l'esempio sopra è più efficiente. Se invece l'operazione ha comportato operazioni ad alta intensità di CPU come la compressione di più file o la conversione di più mp3, senza dubbio la soluzione parallela dovrebbe dimostrarsi più efficace.
AsymLabs,

32

Puoi usare awk

./app | awk '/A/{ print > "A.out"}; /B/{ print > "B.out"}; /C/{ print > "C.out"}'

6
Il titolo della domanda è pipe per più processi , questa risposta riguarda "piping" (invio tramite regex) a più file . Poiché questa risposta è stata accettata, il titolo della domanda dovrebbe essere modificato di conseguenza.
acelent,

@PauloMadeira Hai ragione. Quale pensi che sarebbe un titolo migliore?
sj755,

Ho suggerito una modifica molto piccola "Pipe a più file nella shell", è in attesa di revisione, dai un'occhiata. Mi aspettavo di rimuovere il commento se fosse stato accettato.
acelent,

@PauloMadeira - Ho cambiato il titolo. Non hai visto la tua modifica, ma hai ragione, l'uso dei processi nel titolo era errato se questa è la risposta accettata.
slm

17

Puoi anche usare le abilità di corrispondenza del modello della tua shell :

./app | while read line; do 
     [[ "$line" =~ A ]] && echo $line >> A.out; 
     [[ "$line" =~ B ]] && echo $line >> B.out; 
     [[ "$line" =~ C ]] && echo $line >> C.out; 
 done

O anche:

./app | while read line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && echo $line >> "$foo".out; 
  done; done

Un modo più sicuro che può gestire barre rovesciate e linee che iniziano con -:

./app | while IFS= read -r line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && printf -- "$line\n" >> "$foo".out; 
  done; done

Come sottolinea @StephaneChazelas nei commenti, questo non è molto efficace. La soluzione migliore è probabilmente @ AurélienOoms ' .


Ciò presuppone che l'input non contenga barre rovesciate o spazi vuoti o caratteri jolly o righe che iniziano con -n, -e... Sarà anche terribilmente inefficiente in quanto significa più chiamate di sistema per riga (una read(2)per carattere, il file è aperto, la scrittura chiuso per ogni riga ...). Generalmente, usare i while readloop per elaborare il testo nelle shell è una cattiva pratica.
Stéphane Chazelas,

@StephaneChazelas Ho modificato la mia risposta. Ora dovrebbe funzionare con barre rovesciate -necc. Per quanto posso dire che entrambe le versioni funzionano bene con gli spazi, però, sbaglio?
terdon

No, il primo argomento printfè il formato. Non c'è motivo di lasciarti variabili non quotate.
Stéphane Chazelas,

Ciò interromperà anche bash (e altre shell che usano cstrings in modo simile) se ci sono valori null nell'input.
Chris Down,

9

Se hai più core e vuoi che i processi siano in parallelo, puoi fare:

parallel -j 3 -- './app | grep A > A.out' './app | grep B > B.out' './app | grep C > C.out'

Ciò genererà tre processi in core paralleli. Se vuoi che ci sia un po 'di output sulla console, o un file master, ha il vantaggio di mantenere l'output in un certo ordine, piuttosto che mescolarlo.

L'utilità gnu parallela di Ole Tange può essere ottenuta dalla maggior parte dei repository con il nome parallel o moreutils . La fonte può essere ottenuta da Savannah.gnu.org . Anche un video introduttivo è qui .

appendice

Utilizzando la versione più recente di parallel (non necessariamente la versione nel repository di distribuzione), è possibile utilizzare il costrutto più elegante:

./app | parallel -j3 -k --pipe 'grep {1} >> {1}.log' ::: 'A' 'B' 'C'

Il che ottiene il risultato dell'esecuzione di uno ./app e 3 processi grep paralleli in core o thread separati (come determinato dallo stesso parallelo, considera anche -j3 come opzionale, ma viene fornito in questo esempio a scopo istruttivo).

La versione più recente di parallel può essere ottenuta facendo:

wget http://ftpmirror.gnu.org/parallel/parallel-20131022.tar.bz2

Quindi il solito decomprimere, cd in parallel- {date}, ./configure && make, sudo make install. Questo installerà parallel, man page parallel e man page parallel_tutorial.


7

Eccone uno in Perl:

./app | perl -ne 'BEGIN {open(FDA, ">A.out") and 
                         open(FDB, ">B.out") and 
                         open(FDC, ">C.out") or die("Cannot open files: $!\n")} 
                  print FDA $_ if /A/; print FDB $_ if /B/; print FDC $_ if /C/'

1
sed -ne/A/w\ A.out -e/B/w\ B.out -e/C/p <in >C.out

... se <inè leggibile, tutti e tre gli outfile verranno troncati prima che qualcosa venga scritto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.