Чому знак оклику в подвійних лапках викликає помилку Баша?


25

Будь ласка, подивіться на ці команди:

$ 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-sendі echoробота з"SYNC! TIME"

Може хтось, будь ласка, пояснить, чому bash: !": event not foundз’являється помилка?

Відповіді:


31

!є символом розширення історії за замовчуванням у Bash, див. розділ "ІСТОРІЙНЕ РОЗШИРЕННЯ" на сторінці Bash

  • Розширення історії не відбувається, якщо !додаток одиничних лапок, як у

    notify-send 'SYNC TIME!'
  • Розширення історії не має місце, якщо за !ним супроводжується пробіл, вкладка, нова лінія, повернення каретки чи =, як у в

    notify-send SYNC TIME!
  • Розкриття робить мати місце в

    echo "SYNC TIME!"

    Таким чином, ви отримаєте помилку, якщо "в історії не буде команди, починаючи з


4
Цю неправильну поведінку можна виправити, додавши до .bashrcрядка set +H. Зауважте, що !це вже не спеціально для сценаріїв; трактуючи це як особливе, це порушило б багато сценаріїв, що відповідають стандартам. Це трактується лише як "особливий" в інтерактивних оболонках, і лише за замовчуванням, поки ви не виправите це. :-)
R ..

15

Оскільки у 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!", Баш бачив !", що шукав історію для команди, починаючи з ", не вдався і скаржився на неї. Ви можете отримати ту саму помилку, запустивши, наприклад !nocommandstartswiththis.

Щоб надрукувати знак оклику, вам потрібно відмовитися від нього одним із цих двох способів:

echo 'Hello world!'
echo Hello world\!

6
echo Hello world!чи працює --- розширення історії не спрацьовує пробілами ;-) (ах, гарні кутові випадки ...)
Rmano

1
Однак краще покластися на уникнення знака оклику, ніж пробіли.
Ехтеш Чудхурі

1
@Rmano Я вважаю за краще прямо сказати так, а не керувати мене поведінкою, яку можна змінити
Брайам

!є зарезервованим словом у bash, як POSIX також заявляє . Але я впевнений, що його статус як один абсолютно не пов'язаний з його роллю в розширенні історії та не має відношення до його трактування у подвійних лапках. !це зарезервоване слово, оскільки воно заперечує статус виходу команди / трубопроводу, тому його не можна використовувати як команду. Ні одне інше зарезервоване слово не є особливим в "-quoted контексті, в той час як $це НЕ є зарезервованим словом , але це лікування спеціально.
Елія Каган
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.