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

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

Таким образом, для следующего примера сценария (обратите внимание на -x в шебанге, что приводит к регистрации вывода в 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


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

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

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


person Tim Morgan    schedule 28.02.2012    source источник
comment
Что ты делаешь? Если вы не делаете что-то неприглядное с eval, не имеет большого значения, содержит ли переданная программе строка "*" или *.   -  person sorpigal    schedule 28.02.2012
comment
Для тех, кто в замешательстве: обратите внимание на -x в shebang, что приводит к регистрации вывода.   -  person user123444555621    schedule 28.02.2012
comment
@ Pumbaa80, действительно, я добавил это уточнение к вопросу.   -  person Tim Morgan    schedule 28.02.2012
comment
Полезно вместе с: stackoverflow.com/questions/17338863/   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 07.09.2015


Ответы (4)


Не используйте кавычки, используйте массив (см. 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" (что может вызвать некоторые действительно неприятные ошибки, если вы не будете осторожны), либо проанализировать его самостоятельно (что будет сложно).

person Gordon Davisson    schedule 28.02.2012
comment
Этот подход с массивами работает отлично, большое спасибо! пс. Любая операция печати из подоболочки интерпретируется родительской оболочкой, поэтому любые такие операторы «eval», казалось, давали такие же плохие результаты. - person Tim Morgan; 28.02.2012

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

arg="foo bar"
command "$arg"

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

args=("foo bar" "baz ban" bay)
command "${args[@]}"
person l0b0    schedule 28.02.2012
comment
Как пояснил @gordon-davisson, массивы действительно были ответом, большое спасибо! - person Tim Morgan; 28.02.2012

Существует портативный способ разделить переменную, но сохранить пробелы. Массивы Bash не нужны. Dash (/bin/sh Ubuntu) тоже подойдет.

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

#! /bin/sh
arg_list='hello world;have a nice day'
save_IFS="$IFS"
IFS=';'
for i in $arg_list; do
  IFS="$save_IFS"
  echo "$i"
done
IFS="$save_IFS"

Обратите внимание, что каждый развернутый аргумент печатается отдельно.

$ ./test.sh
hello world
have a nice day
person proski    schedule 08.06.2018

Я не думаю, что он делает то, что вы думаете.

[~]$ 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\""

И массив аргументов:

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

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

[~]$ cargs "string is:" $myArg *
19
[~]$ cargs "string is:" $myArg "\*"
4
[~]$ cargs "string is:" $myArg '*'
4
person David Souther    schedule 28.02.2012
comment
вывод Echo действительно печатает слова в кавычках идеально, но входные аргументы, переданные в echo (как показано во второй строке вывода отладки в вопросе), являются одинарными кавычками-двойными кавычками ('str'), что, к сожалению, является бессмысленным вводом для инструмента, который я использую. - person Tim Morgan; 28.02.2012