我需要我的脚本从终端发送电子邮件。根据我在这里和网上许多其他地方看到的内容,我将其格式化为:
/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
行的代码。我尝试用其他东西(ENDOFMESSAGE
、FINISH
等)替换EOF
,但无济于事。我在网上找到的几乎所有东西都是这样完成的,而且我对 bash 真的很陌生,所以我很难自己弄清楚。谁能提供任何帮助?
EOF
标记必须位于行首,您不能将其与它附带的代码块一起缩进。
如果写<<-EOF
可以缩进,但必须用制表符而不是空格缩进。因此,即使使用代码块,它仍然可能不会结束。
还要确保行上的EOF
标记后没有空格。
开始或结束 here-doc 的行可能有一些不可打印或空格字符(例如,回车符),这意味着第二个"EOF"与第一个不匹配,并且不会像它应该的那样结束 here-doc。这是一个非常常见的错误,仅使用文本编辑器很难检测到。您可以使不可打印的字符可见,例如使用 cat
:
cat -A myfile.sh
一旦你看到cat -A
的输出,解决方案将是显而易见的:删除有问题的字符。
请尝试删除前面的空格EOF
:-
/var/mail -s "$SUBJECT" "$EMAIL" <<-EOF
使用 <tab>
而不是 <spaces>
进行识别和使用 <<-EOF 可以正常工作。
"-"
删除了<tabs>
,而不是<spaces>
,但至少这是有效的。
如果这样做,也可能会出现此错误;
while read line; do
echo $line
done << somefile
因为在这种情况下<< somefile
应该读< somefile
。
对于任何在这里磕磕绊绊的人,如果用谷歌搜索"bash警告:这里文档由文件结尾分隔",可能是你得到了
警告:此处文档第 74 行由文件结尾分隔
。键入警告,因为当您打算使用此处字符串符号 ( <<<
) 时,您不小心使用了此处文档符号 ( <<
)。这就是我的情况。
很旧,但我在结束 EOF 后有一个空格 <<EOF的等等等等EOF<--这就是问题所在。 好好多年了,终于查到这里了
当我想为我的 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-
:检索结果字符串的第二个到尾字符
另请注意,如果没有第一个参数(w/o error),if [ "$1" = "-h" ]
的计算结果为 False
,因为它变为空字符串。
这是一种灵活的方法,可以在不使用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。
除了 Barmar 和 Joni 提到的其他答案外,我还注意到在使用 <<-EOF
时,我有时必须在 EOF 之前和之后留下一个空白行。