如何定义Bash的哈希表?如何定义Bash的哈希表?(How to define hash tabl

2019-05-06 01:48发布

什么是相当于Python字典 ,但在击(应跨OS X和Linux工作)。

Answer 1:

击4

击4原生支持此功能。 确保你的脚本hashbang是#!/usr/bin/env bash#!/bin/bash或其他任何引用bash ,而不是sh 。 确保你执行你的脚本,而不是做一些愚蠢像sh script ,这将导致你bash hashbang被忽略。 这是基本的东西,但这么多的保持它失败了,因此重新迭代。

您可通过执行关联数组声明:

declare -A animals

您可以使用正常的数组赋值运算符元素填充它:

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

庆典4之前,你没有关联数组。 不要使用eval模仿他们 。 你必须避免的eval瘟疫一样,因为它 shell脚本的瘟疫。 最重要的原因是,你不想把你的数据作为可执行代码(还有很多其他的原因也是如此)。

首先 :只需考虑升级到打坏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 。 如果不能,考虑完全切换到awk如上所述做丑陋的黑客面前。 肯定留赫克远离eval两轮牛车。



Answer 2:

有参数替换,尽管它可能是取消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路当然是好,但如果你需要一个黑客...只有一个黑客就行了。 你可以搜索与类似技术的阵列/哈希值。



Answer 3:

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

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


Answer 4:

您可以进一步修改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”,这由首都做国家查找)。 但是,无论哪种方式,我想你会发现,这是所有非常可怕的,性能明智的。

如果你真的想快速散列查询,有实际工作真的很好可怕,可怕的黑客。 它是这样的:写你的键/值进行到一个临时文件,一个每行,然后使用“grep的‘^ $关键’”让他们出去,使用带有剪切或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`


Answer 5:

只要使用文件系统

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

哈希表的创建

hashtable=$(mktemp -d)

添加元素

echo $value > $hashtable/$key

阅读元素

value=$(< $hashtable/$key)

性能

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



Answer 6:

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


Answer 7:

考虑使用如从以下一UFW防火墙脚本代码段内出在bash内置的溶液。 这种方法具有如期望使用尽可能多分隔字段集的优点(不只是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


Answer 8:

我@lhunath和其他人同意,关联数组是一起去猛砸4.如果你坚持击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


Answer 9:

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

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



Answer 10:

此前抨击4有在bash使用关联数组没有什么好办法。 最好的办法是使用实​​际上有这样的事情的支持,如awk的解释性语言。 在另一方面,庆典4 支持他们。

至于在bash 3 不太好办法,这里是不是可以帮助一个参考: http://mywiki.wooledge.org/BashFAQ/006



Answer 11:

有两件事情,您可以通过使用的/ dev / shm的(红帽)其他发行版可能会有所不同使用内存而不是/ tmp目录中任何内核2.6。 也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:

握/ 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



Answer 12:

击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[@])


Answer 13:

一个同事刚刚提到这个线程。 我已经独立实施的bash中的哈希表,它不依赖于版本4.从我的博客文章在2010年3月(之前一些问题的答案在这里...)有权在bash哈希表 :

# Here's the hashing function
ht() { local ht=`echo "$*" |cksum`; echo "${ht//[!0-9]}"; }

# 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

当然,它使外部调用的校验和,因此有所减缓,但执行是非常干净和可用的。 这不是双向的,内置的方式是好了很多,但也确实应该无论如何使用。 Bash是快速一次性的,这样的事情应该相当很少涉及复杂性,可能需要哈希,除非是在你的.bashrc和朋友。



Answer 14:

我还使用了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年3月4日没有问题,在appenging一个存在的主要手段,如果已经存在substisture的actuale值。

我解决了这个刚刚清洗/宣告cicle前statusCheck关联数组:

unset statusCheck; declare -A statusCheck


Answer 15:

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

你也可以采取一看shell_map ,这是在bash 3做一个HashMap的实现。



文章来源: How to define hash tables in Bash?