我有这个(测试)脚本:
#!/bin/bash
my_cmd_bad_ ( ) {
cmd="$@"
$cmd
}
my_cmd_good_ ( ) {
"$@"
}
my_cmd_bad_ ls -l "file with space"
my_cmd_good_ ls -l "file with space"
输出是(文件不存在,不这个问题的要点):
» ~/test.sh
ls: cannot access file: No such file or directory
ls: cannot access with: No such file or directory
ls: cannot access space: No such file or directory
ls: cannot access file with space: No such file or directory
我很惊讶第一个版本没有按预期工作:参数没有引用,而不是处理一个文件,它处理三个。为什么呢?
如何保存我想要执行的命令,正确引用?我需要稍后执行它,我不再有"$@"
。
对此测试脚本的简单修改将不胜感激。
答案 0 :(得分:2)
查看类似问题:How to pass command line parameters with quotes stored in single variable?
使用这些实用程序函数将命令保存到字符串以便以后执行:
bash_escape() {
# backtick indirection strictly necessary here: we use it to strip the
# trailing newline from sed's output, which Solaris/BSD sed *always* output
# (unlike GNU sed, which outputs "test": printf %s test | sed -e s/dummy//)
out=`echo "$1" | sed -e s/\\'/\\''\\\\'\\'\\'/g`
printf \'%s\' "$out"
}
append_bash_escape() {
printf "%s " "$1"
bash_escape "$2"
}
your_cmd_fixed_ ( ) {
cmd="$@"
while [ $# -gt 0 ] ; do
cmd=`append_bash_escape "$cmd" "$1"` ; shift
done
$cmd
}
答案 1 :(得分:1)
您将三个以空格分隔的字符串“ls”,“ - l”和“带空格的文件”组合成一个以空格分隔的字符串cmd
。无法知道最初引用了哪些空格(在“带空格的文件中”)以及在分配给cmd
期间引入了哪些空格。
通常,尝试将命令行构建为单个字符串并不是一个好主意。使用函数或隔离实际命令并将参数保留在$@
。
重写命令如下:
my_cmd_bad_ () {
cmd=$1; shift
$cmd "$@"
}
答案 2 :(得分:1)
您可以引用任何单个参数并在以后对其进行评估:
my_cmd_bad_ ( ) {
j=0
for i in "$@"; do
cmd["$j"]=\"$"$i"\"
j=$(( $j + 1 ))
done;
eval ${cmd[*]}
}
答案 3 :(得分:0)
请参阅http://mywiki.wooledge.org/BashFAQ/050
请注意,您的第二个版本在大多数情况下都是首选。唯一的例外是如果你需要做一些特殊的事情。例如,您不能将赋值或重定向或复合命令捆绑到参数列表中。
处理引用问题的正确方法需要非标准功能。涉及模板的半现实示例:
function myWrapper {
typeset x IFS=$' \t\n'
{ eval "$(</dev/fd/0)"; } <<-EOF
for x in $(printf '%q ' "$@"); do
echo "\$x"
done
EOF
}
myWrapper 'foo bar' $'baz\nbork'
确保您完全了解这里发生了什么,并且您确实有充分的理由这样做。它要求确保副作用不会影响参数。这个具体的例子并没有展示一个非常好的用例,因为所有内容都是硬编码的,因此你可以提前正确地转义,并扩展所引用的参数。