配列配列から繰り返す配列を選択するには?

配列配列から繰り返す配列を選択するには?
#!/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がそれらの1つを選択できるようにしたいと思います。ユーザー入力後、スクリプトは選択した配列の各要素を印刷します。selectarray() of arrays で繰り返す配列を選択したいと思います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の代わりに適切なプログラミング言語を使用してください」と提案する多くの投稿があります。

私はこれに同意する:全体の問題は、実際には4行のPythonで行うことができます。最初の2行は"ARGENT"と"BIENETRE"とそのリストをdict。汎用言語、連想配列はそれほど悪くありません。

答え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(シェル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

関連情報