将shell转义的参数字符串传递给Bourne shell中的子命令

时间:2012-04-02 19:28:46

标签: shell escaping arguments quotes sh

假设我有一个我想要运行的命令(cmd)和一个包含我想要传递给函数的参数的变量(类似--foo 'bar baz' qux)。像这样:

#!/bin/sh
command=cmd
args="--foo 'bar baz' qux"

参数包含引号,如上所示,它将包含空格的参数组合在一起。然后我想运行命令:

$command $args

当然,这会导致使用四个参数运行命令:--foo'barbaz'qux。我习惯的替代方案(即使用"$@"时)提出了另一个问题:

$command "$args"

这将使用一个参数执行命令:--foo 'bar baz' qux

如何按预期使用三个参数(--foobar bazqux)运行命令?

5 个答案:

答案 0 :(得分:8)

使用数组来准确指定你的参数列表,而没有字符串拆分(这就是在这里做错了什么)妨碍你:

args=( --foo "bar baz" qux )
command "${args[@]}"

如果需要动态构建参数列表,可以使用+=附加到数组:

args=( )
while ...; do
   args+=( "$another_argument" )
done
call_your_subprocess "${args[@]}"

请注意,使用引号和[@]代替[*]至关重要。

答案 1 :(得分:5)

如果您可以丢弃当前的位置变量($1 ...),您可以使用以下内容:

set -- '--foo' 'bar baz' 'qux'
echo "$#" # Prints "3" (without quotes)
echo "$2" # Prints "bar baz" (without quotes)
command "$@"

刚刚在#!/usr/bin/env sh脚本中进行了测试,因此它至少在Dash中有效,并且应该适用于任何Bourne Shell变体。不需要eval,Python或Bash。

答案 2 :(得分:4)

一种可能性是使用eval

#!/bin/sh

args="--foo 'bar baz' qux"
cmd="python -c 'import sys; print sys.argv'"

eval $cmd $args

这样就可以解释命令行,而不是仅根据IFS进行拆分。这给出了输出:

$ ./args.sh 
['-c', '--foo', 'bar baz', 'qux']

这样你就可以看到args按你想要的那样传递。

答案 3 :(得分:1)

如果你有以下表格中的命令:

args="--foo 'bar baz' qux"

并且首先将命令作为数组获取不是一个选项,那么您需要使用eval将其转换回数组:

$ args="--foo 'bar baz' qux"
$ eval "arr=($args)"

但重要的是要注意,如果$args由不受信任的来源提供,则这是不安全的,因为它可用于执行任意命令,例如args='$(rm -rf /)'; eval "arr=($args)"会导致上述代码在您使用rm -rf /之前运行arr

然后您可以使用"${arr[@]}"将其展开为命令的参数:

$ bash -c 'echo $0' "${arr[@]}"
--foo
$ bash -c 'echo $1' "${arr[@]}"
bar baz

或运行您的命令:

"$command" "${arr[@]}"

请注意,${arr[*]}${arr[@]}"${arr[*]}""${arr[@]}"之间存在差异,在大多数情况下,只有最后一个符合您的要求

答案 4 :(得分:0)

通过更改shell的IFS(内部字段分隔符)(仅包含换行符),它适用于我。以下是我如何覆盖使用带引号的参数的命令:

$ cat ~/.local/bin/make
#!/bin/sh

# THIS IS IMPORTANT! (don't split up quoted strings in arguments)
IFS="
"
exec /usr/bin/make ${@} -j6

/bin/shdash
当用echo替换命令时它会吃掉引号,所以当"测试&#34 ;;但命令确实按预期获得了它们。

可以通过将exec行替换为

进行测试
for arg in $@; do echo $arg; done