23

Can one use multiple here-docs to provide input to a command in bash?

$ cat <<<foo <<<bar
bar
$ cat <<EOF1 <<EOF2
> foo
> EOF1
> bar
> EOF2
bar

Obviously, in both cases, the second here-doc is used as stdin, and replaces the first reference. Is the solution to use echos instead?

$ cat <(echo -n foo) <(echo bar)
foobar

Also, for some reason, using a combination didn't work for me. Why would that be?

$ cat <<<foo <(echo bar)
bar
$ cat <(echo -n foo) <<<bar
foo
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Sparhawk
  • 19,561
  • 18
  • 86
  • 152
  • Is there any reason behind the fact that you want to use two here-documents rather than combining it into one? – beans Aug 03 '14 at 12:49
  • 1
    @beans I actually ran into it when testing out `paste` with dummy inputs. I suppose I can think of a few other scenarios. If I had had a script with pre-manipulated text in a few variables, then I might want to do something to both with a command that only takes files, for example `diff`. – Sparhawk Aug 03 '14 at 14:48
  • 1
    Another use-case (I found this using here-docs to create a shell script): you want a few lines with variable expansion and then some lines without: `cat < – Toby Speight Jan 16 '18 at 16:41

2 Answers2

27

You can do:

cat /dev/fd/3 3<< E1 /dev/fd/4 4<< E2
foo
E1
bar
E2

There can be only one stdin, as there's only one file descriptor 0.

cat << EOF
eof
EOF

is short for:

cat /dev/fd/0 0<< EOF
eof
EOF

And:

cat <<< foo

is:

cat /dev/fd/0 0<<< foo

You have to make up your mind what to open on file descriptor 0.

cat <(echo foo)

Is:

cat /dev/fd/123

Where 123 is a file descriptor to a pipe, and in parallel, bash runs echo foo in another process with the stdout redirected to the other end of the pipe.

Once you pass a filename to cat, cat no longer read from stdin. You'd need:

cat <(echo foo) /dev/fd/0 << EOF
bar
EOF

Or:

cat <(echo foo) - << EOF
bar
EOF

(- is to tell cat to read from stdin).

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • 2
  • @Mikel, what I meant is that it is _functionally equivalent_. When not passed any argument, `cat` reads from its `fd` 0, as if passed an argument of `-` or `/dev/fd/0` (though on Linux (and Linux only), opening `/dev/fd/0` is not exactly like duplicating the file descriptor 0). – Stéphane Chazelas Aug 03 '14 at 16:28
  • I was very surprised by `/dev/fd/3 3<< E1` construct and I wonder now what exactly are items under /dev/fd/. I though they somehow appears magicaly after process opens a file somewhere on the filesystem with the exception of 1 and 2 which are there by default for every process. But in your example you are using file descriptor 3 and 4 that are not connected to any real file except for that input redirection. I can't comprehend that in my mental model of file descriptors. What if process want to open another file, would it know it has to use fd 5? Do fds have to be 3, 4, 5.. or can be anything? – calavera.info Mar 01 '18 at 08:31
  • @calavera.info, it sounds like you want to create a follow-up question. – Stéphane Chazelas Mar 01 '18 at 08:46
-1

Use multiple cat:

cat <<'__END_OF_USAGE__'; cat <<__END_OF_TOOLS__

Usage of the script: ...
This section does not expand parameters.

__END_OF_USAGE__

Here's a list of tools provided by this script:

$(grep -F tools <"$0")

__END_OF_TOOLS__
KFL
  • 259
  • 1
  • 10