我一直在使用下面的模式在 bash 脚本中将多行消息打印到终端。
read -d '' message <<- EOF
this is a
mulitline
message
EOF
echo "$message"
这一直有效 - 直到几天前该模式才停止工作。通过停止工作,我的意思是当 bash 在脚本中遇到这些定界符表达式时 - 它似乎什么也没做 - 没有输出。
我能想到的在过去几天发生的唯一变化是脚本运行的环境是 Ubuntu 14.04 live USB,而不是“完整”安装。
然后我发现,当我将heredoc移到脚本set -o errexit
语句之前时,它又开始工作了。即这不起作用
#!/bin/bash
set -o errexit
read -d '' message <<- EOF
this is a
mulitline
message
EOF
echo "$message"
结果:(什么也没有)
但是这个确实有效
#!/bin/bash
read -d '' message <<- EOF
this is a
mulitline
message
EOF
echo "$message"
结果
$ sudo ./script.sh
this is a
mulitline
message
- bash --版本-
GNU bash, version 4.3.11(1)-release (i686-pc-linux-gnu)
答案1
read
如果找不到分隔符,则返回非零退出状态。将分隔符设置为空字符串后,它使用 NUL 字节作为分隔符,而这些字节通常在文本文件中找不到。
答案2
当到达文件结尾 (EOF) 标记时,读取命令的退出代码为 1。在这种特殊情况下,当分隔-d
符为空时,这种情况总是会发生''
,其中源流是不能包含 \0 的定界文档。
$ read -d '' message <<-_ThisMessageEnds_
> this is a
> multi line
> message
> _ThisMessageEnds_
$ exitval=$?
$ echo "The exit val was $exitval"
The exit val was 1.
退出值是一个错误(不是 0),这使得可以使用 AND/OR 构造来避免脚本退出:
read -d '' message <<-_ThisMessageEnds_ || echo "$message"
this is a
multi line
message
_ThisMessageEnds_
这会将消息发送到控制台,但避免使用 退出它errexit
。
但既然我们正在这条路上减少,为什么不直接使用它:
cat <<-_ThisMessageEnds_
this is a
mulitline
message
_ThisMessageEnds_
不执行读取命令(速度更快),不需要变量,退出代码不会出错,需要维护的代码更少。
答案3
read -d '' message
读取标准输入,直到第一个未转义的(因为您没有添加-r
)NUL 字符或输入的末尾,并将$IFS
反斜杠字符处理后的数据存储到$message
(不带分隔符)。
如果在输入中找不到未转义的分隔符,read
则 的退出状态为非零。如果读取完整的终止记录,则仅返回 0(成功)。
它对于处理 NUL 分隔的记录(例如 的输出)最有用find -print0
(尽管您随后需要IFS= read -rd '' record
语法)。
在这里,您需要在此处文档中包含 NUL 分隔符才能read
成功返回。然而bash
,从此处文档中删除 NUL 字符是不可能的(这至少比yash
删除第一个 NUL 之后的所有内容或 ksh93 更好,当此处文档包含 NUL 时,ksh93 似乎进入无限循环)。
zsh
是唯一可以在其此处文档中包含 NUL 或将其存储在其变量中或将 NUL 字符作为参数传递给其内置函数/函数的 shell。在 中zsh
,您可以执行以下操作:
NUL=$'\0'
IFS= read -d $NUL -r var << EOF
1
2
3$NUL
EOF
(zsh
也可以理解read -d ''
为 NUL 分隔符,如bash
.read -d $'\0'
也适用,bash
但它确实将空参数传递给read
like in,read -d ''
因为bash
在其命令行中不支持 NUL 字节)。
(请注意,之后有一个额外的换行符$NUL
)
在 中bash
,您可以使用不同的字符:
ONE=$'\1'
IFS= read -d "$ONE" -r var << EOF
1
2
3$ONE
EOF
但你也可以这样做:
var=$(cat <<EOF
message
here
EOF
)
这仍然不允许 NUL 字符。然而,这是标准代码,因此您不需要依赖 zsh/bash 特定的read -d
.另请注意,它会删除所有尾随换行符,除非启用ksh93
内置cat
功能,否则这意味着会生成额外的进程和命令。
答案4
当你使用set -o errexit
并且你的脚本中断时,这意味着有问题。
在这里,是read
,它无法正确读取您的输入。
在 中bash
,当您使用 时read -d ''
,read
内置将使用空字符\0
作为行终止符。因此,当\0
您的输入中没有内容时,read
会将所有输入读取到message
变量中,并返回非零退出状态以指示存在错误:
$ while read -d '' line; do echo "$line"; done < <(printf '1')
在以下情况下不打印任何内容:
$ while read -d '' line; do echo "$line"; done < <(printf '1\0')
1
给你1
。
read
当它到达 EOF 时也会返回非零状态,但这用于指示当您read
与while
循环一起使用时没有更多输入可读取,因此while
可以终止循环。这与你的问题无关。