我有一个用bash编写的项目。接口的一部分要求用户使用间接层调用bash函数,如下所示:
require some_fun "arg 1" "arg 2"
它执行some_fun "arg 1" "arg 2"
(然后做一些额外的事情,因此它存在)。目前我只是调用"$@"
来做这件事,一切都正常工作。
我现在需要将这些函数调用推迟到以后,并且在Bash中找不到这样做的方法。实际上,我想在每次调用require
时将"$@"
推送到堆栈上,然后在该堆栈上循环并执行参数,就像我当前的行为一样。这需要多维数组,bash不支持,所以我尝试这样做:
require() {
dep_num=${#SK_deps[@]}
dep_var="SK_dep_$dep_num"
eval $dep_var="$@"
SK_deps+=($dep_var)
}
它基本上生成一个变量变量来存储"$@",并将变量名放在$SK_deps
中。
然后我尝试用迭代列表
for dep_var in ${SK_deps[@]}
do
${!dep_name}
done
但这似乎打破了引用的论点,所以:
require some_fun "arg 1" "arg 2"
解释为:
require some_fun arg 1 arg 2
没有引号。
有什么办法做我想做的事吗?使用"$*"还会导致报价定位的损失。感觉应该有一种方法可以在Bash中做到这一点,而不需要使用sed/awk。
eval
解决方案还不错。关键是利用printf
的shell引用机制,它将为您处理所有棘手的情况:
require() {
local quoted
printf -v quoted ' %q' "$@"
SK_deps+=("$quoted")
}
然后你就可以正常运行了:
for dep in "${SK_deps[@]}"; do eval $dep; done
展开数组时引用数组:
for dep_var in "${SK_deps[@]}"
do
${!dep_name}
done
同样,下面这句话似乎很可疑:
eval $dep_var="$@"
试着用set -xv
运行脚本,看看变量是如何真正扩展的。
我发现了一个使用eval的非常丑陋的解决方案,因此任何更好的建议都将不胜感激。
require() {
local tmp_var=""
for arg in "$@"
do
tmp_var="$tmp_var "$arg""
done
SK_deps+=("$tmp_var")
}
然后:
for dep in "${SK_deps[@]}"
do
eval $dep
done
很明显,像这样使用eval充满了边缘情况,比如参数中已经有引号,或者参数中已经存在反斜杠,等等。