Here-document 出现“意外结束文件”错误

我需要我的脚本从终端发送电子邮件。基于我在这里看到的和网上其他许多地方,我把它格式化成这样:

/var/mail -s "$SUBJECT" "$EMAIL" << EOF
Here's a line of my message!
And here's another line!
Last line of the message here!
EOF

但是,当我运行这个命令时,我会得到以下警告:

myfile.sh: line x: warning: here-document at line y delimited by end-of-file (wanted 'EOF')


myfile.sh: line x+1: syntax error: unexpected end of file

其中 x 行是程序中最后一行代码 y 行是包含 /var/mail的那行。我试过用其他东西(ENDOFMESSAGEFINISH等)替换 EOF,但没有用。我在网上找到的几乎所有东西都是这么做的,而且我在 bash 还是个新手,所以我很难自己弄明白。有人能帮忙吗?

162456 次浏览

Please try to remove the preceeding spaces before EOF:-

/var/mail -s "$SUBJECT" "$EMAIL" <<-EOF

Using <tab> instead of <spaces> for ident AND using <<-EOF works fine.

The "-" removes the <tabs>, not <spaces>, but at least this works.

The line that starts or ends the here-doc probably has some non-printable or whitespace characters (for example, carriage return) which means that the second "EOF" does not match the first, and doesn't end the here-doc like it should. This is a very common error, and difficult to detect with just a text editor. You can make non-printable characters visible for example with cat:

cat -A myfile.sh

Once you see the output from cat -A the solution will be obvious: remove the offending characters.

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.

Along with the other answers mentioned by Barmar and Joni, I've noticed that I sometimes have to leave a blank line before and after my EOF when using <<-EOF.

Note one can also get this error if you do this;

while read line; do
echo $line
done << somefile

Because << somefile should read < somefile in this case.

Here is a flexible way to do deal with multiple indented lines without using heredoc.

  echo 'Hello!'
sed -e 's:^\s*::' < <(echo '
Some indented text here.
Some indented text here.
')
if [[ true ]]; then
sed -e 's:^\s\{4,4\}::' < <(echo '
Some indented text here.
Some extra indented text here.
Some indented text here.
')
fi

Some notes on this solution:

  • if the content is expected to have simple quotes, either escape them using \ or replace the string delimiters with double quotes. In the latter case, be careful that construction like $(command) will be interpreted. If the string contains both simple and double quotes, you'll have to escape at least of kind.
  • the given example print a trailing empty line, there are numerous way to get rid of it, not included here to keep the proposal to a minimum clutter
  • the flexibility comes from the ease with which you can control how much leading space should stay or go, provided that you know some sed REGEXP of course.

When I want to have docstrings for my bash functions, I use a solution similar to the suggestion of user12205 in a duplicate of this question.

See how I define USAGE for a solution that:

  • auto-formats well for me in my IDE of choice (sublime)
  • is multi-line
  • can use spaces or tabs as indentation
  • preserves indentations within the comment.
function foo {
# Docstring
read -r -d '' USAGE <<'    END'
# This method prints foo to the terminal.
#
# Enter `foo -h` to see the docstring.
#      It has indentations and multiple lines.
#
# Change the delimiter if you need hashtag for some reason.
# This can include $$ and = and eval, but won't be evaluated
END




if [ "$1" = "-h" ]
then
echo "$USAGE" | cut -d "#" -f 2 | cut -c 2-
return
fi


echo "foo"
}

So foo -h yields:

This method prints foo to the terminal.


Enter `foo -h` to see the docstring.
It has indentations and multiple lines.


Change the delimiter if you need hashtag for some reason.
This can include $$ and = and eval, but won't be evaluated

Explanation

cut -d "#" -f 2: Retrieve the second portion of the # delimited lines. (Think a csv with "#" as the delimiter, empty first column).

cut -c 2-: Retrieve the 2nd to end character of the resultant string

Also note that if [ "$1" = "-h" ] evaluates as False if there is no first argument, w/o error, since it becomes an empty string.

make sure where you put the ending EOF you put it at the beginning of a new line

May be old but I had a space after the ending EOF << EOF blah blah EOF <-- this was the issue. Had it for years, finally looked it up here

For anyone stumbling here who googled "bash warning: here-document delimited by end-of-file", it may be that you are getting the

warning: here-document at line 74 delimited by end-of-file

...type warning because you accidentally used a here document symbol (ABC0) when you meant to use a here string symbol (<<<). That was my case.