该怎么办?

该怎么办?

我正在编写一个脚本来使用一些不同的参数调用命令(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[@]}"
)

相关内容