【问题标题】:How to rename an associative array in Bash?如何在 Bash 中重命名关联数组?
【发布时间】:2011-10-03 08:27:39
【问题描述】:

我需要遍历关联数组并将其内容导出到临时数组(并对值执行一些更新)。

然后应该丢弃第一个数组的剩余内容,我想将临时数组分配给原始数组变量。

须藤代码:

declare -A MAINARRAY
declare -A TEMPARRAY
... populate ${MAINARRAY[...]} ...

while something; do     #Drain some values from MAINARRAY to TEMPARRAY
    ${TEMPARRAY["$name"]}=((${MAINARRAY["$name"]} + $somevalue))
done
... other manipulations to TEMPARRAY ...

unset MAINARRAY        #discard left over values that had no update
declare -A MAINARRAY
MAINARRAY=${TEMPARRAY[@]}  #assign updated TEMPARRAY back to MAINARRAY (ERROR HERE)

【问题讨论】:

  • 是的,这就是我现在得到的,我在想应该有一些更有效的方法来重命名变量。
  • 如果只需要变量重命名,[FlorianFeldhaus] 的解决方案将起作用。事实上,在 sed 的帮助下,它可以是一条语句,如下所示:eval $(declare -p old_name |sed 's/old_name=/new_name=/')

标签: bash variables associative-array


【解决方案1】:

这是用于任何类型的 bash 变量的小型复制函数
- 普通标量变量
- 索引数组
- 关联数组

### Function vcp    -VariableCoPy-  
# $1 Name of existing Source-Variable  
# $2 Name for the Copy-Target  
vcp() {
    local var=$(declare -p $1)
    var=${var/declare /declare -g }
    eval "${var/$1=/$2=}"
}

用法,例子:

# declarations
var="  345  89  "
ind_array=(Betty "  345  89  ")
declare -A asso_array=([one]=Harry [two]=Betty [some_signs]=" +*.<\$~,'/ ")  

# produce the copy
vcp var varcopy
vcp ind_array ind_array_copied
vcp asso_array asso_array_2   

# now you can check the equality between original and copy with commands like
# declare -p <name>

结果

--3    1: "${asso_array[@]}"   
(5)       asso_array[one]:        |Harry|   
(11)      asso_array[some_signs]: | +*.<$~,'/ |   
(5)       asso_array[two]:        |Betty|   
--3    4: "${asso_array_2[@]}"   
(5)       asso_array_2[one]:        |Harry|   
(11)      asso_array_2[some_signs]: | +*.<$~,'/ |   
(5)       asso_array_2[two]:        |Betty|   
--2    7: "${ind_array[@]}"   
(5)       ind_array[0]:   |Betty|   
(11)      ind_array[1]:   |  345  89  |   
--2    9: "${ind_array_copied[@]}"   
(5)       ind_array_copied[0]:   |Betty|   
(11)      ind_array_copied[1]:   |  345  89  |   
(11)  11: "$var":   |  345  89  |  
(11)  12: "$varcopy":   |  345  89  |  

【讨论】:

    【解决方案2】:

    这个怎么样(不创建真正的副本,只是指向源变量的链接):

    #!/bin/bash
    declare -A my_array=(["key1"]="value1" ["key2"]="value2")
    declare -n arr=my_array
    arr['LOG_FILE']=/tmp/log.txt
    echo ${arr['key1']}
    echo ${arr['LOG_FILE']}
    

    将打印:

    value1
    /tmp/log.txt
    

    【讨论】:

    【解决方案3】:

    扩展Luca Borrione's cp_hash - 这对我不起作用,我放弃了尝试追踪 eval 扩展问题 - 我在 bash 4.2 之前和之后遇到了差异。在 4.2(something) 之后,这变得容易多了……但这不是向后兼容的。见1和2

    所以我的变体在 4.1.2(1) 和 4.3.46(1) 上进行了测试:

    #!/bin/bash
    ## bash4 due to associative arrays!
    
        function cp_hash() {
            ## REQUIRES you to declare -A $2 in advance.
            local original_hash_name="$1"
            local copy_hash_name="$2"
            #
            # sadly we have no way to identify if you have already declared it, so bull ahead.
            #
            ## store the definition of the old array
            local __copy__=$(declare -p $original_hash_name)
            ## rename the array inside the definition
            __copy__=${__copy__/${original_hash_name}=/__copy__=}
    
            ## for bash 4.2 > we could end here.
            ## declare -A creates local scope variables by default, so add -g
            ## this DOES NOT work prior to 4.2, even w/o -g and w/ a declare outside.
            #    __copy__=${__copy__/${original_hash_name}=/${copy_hash_name}=}
            #    eval ${__copy__/-A/-g -A}
    
            ## for bash4 where we can't do -g, then:
            ## local associative array based on the definition we stored and modified
            eval ${__copy__}
            ## loop through the local copy, and store it in the declared-outside copy.
            for i in "${!__copy__[@]}"
            do
                eval ${copy_hash_name}[$i]=${__copy__[$i]}
            done
        }
    
        declare -A hash
        hash[hello]=world
        hash[ab]=cd
    
        #not required for 4.2+ if you use -g, neither helps nor hinders
        declare -A copy
    
        cp_hash 'hash' 'copy'
    
        echo hash: ${hash[@]}
        echo copy: ${copy[@]}
    
        echo "copy result loop"
        for i in "${!copy[@]}"
        do
            echo "key  : $i | value: ${copy[$i]}"
        done
    

    【讨论】:

    • 如果你的数组包含像's/([a-zA-Z]*).*/\1/p'这样的正则表达式,这将不起作用。
    • @fab 你有什么改进建议可以帮助这种情况吗?
    • 就我而言,Florian Feldhaus 解决方案有效。您也可以使用 n 参数进行声明。声明 -n params=HISTORY_PARAM_MAP
    【解决方案4】:

    这个单行执行关联数组复制:MAINARRAY=TEMPARRAY

    eval $(typeset -A -p TEMPARRAY|sed 's/ TEMPARRAY=/ MAINARRAY=/')
    

    【讨论】:

    • 如果数组包含子字符串 ``TEMPARRAY=`` 是否会导致问题?
    【解决方案5】:

    根据glenn jackman 和ffeldhaus 的建议,您可以构建一个可能会变得方便的函数:

    function cp_hash
    {
        local original_hash_name="$1"
        local copy_hash_name="$2"
    
        local __copy__=$(declare -p $original_hash_name);
        eval declare -A __copy__="${__copy__:$(expr index "${__copy__}" =)}";
    
        for i in "${!__copy__[@]}"
        do
            eval ${copy_hash_name}[$i]=${__copy__[$i]}
        done
    }
    


    用法:

    declare -A copy_hash_name
    cp_hash 'original_hash_name' 'copy_hash_name'
    


    示例:

    declare -A hash
    hash[hello]=world
    hash[ab]=cd
    
    declare -A copy
    cp_hash 'hash' 'copy'
    
    for i in "${!copy[@]}"
    do
        echo "key  : $i | value: ${copy[$i]}"
    done
    


    会输出

    key  : ab | value: cd
    key  : hello | value: world
    

    【讨论】:

    • 尽管 (eval declare -A __copy__="${__copy__:$(expr index "${__copy__}" =)}";) 对我来说很受欢迎——仍然是一个很好的起点。
    • 如果你的数组包含像's/([a-zA-Z]*).*/\1/p'这样的正则表达式,这将不起作用。
    【解决方案6】:

    在 bash 中无法直接复制关联数组。正如已经指出的那样,最好的解决方案可能是遍历数组并逐步复制它。

    还有另一个解决方案I used to pass variables to functions。您可以使用相同的技术来复制关联数组:

    # declare associative array
    declare -A assoc_array=(["key1"]="value1" ["key2"]="value2")
    # convert associative array to string
    assoc_array_string=$(declare -p assoc_array)
    # create new associative array from string
    eval "declare -A new_assoc_array="${assoc_array_string#*=}
    # show array definition
    declare -p new_assoc_array
    

    【讨论】:

    • eval 似乎是必要的。这是为什么呢?
    • ``eval'' 是必要的,因为它声明了新数组。问题是,声明 -A 期望分配中的语句而不是字符串。因此,您需要创建一个字符串并将其作为语句进行评估。
    • 更重要的是declare -A 是绝对必要的——没有它(例如,在使用父环境中的数组而不是创建局部变量的函数中)整个技巧不起作用,因为它会仅使用键 0 和值 (["key1"]="value1" ["key2"]="value2" ) 创建单个项目,因为转义规则在 declare 和 assignment 之间似乎不兼容。
    • 应该选择这个作为答案。它可以工作,而且迭代速度要快得多。
    • 如果你想更好地理解 '#*=',这里有很好的解释:tldp.org/LDP/abs/html/string-manipulation.html('Substring Removal' 部分)
    【解决方案7】:

    对于关联数组,我认为除了迭代之外没有其他方法

    for key in "${!TEMPARRAY[@]}"  # make sure you include the quotes there
    do
      MAINARRAY["$key"]="${TEMPARRAY["$key"]}"
      # or: MAINARRAY+=( ["$key"]="${TEMPARRAY["$key"]}" )
    done
    

    【讨论】:

    • 你如何在函数中做到这一点?你只得到 $1 和 $2 作为变量名(因为现在有办法传递实际变量)?
    【解决方案8】:
    MAINARRAY=( "${TEMPARRAY[@]}" )
    

    【讨论】:

    • 这就是我的想法,但我在该行收到错误:“分配关联数组时必须使用下标”
    猜你喜欢
    • 2015-06-30
    • 2015-04-13
    • 2011-03-07
    • 1970-01-01
    • 2016-04-04
    • 1970-01-01
    • 2023-04-01
    • 2014-08-21
    相关资源
    最近更新 更多