将关联数组作为参数列表传递给脚本


9

在脚本中,我有一个类似的关联数组:

declare -A VARS=( ["key1"]="value1" ["key2"]="value" )

是否有单个命令将其转换为以下形式的参数列表

--key1=value1 --key2=value2

无需手动重写

 --key1="${VARS[key1]}" --key2="${VARS[key2]}"

我想到的用例是将数组作为参数列表传递给脚本,例如

my_script.sh $(to_param_list $VARS)

为了扩展我对@Kusalananda答案的评论,我的确切用例如下:我有一个脚本,该脚本用于使用makeself来构建自解压安装程序,该脚本接收一些参数,这些参数将在以下参数之间进行分隔:

  • 脚本本身的参数
  • 自解压安装程序中安装程序的参数

然后,脚本将按以下方式构建安装程序:

to_param_list installer_param_list installer_param_array
./makeself ./path/to/sourcedir ./path/to/created/installer "My installer" ./path/to/install/inside/package "${installer_param_list[@]}"

但是,我已经使用包中的一个非常简单的安装程序脚本测试了传递的参数:

while ! -z "$1" ; do
    echo "$1"
    shift
done

并传递一个像这样的数组:

installer_param_array=( ["upgrade-from"]="19 .2.0" ["upgrade-to"]="19.3.0" )

结果如下:

--upgrade-to=19.3.0
--upgrade-from=19
.2.0

它不能回答问题,但是另一种方式(在bash中,其中一个标签)是my_script.sh "$(declare -p thearray)$"。在其中myscript.sh 阅读source /dev/stdin <<<"$1"然后thearray在脚本中找到。数组旁边可以有其他参数。您可以传递许多变量:my_script.sh "$(declare -p var1 var2 ...)"在此单个参数中。
Dominic108 '19

Answers:


13

具有辅助功能:

#!/bin/bash

to_param_list () {
    declare -n outlist=$1
    declare -n inhash=$2

    for param in "${!inhash[@]}"; do
        outlist+=( "--$param=${inhash[$param]}" )
    done
}

declare -A my_vars=( ["key1"]="value1" ["key2"]="value" )

to_param_list list my_vars
my_script.sh "${list[@]}"

上面脚本中的最终命令将扩展为等效于编写

my_script.sh "--key2=value" "--key1=value1"

to_param_list函数采用名称的数组变量和所述名称关联数组变量,并且使用这些来创建在函数两个“名称参考”变量(namerefs是在引入bash释放4.3)。然后,将它们用于从关联数组中以适当格式的键和值填充给定的数组变量。

函数中的循环遍历"${!inhash[@]}",这是关联数组中单独引用的键的列表。

函数调用返回后,脚本将使用数组来调用其他脚本或命令。

运行上面的

declare -A my_vars=( ["key1"]="hello world" ["key2"]="some thing" ["key3"]="* * *" )

to_param_list list my_vars
printf 'Arg: %s\n' "${list[@]}"

该脚本将输出

Arg: --key2=some thing
Arg: --key3=* * *
Arg: --key1=hello world

这表明在生成选项时不会拆分字或文件名不起作用。它还表明,可能不会保留键的顺序,因为从关联数组中访问键会以相当随机的顺序进行操作。


您在这里不能真正安全地使用命令替换,因为它只能是一个字符串。如果不加引号,则此字符串将被分割为空白字符(默认情况下),这将另外分割关联数组的键和值。外壳程序还将对结果单词执行文件名遍历。用双引号代替命令将无济于事,因为这将导致my_script.sh使用单个参数调用您。


关于您的问题makeself

makeself脚本使用安装程序脚本的参数来执行此操作:

SCRIPTARGS="$*"

这会将参数另存为字符串$SCRIPTARGS(串联,用空格分隔)。稍后将其按原样插入自解压存档中。为了在重新评估选项时(在运行安装程序时)正确解析这些选项,您必须在参数值中提供一组额外的引号,以便正确定界它们。

installer_param_array=( ["upgrade-from"]="'19 .2.0'" ["upgrade-to"]="'19.3.0'" )

请注意,这不是我代码中的错误。这只是根据用户提供的值makeself生成shell代码的副作用。

理想情况下,makeself脚本应该在每个提供的参数周围写上一组引号,但实际上并非如此,因为很难知道可能会有什么影响。而是将其留给用户提供这些额外的报价。

从上面重新运行我的测试,但是现在

declare -A my_vars=( ["key1"]="'hello world'" ["key2"]="'some value'" ["key3"]="'* * *'" )

to_param_list list my_vars
printf 'Arg: %s\n' "${list[@]}"

产生

Arg: --key2='some value'
Arg: --key3='* * *'
Arg: --key1='hello world'

您会看到,当这些字符串由shell 重新评估时,不会在空格上分割。

显然,您可以使用初始的关联数组,而可以通过以下方式在to_param_list函数中添加引号

outlist+=( "--$param=${inhash[$param]}" )

进入

outlist+=( "--$param='${inhash[$param]}'" )

对您的代码所做的任何更改都将在选项的值中包含单引号,因此有必要对这些值进行重新评估。


我尝试了您的解决方案,但是,如果起始关联数组中的值之一包含任何空格,则生成的命令将中断
Matteo Tassinari

@MatteoTassinari不,不会。如果是这样,则说明您忘记了中"--$param=${inhash[$param]}"或中的双引号,或者"${list[@]}"接收选项的脚本在解析它们时会出错。
库萨兰达

我可以确认您使用的引号是正确的,这些参数被传递到github.com/megastep/makeself来创建一个安装程序,该安装程序在执行时会调用具有给定参数的脚本,也许这段话出错了
Matteo Tassinari

1
不,不是那样,我将尝试编辑问题以更好地展示我的用例。
Matteo Tassinari

1
我添加了我的实际用例和我遇到的错误
Matteo Tassinari
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.