script salida redireccionamiento programas programacion operaciones manejo guardar ejemplos comandos comando cadenas aritmeticas archivo bash diff pipeline

salida - ¿Cómo se pueden diferenciar dos tuberías en Bash?



programacion shell linux ejemplos (3)

¿Cómo se pueden diff dos tuberías sin usar archivos temporales en Bash? Supongamos que tiene dos canales de comando:

foo | bar baz | quux

Y desea encontrar la diff en sus salidas. Una solución obviamente sería:

foo | bar > /tmp/a baz | quux > /tmp/b diff /tmp/a /tmp/b

¿Es posible hacerlo sin el uso de archivos temporales en Bash? Puede deshacerse de un archivo temporal por tuberías en una de las tuberías para diff:

foo | bar > /tmp/a baz | quux | diff /tmp/a -

Pero no puede canalizar ambas tuberías en diff simultáneamente (no de manera obvia, al menos). ¿Hay algún truco ingenioso que involucre /dev/fd para hacer esto sin usar archivos temporales?


Algunas personas que llegan a esta página pueden estar buscando una diferencia línea por línea, para lo cual se debe usar comm o grep -f su lugar.

Una cosa para señalar es que, en todos los ejemplos de las respuestas, los diffs en realidad no comenzarán hasta que ambas transmisiones hayan terminado. Pruebe esto con, por ejemplo:

comm -23 <(seq 100 | sort) <(seq 10 20 && sleep 5 && seq 20 30 | sort)

Si esto es un problema, puede probar sd (stream diff), que no requiere clasificación (como comm does) ni procesar la sustitución como en los ejemplos anteriores, es órdenes o magnitud más rápida que grep -f y admite flujos infinitos.

El ejemplo de prueba que propongo se escribiría así en sd :

seq 100 | sd ''seq 10 20 && sleep 5 && seq 20 30''

Pero la diferencia es que la secuencia seq 100 se difería con la secuencia seq 10 inmediato. Tenga en cuenta que, si una de las secuencias es una tail -f , la diferencia no se puede hacer con la sustitución del proceso.

Aquí hay una entrada de blogpost que escribí sobre transmisiones difusas en la terminal, que presenta sd .


En bash, puede usar subcapas para ejecutar las interconexiones de comando individualmente, al encerrar la interconexión entre paréntesis. Luego puede prefijarlos con <para crear conductos con nombre anónimos que luego puede pasar a diff.

Por ejemplo:

diff <(foo | bar) <(baz | quux)

Las canalizaciones nombradas anónimas son administradas por bash, por lo que se crean y destruyen automáticamente (a diferencia de los archivos temporales).


Una línea con 2 archivos tmp (no lo que desea) sería:

foo | bar > file1.txt && baz | quux > file2.txt && diff file1.txt file2.txt

Con bash , puedes probar sin embargo:

diff <(foo | bar) <(baz | quux) foo | bar | diff - <(baz | quux) # or only use process substitution once

La segunda versión te recordará más claramente qué entrada fue la que, al mostrar
-- /dev/stdin vs. ++ /dev/fd/63 o algo así, en lugar de dos fds numerados.

Ni siquiera aparecerá una tubería con nombre en el sistema de archivos, al menos en sistemas operativos donde bash puede implementar la sustitución de procesos utilizando nombres de archivo como /dev/fd/63 para obtener un nombre de archivo que el comando pueda abrir y leer para leer realmente de un abrir el descriptor de archivo que bash configuró antes de ejecutar el comando. (es decir, bash utiliza la pipe(2) antes de la horquilla, y luego dup2 para redirigir la salida de quux a un descriptor de archivo de entrada para diff , en fd 63.)

En un sistema sin "mágico" /dev/fd o /proc/self/fd , bash podría usar canalizaciones con nombre para implementar la sustitución del proceso, pero al menos las gestionaría él mismo, a diferencia de los archivos temporales, y sus datos no serían escrito en el sistema de archivos.

Puede comprobar cómo bash implementa la sustitución de procesos con echo <(true) para imprimir el nombre del archivo en lugar de leer desde allí. Imprime /dev/fd/63 en un sistema Linux típico. O para obtener más detalles sobre exactamente qué sistema usa bash, este comando en un sistema Linux rastreará las llamadas al sistema de archivos y descriptores de archivos

strace -f -efile,desc,clone,execve bash -c ''/bin/true | diff -u - <(/bin/true)''

Sin bash, podrías hacer un tubo con nombre . Use - para decirle a diff que lea una entrada de STDIN, y use la tubería con nombre como la otra:

mkfifo file1_pipe.txt foo|bar > file1_pipe.txt && baz | quux | diff file1_pipe.txt - && rm file1_pipe.txt

Tenga en cuenta que solo puede canalizar una salida a múltiples entradas con el comando tee:

ls *.txt | tee /dev/tty txtlist.txt

El comando anterior muestra el resultado de ls * .txt en el terminal y lo envía al archivo de texto txtlist.txt.

Pero con la sustitución de procesos, puede usar tee para alimentar los mismos datos en múltiples tuberías:

cat *.txt | tee >(foo | bar > result1.txt) >(baz | quux > result2.txt) | foobar