在彻底搜索了一种在 bash 中创建关联数组的方法之后,我发现这declare -A array
可以解决问题。但问题是,它只适用于 bash 版本 4,而我们系统中服务器的 bash 版本是 3.2.16。
如何在 bash 3 中实现某种类似关联数组的 hack?这些值将传递给脚本,例如
ARG=array[key];
./script.sh ${ARG}
编辑:我知道我可以在 awk 或其他工具中执行此操作,但我要解决的场景需要严格的 bash。
在彻底搜索了一种在 bash 中创建关联数组的方法之后,我发现这declare -A array
可以解决问题。但问题是,它只适用于 bash 版本 4,而我们系统中服务器的 bash 版本是 3.2.16。
如何在 bash 3 中实现某种类似关联数组的 hack?这些值将传递给脚本,例如
ARG=array[key];
./script.sh ${ARG}
编辑:我知道我可以在 awk 或其他工具中执行此操作,但我要解决的场景需要严格的 bash。
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
. 请注意使用指定值的%s
格式。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 中运行子脚本。这样它将继承父级的所有变量。
这是关于 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
}
如果您不想处理大量变量,或者键只是无效的变量标识符,并且您的数组保证少于 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[$?]}
此答案中的更多详细信息和变体
事实证明这非常容易。我必须将使用一堆关联数组的 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]))'
或者这个打印声明:
printf "%3d" ${counts[ping_lo]} >> $return
变成
array_exp 'printf "%3d" ${counts[ping_lo]}' >> $return
唯一改变的语法是清除。这个:
counts=()
变成
array_clear counts
你已经准备好了。您可以轻松地告诉 array_exp 识别像 "=()" 这样的表达式,并通过将它们重写为 array_clear 表达式来处理它们,但我更喜欢上述两个函数的简单性。
您可以将键值对写入文件,然后按键 grep。如果您使用类似的模式
key=value
那么你可以egrep
为此^key=
非常安全。
要“覆盖”一个值,只需将新值附加到文件末尾并用于tail -1
获取最后一个结果egrep
或者,您可以将此信息放入普通数组中,使用key=value
作为数组的值,然后对数组进行迭代以查找该值。