多行双引号字符串会触发随后通过管道传送的单引号命令的历史记录扩展

fed*_*qui 7 bash gnu command-substitution

我在GNU bash,版本4.3.11.

假设我想在文件上打印唯一的行.我正在使用这种方法,它适用于文件:

$ cat a
9
10
9
11
$ awk '!seen[$0]++' a
9
10
11
Run Code Online (Sandbox Code Playgroud)

但是,如果我从stdin获取输入,在多行中使用双引号并输入到awk,则会失败:

$ echo "9
> 10
> 9
> 11" | awk '!seen[$0]++'
bash: !seen[$0]++': event not found
Run Code Online (Sandbox Code Playgroud)

也就是说,bash尝试扩展命令seen,这当然不知道,因为它是一个变量名.但它不应该发生,因为命令放在单引号内.

echo单引号,多行输入效果很好:

$ echo '9
> 10
> 9
> 11' | awk '!seen[$0]++'
9
10
11
Run Code Online (Sandbox Code Playgroud)

有趣的是,它也适用于双引号的单行输入:

$ printf "9\n10\n9\n11" | awk '!seen[$0]++'
9
10
11
Run Code Online (Sandbox Code Playgroud)

我想知道为什么Bash试图扩展历史,如果它发生在多线输入后,即使命令本身使用单引号.

其他考虑:

中间有一个管道也无法修复它:

$ echo "9
> 10
> 9
> 11" | cat - | awk '!seen[$0]++'
bash: !seen[$0]++': event not found
Run Code Online (Sandbox Code Playgroud)

并且设置set +H会关闭历史记录,因此它运行良好,因为它不会尝试扩展任何内容:

$ set +H
$ echo "9
> 10
> 9
> 11" | awk '!seen[$0]++'
9
10
11
Run Code Online (Sandbox Code Playgroud)

我经历了rici关于如何在Bash命令替换中解决错误"bash:!d':event not found"的规范回答,并找到了许多可能的原因,但没有一个符合这种行为.

fed*_*qui 2

这不是一个错误。

在 bash-bugs 邮件列表中询问这个问题后,我得到了以下答案:

历史扩展显然是面向线的。

它不知道跨行的 shell 状态,尤其是 shell 引用状态。

它确实知道在大量 Unix 实用程序中常见的模糊的类似 shell 的引用——因为历史和 readline 库是在 shell 之外使用的——并且双引号引入了一个带引号的字符串,其中单引号并不重要,并且不要抑制历史的扩展。

  • 支持记录这一点,但这是疯狂的行为。 (2认同)