什么是同等的 蟒蛇的字典 但在庆典(应工作的跨OS X和Linux)。

有帮助吗?

解决方案

重击4

Bash 4 本身支持此功能。确保你的脚本的 hashbang 是 #!/usr/bin/env bash 或者 #!/bin/bash 所以你最终不会使用 sh. 。确保您直接执行脚本,或者执行 scriptbash script. 。(实际上并没有使用 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

重击3

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

首先也是最重要的:考虑升级到 bash 4。这将使您的整个过程变得更加容易。

如果有原因导致您无法升级, declare 是一个更安全的选择。它不会像 bash 代码那样评估数据 eval 确实如此,因此不允许轻易注入任意代码。

让我们通过引入概念来准备答案:

第一,间接性。

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

第二, declare:

$ 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 不能放入函数中。任何使用 declare 在 bash 函数内部转换它创建的变量 当地的 到该函数的范围,这意味着我们无法使用它访问或修改全局数组。(在 bash 4 中,您可以使用声明 -g 来声明全局变量 - 但在 bash 4 中,您可以首先使用关联数组,从而避免这种解决方法。)

概括:

  • 升级到 bash 4 并使用 declare -A 对于关联数组。
  • 使用 declare 如果您无法升级,请选择。
  • 考虑使用 awk 相反,并完全避免这个问题。

其他提示

有参数替换,虽然它可以是未PC以及...等间接。

#!/bin/bash

# Array pretending to be a Pythonic dictionary
ARRAY=( "cow:moo"
        "dinosaur:roar"
        "bird:chirp"
        "bash:rock" )

for animal in "${ARRAY[@]}" ; do
    KEY="${animal%%:*}"
    VALUE="${animal##*:}"
    printf "%s likes to %s.\n" "$KEY" "$VALUE"
done

printf "%s is an extinct animal which likes to %s\n" "${ARRAY[1]%%:*}" "${ARRAY[1]##*:}"

在BASH 4的方式当然是好,但如果你需要一个黑客...只有一个黑客就行了。 可以搜索具有类似的技术在阵列/散列。

这就是我一直在寻找在这里:

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" )

您可以进一步修改 hput()/hget() 接口,以便按如下方式命名哈希:

hput() {
    eval "$1""$2"='$3'
}

hget() {
    eval echo '${'"$1$2"'#hash}'
}

进而

hput capitals France Paris
hput capitals Netherlands Amsterdam
hput capitals Spain Madrid
echo `hget capitals France` and `hget capitals Netherlands` and `hget capitals Spain`

这使您可以定义不冲突的其他地图(例如,按首都进行国家/地区查找的“rcapitals”)。但是,无论哪种方式,我认为您都会发现这在性能方面非常糟糕。

如果你真的想要快速的哈希查找,有一个非常非常糟糕的 hack,但实际上效果非常好。它是这个:将您的键/值写入一个临时文件,每行一个,然后使用 'grep "^$key"' 将它们取出,使用带有 cut 或 awk 或 sed 或其他的管道来检索值。

就像我说的,这听起来很糟糕,而且听起来它应该很慢并且执行各种不必要的 IO,但实际上它非常快(磁盘缓存很棒,不是吗?),即使对于非常大的哈希表。您必须自己强制执行密钥唯一性等。即使您只有几百个条目,输出文件/grep 组合也会快得多 - 根据我的经验,快了好几倍。它也消耗更少的内存。

这是一种方法:

hinit() {
    rm -f /tmp/hashmap.$1
}

hput() {
    echo "$2 $3" >> /tmp/hashmap.$1
}

hget() {
    grep "^$2 " /tmp/hashmap.$1 | awk '{ print $2 };'
}

hinit capitals
hput capitals France Paris
hput capitals Netherlands Amsterdam
hput capitals Spain Madrid

echo `hget capitals France` and `hget capitals Netherlands` and `hget capitals Spain`

只需使用文件系统

在文件系统是可被用作一个哈希映射中的树结构。 您的哈希表将是一个临时目录,您的钥匙将文件名和你的价值观将文件内容。的优点是,它可以处理巨大包含HashMap,并且不需要特定的壳。

散列表创建

hashtable=$(mktemp -d)

添加的元素

echo $value > $hashtable/$key

读取的元素

value=$(< $hashtable/$key)

效果

当然,它的慢,但不是的慢。 我测试了我的机器上,与SSD和 BTRFS 和它的 3000绕每秒元件读/写

hput () {
  eval hash"$1"='$2'
}

hget () {
  eval echo '${hash'"$1"'#hash}'
}
hput France Paris
hput Netherlands Amsterdam
hput Spain Madrid
echo `hget France` and `hget Netherlands` and `hget Spain`

$ sh hash.sh
Paris and Amsterdam and Madrid

考虑使用内建在bash的溶液的作为代码段内示出从下面一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

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

  1. 选择 2 个不会在键和值中使用的分隔符(例如',' 和 ':' )
  2. 将地图写为字符串(请注意开头和结尾处的分隔符“,”)

    animals=",moo:cow,woof:dog,"
    
  3. 使用正则表达式提取值

    get_animal {
        echo "$(expr "$animals" : ".*,$1:\([^,]*\),.*")"
    }
    
  4. 拆分字符串以列出项目

    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

我真的很喜欢铝磷的答案,但希望唯一便宜执行,让我把它一步 - 使用目录。有一些明显的局限性(目录文件限制,无效的文件名),但它应该在大多数情况下工作。

hinit() {
    rm -rf /tmp/hashmap.$1
    mkdir -p /tmp/hashmap.$1
}

hput() {
    printf "$3" > /tmp/hashmap.$1/$2
}

hget() {
    cat /tmp/hashmap.$1/$2
}

hkeys() {
    ls -1 /tmp/hashmap.$1
}

hdestroy() {
    rm -rf /tmp/hashmap.$1
}

hinit ids

for (( i = 0; i < 10000; i++ )); do
    hput ids "key$i" "value$i"
done

for (( i = 0; i < 10000; i++ )); do
    printf '%s\n' $(hget ids "key$i") > /dev/null
done

hdestroy ids

它还执行在我的测试一个稍微好一点。

$ time bash hash.sh 
real    0m46.500s
user    0m16.767s
sys     0m51.473s

$ time bash dirhash.sh 
real    0m35.875s
user    0m8.002s
sys     0m24.666s

只是觉得应该英寸干杯球场!

编辑:添加hdestroy()

在 bash 4 之前,没有在 bash 中使用关联数组的好方法。最好的选择是使用实际上支持此类功能的解释语言,例如 awk。另一方面,bash 4 支持他们。

至于 较少的 bash 3 中的好方法,这里是一个可能有帮助的参考: http://mywiki.wooledge.org/BashFAQ/006

两两件事,可以通过使用的/ dev / SHM(红帽)其他发行可能会发生变化在任何内核2.6使用存储器而不是/ TMP的。也hget可重新实现使用如下:

function hget {

  while read key idx
  do
    if [ $key = $2 ]
    then
      echo $idx
      return
    fi
  done < /dev/shm/hashmap.$1
}

在另外通过假定所有密钥都是唯一的,则返回短路读取循环,并防止通过具有所有条目来阅读。如果您的情况可能会有重复键,后来干脆离开了回报。这节省了阅读和两分叉grep和awk的费用。使用的/ dev / SHM为两种实现得到一个3条目的散列下面使用时间hget搜索的最后一个条目:

的grep / awk中:

hget() {
    grep "^$2 " /dev/shm/hashmap.$1 | awk '{ print $2 };'
}

$ time echo $(hget FD oracle)
3

real    0m0.011s
user    0m0.002s
sys     0m0.013s

读/回波:

$ time echo $(hget FD oracle)
3

real    0m0.004s
user    0m0.000s
sys     0m0.004s

在多次调用我从未见过小于50%的改善。 这些都可以归结到餐桌的头上,由于使用/dev/shm的。

击3溶液:

在阅读一些我放在一起答案的一个小巧的功能,我想回贡献,可以帮助别人。

# Define a hash like this
MYHASH=("firstName:Milan"
        "lastName:Adamovsky")

# Function to get value by key
getHashKey()
 {
  declare -a hash=("${!1}")
  local key
  local lookup=$2

  for key in "${hash[@]}" ; do
   KEY=${key%%:*}
   VALUE=${key#*:}
   if [[ $KEY == $lookup ]]
   then
    echo $VALUE
   fi
  done
 }

# Function to get a list of all keys
getHashKeys()
 {
  declare -a hash=("${!1}")
  local KEY
  local VALUE
  local key
  local lookup=$2

  for key in "${hash[@]}" ; do
   KEY=${key%%:*}
   VALUE=${key#*:}
   keys+="${KEY} "
  done

  echo $keys
 }

# Here we want to get the value of 'lastName'
echo $(getHashKey MYHASH[@] "lastName")


# Here we want to get all keys
echo $(getHashKeys MYHASH[@])

一个同事刚刚提到的这个线程。我已经独立实施的散列表内的庆典,并不依赖于版本4。从一篇博客的地雷在2010年(前的一些答案在这里...)题为 哈希表在bash:

先前 使用 cksum 哈希但由于翻译 Java's string哈希码 地bash/aix系统管理的各个方面和扩展.

# 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)

它不是双向的,且建造的方式是一个很好,但也不应该真正被使用。庆典是一个快速核销,这样的事情应该很很少涉及的复杂性,可能需要的散列,也许除了你的 ~/.bashrc 和朋友。

我还使用了bash4的方式,但我觉得和恼人的错误。

我需要动态地更新关联数组内容所以我用这种方式:

for instanceId in $instanceList
do
   aws cloudwatch describe-alarms --output json --alarm-name-prefix $instanceId| jq '.["MetricAlarms"][].StateValue'| xargs | grep -E 'ALARM|INSUFFICIENT_DATA'
   [ $? -eq 0 ] && statusCheck+=([$instanceId]="checkKO") || statusCheck+=([$instanceId]="allCheckOk"
done

我发现,在bash 4.3.11追加在字典现有密钥导致如果已经存在附加的价值。因此,例如,一些repetion后的值的内容是“checkKOcheckKOallCheckOK”,这是不好的。

使用bash 39年4月3日无问题,其中appenging的存在的主要手段substisture的actuale值,如果已经存在。

我解决了这个刚清洗/所述cicle之前声明statusCheck关联数组:

unset statusCheck; declare -A statusCheck

我创建使用动态变量在bash 3包含HashMap。我解释了如何在我的答案工程:在Shell脚本关联数组

你也可以在 shell_map 看看下,这是在bash 3制成一个HashMap实现。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top