When a command takes filenames as argument, how can I avoid creating temporary files?
Suppose I have a command that takes filenames as arguments, like: diff foo.txt bar.txt
What if instead of actual files, I want to use the results of a command in each?
I can use temporary files:
ls /home/alice > /tmp/alice.txt
ls /home/bob > /tmp/bob.txt
diff alice.txt bob.txt
But what if I don't want to create the files?
2 answers
This other answer uses process substitutions. Not every shell supports this feature. If your OS provides pathnames for file descriptors (/dev/fd/N
or /proc/self/fd/N
) then you can use them to achieve the desired result without process substitution and without temporary files:
ls /home/alice | { ls /home/bob | diff /dev/fd/4 -; } 4<&0 </dev/null
The result of ls /home/alice
would be piped to the next command (group of commands { }
in our case) via stdin of the next command, but we make it available as file descriptor 4 (4<&0
). We also redirect stdin to /dev/null
in case something tries to read from it while it shouldn't. In our case this "something" is the second ls
. While ls
does not try to read from its stdin, in general a command may.
Now the result of ls /home/alice
is available to diff
via its file descriptor 4. By passing /dev/fd/4
as an operand we tell diff
to use the file (pipe in this case) associated with the descriptor. Note in some systems opening /dev/fd/4
results in duplicating the descriptor (i.e. referring to the same open file description), but in other systems (particularly in Linux) opening /dev/fd/4
results in opening the file anew (i.e. creating a fresh new open file description). In our case this nuance does not matter, our command should work either way.
The result of ls /home/bob
is piped to diff
via its stdin. By -
we tell diff
to read from its stdin. Note -
means "stdin" only because diff
interprets -
this way. Not all commands interpret -
as "stdin", so in general you may want to use /dev/fd/0
.
0 comment threads
What you're looking for is called process substitution.
In Bash and many bash-like shells, you can use <(foo_command --with --arguments)
instead of the file path:
diff <(ls /home/alice) <(ls /home/bob)
0 comment threads