script mac macos shell unix pipe

macos - mac - OS X/Linux: ¿tubería en dos procesos?



bash script mac (6)

Se acerca de

program1 | program2

y

program1 | tee outputfile | program2

¿pero hay una manera de alimentar la salida de program1 tanto en program2 como en program3?


Introducción sobre la paralelización

Esto parece trivial, pero hacer esto no solo es posible, también hacerlo generará un proceso simultáneo o simultáneo .

Es posible que tenga que cuidar algunos efectos particulares, como el orden de ejecución, el tiempo de ejecución, etc.

Hay algunas muestras al final de este post.

Respuesta compatible primero

Como esta pregunta está marcada como shell y unix , primero daré una respuesta compatible con POSIX . (Para los bashismos, ve más lejos .)

Sí, hay una manera de usar tuberías sin nombre .

En esta muestra, generaré un rango de 100,000 números, los aleatorizaré y comprimiré el resultado utilizando 4 herramientas de compresión diferentes para comparar la relación de compresión ...

Para esto primero correré la preparación:

GZIP_CMD=`which gzip` BZIP2_CMD=`which bzip2` LZMA_CMD=`which lzma` XZ_CMD=`which xz` MD5SUM_CMD=`which md5sum` SED_CMD=`which sed`

Nota: la especificación de la ruta completa a los comandos evita que algún intérprete de shell (como busybox) ejecute el compresor incorporado. Y hacerlo de manera asegurará que la misma sintaxis se ejecute independientemente de la instalación del sistema operativo (las rutas podrían ser diferentes entre MacOs, Ubuntu, RedHat, HP-Ux y así ...).

La sintaxis NN>&1 (donde NN es un número entre 3 y 63) genera una tubería sin nombre que se puede encontrar en /dev/fd/NN . (Los descriptores de archivos 0 a 2 ya están abiertos para 0: STDIN, 1: STDOUT y 2: STDERR).

Prueba esto (probado en dash , busybox y bash ):

(((( seq 1 100000 | shuf | tee /dev/fd/4 /dev/fd/5 /dev/fd/6 /dev/fd/7 | $GZIP_CMD >/tmp/tst.gz ) 4>&1 | $BZIP2_CMD >/tmp/tst.bz2 ) 5>&1 | $LZMA_CMD >/tmp/tst.lzma ) 6>&1 | $XZ_CMD >/tmp/tst.xz ) 7>&1 | $MD5SUM_CMD

o más legible:

GZIP_CMD=`which gzip` BZIP2_CMD=`which bzip2` LZMA_CMD=`which lzma` XZ_CMD=`which xz` MD5SUM_CMD=`which md5sum` ( ( ( ( seq 1 100000 | shuf | tee /dev/fd/4 /dev/fd/5 /dev/fd/6 /dev/fd/7 | $GZIP_CMD >/tmp/tst.gz ) 4>&1 | $BZIP2_CMD >/tmp/tst.bz2 ) 5>&1 | $LZMA_CMD >/tmp/tst.lzma ) 6>&1 | $XZ_CMD >/tmp/tst.xz ) 7>&1 | $MD5SUM_CMD 2e67f6ad33745dc5134767f0954cbdd6 -

Como debe hacer una colocación aleatoria, si intenta esto, debe obtener un resultado diferente,

ls -ltrS /tmp/tst.* -rw-r--r-- 1 user user 230516 oct 1 22:14 /tmp/tst.bz2 -rw-r--r-- 1 user user 254811 oct 1 22:14 /tmp/tst.lzma -rw-r--r-- 1 user user 254892 oct 1 22:14 /tmp/tst.xz -rw-r--r-- 1 user user 275003 oct 1 22:14 /tmp/tst.gz

pero debes poder comparar sumas de comprobación md5 :

SED_CMD=`which sed` for chk in gz:$GZIP_CMD bz2:$BZIP2_CMD lzma:$LZMA_CMD xz:$XZ_CMD;do ${chk#*:} -d < /tmp/tst.${chk%:*} | $MD5SUM_CMD | $SED_CMD s/-$/tst.${chk%:*}/ done 2e67f6ad33745dc5134767f0954cbdd6 tst.gz 2e67f6ad33745dc5134767f0954cbdd6 tst.bz2 2e67f6ad33745dc5134767f0954cbdd6 tst.lzma 2e67f6ad33745dc5134767f0954cbdd6 tst.xz

Usando funciones de bash

Usando algunos bashims , esto podría verse mejor, para el uso de muestra /dev/fd/{4,5,6,7} , en lugar de tee /dev/fd/4 /dev/fd/5 /...

(((( seq 1 100000 | shuf | tee /dev/fd/{4,5,6,7} | gzip >/tmp/tst.gz ) 4>&1 | bzip2 >/tmp/tst.bz2 ) 5>&1 | lzma >/tmp/tst.lzma ) 6>&1 | xz >/tmp/tst.xz ) 7>&1 | md5sum 29078875555e113b31bd1ae876937d4b -

trabajará igual

Revision final

Esto no creará ningún archivo, pero le permitiría comparar el tamaño de un rango comprimido de enteros ordenados, entre 4 herramientas de compresión diferentes (por diversión, usé 4 formas diferentes para formatear la salida):

( ( ( ( ( seq 1 100000 | tee /dev/fd/{4,5,6,7} | gzip | wc -c | sed s/^/gzip:/ / / >&3 ) 4>&1 | bzip2 | wc -c | xargs printf "bzip2: %s/n" >&3 ) 5>&1 | lzma | wc -c | perl -pe ''s/^/lzma: /'' >&3 ) 6>&1 | xz | wc -c | awk ''{printf "xz: %9s/n",$1}'' >&3 ) 7>&1 | wc -c ) 3>&1 gzip: 215157 bzip2: 124009 lzma: 17948 xz: 17992 588895

Esto demuestra cómo usar stdin y stdout redirigido en subshell y fusionado en la consola para la salida final.

Sintaxis >(...) y <(...)

Las versiones recientes de bash permiten una nueva característica de sintaxis.

seq 1 100000 | wc -l 100000 seq 1 100000 > >( wc -l ) 100000 wc -l < <( seq 1 100000 ) 100000

Como | es un conducto sin nombre a /dev/fd/0 , la sintaxis <() genera un conducto sin nombre temporal con el descriptor de archivo /dev/fd/XX .

md5sum <(zcat /tmp/tst.gz) <(bzcat /tmp/tst.bz2) <( lzcat /tmp/tst.lzma) <(xzcat /tmp/tst.xz) 29078875555e113b31bd1ae876937d4b /dev/fd/63 29078875555e113b31bd1ae876937d4b /dev/fd/62 29078875555e113b31bd1ae876937d4b /dev/fd/61 29078875555e113b31bd1ae876937d4b /dev/fd/60

Demo mas sofisticada

Esto requiere que se instale la utilidad de file GNU . Determinará el comando que se ejecutará por extensión o tipo de archivo.

for file in /tmp/tst.*;do cmd=$(which ${file##*.}) || { cmd=$(file -b --mime-type $file) cmd=$(which ${cmd#*-}) } read -a md5 < <($cmd -d <$file|md5sum) echo $md5 / $file done 29078875555e113b31bd1ae876937d4b /tmp/tst.bz2 29078875555e113b31bd1ae876937d4b /tmp/tst.gz 29078875555e113b31bd1ae876937d4b /tmp/tst.lzma 29078875555e113b31bd1ae876937d4b /tmp/tst.xz

Esto te permite hacer lo mismo anterior siguiendo la sintaxis:

seq 1 100000 | shuf | tee >( echo gzip. $( gzip | wc -c ) ) >( echo gzip, $( wc -c < <(gzip)) ) >( gzip | wc -c | sed s/^/gzip:/ / / ) >( bzip2 | wc -c | xargs printf "bzip2: %s/n" ) >( lzma | wc -c | perl -pe ''s/^/lzma: /'' ) >( xz | wc -c | awk ''{printf "xz: %9s/n",$1}'' ) > >( echo raw: $(wc -c) ) | xargs printf "%-8s %9d/n" raw: 588895 xz: 254556 lzma: 254472 bzip2: 231111 gzip: 274867 gzip, 274867 gzip. 274867

Tenga en cuenta que he utilizado diferentes maneras de calcular gzip comprimido cuenta.

Nota Debido a que esta operación se realizó simultáneamente , el orden de salida dependerá del tiempo requerido por cada comando.

Ir más allá sobre la paralelización

Si ejecuta una computadora con varios núcleos o varios procesadores, intente comparar esto:

i=1 time for file in /tmp/tst.*;do cmd=$(which ${file##*.}) || { cmd=$(file -b --mime-type $file) cmd=$(which ${cmd#*-}) } read -a md5 < <($cmd -d <$file|md5sum) echo $((i++)) $md5 / $file done | cat -n

lo que puede hacer:

1 1 29078875555e113b31bd1ae876937d4b /tmp/tst.bz2 2 2 29078875555e113b31bd1ae876937d4b /tmp/tst.gz 3 3 29078875555e113b31bd1ae876937d4b /tmp/tst.lzma 4 4 29078875555e113b31bd1ae876937d4b /tmp/tst.xz real 0m0.101s

con este:

time ( i=1 pids=() for file in /tmp/tst.*;do cmd=$(which ${file##*.}) || { cmd=$(file -b --mime-type $file) cmd=$(which ${cmd#*-}) } ( read -a md5 < <($cmd -d <$file|md5sum) echo $i $md5 / $file ) & pids+=($!) ((i++)) done wait ${pids[@]} ) | cat -n

podria dar:

1 2 29078875555e113b31bd1ae876937d4b /tmp/tst.gz 2 1 29078875555e113b31bd1ae876937d4b /tmp/tst.bz2 3 4 29078875555e113b31bd1ae876937d4b /tmp/tst.xz 4 3 29078875555e113b31bd1ae876937d4b /tmp/tst.lzma real 0m0.070s

donde el orden dependerá del tipo utilizado por cada horquilla .


El manual de bash menciona cómo emula la sintaxis >(...) usando canalizaciones con nombre o descriptores de archivo con nombre, por lo que si no quiere depender de bash , quizás pueda hacerlo manualmente en su script.

mknod FIFO program3 < FIFO & program1 | tee FIFO | program2 wait rm FIFO


Otras respuestas introducen el concepto. Aquí hay una demostración real:

$ echo "Leeroy Jenkins" | tee >(md5sum > out1) >(sha1sum > out2) > out3 $ cat out1 11e001d91e4badcff8fe22aea05a7458 - $ echo "Leeroy Jenkins" | md5sum 11e001d91e4badcff8fe22aea05a7458 - $ cat out2 5ed25619ce04b421fab94f57438d6502c66851c1 - $ echo "Leeroy Jenkins" | sha1sum 5ed25619ce04b421fab94f57438d6502c66851c1 - $ cat out3 Leeroy Jenkins

Por supuesto que puedes > /dev/null lugar de out3.


Puedes hacer esto con tee sustitución de tee y proceso.

program1 | tee >(program2) >(program3)

La salida de program1 se canalizará a lo que esté dentro de ( ) , en este caso program3 y program3 .


Siempre puede intentar guardar la salida de program1 en un archivo y luego introducirla en las entradas program2 y program3.

program1 > temp; program2 < temp; program3 < temp;


use (;) sintaxis ... pruebe ps aux | (head -n 1; tail -n 1) ps aux | (head -n 1; tail -n 1)