bash read -d '' 是什么意思?

13

-d '' 在 bash read 命令中是什么意思?这个例子直接来自之前的 SO 问题。从 read 命令打印的用法来看,它说 -d 选项定义了行内分隔单词的分隔符。空分隔符是什么意思?

read -d '' sql << EOF
select c1, c2 from foo
where c1='something'
EOF

echo "$sql"

我通过实验知道,使用它后,变量被分配了多行。如果没有使用它,则只会分配第一行。基于使用文本来解释这种行为似乎很困难。


3
空的分隔符选项“-d''”适用于“NUL”分隔的输入。 - anubhava
1
是的,\0 是 NUL 字符。 - anubhava
有趣的是,bash字符串中可以包含'\0',而变量则不行。这个问题在以下链接中有更详细的讨论:如何在bash中处理以null字符分隔的输入如何读取以null字符分隔的字段。我会进一步研究这个问题。 - minghua
1
Anubhava,也许您可以将这个nul分隔符的解释放入一个带有一些详细说明的答案中。这对我来说是新的东西。我猜有些人也不知道它。感谢您的快速回答。 - minghua
4
@minghua,...咦?一个Bash字符串绝对不能包含空字符字面值,就是完全没有任何字符。'\0'不是空字符字面值;它只是一个转义序列,printf会将其转换为空字符。 - Charles Duffy
显示剩余6条评论
2个回答

11

read -d 可以改变 read 命令停止读取的字符,从默认的换行符变成下一个参数的第一个字符。

需要理解的是,bash 使用 C 字符串,字符串以 NUL 结束。因此,当下一个参数是 '' 时,第一个(也是唯一的)字符就是终止它的 NUL;因此,当 shell 解引用 char* 来获取它所指向的第一个字符时,它得到的是一个 NUL。


现在,当你用 <<EOF 重定向一个 heredoc 时,该文档实际上不会有任何 NUL——那么你的代码怎么工作呢?

答案是你的代码期望 read 操作失败。即使失败了,read 仍然会填充其目标变量;因此,如果没有终止分隔符,read 会有一个非零的退出状态……但它仍然会将你想要收集的所有数据放入变量中!

如果你不想触发 set -e 错误的版本,请考虑在读取完成后检查目标变量是否为空:

{ IFS= read -r -d '' string || [[ $string ]]; } <<'EOF'
...string goes here...
EOF

我们做出了哪些改变?
  • IFS= 防止首尾空格(或其他字符,如果已重新定义IFS)被修剪。
  • read -r 防止内容中包含反斜杠文字。
  • || [[ $string ]] 意味着如果 read 报告失败,则我们检查字符串是否已填充,并且仍然认为变量非空时整个命令成功。

这很有帮助。否则,read -d $'\0'set -e 不兼容。而且当 read 返回非零时,它甚至没有打印错误消息的礼貌 :-) - Iain Samuel McLean Elder
@IainSamuelMcLeanElder,就我个人而言,我会认为任何人都不应该使用set -e - Charles Duffy

6
在Bash中,read内置的空字符串分隔符-d ''的行为与使用NUL字节或用ANSI C引用的字符串定义的$'\0'(即十六进制表示0x0)作为分隔符相同。 -d ''指定每个输入行应由NUL字节分隔。这意味着在每次调用read时,输入字符串都会读取到下一个NUL字符为止。
通常与IFS=一起使用:
IFS= read -r -d ''

用于去除输入中的前导和尾随空格。

处理NUL分隔输入的常见示例是:

while IFS= read -r -d '' file; do
    echo "$file"
done < <(find . -type f -print0)
  • find 命令会在当前目录中打印文件,使用 NUL 作为每个条目之间的分隔符。
  • read -d ''\0 设置为从 find 命令的输出中读取一个条目的分隔符。

相关问题: 为什么在这个例子中 'read' 不接受 \0 作为分隔符?


1
我认为$'\0'是具有误导性的,因为它的使用暗示它正在生成与''不同的东西,并且bash能够在字符串中表示NUL字面值。 - Charles Duffy
是的,我同意使用 $'\0' 是具有误导性的,但是无论我们使用 -d '' 还是 -d $'0'bash 的行为都是相同的。 - anubhava

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接