Какие символы необходимо экранировать в аргументах командной строки?

14

В Bash, при указании аргументов командной строки для команды, какие символы необходимо экранировать?

Они ограничиваются метасимволами Баша: пробел, табуляция |, &, ;, (, ), <, и >?

Тим
источник
Не забудьте (возможно), что имя файла отображается с * и?
Джефф Шаллер
Благодарю. Не могли бы вы исчерпывающе перечислить виды символов, которые должны быть экранированы в аргументах строки cmd?
Тим
Список полезен, но самое важное, что нужно понимать при цитировании: все, что между одинарными кавычками, передается буквально и без разделения слов. Без исключений. (Между прочим, это означает, что нет никакого способа вставить одинарную кавычку в одинарные кавычки, но это легко обойти .)
Wildcard

Ответы:

22

Следующие символы имеют особое значение для самой оболочки в некоторых контекстах и, возможно, должны быть экранированы в аргументах:

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


Есть несколько угловых случаев, которые явно необязательны:

  • !может быть отключен с помощью set +H, который является значением по умолчанию в неинтерактивных оболочках.
  • {можно отключить с помощью set +B.
  • *и ?может быть отключен с помощью set -fилиset -o noglob .
  • =Знак равенства (U + 003D) также необходимо экранировать, если set -kилиset -o keyword включено.

Выход из новой строки требует цитирования - обратная косая черта не сработает. Любые другие символы, перечисленные в IFS , требуют аналогичной обработки. Вам не нужно бежать ]или }, но вам действительно нужно бежать , )потому что это оператор.

Некоторые из этих персонажей имеют более жесткие ограничения на то, когда им действительно нужно убежать, чем другим. Например, a#bэто нормально, но a #bэто комментарий, в то время как >в обоих контекстах нужно будет экранировать. В любом случае, избежать их всех консервативно, и это легче, чем помнить тонкие различия.

Если имя команды сама оболочка ключевое слово ( if, for, do) , то вам нужно бежать или процитировать его тоже. Единственный интересный из них in, потому что не очевидно, что это всегда ключевое слово. Вам не нужно делать это для ключевых слов, используемых в аргументах, только когда вы (по глупости!) Назвали команду в честь одного из них. Операторы оболочки ( (и &т. Д.) Всегда должны заключать в кавычки, где бы они ни находились.


1 Стефан отметил, что любой другой однобайтовый пустой символ из вашей локали также нуждается в экранировании. В большинстве распространенных, разумных локалей, по крайней мере, основанных на C или UTF-8, это только пробельные символы выше. В некоторых локалях ISO-8859-1 пространство без перерывов U + 00A0 считается пустым, включая Solaris, BSD и OS X (я думаю, что это неправильно). Если вы имеете дело с произвольной неизвестной локалью, это может включать что угодно, включая буквы, так что удачи.

Возможно, один многобайтовый символ, который считается пустым, может появиться в многобайтовом символе, который не является пустым, и у вас не будет никакого способа избежать этого, кроме как поместить все это в кавычки. Это не теоретическая проблема: в приведенном выше стандарте ISO-8859-1 этот A0байт, который считается пустым, может появляться в многобайтовых символах, таких как UTF-8, закодированный как «à» ( C3 A0). Чтобы безопасно обрабатывать эти символы, вам нужно их процитировать "à". Это поведение зависит от конфигурации локали в среде, в которой выполняется скрипт, а не от той, в которой вы его написали.

Я думаю, что это поведение нарушается несколькими способами, но мы должны разыграть ту руку, с которой имеем дело. Если вы работаете с любым несамосинхронизирующимся многобайтовым набором символов, самым безопасным будет процитировать все. Если вы в UTF-8 или C, вы в безопасности (на данный момент).

Майкл Гомер
источник
Другие пробелы в вашей локали также должны были бы избежать ( за исключением в настоящее время многобайтовой из-за ошибки )
Стефан Шазелас
Вы должны выходить из !режима ожидания только при включенном расширении истории csh, как правило, не в сценариях. [ ! -f a ]или find . ! -name...в порядке. Это описано в разделе «Более жесткие ограничения», но, возможно, стоит упомянуть об этом явно.
Стефан Шазелас
Обратите внимание , что существуют контексты , где другие символы должны цитируя как: hash[foo"]"]=, ${var-foo"}"}, [[ "!" = b ]], [[ a = "]]" ]], регулярное выражение операторы [[ x =~ ".+[" ]]. Другие ключевые слова , чем {( if, while, for...) должны быть указаны таким образом , они не признаются в качестве таковых ...
Stephane Chazelas
Если это вообще аргументы командной строки, то интерпретация зависит от рассматриваемой команды (как и я ]), поэтому я не буду перечислять их. Я не думаю, что любое ключевое слово нуждается в цитировании в позиции аргумента.
Майкл Гомер
2
Цитирование встроенных, тире или% ничего не делает.
Майкл Гомер
3

В GNU Parallel это тестируется и широко используется:

$a =~ s/[\002-\011\013-\032\\\#\?\`\(\)\{\}\[\]\^\*\<\=\>\~\|\; \"\!\$\&\'\202-\377]/\\$&/go;
# quote newline as '\n'                                                                                                         
$a =~ s/[\n]/'\n'/go;

Он испытан в bash, dash, ash, ksh, zsh, и fish. Некоторые персонажи не нуждаются в цитировании в некоторых (версиях) оболочек, но вышеописанное работает во всех протестированных оболочках.

Если вам просто нужна строка в кавычках, вы можете передать ее в parallel --shellquote:

printf "&*\t*!" | parallel --shellquote
Оле Танге
источник
Как я не слышал о параллели раньше ...
Том Х
@ TomH будет признателен, если вы сможете потратить 5 минут на размышления о том, как мы могли бы с вами связаться.
Оле Танге
Я думаю, что это проблема прогресса. большинство людей не нуждаются или не понимают параллели, пока не пройдут некоторые стадии сложности. К этому времени они встретили xargs, nohup и все в таком духе. Кроме того, я не вижу много людей, использующих параллель для решения проблем в обмене стека или когда я ищу в Google решения для решения проблем
Tom H
1

Для облегченного экранирования в Perl я следую принципу одинарных кавычек. Строка Bash в одинарных кавычках может содержать любой символ, кроме самой одинарной кавычки.

Мой код:

my $bash_reserved_characters_re = qr([ !"#$&'()*;<>?\[\\`{|~\t\n]);

while(<>) {
    if (/$bash_reserved_characters_re/) {
        my $quoted = s/'/'"'"'/gr;
        print "'$quoted'";
    } else {
        print $_;
    }
}

Пример прогона 1:

$ echo -n "abc" | perl escape_bash_special_chars.pl
abc

Пример прогона 2:

echo "abc" | perl escape_bash_special_chars.pl
'abc
'

Пример прогона 3:

echo -n 'ab^c' | perl escape_bash_special_chars.pl
ab^c

Пример прогона 4:

echo -n 'ab~c' | perl escape_bash_special_chars.pl
'ab~c'

Пример прогона 5:

echo -n "ab'c" | perl escape_bash_special_chars.pl
'ab'"'"'c'

echo 'ab'"'"'c'
ab'c
Яри ​​Туркиа
источник
Да, верный момент, который. Я считаю, что большинство людей попадут на эту страницу, потому что у них есть проблема, которую нужно решить. Не потому, что это делает интересные академические дебаты. Вот почему я хотел бы предложить решения и обсудить их достоинства, хотя и немного не по теме.
Яри ​​Туркиа
Мой код - просто реализация ответа Майкла Гомера. Я не собирался приносить больше информации, чем то, что он сделал.
Яри ​​Туркиа