【问题标题】:Associative arrays in Shell scriptsShell 脚本中的关联数组
【发布时间】:2010-10-15 21:18:58
【问题描述】:

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

【问题讨论】:

    标签: bash shell hashtable associative-array


    【解决方案1】:

    现在回答这个问题。

    以下脚本模拟 shell 脚本中的关联数组。它简单易懂。

    Map 只不过是一个永无止境的字符串,它的 keyValuePair 保存为 --name=Irfan --designation=SSE --company=My:SP:Own:SP:Company

    空格被替换为 ':SP:' 值

    put() {
        if [ "$#" != 3 ]; then exit 1; fi
        mapName=$1; key=$2; value=`echo $3 | sed -e "s/ /:SP:/g"`
        eval map="\"\$$mapName\""
        map="`echo "$map" | sed -e "s/--$key=[^ ]*//g"` --$key=$value"
        eval $mapName="\"$map\""
    }
    
    get() {
        mapName=$1; key=$2; valueFound="false"
    
        eval map=\$$mapName
    
        for keyValuePair in ${map};
        do
            case "$keyValuePair" in
                --$key=*) value=`echo "$keyValuePair" | sed -e 's/^[^=]*=//'`
                          valueFound="true"
            esac
            if [ "$valueFound" == "true" ]; then break; fi
        done
        value=`echo $value | sed -e "s/:SP:/ /g"`
    }
    
    put "newMap" "name" "Irfan Zulfiqar"
    put "newMap" "designation" "SSE"
    put "newMap" "company" "My Own Company"
    
    get "newMap" "company"
    echo $value
    
    get "newMap" "name"
    echo $value
    

    编辑:刚刚添加了另一种方法来获取所有密钥。

    getKeySet() {
        if [ "$#" != 1 ]; 
        then 
            exit 1; 
        fi
    
        mapName=$1; 
    
        eval map="\"\$$mapName\""
    
        keySet=`
               echo $map | 
               sed -e "s/=[^ ]*//g" -e "s/\([ ]*\)--/\1/g"
              `
    }
    

    【讨论】:

    • eval'ing 数据好像是 bash 代码,更重要的是:你没有正确引用它。两者都会导致大量错误和任意代码注入。
    【解决方案2】:

    要添加到Irfan's answer,这里有一个更短、更快的get() 版本,因为它不需要对地图内容进行迭代:

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

    【讨论】:

    • 分叉子外壳和 sed 几乎不是最佳选择。 bash4 本身就支持这一点,而 bash3 有更好的选择。
    【解决方案3】:

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

    现在,考虑一下您在 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
    

    edit:这种方法实际上比提问者建议的使用 sed 的线性搜索要快很多,而且更健壮(它允许键和值包含 -、=、空格、 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
    

    结果:

    $ 时间 ./driver.sh irfan 10 5 实际0m0.975s 用户 0m0.280s 系统 0m0.691s $ 时间 ./driver.sh 布赖恩 10 5 实际0m0.226s 用户 0m0.057s 系统 0m0.123s $ 时间 ./driver.sh 杰里 10 5 实际0m0.706s 用户 0m0.228s 系统 0m0.530s $ 时间 ./driver.sh irfan 100 5 真正的 0m10.633s 用户 0m4.366s 系统 0m7.127s $ 时间 ./driver.sh 布赖恩 100 5 真实0m1.682s 用户 0m0.546s 系统 0m1.082s $ 时间 ./driver.sh 杰里 100 5 真正的 0m9.315s 用户 0m4.565s 系统 0m5.446s $ 时间 ./driver.sh irfan 10 500 真正的 1m46.197s 用户 0m44.869s 系统 1m12.282s $ 时间 ./driver.sh 布赖恩 10 500 真正的 0m16.003s 用户 0m5.135s 系统 0m10.396s $ 时间 ./driver.sh 杰里 10 500 实际 1m24.414s 用户 0m39.696s 系统 0m54.834s $ 时间 ./driver.sh irfan 1000 5 真正的 4m25.145s 用户 3m17.286s 系统 1m21.490s $ 时间 ./driver.sh 布赖恩 1000 5 真正的 0m19.442s 用户 0m5.287s 系统 0m10.751s $ 时间 ./driver.sh 杰里 1000 5 真正的 5m29.136s 用户 4m48.926s 系统 0m59.336s

    【讨论】:

    • 我认为你不应该为地图使用文件系统,基本上使用 IO 来做一些你可以在内存中相当快地完成的事情。
    • 文件不一定会被写入磁盘;除非您调用同步,否则操作系统可能只会将它们留在内存中。您的代码正在调用 sed 并进行几次线性搜索,这些搜索都非常慢。我做了一些快速基准测试,我的版本快了 5-35 倍。
    • 另一方面,bash4 的本机数组是一种明显更好的方法,在 bash3 中,您仍然可以通过使用声明和间接将所有内容保持在磁盘之外,而无需分叉。
    • "fast" 和 "shell" 并没有真正结合在一起:当然不是因为我们在“避免微小的 IO”级别上讨论的那种速度问题。您可以搜索并使用 /dev/shm 来保证没有 IO。
    • 这个解决方案让我吃惊,简直太棒了。 2016 年仍然如此。这确实应该是公认的答案。
    【解决方案4】:

    如果可移植性不是您主要关心的问题,另一种选择是使用内置于 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,或者在某些情况下可能根本不需要。

    【讨论】:

    • 感谢您发布答案,我认为这对于使用 bash 4.0 或更高版本的人来说是最好的方法。
    • 我会添加一些小工具以确保设置了 BASH_VERSION 并且 >= 4。是的,BASH 4 真的非常酷!
    • 我正在使用这样的东西。 “捕获”数组索引/下标不存在的错误的最佳方法是什么?例如,如果我将下标作为命令行选项,而用户打错字并输入“designatio”怎么办?我收到“错误的数组下标”错误,但不知道如何在数组查找时验证输入,如果可能的话?
    • @Jer 这很模糊,但是要确定是否在 shell 中设置了变量,您可以使用test -z ${variable+x}x 无关紧要,可以是任何字符串)。对于 Bash 中的关联数组,您可以执行类似操作;使用test -z ${map[key]+x}
    【解决方案5】:

    如前所述,我发现最好的方法是将键/值写入文件,然后使用 grep/awk 检索它们。这听起来像是各种不必要的 IO,但磁盘缓存会发挥作用并使其非常高效 - 比尝试使用上述方法之一将它们存储在内存中要快得多(如基准所示)。

    这是我喜欢的一种快速、干净的方法:

    hinit() {
        rm -f /tmp/hashmap.$1
    }
    
    hput() {
        echo "$2 $3" >> /tmp/hashmap.$1
    }
    
    hget() {
        grep "^$2 " /tmp/hashmap.$1 | awk '{ print $2 };'
    }
    
    hinit capitols
    hput capitols France Paris
    hput capitols Netherlands Amsterdam
    hput capitols Spain Madrid
    
    echo `hget capitols France` and `hget capitols Netherlands` and `hget capitols Spain`
    

    如果您想对每个键强制执行单值,您还可以在 hput() 中执行一些 grep/sed 操作。

    【讨论】:

      【解决方案6】:

      Bash4 本身就支持这一点。不要使用grepeval,它们是最丑陋的黑客。

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

      【讨论】:

        【解决方案7】:

        另一种非 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 语句进行搜索。如果 [[ $var =~ /blah/ ]]。 或其他。

        【讨论】:

        • 当您确实没有 Bash 4 时,此方法很好。但我认为这样获取 VALUE 的行会更安全:VALUE=${animal#*:}。只有一个 # 字符,匹配将在第一个“:”处停止。这也允许值包含“:”。
        • @Ced-le-pingouin ~ 这是一个很好的观点!我没听懂。我已编辑我的帖子以反映您提出的改进建议。
        • 这是一个使用 BASH 参数替换的关联数组的非常骇人听闻的模拟。 “key” param-sub 替换冒号 before 的所有内容,而 value 模式替换冒号 after 的所有内容。类似于正则表达式通配符匹配。所以 NOT 是一个真正的关联数组。除非您需要一种易于理解的方法来在 BASH 3 或更低版本中执行类似哈希/关联数组的功能,否则不推荐使用。它虽然有效!更多信息:tldp.org/LDP/abs/html/parameter-substitution.html#PSOREX2
        • 这没有实现关联数组,因为它没有提供通过键查找项目的方法。它只提供了一种从数字索引中查找每个键(和值)的方法。 (可以通过遍历数组按键找到一个项目,但这不是关联数组所需要的。)
        • @EricPostpischil 是的。这只是一个黑客。它允许人们在设置中使用熟悉的语法,但仍然需要像您所说的那样遍历数组。我在之前的评论中试图明确表示它绝对不是关联数组,如果你有其他选择,我什至不推荐它。在我看来,唯一有利于它的一点是,对于熟悉 Python 等其他语言的人来说,它易于编写和使用。如果您确实想在 BASH 3 中实现关联数组,那么您可能需要稍微回顾一下您的步骤。
        【解决方案8】:

        很遗憾我之前没有看到这个问题 - 我写了库shell-framework,其中包含地图(关联数组)。可以找到它的最新版本here

        例子:

        #!/bin/bash 
        #include map library
        shF_PATH_TO_LIB="/usr/lib/shell-framework"
        source "${shF_PATH_TO_LIB}/map"
        
        #simple example get/put
        putMapValue "mapName" "mapKey1" "map Value 2"
        echo "mapName[mapKey1]: $(getMapValue "mapName" "mapKey1")"
        
        #redefine old value to new
        putMapValue "mapName" "mapKey1" "map Value 1"
        echo "after change mapName[mapKey1]: $(getMapValue "mapName" "mapKey1")"
        
        #add two new pairs key/values and print all keys
        putMapValue "mapName" "mapKey2" "map Value 2"
        putMapValue "mapName" "mapKey3" "map Value 3"
        echo -e "mapName keys are \n$(getMapKeys "mapName")"
        
        #create new map
        putMapValue "subMapName" "subMapKey1" "sub map Value 1"
        putMapValue "subMapName" "subMapKey2" "sub map Value 2"
        
        #and put it in mapName under key "mapKey4"
        putMapValue "mapName" "mapKey4" "subMapName"
        
        #check if under two key were placed maps
        echo "is map mapName[mapKey3]? - $(if isMap "$(getMapValue "mapName" "mapKey3")" ; then echo Yes; else echo No; fi)"
        echo "is map mapName[mapKey4]? - $(if isMap "$(getMapValue "mapName" "mapKey4")" ; then echo Yes; else echo No; fi)"
        
        #print map with sub maps
        printf "%s\n" "$(mapToString "mapName")"
        

        【讨论】:

        • 这些链接已失效。
        【解决方案9】:
        ####################################################################
        # 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
        

        【讨论】:

          【解决方案10】:

          我修改了 Vadim 的解决方案:

          ####################################################################
          # 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 {
              if type -p "${1}$2"
                  then
                      alias "${1}$2" | awk -F "'" '{ print $2; }';
              fi
          }
          
          # map_keys map_name 
          # @return map keys
          #
          function map_keys
          {
              alias -p | grep $1 | cut -d'=' -f1 | awk -F"$1" '{print $2; }'
          }
          

          更改为 map_get 以防止它在您请求不存在的密钥时返回错误,尽管副作用是它也会默默地忽略丢失的地图,但它更适合我的用例因为我只是想检查一个键以跳过循环中的项目。

          【讨论】:

            【解决方案11】:

            几年前,我为 bash 编写了脚本库,它支持关联数组以及其他功能(日志记录、配置文件、对命令行参数的扩展支持、生成帮助、单元测试等)。该库包含关联数组的包装器并自动切换到适当的模型(对于 bash4 是内部的,对于以前的版本是模拟的)。它被称为 shell-framework 并托管在 origo.ethz.ch 但今天该资源已关闭。如果有人仍然需要它,我可以与您分享。

            【讨论】:

            • 可能值得把它贴在 github 上
            【解决方案12】:

            对于 Bash 3,有一个特殊情况有一个很好且简单的解决方案:

            如果您不想处理大量变量,或者键只是无效的变量标识符,并且您的数组保证少于 256 个项目,您可以滥用函数返回值。该解决方案不需要任何子shell,因为该值可以作为变量随时使用,也不需要任何迭代,从而使性能尖叫。它的可读性也很强,几乎就像 Bash 4 版本一样。

            这是最基本的版本:

            hash_index() {
                case $1 in
                    'foo') return 0;;
                    'bar') return 1;;
                    'baz') return 2;;
                esac
            }
            
            hash_vals=("foo_val"
                       "bar_val"
                       "baz_val");
            
            hash_index "foo"
            echo ${hash_vals[$?]}
            

            请记住,在case 中使用单引号,否则会受到通配符的影响。从一开始就对静态/冻结哈希非常有用,但可以从 hash_keys=() 数组编写索引生成器。

            注意,它默认为第一个,所以你可能想留出第零个元素:

            hash_index() {
                case $1 in
                    'foo') return 1;;
                    'bar') return 2;;
                    'baz') return 3;;
                esac
            }
            
            hash_vals=("",           # sort of like returning null/nil for a non existent key
                       "foo_val"
                       "bar_val"
                       "baz_val");
            
            hash_index "foo" || echo ${hash_vals[$?]}  # It can't get more readable than this
            

            警告:现在长度不正确。

            或者,如果你想保持从零开始的索引,你可以保留另一个索引值并防止不存在的键,但它的可读性较差:

            hash_index() {
                case $1 in
                    'foo') return 0;;
                    'bar') return 1;;
                    'baz') return 2;;
                    *)   return 255;;
                esac
            }
            
            hash_vals=("foo_val"
                       "bar_val"
                       "baz_val");
            
            hash_index "foo"
            [[ $? -ne 255 ]] && echo ${hash_vals[$?]}
            

            或者,为了保持长度正确,将索引偏移一:

            hash_index() {
                case $1 in
                    'foo') return 1;;
                    'bar') return 2;;
                    'baz') return 3;;
                esac
            }
            
            hash_vals=("foo_val"
                       "bar_val"
                       "baz_val");
            
            hash_index "foo" || echo ${hash_vals[$(($? - 1))]}
            

            【讨论】:

            • 但是如果我想将hash_index "foo" || echo ${hash_vals[$(($? - 1))]} 分配给一个变量,这个代码output=foo" || echo ${hash_vals[$(($? - 1))]}。所以如果不介意,你能告诉我分配它的正确方法吗?
            【解决方案13】:

            Shell 没有像数据结构那样的内置映射,我使用原始字符串来描述这样的项目:

            ARRAY=(
                "item_A|attr1|attr2|attr3"
                "item_B|attr1|attr2|attr3"
                "..."
            )
            

            提取物品及其属性时:

            for item in "${ARRAY[@]}"
            do
                item_name=$(echo "${item}"|awk -F "|" '{print $1}')
                item_attr1=$(echo "${item}"|awk -F "|" '{print $2}')
                item_attr2=$(echo "${item}"|awk -F "|" '{print $3}')
            
                echo "${item_name}"
                echo "${item_attr1}"
                echo "${item_attr2}"
            done
            

            这似乎并不比其他人的答案聪明,但对于新人来说很容易理解。

            【讨论】:

              【解决方案14】:

              迟到的回复,但考虑以这种方式解决问题,使用内置的 bash read,如以下 ufw 防火墙脚本中的代码 sn-p 所示。这种方法的优点是可以根据需要使用尽可能多的分隔字段集(而不仅仅是 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
              

              【讨论】:

                【解决方案15】:

                您可以使用动态变量名称,并让变量名称像哈希图的键一样工作。

                例如,如果您有一个包含两列名称、信用的输入文件,如下例所示,并且您想对每个用户的收入求和:

                Mary 100
                John 200
                Mary 50
                John 300
                Paul 100
                Paul 400
                David 100
                

                下面的命令将以 map_${person} 的形式使用动态变量作为键来汇总所有内容:

                while read -r person money; ((map_$person+=$money)); done < <(cat INCOME_REPORT.log)
                

                读取结果:

                set | grep map
                

                输出将是:

                map_David=100
                map_John=500
                map_Mary=150
                map_Paul=500
                

                详细阐述这些技术,我正在 GitHub 上开发一个功能,就像 HashMap 对象shell_map

                为了创建“HashMap 实例”,shell_map 函数 能够以不同的名称创建自身的副本。每个新的函数副本都有一个不同的 $FUNCNAME 变量。然后使用 $FUNCNAME 为每个 Map 实例创建一个命名空间。

                映射键是全局变量,格式为 $FUNCNAME_DATA_$KEY,其中 $KEY 是添加到映射的键。这些变量是dynamic variables

                下面我将放一个简化版本,以便您可以用作示例。

                #!/bin/bash
                
                shell_map () {
                    local METHOD="$1"
                
                    case $METHOD in
                    new)
                        local NEW_MAP="$2"
                
                        # loads shell_map function declaration
                        test -n "$(declare -f shell_map)" || return
                
                        # declares in the Global Scope a copy of shell_map, under a new name.
                        eval "${_/shell_map/$2}"
                    ;;
                    put)
                        local KEY="$2"  
                        local VALUE="$3"
                
                        # declares a variable in the global scope
                        eval ${FUNCNAME}_DATA_${KEY}='$VALUE'
                    ;;
                    get)
                        local KEY="$2"
                        local VALUE="${FUNCNAME}_DATA_${KEY}"
                        echo "${!VALUE}"
                    ;;
                    keys)
                        declare | grep -Po "(?<=${FUNCNAME}_DATA_)\w+((?=\=))"
                    ;;
                    name)
                        echo $FUNCNAME
                    ;;
                    contains_key)
                        local KEY="$2"
                        compgen -v ${FUNCNAME}_DATA_${KEY} > /dev/null && return 0 || return 1
                    ;;
                    clear_all)
                        while read var; do  
                            unset $var
                        done < <(compgen -v ${FUNCNAME}_DATA_)
                    ;;
                    remove)
                        local KEY="$2"
                        unset ${FUNCNAME}_DATA_${KEY}
                    ;;
                    size)
                        compgen -v ${FUNCNAME}_DATA_${KEY} | wc -l
                    ;;
                    *)
                        echo "unsupported operation '$1'."
                        return 1
                    ;;
                    esac
                }
                

                用法:

                shell_map new credit
                credit put Mary 100
                credit put John 200
                for customer in `credit keys`; do 
                    value=`credit get $customer`       
                    echo "customer $customer has $value"
                done
                credit contains_key "Mary" && echo "Mary has credit!"
                

                【讨论】:

                  【解决方案16】:

                  添加另一个选项,如果 jq 可用:

                  export NAMES="{
                    \"Mary\":\"100\",
                    \"John\":\"200\",
                    \"Mary\":\"50\",
                    \"John\":\"300\",
                    \"Paul\":\"100\",
                    \"Paul\":\"400\",
                    \"David\":\"100\"
                  }"
                  export NAME=David
                  echo $NAMES | jq --arg v "$NAME" '.[$v]' | tr -d '"' 
                  

                  【讨论】:

                    【解决方案17】:

                    另一种非 bash-4(即 bash 3,Mac 兼容)方式:

                    val_of_key() {
                        case $1 in
                            'A1') echo 'aaa';;
                            'B2') echo 'bbb';;
                            'C3') echo 'ccc';;
                            *) echo 'zzz';;
                        esac
                    }
                    
                    for x in 'A1' 'B2' 'C3' 'D4'; do
                        y=$(val_of_key "$x")
                        echo "$x => $y"
                    done
                    

                    打印:

                    A1 => aaa
                    B2 => bbb
                    C3 => ccc
                    D4 => zzz
                    

                    带有case 的函数就像一个关联数组。不幸的是它不能使用return,所以它必须echo 输出,但这不是问题,除非你是一个避免分叉子shell 的纯粹主义者。

                    【讨论】:

                      猜你喜欢
                      • 2015-11-06
                      • 2020-08-06
                      • 2015-04-23
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-01-22
                      相关资源
                      最近更新 更多