You probably know to use the diff file1 file2
command to compare file1 and file2 line by line.
Sometimes you don’t want to compare files, but output of commands. For example, compare sort file1
and sort file2
. Or maybe compare grep foo file1
and grep foo file2
.
Of course, there’s the obvious way – run the commands, redirect their output to temp files, and compare the temp files:
$ sort file1 >tmpf1 $ sort file2 >tmpf2 $ diff tmpf1 tmpf2 ... $ rm tmpf*
But, really..? 3 extra commands just for the one command you really wanted?
There must be a better way! This post is about the better way 🙂 .
Even better, the solution is more general than diff. It allows using the STDOUT of any command as a “file” input for other commands!
Shell-Foo is a series of fun ways to take advantage of the powers of the shell. In the series, I highlight shell one-liners that I found useful or interesting. Most of the entries should work on bash on Linux, OS X and other UNIX-variants. Some probably work with other shells as well. Your mileage may vary.
Feel free to suggest your own Shell-Foo one-liners!
The solution
Try this:
$ diff <(seq 8 | grep -v 3) <(seq 9 | grep -v 5) 2a3 > 3 4d4 < 5 7a8 > 9
(for some reason I wasn’t able to make the above snippet render properly, with <
instead of <
. Here it is again, without nice formatting:
$ diff <(seq 8 | grep -v 3) <(seq 9 | grep -v 5) 2a3 > 3 4d4 < 5 7a8 > 9
)
Applied to the teaser example: diff <(sort file1) <(sort file2)
.
Isn’t this great? 🙂
As mentioned above, it’s not just for diff:
$ comm -12 <(seq 8 | grep -v 3) <(seq 9 | grep -v 5) 1 2 4 6 7 8
(see a previous post on comm)
Explanation
To my understanding, <(...)
is a special bash feature (sorry, other shells). In essence, it runs ...
in a sub-shell, creates a “virtual file handle” from its STDOUT, and injects this handle in the calling shell.
Maybe, behind the scenes, it uses actual temp files. Maybe it uses named pipes. I don’t know. I don’t care. It just works!
Extra tips
As mentioned above, as far as I know, this is bash only.
Works for me on both bash on Linux and bash on OS X.
Leave a Reply