here-document给出'意外的文件结束'错误

时间:2013-09-06 14:59:43

标签: bash email heredoc

我需要我的脚本从终端发送电子邮件。基于我在这里和在线的许多其他地方所看到的,我将其格式化为:

/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的行。我已尝试将EOF替换为其他内容(ENDOFMESSAGEFINISH等),但无济于事。我在网上发现的几乎所有东西都是这样做的,而且我在bash上真的很新,所以我很难自己搞清楚。有人可以提供任何帮助吗?

8 个答案:

答案 0 :(得分:114)

EOF令牌必须位于该行的开头,您不能将其与随附的代码块一起缩进。

如果你写<<-EOF,你可以缩进它,但它必须用 Tab 字符缩进,而不是空格。因此,即使使用代码块,它仍然可能不会结束。

此外,请确保在后面的EOF令牌上没有空白

答案 1 :(得分:10)

启动或结束here-doc的行可能有一些不可打印或空格的字符(例如回车),这意味着第二个“EOF”与第一个不匹配,并且不会在此结束-doc喜欢它应该。这是一个非常常见的错误,仅使用文本编辑器很难检测到。您可以使用cat

显示不可打印的字符
cat -A myfile.sh

一旦看到cat -A的输出,解决方案将很明显:删除有问题的字符。

答案 2 :(得分:5)

请尝试删除EOF之前的前一个空格: -

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

使用<tab>代替<spaces>进行身份识别并使用&lt;&lt; -EOF可以正常工作。

"-"删除<tabs>,而不删除<spaces>,但至少会有效。

答案 3 :(得分:1)

请注意,如果执行此操作,也会出现此错误;

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

因为在这种情况下<< somefile应该读为< somefile

答案 4 :(得分:0)

这是一种灵活的方法,可以使用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

有关此解决方案的一些说明:

  • 如果期望内容具有单引号,请使用\对其进行转义,或将字符串定界符替换为双引号。在后一种情况下,请注意将解释类似$(command)的构造。如果字符串同时包含单引号和双引号,则必须至少转义此类。
  • 给定的示例打印出尾随的空行,有很多方法可以消除它,此处未包括将建议保持在最小限度的
  • 灵活性来自于您可以轻松地控制应该保留或离开多少领先空间的前提,前提是您当然知道一些sed REGEXP。

答案 5 :(得分:0)

当我想为bash函数提供 docstrings 时,我使用的解决方案类似于该问题的user12205中对duplicate的建议。

看看我如何定义用于以下解决方案的用法:

  • 在我选择的IDE(出色)中为​​我很好地自动格式化
  • 是多行
  • 可以使用空格或制表符作为缩进
  • 在评论中保留缩进。
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"
}

所以foo -h产生:

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

说明

cut -d "#" -f 2:检索#分隔行的第二部分。 (以“#”为分隔符的csv,第一列为空)。

cut -c 2-:检索结果字符串的第二到结尾字符

还请注意,如果没有第一个参数,并且没有错误,则if [ "$1" = "-h" ]的计算结果为False,因为它成为空字符串。

答案 6 :(得分:0)

确保将结尾的 EOF 放在新行的开头

答案 7 :(得分:-3)

除了Barmar和Joni提到的其他答案之外,我注意到在使用<<-EOF时,我有时必须在EOF之前和之后留下空白。