skipping/ignoring NUL 字节的进程替换是否标准化?

Is skipping/ignoring NUL bytes on process substitution standardized?

执行摘要

shell在进行进程替换时跳过 NUL 字节是标准行为吗?

例如执行

printf '[=11=]abc' | read value && echo $value

将产生 abc。 NUL 值被跳过,即使 printf 输出的 hexdump 显示它显然正在输出。

我的第一个想法是“分词”。但是,当使用实际过程替换时

value=$(printf '[=12=]abc')

结果相似,=不进行分词。

长话短说

在搜索 的正确答案时,我意识到至少有三个 shell 实现(ash、zsh 和 bash)我相当熟悉从进程替换中读取值到变量时忽略 NUL 字符。

发生这种情况时管道中的确切点似乎有所不同,但结果始终是 NUL 字节被丢弃,就好像它一开始就不存在一样。

我检查了一些实现,好吧,这似乎是正常行为。

ashskip over '[=20=]' on input,但从代码中不清楚这是纯属巧合还是有意为之:

if (lastc != '[=13=]') {
    [...]
}

bash 源代码包含一个 explicit, albeit #ifdef'd warning 告诉我们它在进程替换时跳过了 NUL 值:

#if 0
      internal_warning ("read_comsub: ignored null byte in input");
#endif

我不太确定 zsh 的行为。它将 '[=20=]' 识别为元字符(由内部 imeta() 函数定义)并在前面加上一个特殊的 Meta 代理字符并在输入字符上设置位 #5,本质上是 unmetaing 它,这使得 '[=20=]' 也变成了 space ' ')

if (imeta(c)) {
    *ptr++ = Meta;
    c ^= 32;
    cnt++;
}

这似乎稍后会被删除,因为没有证据表明上述 printf 命令中的 value 包含元字符。因为我不熟悉 zsh 的内部结构,所以请多加注意。另请注意无副作用声明。

请注意,zsh 还允许您在 IFS 中包含 NUL(元转义)(例如,可以在不使用 xargs -0 的情况下进行分词 find -print0) .因此 printf '[=36=]abc' | read valuevalue=$(printf '[=37=]abc') 应该根据 IFS 的值产生不同的结果(read 进行字段拆分)。

所有现存的 POSIX shell 都使用 C 字符串(以 NUL 结尾),而不是 Pascal 字符串(将它们的长度作为单独的元数据携带,因此能够包含 NUL)。因此,它们不可能在字符串内容中包含 NUL。 Bourne Shell 和 ksh 尤其如此,它们都对 POSIX sh 标准产生了重大影响。

规范允许 shells 在此处以实现定义的方式运行;在不知道特定的 shell 和目标版本的情况下,我不希望在终止第一个 NUL 处返回的流和完全丢弃 NUL 之间有特定的行为。 Quoting:

The shell shall expand the command substitution by executing command in a subshell environment (see Shell Execution Environment) and replacing the command substitution (the text of command plus the enclosing "$()" or backquotes) with the standard output of the command, removing sequences of one or more characters at the end of the substitution. Embedded characters before the end of the output shall not be removed; however, they may be treated as field delimiters and eliminated during field splitting, depending on the value of IFS and quoting that is in effect. If the output contains any null bytes, the behavior is unspecified.


这并不是说您不能在广泛使用的 shell 中读取和生成包含 NUL 的流!请参阅下面,使用进程替换(为 bash 编写,但应该与 ksh 或 zsh 一起使用,如果有的话,稍作改动):

# read content from stdin into array variable and a scalar variable "suffix"
array=( )
while IFS= read -r -d '' line; do
  array+=( "$line" )
done < <(process that generates NUL stream here)
suffix=$line # content after last NUL, if any

# emit recorded content
printf '%s[=10=]' "${array[@]}"; printf '%s' "$suffix"