【问题标题】:Is skipping/ignoring NUL bytes on process substitution standardized?在进程替换上跳过/忽略 NUL 字节是否标准化?
【发布时间】: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 字节被丢弃,就好像它从一开始就不存在一样。

我已经检查了一些实现,嗯,这似乎是正常行为。

ashskip 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 valuevalue=$(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


【解决方案1】:

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

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

shell 应通过在子 shell 环境中执行命令来扩展命令替换(参见 Shell 执行环境)并将命令替换(命令文本加上封闭的“$()”或反引号)替换为命令,在替换结束时删除一个或多个字符的序列。输出结束前的嵌入字符不得删除;但是,它们可能被视为字段分隔符并在字段拆分期间被消除,具体取决于 IFS 的值和有效的引用。 如果输出包含任何空字节,则行为未指定。


这并不是说您不能在广泛可用的 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\0' "${array[@]}"; printf '%s' "$suffix"

【讨论】:

  • 这个版本比初始版本好很多。接受。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-03
  • 1970-01-01
  • 2016-07-17
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
  • 1970-01-01
相关资源
最近更新 更多