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)