在彻底搜索了一种在bash中创建关联数组的方法之后,我发现declare -A array
可以解决这个问题。但问题是,它仅适用于bash版本4,服务器在我们系统中的bash版本是3.2.16。
如何在bash 3中实现某种类似关联数组的hack?这些值将传递给像
这样的脚本ARG=array[key];
./script.sh ${ARG}
编辑:我知道我可以在awk或其他工具中执行此操作,但我正在尝试解决的场景需要严格的bash。
答案 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
}
答案 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[$?]}
中的更多详情和变体
答案 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表达式来处理它们,但我更喜欢上述两个函数的简单性。