【问题标题】:Multi-line, double quoted string triggers history expansion on subsequent single-quoted commands it gets piped to多行双引号字符串触发后续单引号命令的历史扩展
【发布时间】:2024-01-11 05:38:01
【问题描述】:

我使用的是 GNU bash,版本 4.3.11。

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

$ cat a
9
10
9
11
$ awk '!seen[$0]++' a
9
10
11

但是,如果我从标准输入获取输入,在多行中使用双引号并通过管道连接到 awk,则会失败:

$ echo "9
> 10
> 9
> 11" | awk '!seen[$0]++'
bash: !seen[$0]++': event not found

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

echoing 在单引号多行输入中效果很好:

$ echo '9
> 10
> 9
> 11' | awk '!seen[$0]++'
9
10
11

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

$ printf "9\n10\n9\n11" | awk '!seen[$0]++'
9
10
11

我想知道为什么 Bash 尝试在多行输入之后尝试扩展历史记录,即使命令本身使用单引号。

其他注意事项:

中间有管道也不能解决问题:

$ echo "9
> 10
> 9
> 11" | cat - | awk '!seen[$0]++'
bash: !seen[$0]++': event not found

并设置set +H turns history off,所以它工作得很好,因为它不会尝试扩展任何东西:

$ set +H
$ echo "9
> 10
> 9
> 11" | awk '!seen[$0]++'
9
10
11

我查看了 rici 在 how to address error “bash: !d': event not found” in Bash command substitution 上的规范答案,发现了许多可能的原因,但没有一个符合这种行为。

【问题讨论】:

  • 似乎最后一行被解析为单行,而不是包含之前引用的字符串。这意味着! 的处理就像它是在双引号中一样。您还会注意到,您将在下一行得到一个>,因为它正在等待您关闭这个感知到的引用字符串。这就是它也适用于单行的原因。看起来像一个错误。
  • 可在 4.2.47 中重现。我会提交一个错误。
  • 也可以一直重现到3.2.51
  • $ ls<press enter here> $ echo "<press enter here>asdasd<another enter here>" | echo '!!' 将打印奇怪的输出...
  • @anishsane 与!# 相同,但不是您所期望的,请尝试echo "<ENTER>123<ENTER>" | awk '!#'。它返回" | awk '" | awk ''

标签: bash gnu command-substitution


【解决方案1】:

这不是错误。

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

历史扩展明确是面向行的。

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

它确实知道在 大量的 Unix 实用程序——自历史和 readline 库在外壳之外使用——而且是双引号 引入带引号的字符串,其中单引号不重要 并且不要抑制历史扩展。

【讨论】:

  • 赞成记录这一点,但这是疯狂的行为。