在 bash 3 中创建关联数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11776468/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Create associative array in bash 3
提问by makalshrek
After thoroughly searching for a way to create an associative array in bash, I found that declare -A array
will do the trick. But the problem is, it is only for bash version 4 and the bash version the server has in our system is 3.2.16.
在彻底寻找在 bash 中创建关联数组的方法后,我发现这declare -A array
可以解决问题。但问题是,它仅适用于 bash 版本 4,而我们系统中服务器的 bash 版本是 3.2.16。
How can I achieve some sort of associative array-like hack in bash 3? The values will be passed to a script like
如何在 bash 3 中实现某种类似关联数组的 hack?这些值将被传递给一个脚本,如
ARG=array[key];
./script.sh ${ARG}
EDIT: I know that I can do this in awk, or other tools but strict bash is needed for the scenario I am trying to solve.
编辑:我知道我可以在 awk 或其他工具中做到这一点,但我试图解决的场景需要严格的 bash。
采纳答案by Gilles 'SO- stop being evil'
Bash 3 has no associative arrays, so you're going to have to use some other language feature(s) for your purpose. Note that even under bash 4, the code you wrote doesn't do what you claim it does: ./script.sh ${ARG}
does not pass the associative array to the child script, because ${ARG}
expands to nothing when ARG
is an associative array. You cannot pass an associative array to a child process, you need to encode it anyway.
Bash 3 没有关联数组,因此您将不得不使用其他一些语言功能来实现您的目的。请注意,即使在 bash 4 下,您编写的代码也不会执行您声称的操作:./script.sh ${ARG}
不会将关联数组传递给子脚本,因为${ARG}
当ARG
是关联数组时,扩展为空。您不能将关联数组传递给子进程,无论如何都需要对其进行编码。
You need to define some argument passing protocol between the parent script and the child script. A common one is to pass arguments in the form key=value
. This assumes that the character =
does not appear in keys.
您需要在父脚本和子脚本之间定义一些参数传递协议。一个常见的方法是在表单中传递参数key=value
。这假设字符=
没有出现在键中。
You also need to figure out how to represent the associative array in the parent script and in the child script. They need not use the same representation.
您还需要弄清楚如何在父脚本和子脚本中表示关联数组。它们不需要使用相同的表示。
A common method to represent an associative array is to use separate variables for each element, with a common naming prefix. This requires that the key name only consists of ASCII letters (of either case), digits and underscores. For example, instead of ${myarray[key]}
, write ${myarray__key}
. If the key is determined at run time, you need a round of expansion first: instead of ${myarray[$key]}
, write
表示关联数组的一种常用方法是为每个元素使用单独的变量,并带有公共命名前缀。这要求密钥名称仅包含 ASCII 字母(无论哪种情况)、数字和下划线。例如,代替${myarray[key]}
,写${myarray__key}
。如果key是在运行时确定的,需要先进行一轮展开:而不是${myarray[$key]}
,写
n=myarray__${key}; echo ${!n}
For an assignment, use printf -v
. Note the %s
format to printf
to use the specified value. Do not write since that would treat printf -v "myarray__${key}" %s "$value"
$value
as a format and perform printf %
expansion on it.
对于分配,请使用printf -v
. 注意使用指定值的%s
格式printf
。不要写,因为这会被printf -v "myarray__${key}" %s "$value"
$value
视为一种格式并对其执行 printf%
扩展。
printf -v "myarray__${key}" %s "$value"
If you need to pass an associative array represented like this to a child process with the key=value
argument representation, you can use ${!myarray__*}
to enumerate over all the variables whose name begins with myarray__
.
如果您需要将这样表示的关联数组传递给具有key=value
参数表示的子进程,您可以使用${!myarray__*}
枚举所有名称以 开头的变量myarray__
。
args=()
for k in ${!myarray__*}; do
n=$k
args+=("$k=${!n}")
done
In the child process, to convert arguments of the form key=value
to separate variables with a prefix:
在子进程中,要将表单的参数转换为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
By the way, are you sure that this is what you need? Instead of calling a bash script from another bash script, you might want to run the child script in a subshell instead. That way it would inherit from all the variables of the parent.
顺便说一句,你确定这是你需要的吗?您可能希望在子 shell 中运行子脚本,而不是从另一个 bash 脚本调用 bash 脚本。这样它就会继承父级的所有变量。
回答by Bubnoff
Here is another post/explanation on associative arrays in bash 3 and older using parameter expansion:
https://stackoverflow.com/a/4444841
这是使用参数扩展的 bash 3 及更早版本中关联数组的另一篇文章/解释:https:
//stackoverflow.com/a/4444841
Gilles' method has a nice if
statement to catch delimiter issues, sanitize oddball input ...etc. Use that.
吉尔斯的方法有一个很好的if
声明来捕获分隔符问题,清理奇怪的输入......等。用那个。
If you are somewhat familiar with parameter expansion:
http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
如果您对参数扩展有些熟悉:http:
//www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
To use in your scenario [ as stated: sending to script ]:
Script 1:
sending_array.sh
要在您的场景中使用 [如所述:发送到脚本]:脚本 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[@]}"
Script 2: receive_arr.sh
脚本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
Method 2, sourcing the second script:
Script 1:
sending_array.sh
方法 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[@]
Script 2: receive_arr.sh
脚本2: receive_arr.sh
function process_arr () {
declare -a hash=("${!1}")
for animal in "${hash[@]}"; do
echo "Key: ${animal%%:*}"
echo "Value: ${animal#*:}"
done
}
References:
Passing arrays as parameters in bash
回答by Lloeki
If you don't want to handle a lot of variables, or keys are simply invalid variable identifiers, andyour array is guaranteed to have less than 256 items, you can abuse function return values. This solution does not require any subshell as the value is readily available as a variable, nor any iteration so that performance screams. Also it's very readable, almost like the Bash 4 version.
如果您不想处理大量变量,或者键只是无效的变量标识符,并且您的数组保证少于 256 项,则可以滥用函数返回值。此解决方案不需要任何子 shell,因为该值可以作为变量随时可用,也不需要任何迭代,因此性能会大打折扣。它也非常易读,几乎就像 Bash 4 版本。
Here's the most basic version:
这是最基本的版本:
hash_index() {
case 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[$?]}
More details and variants in this answer
此答案中的更多详细信息和变体
回答by Aaron Digulla
You can write the key-value pairs to a file and then grep by key. If you use a pattern like
您可以将键值对写入文件,然后按键进行 grep。如果你使用像这样的模式
key=value
then you can egrep
for ^key=
which makes this pretty safe.
那么你可以这样做egrep
,^key=
这使得这非常安全。
To "overwrite" a value, just append the new value at the end of the file and use tail -1
to get just the last result of egrep
要“覆盖”一个值,只需在文件末尾附加新值并用于tail -1
获取最后一个结果egrep
Alternatively, you can put this information into a normal array using key=value
as value for the array and then iterator over the array to find the value.
或者,您可以将此信息放入普通数组中,使用key=value
作为数组的值,然后在数组上迭代以找到该值。
回答by Janos Barbero
This turns out to be ridiculously easy. I had to convert a bash 4 script that used a bunch of associative arrays to bash 3. These two helper functions did it all:
事实证明这非常容易。我不得不将使用一堆关联数组的 bash 4 脚本转换为 bash 3。这两个辅助函数完成了这一切:
array_exp() {
exp=${@//[/__}
eval "${exp//]}"
}
array_clear() {
unset $(array_exp "echo ${!__*}")
}
I'm flabbergasted that this actually works, but that's the beauty of bash. E.g.
我很惊讶这确实有效,但这就是 bash 的美妙之处。例如
((all[ping_lo] += counts[ping_lo]))
becomes
变成
array_exp '((all[ping_lo] += counts[ping_lo]))'
Or this print statement:
或者这个打印语句:
printf "%3d" ${counts[ping_lo]} >> $return
becomes
变成
array_exp 'printf "%3d" ${counts[ping_lo]}' >> $return
The only syntax that changes is clearing. This:
唯一改变的语法是清除。这个:
counts=()
becomes
变成
array_clear counts
and you're set. You could easily tell array_exp to recognize expressions like "=()" and handle them by rewriting them as array_clear expressions, but I prefer the simplicity of the above two functions.
你准备好了。你可以很容易地告诉 array_exp 识别像“=()”这样的表达式,并通过将它们重写为 array_clear 表达式来处理它们,但我更喜欢上面两个函数的简单性。