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


当前回答

这里有参数替换,尽管它也可能是非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方式当然更好,但如果你需要一个hack…只有黑客才行。 您可以使用类似的技术搜索数组/散列。

其他回答

我在bash 3中使用动态变量创建hashmap。我在我的回答中解释了它是如何工作的:Shell脚本中的关联数组

您还可以查看shell_map,它是bash 3中实现的HashMap。

Bash 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[@])
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内置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

你可以进一步修改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'根据首都城市进行国家查找)。但是,不管怎样,我想你会发现这一切都很糟糕,就性能而言。

编辑:上面的修改版本,支持非字母数字字符的键

hashKey() {
  # replace non-alphanumeric characters with underscore to make keys valid BASH identifiers
  echo "$1_$2" | sed -E "s/[^a-zA-Z0-9]+/_/g" | sed -E "s/^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+\$//g"
}

hashPut() {
  local KEY=`hashKey $1 $2`
  eval "$KEY"="$3"
}

hashGet() {
  local KEY=`hashKey $1 $2`
  echo "${!KEY}"
}

最后编辑

如果你真的想要快速哈希查找,有一个非常非常糟糕的黑客,它实际上非常有效。它是这样的:将您的键/值写入一个临时文件,每行一个,然后使用'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`