Diff sortie de deux programmes sans fichiers temporaires

Réponses:

211

Utilisez <(command)pour transmettre la sortie d'une commande à un autre programme comme s'il s'agissait d'un nom de fichier. Bash dirige la sortie du programme vers un tube et passe un nom de fichier comme /dev/fd/63à la commande externe.

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

De même, vous pouvez utiliser >(command)si vous souhaitez diriger quelque chose dans une commande.

Cela s'appelle «Process Substitution» dans la page de manuel de Bash.

John Kugelman
la source
1
Un inconvénient à prendre en compte est que si ./a ou ./b échoue, l'appelant ne le découvrira pas.
Alexander Pogrebnyak le
5
L'OP a marqué la question bash, mais pour mémoire, cela ne fonctionne dans aucun autre shell. C'est une extension bash de la norme utilitaire Posix.
DigitalRoss
5
J'ai essayé cette solution avec un programme Java et obtenu cette erreur: -bash: syntax error near unexpected token ('. J'ai essayé à nouveau sans parenthèse et j'ai obtenu -bash: java: No such file or directory. Cela ne fonctionne-t-il pas si la commande a des paramètres?
styfle
1
@DigitalRoss - La solution peut être étendue à d'autres shells en utilisant un alias. Dans tcsh, la laideur suivante fonctionne: alias diffcmd bash -c \'diff \<\(sh -c \!:1\) \<\( sh -c \!:2 \)\'. (Puis par exemple: diffcmd "ls" "ls -a").
Paul Lynch
Pour quiconque se promène hors de Google, cela fonctionne également sous zsh. (De plus, si vous devez alimenter en entrée quelque chose qui appelle fseek, zsh propose =(./a)qui peut être utilisé de manière identique <(./a)mais utilise un fichier temporaire sous le capot, que zsh supprimera pour vous.)
ssokolow
26

En ajoutant aux deux réponses, si vous voulez voir une comparaison côte à côte, utilisez vimdiff:

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

Quelque chose comme ça:

entrez la description de l'image ici

pied cassé
la source
vimdiffcrée des vues de comparaison des différences magnifiques, intelligentes et interactives. Il semble venir avec le vimpackage sur la plupart des systèmes.
Tim Visée
vimdiffmontre également non seulement la ligne qui diffère, mais également le fragment de texte spécifique qui diffère.
Anton Tarasenko
20

Une option serait d'utiliser des tubes nommés (FIFO) :

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

... mais la solution de John Kugelman est beaucoup plus propre.

Martin Clayton
la source
Vous feriez mieux de supprimer les tuyaux nommés après les avoir utilisés, avec rm a_fifo b_fifo.
Franklin Yu
15

Pour toute personne curieuse, voici comment vous effectuez la substitution de processus en utilisant le Fish shell :

Frapper:

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

Poisson:

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

Malheureusement, la mise en œuvre dans les poissons est actuellement insuffisante ; fish se bloque ou utilise un fichier temporaire sur le disque. Vous ne pouvez pas non plus utiliser psub pour la sortie de votre commande.

James McMahon
la source
Cela ne fonctionne pas correctement pour le poisson. Si la sortie des programmes est supérieure à un BUFSIZ, la commande se bloquera. (ou poisson utilisera simplement un fichier temporaire sur le disque)
Evan Benn
7

En ajoutant un peu plus aux réponses déjà bonnes (m'a aidé!):

La commande dockerenvoie son aide àSTD_ERR (c.- -d. Descripteur de fichier 2)

Je voulais voir si docker attachet docker attach --helpdonner le même résultat

$ docker attach

$ docker attach --help

Après avoir tapé ces deux commandes, j'ai fait ce qui suit:

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

!! est le même que! -1 ce qui signifie exécuter la commande 1 avant celle-ci - la dernière commande

! -2 signifie exécuter la commande deux avant celle-ci

2> & 1 signifie envoyer la sortie file_descriptor 2 (STD_ERR) au même endroit que la sortie file_descriptor 1 (STD_OUT)

J'espère que cela a été d'une certaine utilité.

darrenthatcher
la source
0

Pour zsh, l'utilisation =(command)crée automatiquement un fichier temporaire et le remplace =(command)par le chemin du fichier lui-même. Avec la substitution de processus normale, $(command)est remplacé par la sortie de la commande.

Cette fonctionnalité zsh est très utile et peut être utilisée comme ceci pour comparer la sortie de deux commandes à l'aide d'un outil de diff, par exemple Beyond Compare:

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

Pour Beyond Compare, notez que vous devez utiliser bcomppour ce qui précède (au lieu de bcompare) car bcomplance la comparaison et attend qu'elle se termine. Si tu utilisesbcompare , cela lance la comparaison et se termine immédiatement en raison de quoi les fichiers temporaires créés pour stocker la sortie des commandes disparaissent.

En savoir plus ici: http://zsh.sourceforge.net/Intro/intro_7.html

Notez également ceci:

Notez que le shell crée un fichier temporaire et le supprime lorsque la commande est terminée.

et ce qui suit qui est la différence entre $(...)et =(...):

Si vous lisez la page de manuel de zsh, vous remarquerez peut-être que <(...) est une autre forme de substitution de processus similaire à = (...). Il y a une différence importante entre les deux. Dans le cas <(...), le shell crée un tube nommé (FIFO) au lieu d'un fichier. C'est mieux, car cela ne remplit pas le système de fichiers; mais cela ne fonctionne pas dans tous les cas. En fait, si nous avions remplacé = (...) par <(...) dans les exemples ci-dessus, tous auraient cessé de fonctionner sauf pour fgrep -f <(...). Vous ne pouvez pas modifier un tube ou l'ouvrir en tant que dossier de messagerie; fgrep, cependant, n'a aucun problème à lire une liste de mots à partir d'un tube. Vous vous demandez peut-être pourquoi diff <(foo) bar ne fonctionne pas, puisque foo | diff - barre fonctionne; c'est parce que diff crée un fichier temporaire s'il remarque que l'un de ses arguments est -, puis copie son entrée standard dans le fichier temporaire.

Ashutosh Jindal
la source