Canalizar a múltiples archivos en el shell

29

Tengo una aplicación que producirá una gran cantidad de datos que no deseo almacenar en el disco. La aplicación genera principalmente datos que no deseo utilizar, pero un conjunto de información útil que debe dividirse en archivos separados. Por ejemplo, dada la siguiente salida:

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

Podría ejecutar la aplicación tres veces así:

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

Esto me daría lo que quiero, pero tomaría demasiado tiempo. Tampoco quiero volcar todas las salidas en un solo archivo y analizarlo.

¿Hay alguna forma de combinar las tres operaciones que se muestran arriba de tal manera que solo necesito ejecutar la aplicación una vez y aún obtener tres archivos de salida separados?

sj755
fuente

Respuestas:

78

Si tienes tee

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

(desde aquí )

( acerca de la sustitución del proceso )

Aurélien Ooms
fuente
44
Impresionante, esto también podría representarse como:./app | tee >(grep A > A.out) >(grep B > B.out) | grep C > C.out
evilsoup
77
Esta respuesta es actualmente la única correcta, dado el título original de la pregunta "canalización a múltiples procesos".
Acelerado
3
+1. Esta es la respuesta más aplicable en general, ya que no depende del hecho de que el comando de filtrado específico era grep.
ruakh
1
Estoy de acuerdo en que esta es la mejor respuesta para la pregunta planteada y debe marcarse así. Paralelo es otra solución (como se publicó), pero después de hacer algunas comparaciones cronometradas, el ejemplo anterior es más eficiente. Si la operación en su lugar involucraba operaciones altamente intensivas en CPU como la compresión de múltiples archivos o la conversión de múltiples mp3, entonces, sin duda, la solución paralela debería ser más efectiva.
AsymLabs
32

Puedes usar awk

./app | awk '/A/{ print > "A.out"}; /B/{ print > "B.out"}; /C/{ print > "C.out"}'
Rahul Patil
fuente
66
El título de la pregunta es canalizar a múltiples procesos , esta respuesta es sobre "canalización" (envío por expresiones regulares) a múltiples archivos . Dado que esta respuesta fue aceptada, el título de la pregunta debe cambiarse en consecuencia.
Acelerado
@PauloMadeira Tienes razón. ¿Cuál crees que sería un mejor título?
sj755
Sugerí una edición muy pequeña "Pipe to multiple files in the shell", está pendiente de revisión, échale un vistazo. Esperaba eliminar el comentario si era aceptado.
Acelerado
@PauloMadeira - He cambiado el título. No vi su edición, pero tiene razón, el uso de procesos en el título fue incorrecto si esta es la respuesta aceptada.
slm
17

También puedes usar las habilidades de combinación de patrones de tu caparazón :

./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 incluso:

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

Una forma más segura de lidiar con barras invertidas y líneas que comienzan con -:

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

Como @StephaneChazelas señala en los comentarios, esto no es muy eficiente. La mejor solución es probablemente @ AurélienOoms ' .

terdon
fuente
Esto supone la entrada no contiene barras invertidas o espacios en blanco o caracteres comodín, o líneas que comienzan con -n, -e... También va a ser terriblemente ineficiente, ya que significa varias llamadas al sistema por línea (uno read(2)por personaje, el archivo está abierto, la escritura cerrado para cada línea ...). Generalmente, usar while readbucles para procesar texto en shells es una mala práctica.
Stéphane Chazelas
@StephaneChazelas Edité mi respuesta. Debería funcionar con barras invertidas y -netc. ahora. Por lo que puedo decir, ambas versiones funcionan bien con espacios en blanco, ¿me equivoco?
terdon
No, el primer argumento printfes el formato. No hay razón para dejar variables sin citar allí.
Stéphane Chazelas
Esto también se dividirá en bash (y otros shells que usan cstrings de manera similar) si hay valores nulos en la entrada.
Chris Down
9

Si tiene varios núcleos y desea que los procesos estén en paralelo, puede hacer lo siguiente:

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

Esto generará tres procesos en núcleos paralelos. Si desea que haya algún resultado en la consola o un archivo maestro, tiene la ventaja de mantener el resultado en algún orden, en lugar de mezclarlo.

La utilidad gnu paralela de Ole Tange puede obtenerse de la mayoría de los repositorios bajo el nombre paralelo o moreutils . La fuente se puede obtener de Savannah.gnu.org . También hay un video instructivo introductorio aquí .

Apéndice

Usando la versión más reciente de paralelo (no necesariamente la versión en su repositorio de distribución), puede usar la construcción más elegante:

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

Lo que logra el resultado de ejecutar uno ./app y 3 procesos grep paralelos en núcleos o subprocesos separados (según lo determinado por el mismo paralelo, también considere que -j3 es opcional, pero se proporciona en este ejemplo con fines instructivos).

La versión más nueva de paralelo se puede obtener haciendo:

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

Luego, el desempaquetado habitual, cd to parallel- {date}, ./configure && make, sudo make install. Esto instalará paralelo, página de manual paralela y página de manual paralelo_tutorial.

AsymLabs
fuente
7

Aquí hay uno en 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/'
troydj
fuente
1
sed -ne/A/w\ A.out -e/B/w\ B.out -e/C/p <in >C.out

... si <ines legible, los tres archivos se truncarán antes de que se les escriba algo.

mikeserv
fuente