linuxshellfileio-redirectionvirtual-file

How to avoid creating tmp-Files on Linux?


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?


Solution

  • 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.