Standard input is file descriptor 0, which by convention is used to pass the “default” input. Apart from standard input, most commands only let you specify an input by passing a file name.
Linux and most other modern unices have a “special” file name which means “whatever is already open on this file descriptor”: /dev/fd/N
. So to make command
read from file descriptor 3 rather than some disk file, pass command --file /dev/fd/3
. This works with most commands; the only obstacle at this point is that some commands insist a file name with a specific extension. You can usually work around this with a symbolic link.
Since you're telling the command to read from file descriptor 3, you need to have it open when you run the command. You can open file descriptor 3 to read from a file with command --file /dev/fd/3 <some_file
, but if you do that you might as well run command --file some_file
. Where this gets useful is that file descriptor 3 can come from a pipe. Pipelines in the shell always connect the standard output of the left-hand side to the standard input of the right-hand side, but you can use file descritor redirection to move the file descriptors around.
echo "$X" | {
echo "$Y" | command --file /dev/fd/3 -
} 3<&0
In this example, the whole braced group has its file descriptor 3 reading from the pipe that receives $X
. Thus, when command
reads from the file whose name is passed to --file
, it sees the value of X
.
This works in any sh variant.
There's a simpler syntax that works in bash, ksh and zsh: process substitution. The code below is pretty much equivalent to the code above:
echo "$Y" | command --file <(echo "$X") -
The shell picks a free file descriptor and makes a pipe from echo "$X"
to that descriptor, and replaces <(…)
by the correct /dev/fd/N
.
In both cases, the file passed to --file
is a pipe. So this only works if the command works with a pipe. Some commands don't work with pipes because they don't read from the file linearly from start to end; with such commands, you have to use a temporary file.