shell io-redirection cat here-document – How to Use Output Redirection with Here-Documents and Cat?

cathere-documentio-redirectionshell

Let's say I have a script that I want to pipe to another command or redirect to a file (piping to sh for the examples). Assume that I'm using bash.

I could do it using echo:

echo "touch somefile
echo foo > somefile" | sh

I could also do almost the same thing using cat:

cat << EOF
touch somefile
echo foo > somefile
EOF

But if I replace "EOF" with "EOF | sh" it just thinks that it's a part of the heredoc.

How can I make it so that cat outputs text from stdin, and then pipes it to an arbitrary location?

Best Answer

There are multiple ways to do this. The simplest is probably this:

cat <<EOF | sh
touch somefile
echo foo > somefile
EOF

Another, which is nicer syntax in my opinion:

(
cat <<EOF
touch somefile
echo foo > somefile
EOF
) | sh

This works as well, but without the subshell:

{
cat <<EOF
touch somefile
echo foo > somefile
EOF
} | sh

More variations:

cat <<EOF |
touch somefile
echo foo > somefile
EOF
  sh

Or:

{ cat | sh; } << EOF
touch somefile
echo foo > somefile
EOF

By the way, I expect the use of cat in your question is a placeholder for something else. If not, take it out, like this:

sh <<EOF
touch somefile
echo foo > somefile
EOF

Which could be simplified to this:

sh -c 'touch somefile; echo foo > somefile'

or:

sh -c 'touch somefile
echo foo > somefile'

Redirecting output instead of piping

sh >out <<EOF
touch somefile
echo foo > somefile
EOF

Using cat to get the equivalent of echo test > out:

cat >out <<EOF
test
EOF

Multiple Here Documents

( cat; echo ---; cat <&3 ) <<EOF 3<<EOF2
hi
EOF
there
EOF2

This produces the output:

hi
---
there

Here's what's going on:

  • The shell sees the ( ... ) and runs the enclosed commands in a subshell.
  • The cat and echo are simple enough. The cat <&3 says to run cat with file descriptor (fd) 0 (stdin) redirected from fd 3; in other words, cat out the input from fd 3.
  • Before the (...) is started, the shell sees the two here document redirects and substitutes fd 0 (<<EOF) and fd 3 (3<<EOF2) with the read-side of pipes
  • Once the initial command is started, the shell reads its stdin until EOF is reached and sends that to the write-side of the first pipe
  • Next, it does the same with EOF2 and the write-side of the second pipe
Related Question