101

我需要我的脚本从终端发送电子邮件。根据我在这里和网上许多其他地方看到的内容,我将其格式化如下:

/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其他东西(ENDOFMESSAGE,FINISH等)替换,但无济于事。我在网上找到的几乎所有东西都是这样完成的,而且我对 bash 真的很陌生,所以我很难自己弄清楚。任何人都可以提供任何帮助吗?

4

8 回答 8

181

EOF标记必须在行的开头,你不能将它与它的代码块一起缩进。

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

还要确保行上的标记后没有空格。EOF

于 2013-09-06T15:10:23.477 回答
19

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

cat -A myfile.sh

一旦你看到cat -A解决方案的输出将是显而易见的:删除有问题的字符。

于 2013-09-06T15:07:53.383 回答
7

请尝试删除前面的空格EOF:-

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

使用<tab>而不是<spaces>for ident 和使用 <<-EOF 可以正常工作。

"-"删除<tabs>, not <spaces>,但至少这是有效的。

于 2013-09-06T15:06:23.200 回答
3

请注意,如果您这样做,也可能会出现此错误;

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

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

于 2019-09-04T13:21:48.900 回答
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

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

  • 如果内容应该有简单的引号,要么使用转义它们,要么\用双引号替换字符串分隔符。在后一种情况下,请注意结构 like$(command)将被解释。如果字符串同时包含单引号和双引号,则您必须至少进行某种转义。
  • 给定的示例打印一个尾随的空行,有很多方法可以摆脱它,这里不包括在内,以使提案保持最小的混乱
  • 灵活性来自于您可以轻松控制应该保留或离开多少前导空间,前提是您当然知道一些 sed REGEXP。
于 2020-04-07T07:14:44.010 回答
0

当我想为我的 bash 函数提供文档字符串时,我在这个问题的副本中使用类似于user12205的建议的解决方案。

看看我如何为解决方案定义 USAGE:

  • 在我选择的 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好像没有第一个参数,没有错误,因为它变成了一个空字符串。

于 2020-09-20T09:11:03.873 回答
0

确保将结尾 EOF 放在哪里,将其放在新行的开头

于 2021-05-19T13:17:36.677 回答
-2

除了 Barmar 和 Joni 提到的其他答案外,我注意到在使用<<-EOF.

于 2017-05-23T20:06:22.010 回答