我正在编写一个脚本来使用一些不同的参数调用命令(weka,如果你好奇的话)几次。参数之一,'-search "< stuff >" '
需要加引号。在某些情况下,我需要使用多个这些参数,例如。
weka a -search "a params" -search "other a params"
weka b -search "just these params"`
我一直在尝试使用看起来像这样的关联数组:
search=( ["a"]='-search "a params" -search "other a params"'
["b"]='-search "just these params"'
然后拨打电话:
function call_thing_with_the_right_parameters_so_it_works_out_alright {
weka $1 ${search["$1"]}
} # <-- closing brace for the function
call_thing_with_the_right_parameters_so_it_works_out_alright
唉,无论我怎么尝试,引用都被搞乱了:
bash -x ./this_is_the_name_of_my_script_sorry_its_so_long
...
+ weka a -search '"a' 'params"' # <-- (not what I want)
...
有任何想法吗?
答案1
代码中的行将weka $1 ${search["$1"]}
受到 shell 分割的影响。
如果您没有更改变量,$IFS
则将在 上发生拆分spacetabnew line。
该行扩展为:
weka $1 ${search["$1"]}
weka a -search "a params" -search "other a params"
但是如上所述进行拆分,这就是它的含义:
<weka> <a> <-search> <"a> <params"> <-search> <"other> <a> <params">
您可以看到在该行前面添加了完全相同的 printf:
$ printf '<%s> ' weka $1 ${search["$1"]}; echo
<weka> <a> <-search> <"a> <params"> <-search> <"other> <a> <params">
如果正确引用变量,情况会更好:
$ printf '<%s> ' weka "$1" "${search["$1"]}"; echo
<weka> <a> <-search "a params" -search "other a params">
但这不是你想要的。你需要分割它,但不是简单的空间。
该怎么办?
有两种解决方案:
手动拆分
使用一些字符来#
手动标记分割位置:
search=( ["a"]='-search#a params#-search#other a params' ["b"]='-search#just these params' )
然后告诉 bash 你用来分割的字符是哪个IFS
,这样它就可以将字符串分割到一个新数组中b
:
IFS='#'
b=( ${search["a"]} )
printf '<%s> ' "${b[@]}"; echo
其产生:
<-search> <a params> <-search> <other a params>
您想要的精确分割。
唯一的问题是它IFS
被改变了,但我们可以在本地函数中解决这个问题IFS
:
callsplit(){
local IFS='#'
b=( ${search["$1"]} )
weka "$1" "${b[@]}"
}
使用评估
另一个解决方案是使用 eval 重新解析命令行,以便 shell 可以将其拆分,就像 shell 拆分行的常见方式一样。
变量搜索的值将如您所定义的那样:
search=( ["a"]='-search "a params" -search "other a params"'
["b"]='-search "just these params"' )
但我们将用 eval 扩展执行行:
eval weka "$1" "${search["$1"]}"
如果您想查看该行如何展开,请使用以下命令:
$ eval printf "'<%s> '" weka "$1" "${search["$1"]}"; echo
<weka> <a> <-search> <a params> <-search> <other a params>
整个脚本将是:
#!/bin/bash
declare -A search
search+=( ["a"]='-search "a params" -search "other a params"')
search+=( ["b"]='-search "just these params"' )
call_thing() {
eval weka "$1" "${search["$1"]}"
} # <-- closing brace for the function
call_thing "a"
注意:假设设置了搜索值,这将正常工作里面脚本(外部攻击者无法设置它们)并且这些值被引用为通用 shell“命令行”。
警告:使用 eval 可以允许将字符串形式的数据转换为像命令一样的代码。在这个特定的脚本中,这一行:
call_thing "a; touch new_file"
就会执行命令touch new_file
。但也可以执行任何其他命令。小心,非常小心你喂的东西eval
。
如上所述,请记住,有许多危险命令可以在 shell 中执行,例如rm -r /
.该命令eval
并不比任何一个命令更强大。请小心。
答案2
shell 不会按照您想要的方式处理引号。 (一旦引号被引用,它们将被视为常规字符。)
你可以欺骗 bash 做你想做的事。要开始定义关联数组:
declare -A s
s=( ["a"]='-search;a params;-search;other a params;' ["b"]='-search;just these params' )
正如您所看到的,该字符串包含每个命令的参数,并用分号分隔。例如,要提取用于 的参数a
,我们告诉 shell 将其用作;
字段分隔符:
IFS=\; v=(${s[a]})
您可以验证数组v
现在是否具有所需的字段,如下所示:
$ declare -p v
declare -a v='([0]="-search" [1]="a params" [2]="-search" [3]="other a params")'
然后您可以运行weka
为:
weka a "${v[@]}"
因此,将它们放在一起,您的函数定义可能如下所示:
declare -A s
s=( ["a"]='-search;a params;-search;other a params;' ["b"]='-search;just these params' )
callthing() (
IFS=\; v=(${s[$1]})
weka "$1" "${v[@]}"
)