1

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?

jww
  • 97,681
  • 90
  • 411
  • 885
  • What is `command` in this case? Many utilities can read from `stdin`, which would allow you to "_pipe_" the data, something like: `echo "${X}" | command --file -` – Attie Aug 09 '19 at 13:44
  • thanks a lot :) i've updated my initial question – user11896300 Aug 09 '19 at 13:56
  • does `{echo "$X" ; echo "$Y" ; } | command` help? Good luck. – shellter Aug 09 '19 at 15:26
  • AND do you have control (edit rights) on `command`. You could add env var, where if it has a value, it will process it? `cmd_cmd="$Y"; echo "$X" | command`, where `command` now loos at it's enviornment, if it finds `$cmd_cmd` it will read it as input or assign it to other internal variables. Just an idea. Good luck. – shellter Aug 09 '19 at 15:29
  • With bash and a few other shells, `echo "$Y" | command --file <(echo "$X") - ` works. – Shawn Aug 09 '19 at 15:41
  • You can also use a [here string](https://bash.cyberciti.biz/guide/Here_strings) instead of piping that first echo. `command --file <(echo "$X") - <<<"$Y"` – Shawn Aug 09 '19 at 15:45
  • Shouldn't that be `command --file "$some_file"`? You should probably run ShellCheck on your code. Also see [How to use Shellcheck](http://github.com/koalaman/shellcheck), – jww Aug 10 '19 at 08:57

1 Answers1

2

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.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254