bash 如何将带引号的多字字符串替换为参数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9484364/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 01:40:42  来源:igfitidea点击:

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

bashshellcommand-line-argumentsquotes

提问by Tim Morgan

I'm trying to substitute a string variable, containing multiple quoted words, as a parameter to a command.

我正在尝试替换一个包含多个引用词的字符串变量作为命令的参数。

Thus, given the following example script (Note the -x in the shebang, which causes the output to be logged to stderr),

因此,给定以下示例脚本(注意 shebang 中的 -x,它会导致将输出记录到 stderr),

#!/bin/bash -x

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

exit

Which gives us,

这给了我们,

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


Line two shows what is actually passed to the command; bash has added single quotes to each word in the string. If I instead, thusly, quote "$myArg", the same occurs but for the whole string rather than each word.


第二行显示了实际传递给命令的内容;bash 为字符串中的每个单词添加了单引号。如果我因此引用“$myArg”,则会发生同样的情况,但对于整个字符串而不是每个单词。

Now, imagine that instead of echo, we are passing the string to a program where some of the arguments need to be quoted patterns, such as "*"(which mustn't be expanded by the shell).

现在,想象一下,而不是echo,我们将字符串传递给一个程序,其中一些参数需要用引号引起来,例如"*"(不能被 shell 扩展)。

To clarify, I don't want the single quotes added at all during the expansion. How might I achieve this?

澄清一下,我不希望在扩展过程中添加单引号。我怎样才能做到这一点?

回答by Gordon Davisson

Don't use quotes, use an array (see BashFAQ #050):

不要使用引号,使用数组(参见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).

如果它真的需要在字符串中以带引号的字符串的形式出现,你要么必须使用类似的东西eval "echo $myArg"(如果你不小心,这可能会导致一些非常讨厌的错误)或自己解析它(这是会很困难)。

回答by l0b0

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

如果要将变量值作为参数传递(SO 上 99% 的情况),只需使用正确的引用

arg="foo bar"
command "$arg"

If you want to pass several arguments, use arrays:

如果要传递多个参数,请使用数组:

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

回答by David Souther

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- echogets three argument strings.

我看不到任何类型的额外引号 -echo获取三个参数字符串。

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

Bash will expand the variable first, so

Bash 会先扩展变量,所以

cargs "string is:" $myArg

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

变成(虽然没有文字反斜杠 - 这就是为什么字符串转义是一个 PITA)

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

And the args array is:

args 数组是:

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.

现在,如果您*在其中之一中添加, 或 glob 路径扩展,此时 Bash 将扩展它,除非您对其进行转义,或者在您的文字命令中使用单引号。

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

回答by proski

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.

有一种可移植的方法来拆分扩展变量但保留空格。不需要 Bash 数组。Dash(Ubuntu 的 /bin/sh)也可以。

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 IFSvariable to a newline temporarily when the list of arguments is expanded. Restore IFSto 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.

使用一些字符来分隔在参数中绝对没有使用的参数。下面的示例使用分号,但它可以是换行符或其他字符。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"

Note that every expanded argument is printed individually.

请注意,每个扩展参数都是单独打印的。

$ ./test.sh
hello world
have a nice day