配列内のすべての要素を間接的に返す


9

Bashのマニュアルページで${!a}は、名前の内容a(間接参照のレベル)である変数の内容を返すためのの使用について説明しています。

これを使用して配列内のすべての要素を返す方法を知りたい、つまり、

a=(one two three)
echo ${a[*]}

戻り値

one two three

私がしたい:

b=a
echo ${!b[*]}

同じを返すために。残念ながら、そうではありませんが、0代わりに戻ります。

更新

返答を考えると、私の例はもちろん次のようなものなので、単純すぎることに気づきました。

b=("${a[@]}")

私が必要と言ったことを正確に達成します。

だから、これが私がやろうとしていたことです:

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST=LIST_$whichone
LIST=${!_LIST[*]}

もちろん、Bashのmanページを注意深く読むと、最後の行が単に$_LIST(配列ではなく)「配列」のインデックスを返すため、これは期待どおりに機能しないことがわかります。

いずれにせよ、(指摘されたように)次の作業を行う必要があります。

LIST=($(eval echo \${$_LIST[*]}))

または...(最終的に私が行ったルート):

LIST_lys="lys1 lys2"
...
LIST=(${!_LIST})

もちろん、その要素に空白が含まれていないと仮定します。


[@]ポインタに追加し_LIST="LIST_${whichone}[@]"、を使用LIST=("${!_LIST}")して配列をコピーします。環境変数(すべて大文字)との競合を避けるために、小文字の変数名を使用することをお勧めします。
アイザック

回答:


6

bash変数の間接参照の使用は、文字通り処理する必要があると思います。

例えば。あなたの元の例の場合:

a=(one two three)
echo ${a[*]} # one two three
b=a
echo ${!b[*]} # this would not work, because this notation 
              # gives the indices of the variable b which
              # is a string in this case and could be thought
              # as a array that conatins only one element, so
              # we get 0 which means the first element
c='a[*]'
echo ${!c} # this will do exactly what you want in the first
           # place

最後の実際のシナリオでは、以下のコードでうまくいくと思います。

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[*]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one

andパラメータの展開を"${var[@]}"めちゃくちゃにしない表記法を使用することをお勧めし$IFSます。これが最終的なコードです。

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[@]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one
                     # It is essential to have ${!_LIST} quoted

8

要素を明示的にコピーする必要があります。インデックス付き配列の場合:

b=("${a[@]}")

連想配列の場合(これaは配列変数の名前であり、値が配列変数の名前である変数ではないことに注意してください):

typeset -A b
for k in "${!a[@]}"; do b[$k]=${a[$k]}; done

配列に変数名がある場合は、要素ごとのメソッドを使用して、追加のステップでキーを取得できます。

eval "keys=(\"\${!$name[@]}\")"
for k in "${keys[@]}"; do eval "b[\$k]=\${$name[\$k]}"; done

(警告、この投稿のコードはブラウザーで直接入力されたため、テストされていません。)


あなたはまったく正しいですが、残念ながらそれは私の問題を解決しません(私の例は単純すぎました)。私の意図をより明確にするために更新を提供しました。
エリック・スミス

@Eric ksh / bashでは、その段階でevalが必要だと思います。私の編集を参照してください。
ジル 'SO-邪悪なことをやめる'

+1ですが、免責事項に従って、これは実際には機能しません(コードの最後のビット)。ただし、いくつかの小さな調整が必要です。具体的に\${!$name[@]}は、最初の展開が '$ name'のみであるように、最初の行にある必要があり${!a[@]}、eval用に保存され\${$name}ます。'$ k'の前にある他の2つのバックスラッシュは、厳密には必要ありません。
krb686

2

${!b[*]}arrayで使用されるインデックスに展開されbます。

あなたが望むことは2つのステップで行われなければならないので、eval助けになります:eval echo \${$b[*]}。(\これにより$、最初のステップが変数の展開である最初のステップを渡し、によって2番目のステップでのみ展開されることが保証されevalます。)

パラメータ展開によると!、間接展開({!a})、プレフィックスに一致する名前()、${!a*}配列キーのリスト()の両方に使用されます${!a[*]}。配列キーのリストは、意図する間接展開+配列要素展開と同じ構文であるため、後者はそのままではサポートされません。


2
${!a}名前がである変数の値に展開されます$a。これは、かなり簡潔で始まるparragraphに、マニュアルに記載されている「の最初の文字の場合のパラメータは感嘆符(ある!)、変数間接のレベルが導入されています。」
Gilles「SO-邪悪なことをやめよう」

そうです、@ Gillesは正しいですが、@ manatworkは2回目の読み取りで、${!処理している配列の場合、動作が異なるため、少しあいまいであることに気付きました。
エリックスミス

@Gillesあなたはその文章に正しいですが、悲しいことに「これの例外は、以下で説明する$ {!prefix *}と$ {!name [@]}の拡張です」として適用されません。しかし、私の返信は確かにあいまいな混乱なので、編集します。
manatwork

0

配列に間接的にアクセスする[@]には、間接変数に追加するだけですb=a[@]

この変数が設定されている場合:

a=(one two three)
printf '<%s> ' "${a[@]}"; echo

次に、これは動作します:

b="a[@]"
printf '<%s> ' "${!b}"

または単に:

echo "${!b}"

このような配列は次のようにコピーできます。

newArr=( "${!b}" )

そして次に印刷されます:

declare -p newArr

1つのスクリプトで:

#!/bin/bash
a=(one two three)
echo "original array"
printf '<%s> ' "${a[@]}"; echo

echo "Indirect array with variable b=a[@]"
b="a[@]"
printf '<%s> ' "${!b}"; echo

echo "New array copied in newArr, printed with declare"
newArr=( "${!b}" )
declare -p newArr

もちろん、上記のすべてが非スパース配列をコピーします。すべてのインデックスに値があるもの。

スパース配列

スパース配列は、未定義の要素を持つ可能性がある配列です。
たとえば、a[8]=12341つの要素を定義し、bashでは0〜7は存在しません。

このような疎配列をコピーするには、このメソッドを使用します

  1. 古い配列を出力します。

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
    
  2. 配列の名前を置き換え、文字列をキャプチャします。

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
  3. そのように作成された文字列を評価すると、新しい配列が定義されています。

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")
    
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.