Rei*_*ica 520 bash dictionary associative-array hashtable
什么是相似的Python字典,但在Bash中(应该适用于OS X和Linux).
lhu*_*ath 865
Bash 4原生支持此功能.确保你的脚本hashbang是#!/usr/bin/env bash或#!/bin/bash或其他任何引用sh,而不是script.确保你正在执行你的脚本,而不是做一些愚蠢的事情bash script会导致你的animal[sound(key)] = animal(value)hashbang被忽略.这是基本的东西,但是很多人都在努力,因此重新迭代.
通过执行以下操作声明关联数组:
declare -A animals
Run Code Online (Sandbox Code Playgroud)
您可以使用常规数组赋值运算符填充元素:
animals=( ["moo"]="cow" ["woof"]="dog")
Run Code Online (Sandbox Code Playgroud)
或合并它们:
declare -A animals=( ["moo"]="cow" ["woof"]="dog")
Run Code Online (Sandbox Code Playgroud)
然后像普通数组一样使用它们. animals['key']='value'设置值,"${animals[@]}"扩展值,"${!animals[@]}"(注意!)扩展键.别忘了引用它们:
echo "${animals[moo]}"
for sound in "${!animals[@]}"; do echo "$sound - ${animals[$sound]}"; done
Run Code Online (Sandbox Code Playgroud)
在bash 4之前,你没有关联数组. 不要eval用来模仿它们.你必须避免像瘟疫一样的eval,因为它是 shell脚本的瘟疫.最重要的原因是您不希望将数据视为可执行代码(还有许多其他原因).
首先:只考虑升级到bash 4.认真. 现在的未来,停止生活在过去,并通过强迫你的代码上的愚蠢破碎和丑陋的黑客而遭受它的痛苦,每个可怜的灵魂都坚持维持它.
如果你有一些愚蠢的借口,为什么你" 无法升级 ",eval是一个更安全的选择.它没有像bash代码一样评估数据eval,因此它不允许任意代码注入.
让我们通过介绍概念来准备答案:
第一,间接(严肃;从不使用这个,除非你患有精神病或者有其他不好的借口来写黑客).
$ animals_moo=cow; sound=moo; i="animals_$sound"; echo "${!i}"
cow
Run Code Online (Sandbox Code Playgroud)
其次,declare:
$ sound=moo; animal=cow; declare "animals_$sound=$animal"; echo "$animals_moo"
cow
Run Code Online (Sandbox Code Playgroud)
将他们聚集在一起:
# Set a value:
declare "array_$index=$value"
# Get a value:
arrayGet() {
local array=$1 index=$2
local i="${array}_$index"
printf '%s' "${!i}"
}
Run Code Online (Sandbox Code Playgroud)
我们来使用它:
$ sound=moo
$ animal=cow
$ declare "animals_$sound=$animal"
$ arrayGet animals "$sound"
cow
Run Code Online (Sandbox Code Playgroud)
注意:eval不能放入功能.declare在bash函数内部的任何使用都会将它在本地创建的变量转换为该函数的范围,这意味着我们无法使用它来访问或修改全局数组.(在bash 4中你可以使用declare -g来声明全局变量 - 但是在bash 4中,你应该首先使用关联数组,而不是这个hack.)
升级到bash 4并使用declare.如果你不能,可以考虑完全切换到declare如上所述的丑陋黑客之前.绝对要远离declare -Ahackery.
Bub*_*off 116
有参数替换,虽然它也可能是非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]##*:}"
Run Code Online (Sandbox Code Playgroud)
BASH 4的方式当然更好,但是如果你需要一个黑客......只有一个黑客会做.您可以使用类似的技术搜索数组/哈希.
akt*_*ivb 73
这就是我在这里寻找的:
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
Run Code Online (Sandbox Code Playgroud)
这对bash 4.1.5不起作用:
animals=( ["moo"]="cow" )
Run Code Online (Sandbox Code Playgroud)
Al *_* P. 24
您可以进一步修改hput()/ hget()接口,以便命名哈希,如下所示:
hput() {
eval "$1""$2"='$3'
}
hget() {
eval echo '${'"$1$2"'#hash}'
}
Run Code Online (Sandbox Code Playgroud)
然后
hput capitals France Paris
hput capitals Netherlands Amsterdam
hput capitals Spain Madrid
echo `hget capitals France` and `hget capitals Netherlands` and `hget capitals Spain`
Run Code Online (Sandbox Code Playgroud)
这使您可以定义其他不冲突的地图(例如,"资本城市"进行国家查找的"rcapitals").但是,无论哪种方式,我认为你会发现这一切都非常糟糕,性能明智.
如果你真的想要快速哈希查找,那么一个可怕的,可怕的黑客实际上工作得非常好.就是这样:将你的键/值写入一个临时文件,每行一个,然后使用'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`
Run Code Online (Sandbox Code Playgroud)
lov*_*soa 16
文件系统是可以用作哈希映射的树结构.您的哈希表将是一个临时目录,您的密钥将是文件名,您的值将是文件内容.优点是它可以处理巨大的哈希映射,并且不需要特定的shell.
hashtable=$(mktemp -d)
echo $value > $hashtable/$key
value=$(< $hashtable/$key)
当然,它的速度慢,但不是那么慢.我在我的机器上测试了它,使用SSD和btrfs,每秒大约有3000个元素读/写.
Dig*_*oss 14
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`
Run Code Online (Sandbox Code Playgroud)
$ sh hash.sh
Paris and Amsterdam and Madrid
Run Code Online (Sandbox Code Playgroud)
Asy*_*abs 11
考虑使用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
Run Code Online (Sandbox Code Playgroud)
我同意@lhunath和其他人认为关联数组是Bash 4的方法.如果你坚持使用Bash 3(OSX,你无法更新的旧发行版),你可以使用expr,它应该是无处不在的,一个字符串和正则表达式.我喜欢它,特别是当字典不是太大时.
将地图写为字符串(注意分隔符','也在开头和结尾)
animals=",moo:cow,woof:dog,"
Run Code Online (Sandbox Code Playgroud)使用正则表达式提取值
get_animal {
echo "$(expr "$animals" : ".*,$1:\([^,]*\),.*")"
}
Run Code Online (Sandbox Code Playgroud)拆分字符串以列出项目
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
}
Run Code Online (Sandbox Code Playgroud)现在你可以使用它:
$ animal = get_animal "moo"
cow
$ get_animal_items
cow likes to moo
dog likes to woof
Run Code Online (Sandbox Code Playgroud)
我真的很喜欢Al P的答案,但希望廉价执行的独特性,所以我更进一步 - 使用目录.有一些明显的限制(目录文件限制,文件名无效)但它应该适用于大多数情况.
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
Run Code Online (Sandbox Code Playgroud)
它在我的测试中也表现得更好一些.
$ 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
Run Code Online (Sandbox Code Playgroud)
只是想我会投入.干杯!
编辑:添加hdestroy()
一位同事刚刚提到了这个话题。我已经在 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)
Run Code Online (Sandbox Code Playgroud)
它不是双向的,内置的方式要好得多,但无论如何都不应该真正使用。Bash 是为了快速一次性的,这样的事情应该很少涉及可能需要哈希的复杂性,除了你~/.bashrc和朋友之外。