假设我有一个我想要运行的命令(cmd
)和一个包含我想要传递给函数的参数的变量(类似--foo 'bar baz' qux
)。像这样:
#!/bin/sh
command=cmd
args="--foo 'bar baz' qux"
参数包含引号,如上所示,它将包含空格的参数组合在一起。然后我想运行命令:
$command $args
当然,这会导致使用四个参数运行命令:--foo
,'bar
,baz'
和qux
。我习惯的替代方案(即使用"$@"
时)提出了另一个问题:
$command "$args"
这将使用一个参数执行命令:--foo 'bar baz' qux
。
如何按预期使用三个参数(--foo
,bar baz
和qux
)运行命令?
答案 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/sh
是dash
)
当用echo
替换命令时它会吃掉引号,所以当"测试&#34 ;;但命令确实按预期获得了它们。
可以通过将exec
行替换为
for arg in $@; do echo $arg; done