Как заменить цитируемые, многословные строки в качестве аргументов?

Я пытаюсь подставить строковую переменную, содержащую несколько цитированных слов, в качестве параметра для команды.

Таким образом, в следующем примере script (Обратите внимание на -x в shebang, что заставляет вывод записываться в stderr),

#!/bin/bash -x

myArg="\"hello\" \"world\""
echo "string is:" $myArg

exit

Что дает нам,

+ myArg='"hello" "world"'
+ echo 'string is:' '"hello"' '"world"'
string is: "hello" "world"
+ exit


Строка 2 показывает, что фактически передается команде; bash добавляет одинарные кавычки к каждому слову в строке. Если я вместо этого цитирую "$ myArg", то это происходит, но для всей строки, а не для каждого слова.

Теперь представьте, что вместо echo мы передаем строку в программу, где некоторые из аргументов должны быть кавычками, например "*" (которые не должны быть расширены оболочкой).

Чтобы уточнить, я не хочу, чтобы одиночные кавычки были добавлены вообще во время расширения. Как я могу это достичь?

Ответ 1

Не используйте кавычки, используйте массив (см. BashFAQ # 050):

$ myArgs=("hello" "world" "multiword arg with * ?")
+ myArgs=("hello" "world" "multiword arg with * ?")
$ echo "${myArgs[@]}"
+ echo hello world 'multiword arg with * ?'
hello world multiword arg with * ?

Если это действительно должно быть в виде строк с кавычками внутри строки, вам придется либо использовать что-то вроде eval "echo $myArg" (что может вызвать некоторые действительно неприятные ошибки, если вы не будете осторожны) или проанализируйте его самостоятельно (что будет сложно).

Ответ 2

Если вы хотите передать значение переменной в качестве параметра (99% случаев на SO), просто используйте quoting:

arg="foo bar"
command "$arg"

Если вы хотите передать несколько аргументов, используйте массивы:

args=("foo bar" "baz ban" bay)
command "${args[@]}"

Ответ 3

Я не думаю, что он делает то, что, по вашему мнению, делает.

[~]$ myArg="\"hello\" \"world\""
[~]$ echo "string is:" $myArg
string is: "hello" "world"

Я не вижу лишних кавычек любого типа - echo получает три строки аргументов.

[~]$ cargs(){ echo $#; }
[~]$ cargs "string is:" $myArg
3

Bash будет сначала расширять переменную, поэтому

cargs "string is:" $myArg

становится (хотя и без литеральных обратных косых черт - вот почему экранирование строки - это PITA)

cargs "string is:" "\"hello\"" "\"world\""

И массив args:

0x00:string is:0
0x0B:"hello"0
0x13:"world"0
0x1B:0

Теперь, если вы добавите расширение * или glob path в одном из них, Bash в этот момент расширит его, если вы не убежите от него, или используйте одинарные кавычки в своей литеральной команде.

[~]$ cargs "string is:" $myArg *
19
[~]$ cargs "string is:" $myArg "\*"
4
[~]$ cargs "string is:" $myArg '*'
4