Warum verursacht ein Ausrufezeichen in doppelten Anführungszeichen einen Bash-Fehler?

25

Bitte schauen Sie sich diese Befehle an:

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

Die ersten beiden Befehle erzeugen erwartungsgemäß eine Benachrichtigungsblase. Der dritte gibt den angezeigten Fehler an.

und

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

Auch hier echofunktioniert das für die ersten beiden Befehle, aber nicht für den dritten.

Weitere Probleme hier (obwohl ich dies nicht vorhatte): beides notify-send "SYNC!TIME"und echo "SYNC!TIME"geben bash: !TIME": event not found.

Aber beides notify-sendund echoarbeiten mit"SYNC! TIME"

Kann mir bitte jemand erklären, warum der bash: !": event not foundFehler auftritt?

Gerechtigkeit für Monica
quelle

Antworten:

31

!ist das Standard-Verlaufserweiterungszeichen in Bash. Weitere Informationen finden Sie im Abschnitt "ERWEITERUNG DER GESCHICHTE" in der Bash-Manpage

  • Eine Historienerweiterung findet nicht statt, wenn das !wie in in einfachen Anführungszeichen eingeschlossen ist

    notify-send 'SYNC TIME!'
  • Eine Erweiterung des !Verlaufs findet nicht statt, wenn nach dem ein Leerzeichen, ein Tabulator, ein Zeilenumbruch, ein Wagenrücklauf oder =wie in angezeigt wird

    notify-send SYNC TIME!
  • Die Erweiterung der Geschichte findet in statt

    echo "SYNC TIME!"

    Sie erhalten also eine Fehlermeldung, wenn "in Ihrem Verlauf kein Befehl vorhanden ist, der mit "" beginnt

Florian Diesch
quelle
4
Dieses Fehlverhalten kann behoben werden, indem Sie .bashrcdie Leitung ergänzen set +H. Beachten Sie, dass dies !in Skripten bereits keine Besonderheit ist. Wenn Sie es als etwas Besonderes behandeln, können viele standardkonforme Skripte beschädigt werden. Es wird nur in interaktiven Shells als "speziell" behandelt und standardmäßig nur, bis Sie es beheben. :-)
R ..
15

Da es sich bei bash !um ein reserviertes Wort (OK, Zeichen) handelt, hat es in verschiedenen Kontexten eine besondere Bedeutung. In diesem speziellen Fall geraten Sie in Konflikt mit seiner Bedeutung bei der Historiensuche. Von 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.

Grundsätzlich bedeutet dies, dass Bash die Zeichen nach dem in !Ihrem Verlauf nach dem ersten gefundenen Befehl durchsucht, der mit diesen Zeichen beginnt. Es ist einfacher zu demonstrieren als zu erklären:

$ echo foo
foo
$ !e
echo foo
foo

Die !aktivierte Verlaufserweiterung, die dem ersten Befehl entsprach, der mit edem zuvor ausgeführten Befehl begann , echo fooder dann erneut ausgeführt wurde. Als Sie also geschrieben haben "SYNC TIME!", hat bash den !"Verlauf nach einem Befehl durchsucht, der mit " "fehlgeschlagen" begann, und sich darüber beschwert. Sie können den gleichen Fehler erhalten, indem Sie beispielsweise ausführen !nocommandstartswiththis.

Um ein Ausrufezeichen zu drucken, müssen Sie es auf eine der beiden folgenden Arten maskieren:

echo 'Hello world!'
echo Hello world\!
terdon
quelle
6
echo Hello world!funktioniert --- Geschichte Erweiterung wird nicht durch Leerzeichen ausgelöst ;-) (ah, die schönen
Eckfälle
1
Es ist jedoch besser, sich darauf zu verlassen, dass das Ausrufezeichen nicht angezeigt wird als Leerzeichen.
Ehtesh Choudhury
1
@Rmano Ich ziehe es vor, dies ausdrücklich zu sagen, anstatt mich über ein Verhalten zu
informieren
!ist ein reserviertes Wort in bash, wie POSIX auch deklariert . Aber ich bin mir ziemlich sicher, dass sein Status völlig unabhängig von seiner Rolle bei der Erweiterung der Geschichte und irrelevant für seine Behandlung in doppelten Anführungszeichen ist. !ist ein reserviertes Wort, da es den Exit-Status eines Befehls / einer Pipeline negiert und daher nicht als Befehl verwendet werden kann. Kein anderes reserviertes Wort ist etwas Besonderes in einem "-quoted Kontext, während $ist nicht ein reserviertes Wort , sondern wird speziell behandelt.
Eliah Kagan