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"的规范回答,并找到了许多可能的原因,但没有一个符合这种行为.
这不是一个错误。
在 bash-bugs 邮件列表中询问这个问题后,我得到了以下答案:
历史扩展显然是面向线的。
它不知道跨行的 shell 状态,尤其是 shell 引用状态。
它确实知道在大量 Unix 实用程序中常见的模糊的类似 shell 的引用——因为历史和 readline 库是在 shell 之外使用的——并且双引号引入了一个带引号的字符串,其中单引号并不重要,并且不要抑制历史的扩展。