代码之家  ›  专栏  ›  技术社区  ›  Genki

Bash:如何从多维数组中的命名键获取值

  •  0
  • Genki  · 技术社区  · 1 年前

    当试图从动态引用的数组中获取命名键时,我遇到了一个“错误替换”错误。

    GNU Bash版本4.1.2(2)

    作品:

    declare -rA DBONE=([system_name]='ABC' [dbname]='abc_database' [dbusername]='user1')
    declare -rA DBTWO=([system_name]='DEF' [dbname]='def_database' [dbusername]='user2')
    declare -rA DBTHREE=([system_name]='XYZ' [dbname]='xyz_database' [dbusername]='user3')
    
    # works: get value when using the real name of the array, e.g. "DBTWO"
    echo ${DBTWO["dbname"]}
    

    输出:“def_database”(正确)

    错误:替换错误

    declare -rA DBONE=([system_name]='ABC' [dbname]='abc_database' [dbusername]='user1')
    declare -rA DBTWO=([system_name]='DEF' [dbname]='def_database' [dbusername]='user2')
    declare -rA DBTHREE=([system_name]='XYZ' [dbname]='xyz_database' [dbusername]='user3')
    
    # create an array of the arrays
    declare -a databases=(DBONE DBTWO DBTHREE)
    
    # get the first array (e.g. DBONE)
    database="${databases[0]}"
    echo "The dbname is ${$database[dbname]}"
    echo "The dbname is ${$database}[dbname]"
    

    输出

    ./myscript.sh: ${$database[dbname]}: bad substitution
    ./myscript.sh: ${$database}[dbname]: bad substitution
    

    最终,我的最终目标是在数组上循环,如下所示:

    # create an array of the arrays
    declare -a databases=(DBONE DBTWO DBTHREE)
    for ((i=0; i<"${#databases[*]}"; i++)); do
        database="${databases[$i]}"
        dbname="${database[dbname]}"
        echo "The database is $database, and the dbname is $dbname"
    done    
    

    注意:我的bash版本(GNU bash版本4.1.2(2))不支持 declare "-n" 。它允许以下选项: declare: usage: declare [-aAfFilrtux] [-p] [name[=value] ...]

    1 回复  |  直到 1 年前
        1
  •  6
  •   Philippe    1 年前

    您可以使用间接引用:

    declare -rA DBONE=([system_name]='ABC' [dbname]='abc_database' [dbusername]='user1')
    declare -rA DBTWO=([system_name]='DEF' [dbname]='def_database' [dbusername]='user2')
    declare -rA DBTHREE=([system_name]='XYZ' [dbname]='xyz_database' [dbusername]='user3')
    
    declare -a databases=(DBONE DBTWO DBTHREE)
    for ((i=0; i<"${#databases[*]}"; i++)); do
        database="${databases[$i]}"
        ref="$database[dbname]"
        dbname="${!ref}"
        echo "The database is $database, and the dbname is $dbname"
    done
    
        2
  •  1
  •   Paul Hodges    1 年前

    bash 不做多维数组,但关联数组允许任意复杂的字符串作为键,所以如果你只重构一点设计,实际上也是一样的。

    只需为每个字段使用一个标准名称,并附加一个关键字来区分它们。

    #! /usr/bin/env bash
    
    declare -rA db=( [system_name:DB1]='ABC' [dbname:DB1]='abc_database' [dbusername:DB1]='user1'
                     [system_name:DB2]='DEF' [dbname:DB2]='def_database' [dbusername:DB2]='user2'
                     [system_name:DB3]='XYZ' [dbname:DB3]='xyz_database' [dbusername:DB3]='user3' )
    
    declare -ra databases=( DB1 DB2 DB3 )
    
    setdata(){
      local dbkey="$1";
      declare -g dbname=${db["dbname:$dbkey"]}
      declare -g username=${db["dbusername:$dbkey"]}
      declare -g system_name=${db["system_name:$dbkey"]}
    }
    
    echo "With key variables -"
    field=dbname; database="${databases[0]}"; key="$field:$database"
    printf '\n%s\n' "The $field for $database is ${db[$key]}"
    
    printf '\nRaw access -\n'
    printf '\n%s\n' "The system_name for ${databases[1]} is ${db[system_name:${databases[1]}]}"
    
    printf '\nLooping with function:\n\n'
    
    for database in "${databases[@]}"
    do setdata $database
       echo "$database: dbname=$dbname username=$username system_name=$system_name"
    done
    echo
    

    使用中:

    $: ./tst
    With key variables -
    
    The dbname for DB1 is abc_database
    
    Raw access -
    
    The system_name for DB2 is DEF
    
    Looping with function:
    
    DB1: dbname=abc_database username=user1 system_name=ABC
    DB2: dbname=def_database username=user2 system_name=DEF
    DB3: dbname=xyz_database username=user3 system_name=XYZ
    
        3
  •  0
  •   glenn jackman    1 年前

    如果您可以使用较新的bash版本,那么使用namerefs将变得非常容易:

    for name in DB{ONE,TWO,THREE}; do
      declare -n ref="$name"
      printf 'array: %s; system: %s; db: %s\n' "$name" "${ref[system_name]}" "${ref[dbname]}"
    done
    
    array: DBONE; system: ABC; db: abc_database
    array: DBTWO; system: DEF; db: def_database
    array: DBTHREE; system: XYZ; db: xyz_database
    

    我不记得namerefs是在4.4版还是5.0版中引入的