Краткий ответ: см. BashFAQ # 50: я пытаюсь поместить команду в переменную, но сложные случаи всегда терпят неудачу! ,
Длинный ответ: Когда оболочка анализирует командную строку, она делает такие вещи, как выяснение того, какие части строки находятся в кавычках (или экранированные или какие-либо еще), прежде чем заменить переменные; таким образом, если у вас есть какие-либо кавычки или экранирование внутри значений переменных, к тому времени, когда они подставляются в команду, им уже поздно что-либо делать. Он немного разбирает значения подставляемых переменных: он разбивает их на "слова" на основе пробелов, табуляции и т.д. (Независимо от того, находятся ли они в кавычках) и расширяет подстановочные знаки (опять же, даже если они в кавычках). Кстати, уже слишком поздно, чтобы другие части синтаксиса оболочки, такие как каналы, перенаправления и т.д., Вступили в силу. Чтобы проиллюстрировать это, у меня есть команда printargs
которая печатает свои аргументы. Вот что происходит, когда я пытаюсь сохранить сложную команду в переменной:
$ cmd='printargs " * " | cat >outfile &'
$ $cmd
Got 8 arguments:
'"'
'file1.txt'
'file2.txt'
'"'
'|'
'cat'
'>outfile'
'&'
Обратите внимание, что кавычки, канал и т.д. Все обрабатываются как обычные символы, а не синтаксис оболочки, но звездочка рассматривается как подстановочный знак и заменяется списком имен файлов.
Есть несколько решений, в зависимости от того, почему вы помещаете команду в переменную в первую очередь:
Если вам не нужно помещать команду в переменную, не делайте этого. Команды предназначены для выполнения, поэтому, если нет веской причины не делать этого, просто выполняйте их напрямую.
Если вы хотите использовать по существу одну и ту же команду несколько раз и не хотите каждый раз выписывать все целиком (правило программирования "не повторяйте себя"), используйте функцию:
execute_command() {
foo "$1" --option1 --option2 "$2"
}
... а затем позвонить несколько раз. Обратите внимание, что я помещаю ссылки на переменные в двойные кавычки; Вы должны (почти) всегда делать это, чтобы к ним не применялось разбиение слов и расширение по шаблону.
Если вам нужно построить команду динамически, используйте массив:
post_data=("var1=value1" "var2=value2" ...)
post_args=()
for post_arg in "${post_data{@]}"; do # Note that this is the correct idiom for expanding an array in bash
post_data+=(-d "$post_arg")
done
curl "${post_args[@]}" "$url"
Обратите внимание, что это работает для сложных аргументов для одной команды, но не будет работать для таких вещей, как конвейеры, перенаправления и фоновые операции (&
), потому что они снова анализируются перед заменой переменных.
Напоследок несколько предупреждений:
Не используйте eval
или bash -c
если вы точно не знаете , как работает синтаксический анализ оболочки (и если вы задаете этот вопрос, вы точно не знаете, как работает синтаксический анализ оболочки). Оба эти фактора вызывают дополнительный уровень синтаксического анализа, который, как правило, отлично работает при тестировании, но иногда дает сбой (по непонятным причинам). eval
имеет заслуженную репутацию источника действительно странных и тонких ошибок; и bash -c
делает то же самое, просто добавив подоболочку, чтобы сделать ее еще более странной.
Двойные кавычки для ссылок на переменные для предотвращения неожиданного разделения слов и расширения подстановочных знаков.
Вы, вероятно, не хотите использовать exec
- он выходит из текущей оболочки (и сценария оболочки) и заменяет ее командой; это, вероятно, не то, что вы хотели.