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