Por que o ponto de exclamação entre aspas duplas causa um erro de Bash?

23

Por favor, olhe estes comandos:

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

Os dois primeiros comandos produzem uma bolha de notificação conforme o esperado. O terceiro dá o erro mostrado.

e

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

Aqui também, o echo funciona para os dois primeiros comandos, mas não no terceiro.

Mais problemas aqui (embora eu não estivesse planejando usar isso): notify-send "SYNC!TIME" e echo "SYNC!TIME" fornecem bash: !TIME": event not found .

Mas os dois notify-send e echo trabalham com "SYNC! TIME"

Alguém pode explicar por que o erro bash: !": event not found aparece?

    
por DK Bose 16.03.2014 / 04:15

2 respostas

29

! é o caractere de expansão de histórico padrão no Bash, veja a seção "EXPANSÃO DE HISTÓRIA" no Página manpage de Bash

  • A expansão do histórico não ocorre se o ! estiver entre aspas simples, como em

    notify-send 'SYNC TIME!'
    
  • A expansão do histórico não ocorre se o ! for seguido por um espaço, tabulação, nova linha, retorno de carro ou = , como em

    notify-send SYNC TIME!
    
  • Expansão do histórico faz acontecer em

    echo "SYNC TIME!"
    

    Você receberá um erro se não houver um comando que comece com " em seu histórico

por Florian Diesch 16.03.2014 / 04:36
15

Como no bash, ! é uma palavra reservada (OK, caractere), ela tem um significado especial em diferentes contextos. Neste caso particular, você está caindo em conflito com sua importância na busca de histórico. De 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.

Basicamente, o que isto significa é que o bash pegará os caracteres após o ! e pesquisará seu histórico pelo primeiro comando que encontrar que comece com esses caracteres. É mais fácil demonstrar do que explicar:

$ echo foo
foo
$ !e
echo foo
foo

A expansão do histórico ! ativado, que corresponde ao primeiro comando que começa com e , que foi a execução anterior echo foo que foi executada novamente. Então, quando você escreveu "SYNC TIME!" , o bash viu o !" , o histórico pesquisado de um comando que começou com " , falhou e reclamou dele. Você pode obter o mesmo erro executando, por exemplo, !nocommandstartswiththis .

Para imprimir um ponto de exclamação, você precisa escapar de uma destas duas maneiras:

echo 'Hello world!'
echo Hello world\!
    
por terdon 16.03.2014 / 04:34