当前位置:  开发笔记 > 编程语言 > 正文

Shell脚本中的关联数组

如何解决《Shell脚本中的关联数组》经验,为你挑选了7个好方法。

我们需要一个模拟关联数组的脚本或类似于Shell Scripting的数据结构的Map,任何主体?



1> Brian Campbe..:

如果可移植性不是您主要关注的另一个选择,则使用内置于shell的关联数组.这应该适用于bash 4.0(现在大多数主要发行版都可用,但不是在OS X上,除非你自己安装),ksh和zsh:

declare -A newmap
newmap[name]="Irfan Zulfiqar"
newmap[designation]=SSE
newmap[company]="My Own Company"

echo ${newmap[company]}
echo ${newmap[name]}

根据shell的不同,您可能需要typeset -A newmap代替declare -A newmap,或者在某些情况下可能根本不需要.


@Jer它很晦涩难懂,但要确定shell中是否设置了变量,可以使用`test -z $ {variable + x}`(```无关紧要,可以是任何字符串).对于Bash中的关联数组,你可以做类似的事情; 使用`test -z $ {map [key] + x}`.

2> Bubnoff..:

另一种非bash 4种方式.

#!/bin/bash

# A pretend Python dictionary with bash 3 
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

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

你也可以在那里抛出一个if语句进行搜索.if [[$ var =〜/ blah /]].管他呢.


当您确实没有Bash 4时,此方法非常有用。但我认为以这种方式获取VALUE的行会更安全:VALUE = $ {animal#*:}。仅使用一个#字符,匹配将停止在第一个“:”上。这也允许值包含“:”。

3> Brian Campbe..:

我认为你需要退一步思考一下地图或关联数组究竟是什么.它只是一种存储给定键值的方法,可以快速有效地恢复该值.您可能还希望能够遍历密钥以检索每个键值对,或删除键及其关联值.

现在,考虑一下您在shell脚本中一直使用的数据结构,甚至只是在没有编写脚本的shell中,它具有这些属性.难倒?这是文件系统.

真的,你需要在shell编程中拥有一个关联数组就是一个临时目录.mktemp -d是你的关联数组构造函数:

prefix=$(basename -- "$0")
map=$(mktemp -dt ${prefix})
echo >${map}/key somevalue
value=$(cat ${map}/key)

如果您不想使用echocat,您可以随时写一些小包装; 这些是以Irfan为模型的,虽然它们只是输出值而不是设置任意变量,如$value:

#!/bin/sh

prefix=$(basename -- "$0")
mapdir=$(mktemp -dt ${prefix})
trap 'rm -r ${mapdir}' EXIT

put() {
  [ "$#" != 3 ] && exit 1
  mapname=$1; key=$2; value=$3
  [ -d "${mapdir}/${mapname}" ] || mkdir "${mapdir}/${mapname}"
  echo $value >"${mapdir}/${mapname}/${key}"
}

get() {
  [ "$#" != 2 ] && exit 1
  mapname=$1; key=$2
  cat "${mapdir}/${mapname}/${key}"
}

put "newMap" "name" "Irfan Zulfiqar"
put "newMap" "designation" "SSE"
put "newMap" "company" "My Own Company"

value=$(get "newMap" "company")
echo $value

value=$(get "newMap" "name")
echo $value

编辑:这种方法实际上比使用提问者建议的sed的线性搜索快得多,而且更健壮(它允许键和值包含 - ,=,space,qnd":SP:").它使用文件系统的事实并没有使它变慢; 除非你打电话sync,否则这些文件实际上永远不会保证写入磁盘; 对于这样的临时文件,如果生命周期很短,那么它们中的许多文件永远不会被写入磁盘.

我使用以下驱动程序对Irfan的代码,Jerry修改Irfan的代码和我的代码做了一些基准测试:

#!/bin/sh

mapimpl=$1
numkeys=$2
numvals=$3

. ./${mapimpl}.sh    #/ <- fix broken stack overflow syntax highlighting

for (( i = 0 ; $i < $numkeys ; i += 1 ))
do
    for (( j = 0 ; $j < $numvals ; j += 1 ))
    do
        put "newMap" "key$i" "value$j"
        get "newMap" "key$i"
    done
done

结果:

    $ time ./driver.sh irfan 10 5

    real    0m0.975s
    user    0m0.280s
    sys     0m0.691s

    $ time ./driver.sh brian 10 5

    real    0m0.226s
    user    0m0.057s
    sys     0m0.123s

    $ time ./driver.sh jerry 10 5

    real    0m0.706s
    user    0m0.228s
    sys     0m0.530s

    $ time ./driver.sh irfan 100 5

    real    0m10.633s
    user    0m4.366s
    sys     0m7.127s

    $ time ./driver.sh brian 100 5

    real    0m1.682s
    user    0m0.546s
    sys     0m1.082s

    $ time ./driver.sh jerry 100 5

    real    0m9.315s
    user    0m4.565s
    sys     0m5.446s

    $ time ./driver.sh irfan 10 500

    real    1m46.197s
    user    0m44.869s
    sys     1m12.282s

    $ time ./driver.sh brian 10 500

    real    0m16.003s
    user    0m5.135s
    sys     0m10.396s

    $ time ./driver.sh jerry 10 500

    real    1m24.414s
    user    0m39.696s
    sys     0m54.834s

    $ time ./driver.sh irfan 1000 5

    real    4m25.145s
    user    3m17.286s
    sys     1m21.490s

    $ time ./driver.sh brian 1000 5

    real    0m19.442s
    user    0m5.287s
    sys     0m10.751s

    $ time ./driver.sh jerry 1000 5

    real    5m29.136s
    user    4m48.926s
    sys     0m59.336s


这些文件不一定会被写入磁盘; 除非你调用同步,否则操作系统可能会将它们留在内存中.你的代码正在调用sed并进行几次线性搜索,这些搜索都非常慢.我做了一些快速基准测试,我的版本快了5-35倍.
无论如何,"快"和"贝壳"并没有真正结合在一起:当然不是因为我们在"避免微不足道的IO"级别上谈论的那种速度问题.您可以搜索并使用/ dev/shm来保证没有IO.

4> Jerry Penner..:

要添加Irfan的答案,这里是一个更短更快的版本,get()因为它不需要迭代地图内容:

get() {
    mapName=$1; key=$2

    map=${!mapName}
    value="$(echo $map |sed -e "s/.*--${key}=\([^ ]*\).*/\1/" -e 's/:SP:/ /g' )"
}


分叉子壳和sed几乎不是最佳的.Bash4原生支持这个,bash3有更好的选择.

5> DigitalRoss..:
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



6> lhunath..:

Bash4原生支持这一点.不要使用grepeval,他们是最丑陋的黑客.

有关示例代码的详细详细答案,请参阅:https: //stackoverflow.com/questions/3467959



7> 小智..:
####################################################################
# Bash v3 does not support associative arrays
# and we cannot use ksh since all generic scripts are on bash
# Usage: map_put map_name key value
#
function map_put
{
    alias "${1}$2"="$3"
}

# map_get map_name key
# @return value
#
function map_get
{
    alias "${1}$2" | awk -F"'" '{ print $2; }'
}

# map_keys map_name 
# @return map keys
#
function map_keys
{
    alias -p | grep $1 | cut -d'=' -f1 | awk -F"$1" '{print $2; }'
}

例:

mapName=$(basename $0)_map_
map_put $mapName "name" "Irfan Zulfiqar"
map_put $mapName "designation" "SSE"

for key in $(map_keys $mapName)
do
    echo "$key = $(map_get $mapName $key)
done

推荐阅读
雯颜哥_135
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有