![如何从数组数组中选择要循环的数组?](https://linux22.com/image/223940/%E5%A6%82%E4%BD%95%E4%BB%8E%E6%95%B0%E7%BB%84%E6%95%B0%E7%BB%84%E4%B8%AD%E9%80%89%E6%8B%A9%E8%A6%81%E5%BE%AA%E7%8E%AF%E7%9A%84%E6%95%B0%E7%BB%84%EF%BC%9F.png)
#!/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
我希望此脚本将数组名称打印给用户ARGENT
和BIENETRE
,以便用户可以选择其中之一。用户输入后,脚本将打印所选数组的每个元素。我想选择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
您需要“键”到“值”的映射,其中“值”是字符串列表,“键”ARGENT
是BIENETRE
……
您走在正确的道路上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