Diff Ausgabe von zwei Programmen ohne temporäre Dateien

Antworten:

211

Verwenden Sie <(command)diese Option, um die Ausgabe eines Befehls an ein anderes Programm zu übergeben, als wäre es ein Dateiname. Bash leitet die Ausgabe des Programms an eine Pipe weiter und übergibt einen Dateinamen wie /dev/fd/63an den äußeren Befehl.

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

Ebenso können Sie verwenden, >(command)wenn Sie etwas in einen Befehl einfügen möchten .

Dies wird in Bashs Manpage als "Process Substitution" bezeichnet.

John Kugelman
quelle
1
Ein Nachteil ist, dass der Anrufer dies nicht herausfindet, wenn ./a oder ./b fehlschlägt.
Alexander Pogrebnyak
5
Das OP hat die Frage-Bash markiert, aber für die Aufzeichnung funktioniert dies in keiner anderen Shell. Es ist eine Bash-Erweiterung des Posix-Dienstprogrammstandards.
DigitalRoss
5
Ich habe diese Lösung mit einem Java-Programm ausprobiert und folgende Fehlermeldung erhalten : -bash: syntax error near unexpected token ('. Ich versuchte es erneut ohne Klammern und bekam -bash: java: No such file or directory. Funktioniert es nicht, wenn der Befehl Parameter hat?
Styfle
1
@DigitalRoss - Die Lösung kann mithilfe eines Alias ​​auf andere Shells erweitert werden. In tcsh funktioniert die folgende Hässlichkeit : alias diffcmd bash -c \'diff \<\(sh -c \!:1\) \<\( sh -c \!:2 \)\'. (Dann zum Beispiel: diffcmd "ls" "ls -a").
Paul Lynch
Für alle, die außerhalb von Google wandern, funktioniert dies auch unter zsh. (Wenn Sie Eingaben in etwas fseekeinspeisen müssen, das aufruft , bietet zsh Angebote an =(./a), die identisch <(./a)verwendet werden können, aber eine temporäre Datei unter der Haube verwenden, die zsh für Sie löscht.)
ssokolow
26

Wenn Sie zu beiden Antworten hinzufügen möchten, verwenden Sie vimdiff:

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

Etwas wie das:

Geben Sie hier die Bildbeschreibung ein

gebrochener Fuß
quelle
vimdifferstellt schöne, intelligente und interaktive Differenzvergleichsansichten. Es scheint auf den vimmeisten Systemen mit dem Paket zu kommen.
Tim Visée
vimdiffzeigt auch nicht nur die unterschiedliche Zeile, sondern auch das spezifische Textfragment, das sich unterscheidet.
Anton Tarasenko
20

Eine Möglichkeit wäre die Verwendung von Named Pipes (FIFOs) :

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

... aber John Kugelmans Lösung ist viel sauberer.

Martin Clayton
quelle
Sie sollten die genannten Rohre besser entfernen, nachdem Sie sie verwendet haben rm a_fifo b_fifo.
Franklin Yu
15

Für alle Neugierigen ist dies die Art und Weise, wie Sie bei der Verwendung der Fischschale eine Prozessersetzung durchführen :

Bash:

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

Fisch:

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

Leider ist die Umsetzung bei Fischen derzeit mangelhaft ; Fische hängen entweder oder verwenden eine temporäre Datei auf der Festplatte. Sie können psub auch nicht für die Ausgabe Ihres Befehls verwenden.

James McMahon
quelle
Dies funktioniert derzeit bei Fischen nicht richtig. Wenn die Ausgabe der Programme größer als eine BUFSIZ ist, bleibt der Befehl hängen. (oder Fisch verwendet eigentlich nur eine temporäre Datei auf der Festplatte)
Evan Benn
7

Ein bisschen mehr zu den bereits guten Antworten hinzufügen (hat mir geholfen!):

Der Befehl dockergibt seine Hilfe an STD_ERR(dh Dateideskriptor 2) aus.

Ich wollte sehen, ob docker attachund docker attach --helpgab die gleiche Ausgabe

$ docker attach

$ docker attach --help

Nachdem ich gerade diese beiden Befehle eingegeben hatte, tat ich Folgendes:

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

!! ist dasselbe wie! -1, was bedeutet, dass der Befehl 1 vor diesem ausgeführt wird - dem letzten Befehl

! -2 bedeutet, dass Sie den Befehl zwei vor diesem ausführen

2> & 1 bedeutet, dass die Ausgabe von file_descriptor 2 (STD_ERR) an dieselbe Stelle gesendet wird wie die Ausgabe von file_descriptor 1 (STD_OUT).

Hoffe, dass dies von Nutzen war.

Darrenthatcher
quelle
0

Bei zsh wird mit using =(command)automatisch eine temporäre Datei erstellt und =(command)durch den Pfad der Datei selbst ersetzt. Bei normaler Prozessersetzung $(command)wird durch die Ausgabe des Befehls ersetzt.

Diese zsh-Funktion ist sehr nützlich und kann wie folgt verwendet werden, um die Ausgabe von zwei Befehlen mit einem Diff-Tool zu vergleichen, z. B. Beyond Compare:

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

Beachten Sie für Beyond Compare, dass Sie bcompfür das oben Gesagte (anstelle von bcompare) verwenden müssen, da bcompder Vergleich gestartet und auf den Abschluss gewartet wird . Wenn Sie verwenden bcompare, wird der Vergleich gestartet und sofort beendet, wodurch die temporären Dateien, die zum Speichern der Ausgabe der Befehle erstellt wurden, verschwinden.

Lesen Sie hier mehr: http://zsh.sourceforge.net/Intro/intro_7.html

Beachten Sie auch Folgendes:

Beachten Sie, dass die Shell eine temporäre Datei erstellt und diese löscht, wenn der Befehl beendet ist.

und das Folgende ist der Unterschied zwischen $(...)und =(...):

Wenn Sie die Manpage von zsh lesen, stellen Sie möglicherweise fest, dass <(...) eine andere Form der Prozessersetzung ist, die = (...) ähnelt. Es gibt einen wichtigen Unterschied zwischen den beiden. Im Fall <(...) erstellt die Shell anstelle einer Datei eine Named Pipe (FIFO). Dies ist besser, da es das Dateisystem nicht ausfüllt. aber es funktioniert nicht in allen Fällen. Wenn wir in den obigen Beispielen = (...) durch <(...) ersetzt hätten, hätten alle außer fgrep -f <(...) aufgehört zu arbeiten. Sie können eine Pipe nicht bearbeiten oder als E-Mail-Ordner öffnen. fgrep hat jedoch kein Problem damit, eine Liste von Wörtern aus einer Pipe zu lesen. Sie fragen sich vielleicht, warum diff <(foo) bar nicht funktioniert, da foo | Diff - Bar funktioniert; Dies liegt daran, dass diff eine temporäre Datei erstellt, wenn es feststellt, dass eines seiner Argumente - ist, und dann seine Standardeingabe in die temporäre Datei kopiert.

Ashutosh Jindal
quelle