在bash 3中创建关联数组

时间:2012-08-02 11:27:39

标签: bash

在彻底搜索了一种在bash中创建关联数组的方法之后,我发现declare -A array可以解决这个问题。但问题是,它仅适用于bash版本4,服务器在我们系统中的bash版本是3.2.16。

如何在bash 3中实现某种类似关联数组的hack?这些值将传递给像

这样的脚本
ARG=array[key];

./script.sh ${ARG}
编辑:我知道我可以在awk或其他工具中执行此操作,但我正在尝试解决的场景需要严格的bash。

5 个答案:

答案 0 :(得分:29)

Bash 3没有关联数组,因此您必须为您的目的使用其他一些语言功能。请注意,即使在bash 4下,您编写的代码也不会执行您声称的操作:./script.sh ${ARG}不会将关联数组传递给子脚本,因为${ARG}ARG时扩展为空{1}}是一个关联数组。您无法将关联数组传递给子进程,无论如何都需要对其进行编码。

您需要在父脚本和子脚本之间定义一些参数传递协议。一个常见的方法是以key=value的形式传递参数。这假设字符=没有出现在键中。

您还需要弄清楚如何在父脚本和子脚本中表示关联数组。他们不需要使用相同的表示。

表示关联数组的常用方法是为每个元素使用单独的变量,并使用通用的命名前缀。这要求密钥名称仅由ASCII字母(任何一种情况),数字和下划线组成。例如,代替${myarray[key]},写下${myarray__key}。如果在运行时确定密钥,则需要先进行一轮扩展:而不是${myarray[$key]},写入

n=myarray__${key}; echo ${!n}

对于作业,请使用printf -v。请注意%s格式为printf以使用指定的值。不要写 printf -v "myarray__${key}" %s "$value" ,因为这会将$value视为格式并对其执行printf %扩展。

printf -v "myarray__${key}" %s "$value"

如果需要将表示为此类的关联数组传递给具有key=value参数表示的子进程,则可以使用${!myarray__*}枚举名称以{{1}开头的所有变量}。

myarray__

在子进程中,将args=() for k in ${!myarray__*}; do n=$k args+=("$k=${!n}") done 形式的参数转换为带前缀的单独变量:

key=value

顺便问一下,你确定这就是你需要的吗?您可能希望在子shell中运行子脚本,而不是从另一个bash脚本调用bash脚本。这样它就会继承父母的所有变量。

答案 1 :(得分:9)

以下是使用参数扩展的bash 3及更早版本中关联数组的另一篇文章/解释:
https://stackoverflow.com/a/4444841

Gilles的方法有一个很好的if语句来捕捉分隔符问题,消除奇怪的输入......等等。用那个。

如果您对参数扩展有些熟悉:
http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

在您的场景中使用[如上所述:发送到脚本]: 脚本1: sending_array.sh

# A pretend Python dictionary with bash 3 
ARRAY=( "cow:moo"
        "dinosaur:roar"
        "bird:chirp"
        "bash:rock" )

bash ./receive_arr.sh "${ARRAY[@]}"

脚本2:receive_arr.sh

argAry1=("$@")

function process_arr () {
    declare -a hash=("${!1}")
    for animal in "${hash[@]}"; do
        echo "Key: ${animal%%:*}"
        echo "Value: ${animal#*:}"
    done
}

process_arr argAry1[@]

exit 0

方法2,获取第二个脚本: 脚本1: sending_array.sh

source ./receive_arr.sh
# A pretend Python dictionary with bash 3 
ARRAY=( "cow:moo"
        "dinosaur:roar"
        "bird:chirp"
        "bash:rock" )

process_arr ARRAY[@]

脚本2:receive_arr.sh

function process_arr () {
    declare -a hash=("${!1}")
    for animal in "${hash[@]}"; do
        echo "Key: ${animal%%:*}"
        echo "Value: ${animal#*:}"
    done
}

参考文献:
Passing arrays as parameters in bash

答案 2 :(得分:3)

如果你不想处理很多变量,或者键只是无效的变量标识符,你的数组保证少于256项,你可以滥用功能返回值。这个解决方案不需要任何子shell,因为值可以作为变量使用,也不需要任何迭代,以便性能尖叫。它也非常易读,几乎就像Bash 4版本一样。

这是最基本的版本:

hash_index() {
    case $1 in
        'foo') return 0;;
        'bar') return 1;;
        'baz') return 2;;
    esac
}

hash_vals=("foo_val"
           "bar_val"
           "baz_val");

hash_index "foo"
echo ${hash_vals[$?]}

this answer

中的更多详情和变体

答案 3 :(得分:2)

您可以将键值对写入文件,然后按键grep。如果你使用像

这样的模式
key=value

然后你可以egrep ^key=使{非常安全。

要“覆盖”某个值,只需将新值附加到文件末尾,然后使用tail -1获取egrep的最后结果

或者,您可以使用key=value作为数组的值将此信息放入普通数组中,然后使用数组上的迭代器来查找值。

答案 4 :(得分:1)

事实证明这很容易。我不得不将一个使用一堆关联数组的bash 4脚本转换为bash 3.这两个辅助函数完成了所有操作:

array_exp() {
    exp=${@//[/__}
    eval "${exp//]}"
}

array_clear() {
    unset $(array_exp "echo \${!$1__*}")
}

我惊讶于这确实有效,但这就是bash的美丽。 E.g。

((all[ping_lo] += counts[ping_lo]))

变为

array_exp '((all[ping_lo] += counts[ping_lo]))'

或者这份印刷声明:

printf "%3d" ${counts[ping_lo]} >> $return

变为

array_exp 'printf "%3d" ${counts[ping_lo]}' >> $return

唯一更改的语法是清除。这样:

counts=()

变为

array_clear counts

你已经确定了。你可以很容易地告诉array_exp识别像" =()"这样的表达式。并通过将它们重写为array_clear表达式来处理它们,但我更喜欢上述两个函数的简单性。