В bash, почему команды оболочки игнорируют кавычки в аргументах, когда аргументы передаются им как переменная?

Это работает как рекламируемый:

# example 1
#!/bin/bash
grep -ir 'hello world' .

Это не означает:

# example 2
#!/bin/bash
argumentString="-ir 'hello world'"
grep $argumentString .

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

Опять же, это происходит только тогда, когда аргументы расширены из переменной argumentString. grep правильно интерпретирует 'hello world' как один аргумент в первом примере.

Может кто-нибудь объяснить, почему это? Есть ли способ расширить строковую переменную, которая сохранит синтаксис каждого символа, чтобы он был правильно интерпретирован командами оболочки?

Ответы

Ответ 1

Почему

Когда строка расширяется, она разбивается на слова, но ее не переоценивают, чтобы найти специальные символы, такие как кавычки или знаки доллара, или... Это то, как оболочка всегда "вела себя", поскольку Bourne в 1978 году или около того.

Fix

В bash используйте массив для хранения аргументов:

argumentArray=(-ir 'hello world')
grep "${argumentArray[@]}" .

Или, если храбрый/безрассудный, используйте eval:

argumentString="-ir 'hello world'"
eval "grep $argumentString ."

С другой стороны, усмотрение часто является лучшей частью доблести, а работа с eval - это место, где усмотрение лучше, чем храбрость. Если вы не полностью контролируете строку eval 'd (если какой-либо пользовательский ввод в командной строке, которая не была строго проверена), то вы открываете себе потенциально серьезные проблемы.

Обратите внимание, что последовательность расширений для Bash описана в Расширения оболочки в руководстве GNU Bash. Обратите внимание, в частности, разделы 3.5.3 Расширение параметров оболочки, 3.5.7 Разделение слов и 3.5.9 Удаление цитаты.

Ответ 2

Когда вы добавляете символы котировки в переменные, они просто становятся простыми литералами (см. http://mywiki.wooledge.org/BashFAQ/050; спасибо @tripleee за указание этой ссылки)

Вместо этого попробуйте использовать массив для передачи ваших аргументов:

argumentString=(-ir 'hello world')
grep "${argumentString[@]}" .

Ответ 3

В двойных кавычках одинарные кавычки теряют свое особое значение, поэтому они просто обычные персонажи. Для этого используйте что-то вроде eval:

eval grep $argumentString .

который должен правильно собрать командную строку из конкатенированных строк.

Ответ 4

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

argumentString="-ir 'hello world'"
bash -c "grep $argumentString ."

Выполняется точно так же, как и исходный вопросник. Для этой техники существуют два ограничения:

  • Вы можете использовать только одинарные кавычки в строке команды или аргумента.
  • Доступны только экспортированные переменные среды для команды

Кроме того, этот метод обрабатывает перенаправление и трубопроводы, а также другие раллизмы. Вы также можете использовать внутренние команды bash, а также любую другую команду, которая работает в командной строке, потому что вы по существу просите подоболочку bash интерпретировать ее напрямую как командную строку. Здесь более сложный пример, несколько безвозмездно сложный вариант ls-l.

cmd="prefix=`pwd` && ls | xargs -n 1 echo \'In $prefix:\'"
bash -c "$cmd"

Я построил командные процессоры так и с массивами параметров. Как правило, этот способ намного проще писать и отлаживать, и это тривиально, чтобы эхо выполнить команду, которую вы выполняете. OTOH, param-массивы работают хорошо, когда у вас действительно есть абстрактные массивы параметров, а не просто для простого варианта команды.