2012-08-02 93 views
20

在彻底搜索在bash中创建关联数组的方法之后,我发现declare -A array将执行此操作。但问题是,它仅适用于bash版本4,服务器在我们系统中的bash版本是3.2.16。在bash中创建关联数组3

我该如何在bash 3中实现某种类型的关联数组 - 像hack?该值将被传递给脚本像

ARG=array[key]; 

./script.sh ${ARG} 

编辑:我知道我可以在AWK做到这一点,或者是需要我试图解决方案中的其他工具,但严格的bash。

+0

由于bash 3具有正常的数组,因此您只需基于它们实现关联数组。但严重的是,为什么这是必要的? – 2012-08-02 11:44:11

+3

请参阅[BashFAQ/006](http://mywiki.wooledge.org/BashFAQ/006)。 – 2012-08-03 01:40:13

回答

25

Bash 3没有关联数组,所以你将不得不使用一些其他语言功能来实现你的目的。请注意,即使在bash 4下,您编写的代码也不会执行您声称的操作:./script.sh ${ARG}不会将关联数组传递给子脚本,因为当ARG是关联数组时,${ARG}会展开为空。您无法将关联数组传递给子进程,您仍然需要对其进行编码。

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

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

表示关联数组的常用方法是对每个元素使用单独的变量,并使用一个通用的命名前缀。这要求密钥名称只包含ASCII字母(无论是哪种情况),数字和下划线。例如,而不是${myarray[key]},请写${myarray__key}。如果密钥是在运行时确定的,需要一轮扩张第一:不是${myarray[$key]},写

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

对于分配,使用printf -v。请注意0​​格式为printf以使用指定的值。不要编写printf -v "myarray__${key}" %s "$value"因为那样会将$value视为一种格式并对其执行printf %扩展。

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

如果你需要这样表示的关联数组传递给一个子进程与key=value说法表示,你可以使用${!myarray__*}来枚举名称以myarray__所有的变量。

args=() 
for k in ${!myarray__*}; do 
    n=$k 
    args+=("$k=${!n}") 
done 

子进程,窗体key=value与前缀独立的变量的参数转换:

for x; do 
    if [[ $x != *=* ]]; then echo 1>&2 "KEY=VALUE expected, but got $x"; exit 120; fi 
    printf -v "myarray__${x%%=*}" %s "${x#*=}" 
done 

顺便说一句,你确定这是你需要什么?而不是从另一个bash脚本调用bash脚本,您可能希望在子shell中运行子脚本。这样它将继承父级的所有变量。

+1

您可以使用'printf -v'而不是'declare'来避免函数中的变量本地化。找到子脚本而不是调用子脚本也可能是使父级的数组和其他变量可用于子级的一种方法。 – 2012-08-03 01:38:23

+0

辉煌的东西! – 2013-06-22 14:11:23

+0

我不明白你为什么在''$ {!myarray __ *}''中为'k放置空格。这不是命令。 – 2013-11-28 14:46:24

2

您可以将键值对写入文件,然后通过键将grep键入。如果您使用的图案像

key=value 

那么你可以egrep^key=这使得这个非常安全的。

要“覆盖”的值,只是在文件末尾附加新的价值和使用tail -1得到公正的egrep

或者最后的结果,你可以把此信息将使用key=value正常阵列作为数组的值,然后遍历数组以找到值。

6

下面是在bash 3关联数组和老年人使用参数扩展另一篇文章/解释:
https://stackoverflow.com/a/4444841

吉尔方法有一个很好的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=("[email protected]") 

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

如果你不想处理很多变量或密钥都是简单无效的变量标识符,你的数组保证有少于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

1

这原来是可笑容易。我必须将使用一堆关联数组的bash 4脚本转换为bash 3。这两个帮手功能做到了这一切:

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

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

我很惊讶,这实际上工作,但这是bash的美丽。 例如

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

成为

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

还是这个print语句:

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

成为

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

改变正在清理唯一的语法。这:

counts=() 

成为

array_clear counts 

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

+0

这看起来很酷。我想知道如何处理这样的语句:let'map [$ i] ++',特别是单引号。思想/评论? – Ray 2015-08-18 17:08:31

+0

在上面的回答中,当我运行脚本时,array_exp()中的eval语句失败;它在抱怨“糟糕的替代品” – Ray 2015-08-18 17:12:11