在彻底搜索了一种在Bash中创建关联数组的方法后,我发现declare -A array
可以解决问题。但问题是,它只适用于Bash版本4,而我们系统中服务器所用的Bash版本为3.2.16。
如何在Bash 3中实现类似关联数组的操作?这些值将会被传递给一个脚本,例如:
ARG=array[key];
./script.sh ${ARG}
编辑:我知道可以使用awk或其他工具来完成这个任务,但是我需要在严格的bash环境下解决这个问题。
在彻底搜索了一种在Bash中创建关联数组的方法后,我发现declare -A array
可以解决问题。但问题是,它只适用于Bash版本4,而我们系统中服务器所用的Bash版本为3.2.16。
如何在Bash 3中实现类似关联数组的操作?这些值将会被传递给一个脚本,例如:
ARG=array[key];
./script.sh ${ARG}
编辑:我知道可以使用awk或其他工具来完成这个任务,但是我需要在严格的bash环境下解决这个问题。
./script.sh ${ARG}
不会将关联数组传递到子脚本,因为当ARG
是关联数组时,${ARG}
会扩展为空。您无法将关联数组传递给子进程,因此需要对其进行编码。key=value
的形式传递参数。这假设字符=
不出现在键中。${myarray[key]}
,请写成${myarray__key}
。如果键是在运行时确定的,则需要先进行一轮扩展:而不是${myarray[$key]}
,请写成n=myarray__${key}; echo ${!n}
在一个任务中,使用printf -v
。请注意,在printf
中使用%s
格式来使用指定的值。不要编写,因为这将把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中运行该脚本。这样它将继承父进程的所有变量。
printf -v
代替 declare
来避免在函数中局部化变量。而不是调用它,引入子脚本可能也是使父级数组和其他变量对子级可用的一种方法。 - Dennis Williamsonfor k in ``${!myarray__*}``; do
中加入了退格符。这不是一个命令。 - Jocelyn delalanden
吗?我认为这是不可能的。 - Gilles 'SO- stop being evil'if
statement to catch delimiter issues, sanitize oddball input ...etc. Use that.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
}
参考资料:
在bash中将数组作为参数传递
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
表达式来处理它们,但我更喜欢上述两个函数的简单性。
key=value
egrep
寻找以^key=
开头的内容,这使得操作非常安全。tail -1
获取egrep
的最后一个结果即可。key=value
作为数组的值,然后遍历数组以查找值。最终创建了两个数组:
HOST_LIST=(host1 host2)
URL_LIST=(url1 url2)
然后使用索引:
for i in ${!HOST_LIST[@]}; do
echo ${HOST_LIST[$i]} mapped to ${URL_LIST[$i]}
done
更有用的提取函数:
function GetUrl()
{
local _HOSTNAME=$1
local retVal=$2
for i in ${!HOST_LIST[@]}; do
if [ "${HOST_LIST[$i]}" = "$_HOSTNAME" ]; then
eval $retVal="'${URL_LIST[$i]}'"
return 0
fi
done
return 1
}
被称为:
GetUrl host2 resultUrl
echo $resultUrl