I have a program that needs to access a file. The content of this file is in a variable and I want to avoid creating a real file. Is it possible to create some kind of virtual file??
X='some stuff'
Y='other stuff'
echo $X > some_file
echo "$Y" | command --file some_file -
Normally I would pipe this to the command, but STDIN already "in use". Is there any way to pass both variables to the command, without creating tmp files?
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.