Shell Script Won't Recognize Heredoc Delimiter

Shell script won't recognize heredoc delimiter

When using HEREDOC notation, you must write the closing word (EOF in your case) in a line by itself, without any leading or trailing whitespace.

But in this case I would use another approach:

You can use chpasswd. This command is shipped in all linux distros (that I know of) and I think in UNIX variants too.

The usage is pretty simple, you just pass via the STDIN a username:password combination and that's all, one combo per line. To change the password for a single user, just do a simple echo:

echo "demo:myPasswd" | chpasswd

sh -c doesn't recognize heredoc syntax

The commands are not equivalent.

/bin/sh -c <<EOF
echo 'hello'
EOF

is equivalent to

echo "echo 'hello'" | /bin/sh -c

or, with here-string:

/bin/sh -c <<< "echo 'hello'"

but sh -c requires an argument. It would work with

echo "echo 'hello'" | /bin/sh

here-document gives 'unexpected end of file' error

The EOF token must be at the beginning of the line, you can't indent it along with the block of code it goes with.

If you write <<-EOF you may indent it, but it must be indented with Tab characters, not spaces. So it still might not end up even with the block of code.

Also make sure you have no whitespace after the EOF token on the line.

Ansible quoted heredoc in expect/command block not finding delimiter / multiline command in expect command

Move the closing EOF left by two spaces. At the moment it does not start at the beginning of its line, so bash won't see it as a delimiter.

From the bash man-page:

Here Documents

This type of redirection instructs the shell to read input from the
current source until a line containing only delimiter (with no
trailing blanks) is seen. All of the lines read up to that point are
then used as the standard input for a command. The format of
here-documents is:

<<[-]word
here-document
delimiter

No parameter expansion, command substitution, arithmetic expansion, or pathname expansion is performed
on word. If any characters in word are quoted, the delimiter is the
result of quote removal on word, and the lines in the here-document
are not expanded. If word is unquoted, all lines of the here-document
are subjected to parameter expansion, command substitution, and
arithmetic expansion. In the latter case, the character sequence
\<newline> is ignored, and \ must be used to quote the characters \,
$, and `. If the redirection operator is <<-, then all leading tab
characters are stripped from input lines and the line containing
delimiter. This allows here-documents within shell scripts to be
indented in a natural fashion.

So you'll either need to remove the indentation from the EOF line or indent everything with TABs instead of spaces. I suggest that the former option is simpler.

Why won't this here-doc bash statement execute in a switch case

Since your here doc is indented, you should use the - form, so that leading tabs are removed. You must also have the ending token at column 0, unless you use tabs for indentation:

    cat <<-'EOF'
#!/bin/sh -e

iwconfig wlan0 mode ad-hoc essid pi-adhoc channel 6 txpower 0

exit 0
EOF

from GNU Bash Manual

If the redirection operator is ‘<<-’, then all leading tab characters
are stripped from input lines and the line containing delimiter. This
allows here-documents within shell scripts to be indented in a natural
fashion.

Using variables inside a bash heredoc

In answer to your first question, there's no parameter substitution because you've put the delimiter in quotes - the bash manual says:

The format of here-documents is:

      <<[-]word
here-document
delimiter

No parameter expansion, command substitution, arithmetic expansion, or
pathname expansion is performed on word. If any characters in word are
quoted, the delimiter is the result of quote removal on word, and the
lines in the here-document are not expanded. If word is unquoted, all
lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion. [...]

If you change your first example to use <<EOF instead of << "EOF" you'll find that it works.

In your second example, the shell invokes sudo only with the parameter cat, and the redirection applies to the output of sudo cat as the original user. It'll work if you try:

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT

How can I write a heredoc to a file in Bash script?

Read the Advanced Bash-Scripting Guide Chapter 19. Here Documents.

Here's an example which will write the contents to a file at /tmp/yourfilehere

cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
This line is indented.
EOF

Note that the final 'EOF' (The LimitString) should not have any whitespace in front of the word, because it means that the LimitString will not be recognized.

In a shell script, you may want to use indentation to make the code readable, however this can have the undesirable effect of indenting the text within your here document. In this case, use <<- (followed by a dash) to disable leading tabs (Note that to test this you will need to replace the leading whitespace with a tab character, since I cannot print actual tab characters here.)

#!/usr/bin/env bash

if true ; then
cat <<- EOF > /tmp/yourfilehere
The leading tab is ignored.
EOF
fi

If you don't want to interpret variables in the text, then use single quotes:

cat << 'EOF' > /tmp/yourfilehere
The variable $FOO will not be interpreted.
EOF

To pipe the heredoc through a command pipeline:

cat <<'EOF' |  sed 's/a/b/'
foo
bar
baz
EOF

Output:

foo
bbr
bbz

... or to write the the heredoc to a file using sudo:

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF

Invoke function from bash heredoc

You'll need to transfer the code verbatim to the other server to run it there. A heredoc with an unquoted starting delimiter works similarly to a double quoted string, which means any command substitutions like the one you have running before the resulting string is passed to the remote server. You can solve this by putting the function definition inside the heredoc and single quoting the starting delimiter:

ssh myserver <<'EOF'
_git_clone() {
git clone myrepo
git fetch --all --tags
}
echo $(_git_clone)
EOF

Alternatively you can define the function locally but then substitute its definition within the heredoc:

_git_clone() {
git clone myrepo
git fetch --all --tags
}

ssh myserver <<EOF
$(declare -f _git_clone)
EOF

Confused about some standard input or heredoc usage in shell

Some reading material, all from the very useful Bash manual:

  • Redirection (<filename) -- causes standard input to be redirected to the file filename

  • Here documents (<<WORD) -- causes standard input to be redirected to the script source from the next line, up to but not including the line WORD

  • Here strings (<<<"string") -- causes standard input to be redirected to the string string (as though the string were written to a temporary file and then standard input redirected to that file)

  • Process substitution (<(command)) -- starts a process executing command and inserts a name onto the command line which acts like a filename, such that reading from that "file" produces the output of the command

The use of - to indicate the the source file is standard input is common to many commands and recommended by Posix. Many commands read from standard input if no file is specified. Some, like cat, implement both ways of indicating that the intention is to read from standard input.

Note that - and <(command) are both filename arguments, while <filename, <<WORD and <<<"string" are redirections. So while they superficially look similar, they are quite different under the hood. What they have in common is that they have to do with input; some of them (but not here-docs/strings) have analogues that have to do with output, using > instead of <.



Related Topics



Leave a reply



Submit