Как повторить взрыв!


59

Я попытался создать скрипт, echoвставив содержимое в файл, а не открывая его в редакторе.

echo -e "#!/bin/bash \n /usr/bin/command args"  > .scripts/command

Выход :

bash:! / bin / bash: событие не найдено

Я выделил это странное поведение на ура .

$ echo !
!  

$ echo "!"
bash: !: event not found

$ echo \#!
#!

$ echo \#!/bin/bash
bash: !/bin/bash: event not found
  • Почему взрыв вызывает это?
  • К каким «событиям» относится bash?
  • Как мне обойти эту проблему и вывести «#! / Bin / bash» на экран или в мой файл?

Ответы:


59

Попробуйте использовать одинарные кавычки.

echo -e '#!/bin/bash \n /usr/bin/command args'  > .scripts/command

echo '#!'

echo '#!/bin/bash'

Проблема возникает, потому что bash ищет в своей истории! / Bin / bash. Использование одинарных кавычек позволяет избежать этого поведения.


3
Он также отключает интерполяцию строк :(
Hubro

В этом-то и дело! Вы можете добавить другие аргументы к той же команде echo, используя двойные кавычки, и получить интерполяцию по этим частям.
Ричм

3
Вы также можете использовать строку стиля C в bash с $''. Например, echo $'1!\n2!\n3!\n'печатает каждый номер, за которым следует удар и символ новой строки.
spelufo

1
Помещение взрыва в одинарные кавычки не помогло мне при вводе строки из нескольких строк: bash - одинарные кавычки не избежать взрыва (по-настоящему)
binki

1
@kbolino Вы правы. Таким образом, полная строка примера будет:, echo '0123'"$var"'456'обратите внимание на две пары одинарных кавычек и одну пару двойных кавычек.
Phyatt

16

Как сказал Ричм , bash пытается сделать исторический матч . Другой способ избежать этого - просто избежать взрыва с помощью \:

$ echo \#\!/bin/bash
#!/bin/bash

Хотя имейте в виду, что внутри двойных кавычек \не удаляется:

$ echo "\!"
\!

8
В bash "\!"фактически расширяется \!, а не !, предположительно, для соответствия POSIX.
Микель

@ Майк вздыхает. Так много различий между zsh и bash
Майкл Мрозек

Это работает даже при вводе многострочной строки. Помните, что: 1. быть за пределами строки при вводе взрыва и 2. использовать этот метод (обратный слеш), похоже, работает с наименьшим удивлением в большинстве сценариев.
Бинки

1
Было бы полезно отметить, что bash не выполняет поиск в истории при выполнении скрипта, поэтому нет необходимости делать что-то особенное для него.
Бинки

Есть ли какой-нибудь синтаксис, чтобы просто убежать !в двойные кавычки без магического поведения? Это безумие ...
Ласси

13

!запускает подстановку истории («событие» - это строка в истории команд); например, !lsрасширяется до последней содержащей командную строку lsи !?fooрасширяется до последней содержащей командную строку foo. Вы также можете извлечь конкретные слова (например, !!:1относится к первому слову предыдущей команды) и многое другое; см. руководство для деталей.

Эта функция была изобретена для быстрого вызова предыдущих команд в те времена, когда редакция командной строки была примитивной. С современными оболочками (по крайней мере, bash и zsh) и копированием и вставкой расширение истории не так полезно, как раньше - оно все еще полезно, но вы можете обойтись без него.

Вы можете изменить, какой символ запускает подстановку истории, установив histcharsпеременную; если вы редко используете подстановку истории, вы можете установить, например, histchars='¡^'так, чтобы ¡запускать расширение истории вместо !. Вы даже можете отключить эту функцию вместе с set +o histexpand.


3

Чтобы иметь возможность отключить расширение истории в определенной командной строке, вы можете использовать spaceв качестве 3-го символа $histchars:

histchars='!^ '

Затем, если вы введете команду с начальным пробелом, расширение истории не будет выполнено.

bash-4.3$ echo "#!/bin/bash"
bash: !/bin/bash: event not found
bash-4.3$  echo "#!/bin/bash"
#!/bin/bash

Тем не менее, обратите внимание, что начальные пробелы также используются, когда $HISTCONTROLсодержатся ignorespaceкак способ запретить bashзапись командной строки в историю.

Если вы хотите, чтобы обе функции были независимыми, вам нужно выбрать другой символ в качестве 3-го символа $histchars. Вы хотите тот, который не влияет на интерпретацию вашей команды. Несколько вариантов:

  • использование backslash ( \): \echo fooработает, но имеет побочный эффект отключения псевдонимов или ключевых слов.
  • TAB: чтобы вставить его в первую позицию, вам нужно нажать Ctrl+VTabхотя.
  • если вы не возражаете , набрав два ключа, вы можете выбрать любой символ , который обычно не появляется в первой позиции ( %, @, ?, выбрать свой собственный) и сделать пустой псевдоним для него:

    histchars='!^%'
    alias %=

    Затем введите этот символ, пробел и вашу команду:

    bash-4.3$ % echo !!
    !!

(Вы не сможете не записать команду, в которой подстановка истории была отключена. Также обратите внимание, что третий символ по умолчанию используется для того $histchars, #чтобы расширение истории не делалось в комментариях. Если вы измените его, и вы введете комментарии в подскажите, вы должны знать о том, что! последовательности могут быть расширены там).


2

Предлагаемые решения не работают, например, в следующем примере:

$ bash -c "echo 'hello World!'"
-bash: !'": event not found
$

В этом случае взрыв может быть напечатан с использованием восьмеричного кода ASCII:

$ bash -c "echo -e 'hello World\0041'"
hello World!
$

1
В вашей первой команде символ !находится между двойными кавычками, где, как указывают другие ответы, он сохраняет свое значение расширения истории. Вы можете использовать bash -c 'echo '\''hello World!'\'или bash -c "echo 'hello World"\!"'".
Жиль "ТАК - перестать быть злым"

Без параметра -i среда может быть изменена (например, ваш ~ / .profile не запускается). Однако выполнение -i означает, что любой вывод из вашего .profile будет записан в вывод команды, которую вы на самом деле хотите выполнить.
Брент

-1

Начиная с Bash 4.3, теперь вы можете использовать двойные кавычки для цитирования символа расширения истории:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!

4
Нет, только !за этим следует, "что больше не является особенным. echo "foo!"все в порядке, echo "foo!bar"нет
Стефан Шазелас
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.