Say I have too programs a
and b
that I can run with ./a
and ./b
.
Is it possible to diff their outputs without first writing to temporary files?
Say I have too programs a
and b
that I can run with ./a
and ./b
.
Is it possible to diff their outputs without first writing to temporary files?
Use <(command)
to pass one command's output to another program as if it were a file name. Bash pipes the program's output to a pipe and passes a file name like /dev/fd/63
to the outer command.
diff <(./a) <(./b)
Similarly you can use >(command)
if you want to pipe something into a command.
This is called "Process Substitution" in Bash's man page.
While process substitution is not POSIX, it is supported by bash, ksh, and zsh. (ref: SE's network answer by John1024 or comment by Gilles 'SO- stop being evil')
-bash: syntax error near unexpected token ('
. I tried again without parenthesis and got -bash: java: No such file or directory
. Does it not work if the command has parameters?
alias diffcmd bash -c \'diff \<\(sh -c \!:1\) \<\( sh -c \!:2 \)\'
. (Then for, example: diffcmd "ls" "ls -a").
Commented
May 15, 2013 at 18:36
fseek
, zsh offers =(./a)
which can be used identically to <(./a)
but uses a temporary file under the hood, which zsh will delete for you.)
Adding to both the answers, if you want to see a side by side comparison, use vimdiff
:
vimdiff <(./a) <(./b)
Something like this:
vimdiff
creates beautiful, smart and interactive difference comparison views. It seems to come with the vim
package on most systems.
Commented
Feb 6, 2017 at 2:42
vimdiff
also shows not only the line that differs but also the specific text fragment that differs.
Commented
Nov 27, 2018 at 12:08
One option would be to use named pipes (FIFOs):
mkfifo a_fifo b_fifo
./a > a_fifo &
./b > b_fifo &
diff a_fifo b_fifo
... but John Kugelman's solution is much cleaner.
rm a_fifo b_fifo
.
Commented
Apr 14, 2016 at 4:55
For anyone curious, this is how you perform process substitution in using the Fish shell:
Bash:
diff <(./a) <(./b)
Fish:
diff (./a | psub) (./b | psub)
Unfortunately the implementation in fish is currently deficient; fish will either hang or use a temporary file on disk. You also cannot use psub for output from your command.
Adding a little more to the already good answers (helped me!):
The command docker
outputs its help to STD_ERR
(i.e. file descriptor 2)
I wanted to see if docker attach
and docker attach --help
gave the same output
$ docker attach
$ docker attach --help
Having just typed those two commands, I did the following:
$ diff <(!-2 2>&1) <(!! 2>&1)
!! is the same as !-1 which means run the command 1 before this one - the last command
!-2 means run the command two before this one
2>&1 means send file_descriptor 2 output (STD_ERR) to the same place as file_descriptor 1 output (STD_OUT)
Hope this has been of some use.
For zsh, using =(command)
automatically creates a temporary file and replaces =(command)
with the path of the file itself. With normal Process Substitution, $(command)
is replaced with the output of the command.
This zsh feature is very useful and can be used like so to compare the output of two commands using a diff tool, for example Beyond Compare:
bcomp =(ulimit -Sa | sort) =(ulimit -Ha | sort)
For Beyond Compare, note that you must use bcomp
for the above (instead of bcompare
) since bcomp
launches the comparison and waits for it to complete. If you use bcompare
, that launches comparison and immediately exits due to which the temporary files created to store the output of the commands disappear.
Read more here: http://zsh.sourceforge.net/Intro/intro_7.html
Also notice this:
Note that the shell creates a temporary file, and deletes it when the command is finished.
and the following which is the difference between $(...)
and =(...)
:
If you read zsh's man page, you may notice that <(...) is another form of process substitution which is similar to =(...). There is an important difference between the two. In the <(...) case, the shell creates a named pipe (FIFO) instead of a file. This is better, since it does not fill up the file system; but it does not work in all cases. In fact, if we had replaced =(...) with <(...) in the examples above, all of them would have stopped working except for fgrep -f <(...). You can not edit a pipe, or open it as a mail folder; fgrep, however, has no problem with reading a list of words from a pipe. You may wonder why diff <(foo) bar doesn't work, since foo | diff - bar works; this is because diff creates a temporary file if it notices that one of its arguments is -, and then copies its standard input to the temporary file.