为什么感叹号 `!` 有时会打乱 bash?

Pet*_*r.O 15 bash command-history quoting

我意识到这!在命令行历史上下文中对命令行具有特殊意义,但除此之外,在运行脚本中,感叹号有时会导致解析错误。
我认为它与 an 有关event,但我不知道事件是什么或它做什么。即便如此,相同的命令在不同情况下的行为也会有所不同。
下面的最后一个示例导致错误;但是为什么,当相同的代码在命令替换之外工作时呢?.. 使用 GNU bash 4.1.5

# This works, with or without a space between ! and p
  { echo -e "foo\nbar" | sed -nre '/foo/! p'
    echo -e "foo\nbar" | sed -nre '/foo/!p'; }
# bar
# bar

# This works, works when there is a space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/! p')"; echo "$var"
# bar

# This causes an ERROR, with NO space between ! and p
  var="$(echo -e "foo\nbar" | sed -nre '/foo/!p')"; echo "$var"
# bash: !p': event not found
Run Code Online (Sandbox Code Playgroud)

Cal*_*leb 12

!字符调用 bash 的历史替换(在交互式 shell 中默认启用)。当后跟一个字符串时(如在您失败的示例中),它会尝试扩展到以该字符串开头的最后一个历史事件。就像$var扩展到该字符串的值一样,!echo将扩展到历史记录中的最后一个 echo 命令。

在这种扩展中,空间是一个破坏性的字符。首先注意这将如何与变量一起工作:

# var="like"
# echo "$var"
like
# echo "$"
$
# echo "Do you $var frogs?"
Do you like frogs?       <- as expected, variable name broken at space
# echo "Do you $varfrogs?"
Do you?                  <- $varfrogs not defined, replaced with blank
# echo "Do you $ var frogs?"
Do you $ var frogs?      <- $ not a valid variable name, ignored
Run Code Online (Sandbox Code Playgroud)

历史扩展也会发生同样的事情。bang 字符 ( !) 从历史替换序列开始,但前提是后跟字符串。在它后面加上一个空格使它成为文字爆炸而不是替换序列的一部分。

您可以通过使用单引号来避免这种对变量和历史扩展的替换。您的第一个示例使用单引号,因此运行良好。您的最后一个示例用双引号括起来,因此 bash 在执行任何其他操作之前先扫描它们以查找扩展序列。第一个没有跳闸的唯一原因是空格是一个中断字符,如上所示。


enz*_*tib 6

正如Caleb已经说过的!用于调用 bash 的历史替换。

如果像我一样你觉得你不需要这样的功能,你可以禁用它在中插入以下行~/.bashrc

set +H
Run Code Online (Sandbox Code Playgroud)

我不需要它,因为历史可以通过向上箭头和Ctrl-r增量反向搜索恢复。有关快捷方式的详细列表,请参阅 bash 的手册页,用于操作历史记录的命令部分。

  • 没有`!!`你怎么活? (2认同)
  • @fred:奇怪,通常历史扩展只对交互式 shell 是“开启的”。 (2认同)