在Bash中迭代另一个数组时使用变量动态命名数组。

3

我有一个数组

FIRST_ARRAY=(NEWYORK CALIFORNIA TEXAS)

一个可以接受州名作为输入的脚本,会返回该州内所有城市的信息。

例如,以下输入将返回:

user@localhost:~$ search NEWYORK cities
newyorkcity
buffalo
albany

user@localhost:~$ search CALIFORNIA cities
sanfrancisco 
paloalto 
losangeles

user@localhost:~$ search TEXAS cities
houston 
dallas 
austin

我希望能够遍历FIRST_ARRAY

for state in ${FIRST_ARRAY[@]}
   do
     cities=`search ${FIRST_ARRAY[state]} cities`
     ARRAY_$state=($cities}
done

在最后,我希望以下数组已被创建,并且它们包含以下值。
ARRAY_NEWYORK=(newyorkcity buffalo albany)
ARRAY_CALIFORNIA=(sanfrancisco paloalto losangeles)
ARRAY_TEXAS=(houston dallas austin)

如果这样可以运行,例如,我想通过以下方式调用动态创建的数组来访问 austin
echo ${ARRAY_TEXAS[2]}

谢谢!


https://dev59.com/VmQn5IYBdhLWcg3wzZ36 - sozkul
不是尝试动态命名变量,而是在循环运行时动态命名数组。 - ARL
也可以参考以下链接:http://unix.stackexchange.com/questions/288886/bash-array-values-like-variables-inside-loop - Sundeep
哪个特定的Bash版本?4.0和4.3都添加了相关功能。 - Charles Duffy
1
(旁注:您不应该使用全大写的变量名;请参阅http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html,第四段,有关POSIX规范的环境变量命名约定[表明小写命名空间保留供应用程序使用,而操作系统和shell工具将使用全大写];由于shell变量和环境变量名称存在于同一命名空间中,因此该约定也适用于shell变量。) - Charles Duffy
顺便提一下,您意识到 ${FIRST_ARRAY[state]} 在所有情况下都会将 state 数值化(为 0),因此总是引用 NEWYORK? - Charles Duffy
1个回答

3

在bash 4.0或更高版本中,使用可用的readarray / mapfile命令,以下内容可以作为简洁且正确的实现:

for state in "${FIRST_ARRAY[@]}"; do
    readarray -t "ARRAY_$state" < <(search "$state" cities)
done

在bash 4.3中,你可以使用一个安全、字面上的代码翻译:
for state in "${FIRST_ARRAY[@]}"; do
    readarray -t cities < <(search "$state" cities)

    # make "dest" a namevar -- an alias -- for your intended destination
    # skip to next state if current state name is invalid
    # otherwise we could be assigning to an utterly different "dest"
    declare -n dest="ARRAY_$state" || continue

    # assign to that namevar
    dest=( "$cities" )

    # and discard it
    unset -n dest
done

在Bash 3.x中,安全地执行此操作需要使用一些printf %q魔法来准备要由eval解析的内容:
for state in "${FIRST_ARRAY[@]}"; do

    # why this, and not array=( $cities )? Try a city with spaces in its name.
    # or look at what happens when you have a "city name" that's a wildcard.
    cities=( )
    while read -r city; do
        cities+=( "$city" )
    done < <(search "$state" cities)

    # generate eval-safe replacement for the cities array
    printf -v cities_str '%q ' "${cities[@]}"

    # extra paranoia: make sure we fail with a parse error rather than doing something
    # unexpected if the state name is not a valid shell variable
    printf -v eval_str 'ARRAY_%q=( %s )' "$state" "$cities_str"

    eval "$eval_str" # and evaluate that
done

原始问题没有提供可测试答案的search实现。对于这个答案,我使用了以下实现:

search() {
  case $1 in
    NEWYORK) printf '%s\n' newyorkcity buffalo albany ;;
    CALIFORNIA) printf '%s\n' sanfrancisco paloalto losangeles ;;
    TEXAS) printf '%s\n' houston dallas austin ;;
  esac
}

有了上述定义,结果可以按以下方式进行验证:
$ declare -p ARRAY_NEWYORK ARRAY_CALIFORNIA ARRAY_TEXAS
declare -a ARRAY_NEWYORK='([0]="newyorkcity" [1]="buffalo" [2]="albany")'
declare -a ARRAY_CALIFORNIA='([0]="sanfrancisco" [1]="paloalto" [2]="losangeles")'
declare -a ARRAY_TEXAS='([0]="houston" [1]="dallas" [2]="austin")'

查尔斯,我应该指出我正在使用BASH 4.1,这可能为你节省了很多麻烦。提前道歉(虽然在老旧环境中了解如何应用此解决方案也很方便)。“search”是一个内部cli,用于查询mongodb。还有其他信息可以提供以获取更多帮助吗? - ARL
你的第一个解决方案非常准确,给了我我所期望的结果。现在我知道倒置数组是解决方案,我可以多了解一些并更好地理解它的工作原理。非常感谢您花费时间和精力提供如此信息丰富的答案! - ARL
非常抱歉——第一段提到的“反转数组”实际上是我后来意识到不必要并删除的修正案的一部分。(这将提供一个${FIRST_ARRAY[state]}的替代方案,实际上映射回索引,但仔细查看后,我意识到该扩展可以简单地替换为$state)。 - Charles Duffy
1
@ARL,至于你可能提供的其他任何内容,我认为你可以在未来的问题中(当这些问题依赖于内部工具时)包括类似于我在答案中给出的“搜索”实现,以便提供足够的内容让人们运行他们提出的答案并验证它们是否有效(在MCVE中给出的V,网址为http://stackoverflow.com/help/mcve)。描述行为的细节足以构建模拟实现(非常感激!),但是如果您自己提供它会更好。 - Charles Duffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接