¿Por qué el signo de exclamación entre comillas dobles causa un error Bash?

25

Mire estos comandos:

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

Los primeros dos comandos producen una burbuja de notificación como se esperaba. El tercero da el error que se muestra.

y

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

Aquí también, echofunciona para los dos primeros comandos pero no en el tercero.

Más problemas aquí (aunque no estaba planeando usar esto): ambos notify-send "SYNC!TIME"y echo "SYNC!TIME"dar bash: !TIME": event not found.

Pero ambos notify-sendy echotrabajar con"SYNC! TIME"

¿Alguien puede explicar por qué bash: !": event not foundaparece el error?

Justicia para Monica
fuente

Respuestas:

31

!es el carácter de expansión de historial predeterminado en Bash, consulte la sección "EXPANSIÓN DE HISTORIAL" en la página de manual de Bash

  • La expansión del historial no tiene lugar si !se incluye entre comillas simples, como en

    notify-send 'SYNC TIME!'
  • La expansión del historial no tiene lugar si !va seguida de un espacio, tabulación, nueva línea, retorno de carro o =, como en

    notify-send SYNC TIME!
  • La expansión de la historia tiene lugar en

    echo "SYNC TIME!"

    Entonces obtendrá un error si no hay un comando que comience "en su historial

Florian Diesch
fuente
44
Este mal comportamiento se puede solucionar agregando a su .bashrclínea set +H. Tenga en cuenta que !ya no es especial en secuencias de comandos; tratarlo como especial rompería muchos scripts que cumplen con los estándares. Solo se trata como "especial" en shells interactivos, y solo de forma predeterminada hasta que lo arregles. :-)
R ..
15

Porque en bash, !es una palabra reservada (OK, carácter), tiene un significado especial en diferentes contextos. En este caso particular, estás cayendo en conflicto con su importancia en la búsqueda de la historia. 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.

Básicamente, lo que esto significa es que bash tomará los caracteres después del !y buscará en su historial el primer comando que encuentre que comience con esos caracteres. Es más fácil demostrar que explicar:

$ echo foo
foo
$ !e
echo foo
foo

La !expansión del historial activado, que coincidía con el primer comando que comenzaba con el eque se ejecutó anteriormente, echo fooque luego se ejecutó nuevamente. Entonces, cuando escribiste "SYNC TIME!", bash vio el !", buscó en el historial un comando que comenzó ", falló y se quejó. Puede obtener el mismo error ejecutando, por ejemplo !nocommandstartswiththis.

Para imprimir un signo de exclamación, debe escapar de una de estas dos formas:

echo 'Hello world!'
echo Hello world\!
terdon
fuente
66
echo Hello world!funciona --- la expansión del historial no se desencadena por espacios en blanco ;-) (ah, los buenos casos de esquina ...)
Rmano
1
Sin embargo, es mejor confiar en escapar del signo de exclamación que en blanco.
Ehtesh Choudhury
1
@Rmano Prefiero decirlo explícitamente en lugar de guiarme sobre un comportamiento que es posible cambiar
Braiam
!es una palabra reservada en bash, como POSIX también declara . Pero estoy bastante seguro de que su estado como uno no está relacionado con su papel en la expansión de la historia y es irrelevante para su tratamiento entre comillas dobles. !es una palabra reservada porque niega el estado de salida de un comando / canalización, por lo que no se puede usar como un comando. Ninguna otra palabra reservada es especial en un "contexto entre comillas, mientras $que no es una palabra reservada, pero se trata especialmente.
Eliah Kagan