如何在Bash中定义哈希表?

如何在Bash中定义哈希表?,bash,dictionary,hashtable,associative-array,Bash,Dictionary,Hashtable,Associative Array,但是在Bash中,它的等价物是什么(应该可以跨OS X和Linux工作) 您可以进一步修改hput()/hget()接口,以便对哈希进行如下命名: hput() { eval "$1""$2"='$3' } hget() { eval echo '${'"$1$2"'#hash}' } function hget { while read key idx do if [ $key = $2 ] then echo $idx

但是在Bash中,它的等价物是什么(应该可以跨OS X和Linux工作)




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

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

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

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

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`

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

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

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

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

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`

在Bash4之前,在bash中没有使用关联数组的好方法。您最好的选择是使用一种解释语言,它实际上支持这些东西,比如awk。另一方面,bash4确实支持它们


至于bash3中不太好的方法,这里有一个可能有用的参考:

在bash4之前,在bash中没有使用关联数组的好方法。您最好的选择是使用一种解释语言,它实际上支持这些东西,比如awk。另一方面,bash4确实支持它们

至于bash3中不太好的方法,这里有一个可能有用的参考:

bash4 Bash4本机支持此功能。确保脚本的hashbang是
#/usr/bin/env bash
#/bin/bash
这样就不会使用
sh
。确保直接执行脚本,或者使用
bash脚本执行
script
。(不使用Bash实际执行Bash脚本的情况确实发生了,这将非常令人困惑!)

通过执行以下操作来声明关联数组:

declare -A animals
可以使用普通数组赋值运算符将其填充为元素。例如,如果您想拥有
动物[声音(键)]=动物(值)
的地图:

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

declare -A animals=( ["moo"]="cow" ["woof"]="dog")
然后像普通数组一样使用它们。使用

  • 动物['key']='value'
    设置值

  • “${animals[@]}”
    展开值

    get_animal {
        echo "$(expr "$animals" : ".*,$1:\([^,]*\),.*")"
    }
    
  • “${!动物[@]}”
    (注意
    )以展开键

别忘了引用它们:

echo "${animals[moo]}"
for sound in "${!animals[@]}"; do echo "$sound - ${animals[$sound]}"; done
Bash 3 在Bash4之前,没有关联数组不要使用
eval
来模拟它们
。避免像瘟疫一样使用
eval
,因为它是shell脚本的瘟疫。最重要的原因是
eval
将数据视为可执行代码(还有许多其他原因)

首先,考虑升级到BASH 4。这将使整个过程更加容易

如果有无法升级的原因,
declare
是一个更安全的选择。它不像bash代码那样对数据求值,比如
eval
,因此不允许如此轻松地进行任意代码注入

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

首先是间接的

$ animals_moo=cow; sound=moo; i="animals_$sound"; echo "${!i}"
cow
#!/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]##*:}"
其次,
声明

$ 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
会将它创建的变量转换为该函数的局部范围,这意味着我们不能使用它访问或修改全局数组。(在Bash4中,您可以使用declare-g来声明全局变量,但在Bash4中,您可以首先使用关联数组,从而避免这种变通方法。)

总结:

  • 升级到bash4并对关联数组使用
    declare-A
  • 如果无法升级,请使用
    declare
    选项
  • 考虑改用
    awk
    ,完全避免这个问题
Bash 4 Bash4本机支持此功能。确保脚本的hashbang是
#/usr/bin/env bash
#/bin/bash
这样就不会使用
sh
。确保直接执行脚本,或者使用
bash脚本执行
script
。(不使用Bash实际执行Bash脚本的情况确实发生了,这将非常令人困惑!)

通过执行以下操作来声明关联数组:

declare -A animals
可以使用普通数组赋值运算符将其填充为元素。例如,如果你想
#!/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]##*:}"
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
animals=( ["moo"]="cow" )
# 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[@])
animals=",moo:cow,woof:dog,"
get_animal {
    echo "$(expr "$animals" : ".*,$1:\([^,]*\),.*")"
}
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
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
unset statusCheck; declare -A statusCheck
#!/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