Salida diferida de dos programas sin archivos temporales

Respuestas:

211

Use <(command)para pasar la salida de un comando a otro programa como si fuera un nombre de archivo. Bash canaliza la salida del programa a una canalización y pasa un nombre de archivo como /dev/fd/63al comando externo.

diff <(./a) <(./b)

Del mismo modo, puede usar >(command)si desea canalizar algo en un comando.

Esto se llama "Sustitución de proceso" en la página de manual de Bash.

John Kugelman
fuente
1
Un inconveniente a tener en cuenta es que si ./a o ./b falla, la persona que llama no lo descubrirá.
Alexander Pogrebnyak
55
El OP etiquetó la pregunta bash, pero para el registro, esto no funciona en ningún otro shell. Es una extensión bash del estándar de utilidad Posix.
DigitalRoss
55
Probé esta solución con un programa Java y dio este mensaje: -bash: syntax error near unexpected token ('. Lo intenté de nuevo sin paréntesis y lo conseguí -bash: java: No such file or directory. ¿No funciona si el comando tiene parámetros?
styfle
1
@DigitalRoss: la solución se puede extender a otros shells utilizando un alias. En tcsh, la siguiente fealdad funciona: alias diffcmd bash -c \'diff \<\(sh -c \!:1\) \<\( sh -c \!:2 \)\'. (Entonces, por ejemplo: diffcmd "ls" "ls -a").
Paul Lynch
Para cualquiera que deambule fuera de Google, esto también funciona bajo zsh. (También, si necesita de entrada de alimentación en algo que se pide fseek, ofertas zsh =(./a)que se pueden utilizar de forma idéntica a <(./a), pero utiliza un archivo temporal bajo el capó, que zsh eliminará para usted.)
ssokolow
26

Agregando a ambas respuestas, si desea ver una comparación lado a lado, use vimdiff:

vimdiff <(./a) <(./b)

Algo como esto:

ingrese la descripción de la imagen aquí

pie roto
fuente
vimdiffcrea vistas de comparación de diferencias hermosas, inteligentes e interactivas. Parece venir con el vimpaquete en la mayoría de los sistemas.
Tim Visée
vimdifftambién muestra no solo la línea que difiere sino también el fragmento de texto específico que difiere.
Anton Tarasenko
20

Una opción sería utilizar tuberías con nombre (FIFO) :

mkfifo a_fifo b_fifo
./a > a_fifo &
./b > b_fifo &
diff a_fifo b_fifo

... pero la solución de John Kugelman es mucho más limpia.

Martin Clayton
fuente
Será mejor que elimines las tuberías con nombre después de usarlas, con rm a_fifo b_fifo.
Franklin Yu
15

Para cualquier persona curiosa, así es como se realiza la sustitución del proceso al usar el shell Fish :

Intento:

diff <(./a) <(./b)

Pez:

diff (./a | psub) (./b | psub)

Desafortunadamente, la implementación en peces es actualmente deficiente ; fish se colgará o usará un archivo temporal en el disco. Tampoco puede usar psub para la salida de su comando.

James McMahon
fuente
Actualmente, esto no funciona correctamente en peces. Si la salida de los programas es mayor que un BUFSIZ, el comando se bloqueará. (o fish en realidad solo usará un archivo temporal en el disco)
Evan Benn
7

Agregar un poco más a las respuestas ya buenas (¡me ayudó!):

El comando dockerenvía su ayuda a STD_ERR(es decir, descriptor de archivo 2)

Quería ver si docker attachy docker attach --helpdaba el mismo resultado

$ docker attach

$ docker attach --help

Después de escribir esos dos comandos, hice lo siguiente:

$ diff <(!-2 2>&1) <(!! 2>&1)

!! es lo mismo que! -1, lo que significa ejecutar el comando 1 antes de este: el último comando

! -2 significa ejecutar el comando dos antes de este

2> & 1 significa enviar la salida file_descriptor 2 (STD_ERR) al mismo lugar que la salida file_descriptor 1 (STD_OUT)

Espero que esto haya sido de alguna utilidad.

Darrenthatcher
fuente
0

Para zsh, el uso =(command)crea automáticamente un archivo temporal y lo reemplaza =(command)con la ruta del archivo en sí. Con la sustitución de proceso normal, $(command)se reemplaza con la salida del comando.

Esta función zsh es muy útil y se puede usar para comparar la salida de dos comandos con una herramienta diff, por ejemplo, Beyond Compare:

bcomp  =(ulimit -Sa | sort) =(ulimit -Ha | sort)

Para Beyond Compare, tenga en cuenta que debe usar bcomplo anterior (en lugar de bcompare) desde que bcompinicia la comparación y espera a que se complete. Si lo usa bcompare, inicia la comparación e inmediatamente se cierra debido a que los archivos temporales creados para almacenar la salida de los comandos desaparecen.

Lea más aquí: http://zsh.sourceforge.net/Intro/intro_7.html

También tenga en cuenta esto:

Tenga en cuenta que el shell crea un archivo temporal y lo elimina cuando finaliza el comando.

y el siguiente, que es la diferencia entre $(...)y =(...):

Si lee la página de manual de zsh, puede notar que <(...) es otra forma de sustitución de proceso que es similar a = (...). Hay una diferencia importante entre los dos. En el caso <(...), el shell crea una tubería con nombre (FIFO) en lugar de un archivo. Esto es mejor, ya que no llena el sistema de archivos; pero no funciona en todos los casos. De hecho, si hubiéramos reemplazado = (...) con <(...) en los ejemplos anteriores, todos habrían dejado de funcionar, excepto fgrep -f <(...). No puede editar una tubería o abrirla como una carpeta de correo; fgrep, sin embargo, no tiene problemas para leer una lista de palabras de una tubería. Quizás se pregunte por qué la barra diff <(foo) no funciona, ya que foo | trabajos de barra diferencial; esto se debe a que diff crea un archivo temporal si nota que uno de sus argumentos es -, y luego copia su entrada estándar al archivo temporal.

Ashutosh Jindal
fuente