【发布时间】:2015-12-19 17:37:24
【问题描述】:
执行摘要
shell 在进行进程替换时跳过 NUL 字节是标准行为吗?
例如执行
printf '\0abc' | read value && echo $value
将产生abc。 NUL 值被跳过,即使 printf 输出的 hexdump 显示它显然正在输出。
我的第一个想法是“分词”。但是,当使用实际的进程替换时
value=$(printf '\0abc')
结果相似,= 不进行分词。
长篇大论
在寻找this question 的正确答案时,我意识到我相当熟悉的至少三个 shell 实现(ash、zsh 和 bash)会忽略一个 NUL 字符将进程替换中的值读取到变量中。
当这种情况发生时,管道中的确切点似乎不同,但结果始终是 NUL 字节被丢弃,就好像它从一开始就不存在一样。
我已经检查了一些实现,嗯,这似乎是正常行为。
ash 将skip over '\0' on input,但从代码中不清楚这是纯巧合还是故意行为:
if (lastc != '\0') {
[...]
}
bash 源代码包含一个 explicit, albeit #ifdef'd warning,告诉我们它在进程替换时跳过了 NUL 值:
#if 0
internal_warning ("read_comsub: ignored null byte in input");
#endif
我不太确定zsh 的行为。它将'\0' 识别为元字符(由内部imeta() 函数定义)并预先添加一个特殊的Meta 代理字符并在输入字符上设置第5 位,基本上unmetaing 它,这也使得'\0'变成了一个空间' ')
if (imeta(c)) {
*ptr++ = Meta;
c ^= 32;
cnt++;
}
这似乎在稍后被剥离,因为没有证据表明上述printf 命令中的value 包含元字符。由于我不熟悉zsh 的内部结构,因此请多加盐。另请注意无副作用的声明。
请注意,zsh 还允许您在 IFS 中包含 NUL(元转义)(例如,可以在没有 xargs -0 的情况下拆分 find -print0)。因此printf '\0abc' | read value 和value=$(printf '\0abc') 应该根据IFS 的值产生不同的结果(read 进行字段拆分)。
【问题讨论】:
-
有趣!但我认为你已经回答了你自己的问题:)
-
@davmac 不一定。这种行为可能只是在最初的 Bourne shell 中“存在”。使用 bash 时,或多或少清楚地表明这是出于警告的目的,但我不太确定这是有意还是只是最初就是这样。但尽管如此,即使没有答案,它也确实很有趣。
-
POSIX shell 使用 C 字符串。 C 字符串不能包含 NUL 字节。所以,呃,你期待什么?
-
顺便说一句,您可以表示包含 NUL 字节的流:读入以 NUL 作为分隔符的数组。
-
pubs.opengroup.org/onlinepubs/9699919799/utilities/… 表示命令替换输出中的空字节是未定义的行为。这是我能找到的最接近的规范语言。
标签: shell environment-variables posix nul process-substitution