在 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 22:30:29  来源:igfitidea点击:

Create associative array in bash 3

bash

提问by makalshrek

After thoroughly searching for a way to create an associative array in bash, I found that declare -A arraywill 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 ARGis 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 %sformat to printfto use the specified value. Do not write printf -v "myarray__${key}" %s "$value"since that would treat $valueas 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=valueargument 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=valueto 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 ifstatement 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

参考:
在 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 egrepfor ^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 -1to get just the last result of egrep

要“覆盖”一个值,只需在文件末尾附加新值并用于tail -1获取最后一个结果egrep

Alternatively, you can put this information into a normal array using key=valueas 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:

事实证明这非常容易。我不得不将使用一堆关联数组的 ba​​sh 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 表达式来处理它们,但我更喜欢上面两个函数的简单性。