Linux – How to Run a Multiline Bash Script Piped from Stdin

bashlinuxpipeshell-scriptingstdin

I'm looking for a simple way to include multi-line scripts inline in documentation to make it transparent what each step does.

This works

echo "echo 1234" | bash

So does this

echo 'for N in 1 2 3; do echo $N; done' | bash

However, I'd like to use something like this (which does not work):

echo <<EOF | bash
for N in 1 2 3; do
echo "N is ${N}"
done
EOF

Update, the closest working solution so far is:

cat <<EOF|bash
for N in 1 2 3 4; do
    echo "N is now \$N"
done
EOF

Ideally though I'd like to not have to escape the \$N

A correct solution will not:

  • use tools not installed default debian config
  • involve a temp file
  • downloaded the script ( curl -o - htpp://blah.com/script | bash etc )

Ideally it's something something the reader can copy paste, press enter, and will execute without further action.

Best Answer

If you quote or escape the here-document delimiter (i.e. <<'EOF' or <<\EOFinstead of just <<EOF), the shell won't do expansions (of variables etc) on the content.

cat <<'EOF' | bash
for N in 1 2 3; do
echo "N is ${N}"
done
EOF

BTW, the cat command isn't really doing anything useful here (it's what's known as a "useless use of cat", or UUoC). You can eliminate it and send input directly to bash:

bash <<'EOF'
for N in 1 2 3; do
echo "N is ${N}"
done
EOF

If the snippet needs to read input, that's a little tricky because the script's input is... the script snippet itself. On most unix-like OSes, you can avoid this by passing the script over a different file descriptor, and telling bash to "execute" that file descriptor:

bash /dev/fd/3 3<<'EOF'
read -p "Enter something here: " input
echo "You entered: ${input}"
EOF

But note that any variables set in one of these snippets (like $input here) are only set in that one shell process, and will be lost when it exits.