How to substitute quoted, multi-word strings as arguments?

只谈情不闲聊 提交于 2019-11-27 09:14:37

Don't use quotes, use an array (see 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 * ?

If it really needs to be in the form of quoted strings within a string, you're either going to have to use something like eval "echo $myArg" (which can cause some really nasty bugs, if you aren't careful) or parse it yourself (which is going to be difficult).

If you want to pass a variable value as a parameter (99% of cases on SO), simply use proper quoting:

arg="foo bar"
command "$arg"

If you want to pass several arguments, use arrays:

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

I don't think it is doing what you think it is doing.

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

I see no extra quotes of any kind- echo gets three argument strings.

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

Bash will expand the variable first, so

cargs "string is:" $myArg

becomes (though without the literal backslashes- this is why string escaping is a PITA)

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

And the args array is:

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

Now, if you add the *, or glob path expansion in one of those, Bash will at this point expand it, unless you escape it, or use single quotes in your literal command.

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

There is a portable way to split expand a variable but keep spaces. Bash arrays are not needed. Dash (Ubuntu's /bin/sh) would work too.

Use some character to separate arguments that is definitely not used inside the arguments. The below example uses semicolon, but it could be a newline or another character. Change the IFS variable to a newline temporarily when the list of arguments is expanded. Restore IFS to the original value as soon as possible, even if it means doing it in the loop. If the loop is not guaranteed to run at least once, do it after the loop as well.

#! /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"

Note that every expanded argument is printed individually.

$ ./test.sh
hello world
have a nice day
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!