Perché il punto esclamativo tra virgolette doppie causa un errore Bash?


25

Si prega di guardare questi comandi:

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

I primi due comandi producono un fumetto di notifica come previsto. Il terzo fornisce l'errore mostrato.

e

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

Anche qui echofunziona per i primi due comandi ma non per il terzo.

Altri problemi qui (anche se non avevo intenzione di usarlo): entrambi notify-send "SYNC!TIME"e echo "SYNC!TIME"dare bash: !TIME": event not found.

Ma entrambi notify-sende echolavorare con"SYNC! TIME"

Qualcuno può spiegare perché bash: !": event not foundappare l' errore?

Risposte:


31

!è il carattere di espansione della cronologia predefinito in Bash, vedere la sezione "ESPANSIONE STORIA" nella manpage di Bash

  • L'espansione della cronologia non ha luogo se !racchiusa tra virgolette singole, come in

    notify-send 'SYNC TIME!'
  • L'espansione della cronologia non ha luogo se !è seguita da uno spazio, una scheda, una nuova riga, un ritorno a capo o =, come in

    notify-send SYNC TIME!
  • L'espansione della storia ha luogo in

    echo "SYNC TIME!"

    Quindi riceverai un errore se non c'è un comando che inizia "nella tua cronologia


4
Questo comportamento scorretto può essere corretto aggiungendo alla tua .bashrclinea set +H. Si noti che non !è già speciale nello scripting; trattarlo come speciale romperebbe molti script conformi agli standard. Viene trattato solo come "speciale" nelle shell interattive e solo per impostazione predefinita fino a quando non viene risolto. :-)
R ..

15

Perché in bash !è una parola riservata (OK, carattere), ha un significato speciale in diversi contesti. In questo caso particolare, stai cadendo in conflitto con il suo significato nella ricerca della storia. Da 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.

Fondamentalmente, ciò significa che bash prenderà i personaggi dopo !e cercherà nella tua cronologia il primo comando che trova che inizia con quei personaggi. È più facile dimostrare che spiegare:

$ echo foo
foo
$ !e
echo foo
foo

L' !espansione della cronologia attivata, che accompagnano il primo comando iniziando con ecui era precedentemente gestito echo fooche è stato poi eseguito nuovamente. Quindi, quando hai scritto "SYNC TIME!", Bash ha visto la !", ha cercato nella cronologia un comando che iniziava ", non è riuscito e si è lamentato. È possibile ottenere lo stesso errore eseguendo, ad esempio !nocommandstartswiththis.

Per stampare un punto esclamativo, è necessario evitarlo in uno di questi due modi:

echo 'Hello world!'
echo Hello world\!

6
echo Hello world!funziona --- l'espansione della storia non è innescata da spazi vuoti ;-) (ah, i bei casi angolari ...)
Rmano

1
Tuttavia, è meglio fare affidamento sull'evasione del punto esclamativo rispetto agli spazi.
Ehtesh Choudhury,

1
@Rmano Preferisco dire esplicitamente piuttosto che guidarmi di un comportamento che è possibile cambiare
Braiam

!è una parola riservata in bash, come afferma anche POSIX . Ma sono abbastanza sicuro che il suo stato di essere completamente estraneo al suo ruolo nell'espansione della storia e irrilevante per il suo trattamento tra virgolette doppie. !è una parola riservata perché nega lo stato di uscita di un comando / pipeline, quindi non può essere utilizzata come comando. Nessun'altra parola riservata è speciale in un "contesto quotato, mentre non$ è una parola riservata ma è trattata in modo speciale.
Eliah Kagan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.