为什么双引号内的感叹号会导致 Bash 错误?

为什么双引号内的感叹号会导致 Bash 错误?

请看一下这些命令:

$ notify-send SYNC TIME!
$ notify-send 'SYNC TIME!'
$ notify-send "SYNC TIME!"
bash: !": event not found
$

前两个命令按预期生成了通知气泡。第三个命令给出了所示的错误。

$ echo SYNC TIME!
SYNC TIME!
$ echo 'SYNC TIME!'
SYNC TIME!
$ echo "SYNC TIME!"
bash: !": event not found
$

这里同样如此,echo前两个命令有效,但第三个命令无效。

这里有更多问题(虽然我本来不打算使用这个):和notify-send "SYNC!TIME"echo "SYNC!TIME"给予bash: !TIME": event not found

但两者都notify-sendecho"SYNC! TIME"

有人能解释一下为什么bash: !": event not found会出现这个错误吗?

答案1

!是 Bash 中的默认历史扩展字符,请参阅Bash 手册页

  • 如果用单引号引起来,则不会进行历史扩展!,例如

    notify-send 'SYNC TIME!'
    
  • !如果 后面跟着空格、制表符、换行符、回车符或=,则不会进行历史记录扩展,例如

    notify-send SYNC TIME!
    
  • 历史扩展发生在

    echo "SYNC TIME!"
    

    "因此,如果历史记录中没有以 开头的命令,则会收到错误

答案2

因为在 bash 中,!是一个保留字(好吧,字符),它在不同的上下文中具有特殊含义。在这个特定情况下,您与它在历史搜索中的重要性相冲突。来自man bash

   History expansions introduce words from the history list into the input
   stream, making it easy to repeat commands, insert the  arguments  to  a
   previous command into the current input line, or fix errors in previous
   commands quickly.

  [...]

   History expansions are introduced by
   the appearance of the  history  expansion  character,  which  is  !  by
   default.   Only  backslash  (\) and single quotes can quote the history
   expansion character.

基本上,这意味着 bash 将获取后面的字符,!并在历史记录中搜索以这些字符开头的第一个命令。演示起来比解释起来更容易:

$ echo foo
foo
$ !e
echo foo
foo

激活的历史记录扩展!与第一个以 开头的命令匹配,该命令e是之前运行的echo foo,然后再次运行。因此,当您写入 时"SYNC TIME!",bash 会看到!",在历史记录中搜索以 开头的命令",失败并对此发出警告。例如,您可以通过运行 得到相同的错误!nocommandstartswiththis

要打印感叹号,您需要采用以下两种方式之一对其进行转义:

echo 'Hello world!'
echo Hello world\!

相关内容