什么是Python字典的等效,但在Bash中(应该适用于OS X和Linux)。


当前回答

Bash 4

Bash 4本身支持这个特性。确保脚本的hashbang是#!/usr/bin/env bash或#!/bin/bash,这样你就不会使用sh。确保你要么直接执行你的脚本,要么用bash脚本执行脚本。(没有实际使用Bash执行Bash脚本的情况确实会发生,这将非常令人困惑!)

你可以这样声明一个关联数组:

declare -A animals

可以使用普通数组赋值操作符将其填充为元素。例如,如果你想要一个animal[sound(key)] = animal(value)的映射:

animals=( ["moo"]="cow" ["woof"]="dog")

或者在一行中声明和实例化:

declare -A animals=( ["moo"]="cow" ["woof"]="dog")

然后像普通数组一样使用它们。使用

Animals ['key']='value'设置值 ${animals[@]}展开值 “$ {!animals[@]}”(注意!)来展开键

别忘了引用他们的话:

echo "${animals[moo]}"
for sound in "${!animals[@]}"; do echo "$sound - ${animals[$sound]}"; done

Bash 3

在bash 4之前,没有关联数组。不要使用eval来模拟它们。像避免瘟疫一样避免eval,因为它是shell脚本的瘟疫。最重要的原因是eval将数据视为可执行代码(还有许多其他原因)。

首先也是最重要的:考虑升级到bash 4。这将使整个过程对你来说容易得多。

如果有不能升级的原因,声明是一个更安全的选择。它不像bash代码那样像eval那样计算数据,因此不允许如此容易地进行任意代码注入。

让我们通过介绍概念来准备答案:

首先,间接。

$ animals_moo=cow; sound=moo; i="animals_$sound"; echo "${!i}"
cow

其次,声明:

$ sound=moo; animal=cow; declare "animals_$sound=$animal"; echo "$animals_moo"
cow

把它们放在一起:

# Set a value:
declare "array_$index=$value"

# Get a value:
arrayGet() { 
    local array=$1 index=$2
    local i="${array}_$index"
    printf '%s' "${!i}"
}

让我们使用它:

$ sound=moo
$ animal=cow
$ declare "animals_$sound=$animal"
$ arrayGet animals "$sound"
cow

注意:declare不能放在函数中。在bash函数中使用declare将其创建的变量转换为该函数范围内的局部变量,这意味着我们不能使用它访问或修改全局数组。(在bash 4中,您可以使用declare -g来声明全局变量——但在bash 4中,您可以首先使用关联数组,从而避免这种变通方法。)

简介:

升级到bash 4并对关联数组使用declare -A。 如果不能升级,请使用声明选项。 可以考虑使用awk,并完全避免这个问题。

其他回答

Bash 4

Bash 4本身支持这个特性。确保脚本的hashbang是#!/usr/bin/env bash或#!/bin/bash,这样你就不会使用sh。确保你要么直接执行你的脚本,要么用bash脚本执行脚本。(没有实际使用Bash执行Bash脚本的情况确实会发生,这将非常令人困惑!)

你可以这样声明一个关联数组:

declare -A animals

可以使用普通数组赋值操作符将其填充为元素。例如,如果你想要一个animal[sound(key)] = animal(value)的映射:

animals=( ["moo"]="cow" ["woof"]="dog")

或者在一行中声明和实例化:

declare -A animals=( ["moo"]="cow" ["woof"]="dog")

然后像普通数组一样使用它们。使用

Animals ['key']='value'设置值 ${animals[@]}展开值 “$ {!animals[@]}”(注意!)来展开键

别忘了引用他们的话:

echo "${animals[moo]}"
for sound in "${!animals[@]}"; do echo "$sound - ${animals[$sound]}"; done

Bash 3

在bash 4之前,没有关联数组。不要使用eval来模拟它们。像避免瘟疫一样避免eval,因为它是shell脚本的瘟疫。最重要的原因是eval将数据视为可执行代码(还有许多其他原因)。

首先也是最重要的:考虑升级到bash 4。这将使整个过程对你来说容易得多。

如果有不能升级的原因,声明是一个更安全的选择。它不像bash代码那样像eval那样计算数据,因此不允许如此容易地进行任意代码注入。

让我们通过介绍概念来准备答案:

首先,间接。

$ animals_moo=cow; sound=moo; i="animals_$sound"; echo "${!i}"
cow

其次,声明:

$ sound=moo; animal=cow; declare "animals_$sound=$animal"; echo "$animals_moo"
cow

把它们放在一起:

# Set a value:
declare "array_$index=$value"

# Get a value:
arrayGet() { 
    local array=$1 index=$2
    local i="${array}_$index"
    printf '%s' "${!i}"
}

让我们使用它:

$ sound=moo
$ animal=cow
$ declare "animals_$sound=$animal"
$ arrayGet animals "$sound"
cow

注意:declare不能放在函数中。在bash函数中使用declare将其创建的变量转换为该函数范围内的局部变量,这意味着我们不能使用它访问或修改全局数组。(在bash 4中,您可以使用declare -g来声明全局变量——但在bash 4中,您可以首先使用关联数组,从而避免这种变通方法。)

简介:

升级到bash 4并对关联数组使用declare -A。 如果不能升级,请使用声明选项。 可以考虑使用awk,并完全避免这个问题。

考虑使用bash内置read的解决方案,如下面的ufw防火墙脚本代码片段所示。这种方法的优点是可以根据需要使用尽可能多的带分隔符的字段集(不只是2个)。我们使用了|分隔符,因为端口范围说明符可能需要冒号,即6001:6010。

#!/usr/bin/env bash

readonly connections=(       
                            '192.168.1.4/24|tcp|22'
                            '192.168.1.4/24|tcp|53'
                            '192.168.1.4/24|tcp|80'
                            '192.168.1.4/24|tcp|139'
                            '192.168.1.4/24|tcp|443'
                            '192.168.1.4/24|tcp|445'
                            '192.168.1.4/24|tcp|631'
                            '192.168.1.4/24|tcp|5901'
                            '192.168.1.4/24|tcp|6566'
)

function set_connections(){
    local range proto port
    for fields in ${connections[@]}
    do
            IFS=$'|' read -r range proto port <<< "$fields"
            ufw allow from "$range" proto "$proto" to any port "$port"
    done
}

set_connections

一位同事刚刚提到了这个帖子。我在bash中独立实现了哈希表,它不依赖于版本4。我在2010年3月的一篇博客文章(在这里的一些答案之前…)题为bash中的哈希表:

我以前使用cksum来散列,但后来将Java的字符串hashCode转换为本地bash/zsh。

# Here's the hashing function
ht() {
  local h=0 i
  for (( i=0; i < ${#1}; i++ )); do
    let "h=( (h<<5) - h ) + $(printf %d \'${1:$i:1})"
    let "h |= h"
  done
  printf "$h"
}

# Example:

myhash[`ht foo bar`]="a value"
myhash[`ht baz baf`]="b value"

echo ${myhash[`ht baz baf`]} # "b value"
echo ${myhash[@]} # "a value b value" though perhaps reversed
echo ${#myhash[@]} # "2" - there are two values (note, zsh doesn't count right)

它不是双向的,内置的方式要好得多,但无论如何都不应该使用。Bash是用于快速一次性的,这类事情很少涉及可能需要散列的复杂性,除非在~/中。Bashrc和朋友们。

这就是我要找的东西:

declare -A hashmap
hashmap["key"]="value"
hashmap["key2"]="value2"
echo "${hashmap["key"]}"
for key in ${!hashmap[@]}; do echo $key; done
for value in ${hashmap[@]}; do echo $value; done
echo hashmap has ${#hashmap[@]} elements

这在bash 4.1.5中并不适用:

animals=( ["moo"]="cow" )

我同意@lhunath和其他人的观点,关联数组是Bash 4的首选。如果你坚持使用Bash 3 (OSX,你不能更新的旧发行版),你也可以使用expr,它应该无处不在,一个字符串和正则表达式。我喜欢它,尤其是当字典不是太大。

Choose 2 separators that you will not use in keys and values (e.g. ',' and ':' ) Write your map as a string (note the separator ',' also at beginning and end) animals=",moo:cow,woof:dog," Use a regex to extract the values get_animal { echo "$(expr "$animals" : ".*,$1:\([^,]*\),.*")" } Split the string to list the items get_animal_items { arr=$(echo "${animals:1:${#animals}-2}" | tr "," "\n") for i in $arr do value="${i##*:}" key="${i%%:*}" echo "${value} likes to $key" done }

现在你可以使用它:

$ animal = get_animal "moo"
cow
$ get_animal_items
cow likes to moo
dog likes to woof