Не можете использовать восклицательный знак (!) В bash?

87

Я пытаюсь использовать команду curl для доступа к URL-адресу http с восклицательным знаком ( !) на своем пути. например:

curl -v "http://example.org/!287s87asdjh2/somepath/someresource"

консоль отвечает с bash: ... event not found.

Что здесь происходит? и какой будет правильный синтаксис, чтобы избежать восклицательного знака?

netbrain
источник
Решено в bash 4.4+
Исаак

Ответы:

99

Восклицательный знак является частью истории расширения в bash. Чтобы использовать его, он должен быть заключен в одинарные кавычки (например:) 'http://example.org/!132'или непосредственно экранирован с помощью обратной косой черты ( \) перед символом (например:) "http://example.org/\!132".

Обратите внимание, что в двойных кавычках, обратная косая черта перед восклицанием предотвращает расширение истории, НО обратная косая черта в этом случае не удаляется. Так что лучше использовать одинарные кавычки, чтобы вы не передавали буквальную обратную косую черту curlкак часть URL.

Даниэль Питтман
источник
8
"http://example.org/\!132"фактически расширяется без интерпретации обратной косой черты (я полагаю, что это связано с требованиями соблюдения POSIX).
Крис Даун
@ChrisDown, я попытался уточнить, что это был мой второй вариант в тексте. Спасибо за указание на возможность путаницы.
Даниэль Питтман
6
Для справки: это не портативно, чтобы попытаться избежать "!". Рекомендация по передовому опыту всегда заключать в кавычки (!). Связанный: «^» (каретка), это неметасимвол, который требует цитирования для переносимости. В заключение, "!" не должен использоваться в операторе if; используйте его в качестве аргумента для проверки вместо этого, если это возможно (опять же из-за Solaris / bin / sh).
Николас Уилсон
5
У меня работали только одинарные кавычки. Zsh по-прежнему интерпретирует \!и двойные кавычки.
orkoden
1
В Solaris (старомодная оболочка до XPG4) '^' является псевдонимом для |и используется для создания канала. Если вы отправляете сценарии клиентам и не можете быть уверены, в какой оболочке они будут работать, вам нужно протестировать их все!
Николас Уилсон
61

Помимо ответа Даниэля, вы также можете просто полностью отключить расширение истории, если не используете его set +H.

Крис Даун
источник
19
Отключить расширение истории - лучший совет, который я слышал весь день! Расширение истории является опасным и византийским, когда есть намного лучшие альтернативы (инкрементальный поиск в истории Ctrl-R), которые позволяют вам предварительно просматривать и редактировать свою команду, чтобы не слепо отмахиваться от команды, !-14которой вы, хотя и были !-12, к сожалению, оказались rm -rf *. Быть в безопасности. Отключить расширение истории! Eschew !!
aculich
6
Самый большой ответ: расширение истории - огромный риск для безопасности! Он может быть использован для атаки на ваш Unix через специально созданный URL.
дан
@aculich, или просто используйте вместо этого указанную POSIX команду fc -14. Но это правда, что вы можете сделать это без включения расширения истории. Лично я использую !$и, !viи sudo !!даже git add !vi:$достаточно часто, чтобы оставить расширение истории включенным.
Wildcard
Я думаю, что я добавлю это к моим файлам оболочки RC. Я только когда-либо использовал это как аккуратный "трюк"
TonyH
17

Лично я бы делал одинарные кавычки, но для полноты я также отмечу, что, поскольку это URL, вы можете кодировать !как %21, например curl -v http://example.org/%21132.

Аарон Д. Мараско
источник
13

Это также может сделать

curl -v "http://example.org/"'!'"287s87asdjh2/somepath/someresource"
или же
curl -v "http://example.org/"\!"287s87asdjh2/somepath/someresource"

Что работает, потому что bash объединяет смежные строки. Этот подход особенно полезен, когда у вас есть другие вещи, которые требуют расширения оболочки, поэтому вы не можете использовать одинарные кавычки для всей строки:

curl -v 'http://example.org/!'"287s87asdjh2/${basepath}/someresource"

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

mug896
источник
Есть много способов заставить команды Unix и английские предложения использовать больше символов, чем им нужно, и быть более запутанными, чем они должны быть. Чем этот ответ превосходит первый / принятый / получивший наибольшее количество голосов ответ, а именно - поместить весь URL-адрес в одинарные кавычки?
G-Man
2
@ G-Man: это говорит о другом способе построения аргументов bash. Я не знал об этом методе. Ничего плохого в изучении новых вещей.
Сахил Сингх,
@SahilSingh Как это новое? Он объединяет три строки, две в двойных кавычках и одну в одинарных кавычках. Здесь нет вложенности.
Рафаэль
@ G-Man Не очевидно, что когда вы кладете 2 строки рядом друг с другом, они объединяются. printf ("hello" "world") также будет работать в c, но printf ("hello" 'w') не будет работать, так что вы видите, что знание того, что bash поддерживает такие выражения, было для меня новым, но я согласен с С точки зрения полезности, это не лучше. Мне понравился ответ, как и Марк Шуст.
Сахил Сингх
2
@ G-Man Это также полезно, когда есть другие раскрытия строки, которые действительно должны происходить в одной и той же строке. Это простой способ разделения двух типов поведения цитирования.
WAF
7

Я столкнулся с той же проблемой, и мое простое решение было использовать переменную:

E=!  
curl -v "http://example.org/${E}287s87asdjh2/somepath/someresource"

Здесь простота заключается в том, что (1) он переносим между оболочками и командами (2) не требует знания escape-синтаксиса и кодов ASCII.

Prem
источник
3

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

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!
Флимм
источник
это не работает вне echo, эхо, кажется, обрабатывает это по-своему
phil294
@Blauhirn Это не имеет никакого отношения к эхо, и все, что связано с цитированием и версией bash, которую вы используете.
Флим
2
Этот ответ неверен и должен быть удален. Ваша bashверсия не имеет ничего общего с расширением взрыва, это связано с тем, что в вашем примере за !ним следует «конец строки», и это не позволяет оболочке пытаться его расширить. Попробуйте, echo "!Hello World"и вы увидите, что bashответит bash: !Hello: event not found. См. Руководство для более подробной информации
don_crissti
0

Для тех, кто использует git bash в Windows, принятый ответ от @DanielPittman работает. Однако вы должны заменить обратную косую черту (\) на прямую косую черту (/).

Например, в Unix это будет выглядеть примерно так:

curl https://abc.com/services -H 'Authorization: Bearer 111A80BBZZCnS\!ZR412543s'

Для окон это было бы что-то вроде этого (фокус на косой черте в части заголовка авторизации)

curl https://abc.com/services -H 'Authorization: Bearer 111A80BBZZCnS/!ZR412543s'

SamuelDev
источник
Это не имеет особого смысла. У вас есть аргумент с одинарной кавычкой, поэтому независимо от косых черт, восклицание не приведет к расширению истории.
Wildcard
О, ты прав. Я только опубликовал этот ответ, потому что, когда я использовал ответ Дэниела (используя обратную косую черту), всплывала ошибка.
SamuelDev