2008-12-05 8 views
110

Comment pouvez-vous diff deux pipelines sans utiliser de fichiers temporaires dans Bash? Disons que vous avez deux pipelines de commande:Comment pouvez-vous comparer deux pipelines dans Bash?

foo | bar 
baz | quux 

Et vous voulez trouver le diff dans leurs sorties. Une solution serait évidemment:

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

Est-il possible de le faire sans l'utilisation de fichiers temporaires Bash? Vous pouvez vous débarrasser d'un fichier temporaire par une canalisation dans l'un des pipelines de diff:

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

Mais vous ne pouvez pas conduit les deux conduites dans diff simultanément (en aucune manière évidente, au moins). Y a-t-il un truc intelligent impliquant /dev/fd pour ce faire sans utiliser de fichiers temporaires?

Répondre

112

une ligne avec 2 fichiers tmp (pas ce que vous voulez) serait:

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

Avec bash, vous pouvez essayer si:

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

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

La 2ème version sera plus clairement vous rappeler quelle entrée était qui, en montrant
-- /dev/stdin par rapport à ++ /dev/fd/63 ou quelque chose, au lieu de deux fds numérotés.


Pas même un tube nommé apparaît dans le système de fichiers, au moins sur les systèmes d'exploitation où bash peut mettre en œuvre la substitution de processus en utilisant des noms comme /dev/fd/63 pour obtenir un nom de fichier que la commande peut ouvrir et lire à lire en fait à partir d'un descripteur de fichier déjà ouvert que bash a mis en place avant l'exécution de la commande. (C.-à-bash utilise pipe(2) avant la fourche, puis dup2 pour rediriger de la sortie de quux à un descripteur de fichier d'entrée pour diff, sur fd 63.)

Sur un système sans « magique » /dev/fd ou /proc/self/fd, bash peut utiliser Des canaux nommés pour implémenter la substitution de processus, mais au moins les gérer eux-mêmes, contrairement aux fichiers temporaires, et vos données ne seraient pas écrites dans le système de fichiers.

Vous pouvez vérifier comment bash implémente la substitution de processus avec echo <(true) pour imprimer le nom de fichier au lieu d'en lire. Il imprime /dev/fd/63 sur un système Linux typique. Ou pour plus de détails sur exactement ce que le système appelle bash utilise, cette commande sur un système Linux le fichier de trace et système descripteur de fichier appelle

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

Sans bash, vous pouvez faire une pipe nommé.Utilisez - pour dire diff lire une entrée de STDIN et utiliser le canal nommé comme l'autre:

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

Notez que vous ne pouvez diriger une sortie à entrées multiples avec la commande tee :

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

les affichages de commande ci-dessus la sortie de ls * txt à la borne et les sorties dans le fichier texte txtlist.txt.

Mais avec la substitution de processus, vous pouvez utiliser tee pour nourrir les mêmes données dans plusieurs pipelines:

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

même sans bash, vous pouvez utiliser 'mkfifo a du fifo temporaire; cmd> a & cmd2 | diff a -; rm a' – unhammer

+0

Vous pouvez utiliser un tube standard pour l'un des arguments: 'pipeline1 | diff -u - <(pipeline2) '. Ensuite, la sortie vous rappellera plus clairement quelle entrée était, en montrant '-/dev/stdin' vs'/dev/fd/67' ou quelque chose, au lieu de deux fds numérotés. –

+0

La substitution de processus ('foo <(pipe)') ne modifie pas le système de fichiers. ** Le tuyau est * anonyme *; il n'a pas de nom dans le système de fichiers **. Le shell utilise l'appel système 'pipe' pour le créer, pas' mkfifo'. Utilisez 'strace -f -file, desc, clone, execve bash -c '/ bin/true | diff -u - <(/ bin/true) ''pour suivre les appels système de fichier et de descripteur de fichier si vous voulez voir par vous-même.Sous Linux, '/ dev/fd/63' fait partie du système de fichiers virtuel'/proc'; il a automatiquement des entrées pour chaque descripteur de fichier, et ce n'est pas une copie du contenu. Vous ne pouvez donc pas appeler cela un "fichier temporaire" à moins que "foo 3

106

En bash, vous pouvez utiliser les sous-shells, pour exécuter les conduites de commande individuellement, en enfermant le pipeline, entre parenthèses. Vous pouvez ensuite les préfixer avec < pour créer des canaux nommés anonymes que vous pouvez ensuite passer à diff.

Par exemple:

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

Les canaux nommés anonymes sont gérés par bash ils sont créés et détruits automatiquement (contrairement aux fichiers temporaires).

+1

Beaucoup plus détaillé que ma rédaction sur la même solution - lot anonyme -. +1 – VonC

+4

Ceci est appelé [substitution de processus] (https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html) dans Bash. –

3

Certaines personnes qui arrivent sur cette page pourraient être à la recherche d'un diff ligne par ligne, pour lequel comm ou grep -f devrait être utilisé à la place. Une chose à souligner est que, dans tous les exemples de réponses, les différences ne commenceront pas réellement tant que les deux flux n'auront pas été terminés. Testez ce avec par exemple:

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

Si cela est un problème, vous pouvez essayer sd (flux diff), qui ne nécessite pas de tri (comme comm DOE), ni substitution de processus comme les exemples ci-dessus, les ordres ou grandeur plus rapide que grep -f et prend en charge les flux infinis.

L'exemple de test que je propose serait écrit comme ça dans sd:

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

Mais la différence est que seq 100 serait à seq 10 mis au format tout de suite. Notez que si l'un des flux est tail -f, le diff ne peut pas être effectué avec la substitution de processus.

Voici un blogpost J'ai écrit au sujet des flux diffing sur le terminal, qui introduit sd.

Questions connexes