Почему shell игнорирует кавычки в аргументах, переданных ему через переменные?

это работает как рекламируется:

# 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' как один аргумент в первом примере.

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

4 ответов


почему

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

исправить

на 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 удаление цитат.


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

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

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

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

eval grep $argumentString .

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


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

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

работает точно так же, как оригинальный задающий вопрос. У этой техники есть два ограничения:--3-->

  1. вы можете использовать только одинарные кавычки в строках команд или аргументов.
  2. только экспортированные переменные среды будет доступна команда

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

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

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