如何从数组数组中选择要循环的数组?

如何从数组数组中选择要循环的数组?
#!/usr/bin/bash


ARGENT=("Nous devons économiser de l'argent."
"Je dois économiser de l'argent.")

BIENETRE=("Comment vas-tu?" "Tout va bien ?")

aoarrs=("${ARGENT}" "${BIENETRE}")

select arr in "${aoarrs[@]}"; do
  for el in "${arr[@]}"; do
    echo "$el"
  done
  break
done 

我希望此脚本将数组名称打印给用户ARGENTBIENETRE,以便用户可以选择其中之一。用户输入后,脚本将打印所选数组的每个元素。我想选择select一个数组来从数组的数组()中循环aoarrs。我想使用 select 的原因是因为在现实世界中,我的数组数组中可能不止两个数组。我怎样才能做到这一点?

答案1

您将存储数组名字aoarrs,并在选择体内声明一个名称引用到所选的名称:

ARGENT=("Nous devons économiser de l'argent."
"Je dois économiser de l'argent.")
BIENETRE=("Comment vas-tu?" "Tout va bien ?")
aoarrs=(ARGENT BIENETRE)

PS3='Which array? '
select arr in "${aoarrs[@]}"; do 
    [[ $arr ]] || continue
    declare -n ref=$arr
    for i in "${!ref[@]}"; do 
        printf '%d\t%s\n' $i "${ref[i]}"
    done 
    break
done

跑步可能看起来像

1) ARGENT
2) BIENETRE
Which array? 3
Which array? 4
Which array? 5
Which array? 2
0   Comment vas-tu?
1   Tout va bien ?

答案2

您需要“键”到“值”的映射,其中“值”是字符串列表,“键”ARGENTBIENETRE……

您走在正确的道路上aoarrs,因为您可以将该数组用作关联数组:

declare -A aoarrs
aoarrs[ARGENT]=$ARGENT
aoarrs[BIENETRE]=$BIENETRE

然后使用类似 的方法迭代该数组中的所有键for key in ${!aoarrs[@]}…

可悲的是,无论出于何种原因,bash 都不允许列表作为这些关联数组的元素。

所以,事情很糟糕。例如,您可以使用保留字符连接列表的元素,以便稍后分割它们(这很愚蠢,因为这意味着您不能在字符串中包含所有字符,或者需要开始转义它们),或者您构建自己的函数获取字符串列表,将它们附加到一个大数组中,然后在该容器上实现自己的关联查找函数(这将是愚蠢的;它不仅会很慢,而且还需要您在相对较大的空间中编写相对较多的代码)语言不合适)。看起来会很糟糕。这是一个我在没有测试的情况下写下来的例子,因为它太丑陋了,我需要把它从我的脑海中清除,但不想再进一步处理它:

#!/bin/bash
###############################
#        UNTESTED STUFF       #
# If you think this code is   #
# acceptable, consider doing  #
# something good for yourself #
# once in a while             #
###############################
declare -A index
declare -A lengths
declare -a storage

# Adds an entry to our our custom container
# 
# Usage:
# add_key_values KEY "list element 1" "list element 2" …
function add_key_values() {
  local key="$1"
  shift
  local -n valuelist=$@

  # get the length of the passed list, to save it
  local lengthlist=${#valuelist[@]}


  # get the end of the current storage, that's where we start adding
  # our list
  local start_index=${#storage[@]}

  # finally, actually store the list items in the storage
  for item in "${valuelist[@]}"; do
    storage+=("${item}")
  done
  lengths["${key}"]=$lengthlist
  index["${key}"]=$start_index
}

# Retrieve a list from the storage
# Sadly, bash is not a proper programming language, because what it calls
# "functions" don't do the one thing that a function needs to do:
# return a value for an argument. There's just no "return" mechanism in bash.
# 
# Returns an empty list if the key wasn't found.
#
# So, after sobbing a bit, we just say
# Usage:
# get_values $key
# Overwrites the `ret_val` variable with the list that was saved earlier
function get_values() {
  # prepare ret_val
  declare -g -a ret_val=()

  local key=$1

  # We return (with ret_val empty) if key isn't present
  # frigging bash doesn't have a "is key present in associative array" function…
  # so this is the workaround to check whether there's $key in $index.
  # seriously.
  local teststring
  teststring="$(printf 'index[%s]' "${key}")"
  # you need quite modern bash to even get the "is defined" -v test
  [[ -v "${teststring}" ]] || return

  # let's get all the elements from storage and append them to ret_val
  local start=${index[$key]}
  local length=${lengths[$key]}
  for idx in $(seq $(( start - 1 )) $((start - 1 + length)) ); do 
    ret_val+=("${storage[idx]}")
  done
}

####################
# EXAMPLE USAGE
####################
add_key_values "ARGENT" "Nous devons économiser de l'argent." "Je dois économiser de l'argent."
add_key_values "BIENETRE" ("Comment vas-tu?" "Tout va bien ?")

for key in ${!index[@]}; do
  echo "the list for value $key contains"
  get_values "${key}"
  for element in ${ret_val[@]}; do
    echo "element: ${element}"
  done
done

下一个选项很神奇,涉及使用 . 按名称“间接寻址”变量eval。这是一种邪恶、愚蠢的行为,这里有很多帖子暗示“如果你到了那个时候,那么也许可以使用适当的编程语言而不是 bash”。

我同意这一点:整个问题实际上可以用四行 python 来完成,前两行将把“ARGENT”和“BIENETRE”及其列表存储到一个dict.或者实际上,在除 bash(或 C,就此而言)之外的任何其他通用语言中,关联数组都没有那么糟糕。

答案3

生成数组仅包含变量名称:

aoarrs=(ARGENT BIENETRE)

while :; do
        select arr in "${aoarrs[@]}" quit; do
                declare -n ref=$arr
                case $arr in
                        quit)
                                break 2;; 
                        *)
                                select item in "${ref[@]}"; do
                                        echo $item
                                        break
                                done;;
                esac
                break
        done
done

declare -n ref=$arr-引用由其值命名的变量。
break 2-打破 2 个封闭循环。

答案4

在 ksh93(shell bash 尝试模拟)中会更容易,其中关联数组可以将数组(除其他外)作为值。

#! /bin/ksh -

typeset -A all=(
  [argent]=(
    "Nous devons économiser de l'argent."
    "Je dois économiser de l'argent."
  )

  [bien-être]=(
    "Comment vas-tu?"
    "Tout va bien ?"
  )
)

select topic in "${!all[@]}"; do
  for sentence in "${all[$topic][@]}"; do
    print -r -- "$sentence"
  done
  break
done

相关内容