配列のループ、インデックスと値の両方の出力


184

私はこのようなことをしたいです:

foo=( )
foo[0]="bar"
foo[35]="baz"
for((i=0;i<${#foo[@]};i++))
do
    echo "$i: ${foo[$i]}"
done
# Output:
# 0: bar
# 1: 

次に、for inを使用してそれをループしようとしました:

foo=( )
foo[0]="bar"
foo[35]="baz"
for i in ${foo[@]}
do
    echo "?: $i"
done
# Output:
# ?: bar
# ?: naz

しかし、ここではインデックス値がわかりません。

私はあなたが次のようなことができることを知っています

foo=( )
foo[0]="bar"
foo[35]="baz"
declare -p foo
# Output:
# declare -a foo='([0]="bar" [35]="baz")'

しかし、別の方法でそれを行うことはできませんか?

回答:


317

"${!foo[@]}"reference)で配列キーを見つけるので、

for i in "${!foo[@]}"; do 
  printf "%s\t%s\n" "$i" "${foo[$i]}"
done

これは$i、要素自体にアクセスする必要がある間、インデックスが存在することを意味します${foo[$i]}


6
重要な注意、反復可能ですが、スペースで区切られた単語のリストは配列ではありません。そのようにラップし(a b c)て、配列に変換します。
ブリードリー、

4
[@]二重引用符の使用は、「スペースで区切られた単語のリスト」ではないことを意味します。個々のキーに空白が含まれている場合でも、実際の配列キーのリストを取得します。
グレン・ジャックマン2016

@glennjackmanはこれについてもっと説明できますかThe use of [@] and double quotes means it's not a "space separated list of words"
Kasun Siyambalapitiya

1
"${foo[@]}"(配列)変数fooを受け取り、それを配列として展開し、要素のアイデンティティを維持します。つまり、空白でそれらを分割しません。もしはfoo=(x 'y z')、その後、f "${foo[@]}"呼び出し、f二つの引数で、xそして'y z'。メタデータは次のように照会"${!foo[@]}"し、"${#foo[@]}"同様に行動するfoo配列として。
BallpointBen 2018

正解ですが、その参照は不可解です。この回答"${!foo[@]}"は、「配列に設定されているすべてのインデックスのリストです」という便利な説明です。
pjhsea

17

常にイテレーションパラメータを使用できます。

ITER=0
for I in ${FOO[@]}
do  
    echo ${I} ${ITER}
    ITER=$(expr $ITER + 1)
done

5
((ITER++))モダンバッシュ
デビッドトンホーファー

なぜポストインクリメントですか?あなたは値をインクリメントしたいだけなので、((++ ITER))はより直接的にあなたが何をしたいのかのステートメントです...
MikeW

3
いいえ、「常に」ではありません。ハッシュは「穴」を持つことができます。つまり、すべての数値がインデックスであるとは限りません。あなたの例では、$ {ITER}が常に$ {I}のインデックスであるとは限りません。
マルコ

10
INDEX=0
for i in $list; do 
    echo ${INDEX}_$i
    let INDEX=${INDEX}+1
done

あなたの答えは確かに少し説明する価値があります。stackoverflow.com/help/how-to-answerを参照してください。コメントは検索可能なコンテンツの作成に役立ちます。
J.チョメル2017

3
このコードスニペットは質問を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。また、コードを説明コメントで混雑させないようにしてください。これにより、コードと説明の両方が読みにくくなります。
kayess

2

bash 4では、連想配列を使用できます。

declare -A foo
foo[0]="bar"
foo[35]="baz"
for key in "${!foo[@]}"
do
    echo "key: $key, value: ${foo[$key]}"
done

# output
# $ key: 0, value bar.
# $ key: 35, value baz.

bash 3では、これは機能します(zshでも機能します)。

map=( )
map+=("0:bar")
map+=("35:baz")

for keyvalue in "${map[@]}" ; do
    key=${keyvalue%%:*}
    value=${keyvalue#*:}
    echo "key: $key, value $value."
done

1
users=("kamal" "jamal" "rahim" "karim" "sadia")
index=()
t=-1

for i in ${users[@]}; do
  t=$(( t + 1 ))
  if [ $t -eq 0 ]; then
    for j in ${!users[@]}; do
      index[$j]=$j
    done
  fi
  echo "${index[$t]} is $i"
done

1
これは間違っています!内部ループは役に立たず、間違った結果につながる可能性があります!
F.ハウリ

1

配列をダンプするための簡単な1行のトリック

スペースを含む1つの値を追加しました。

foo=()
foo[12]="bar"
foo[42]="foo bar baz"
foo[35]="baz"

私、すぐに捨てる 使用する配列または連想配列

この1行のコマンド:

paste <(printf "%s\n" "${!foo[@]}") <(printf "%s\n" "${foo[@]}")

レンダリングします:

12  bar
35  baz
42  foo bar baz

説明しました

  • printf "%s\n" "${!foo[@]}"改行で区切られたすべてのキーを印刷します
  • printf "%s\n" "${foo[@]}"改行で区切られたすべてのを出力します
  • paste <(cmd1) <(cmd2)出力合併するcmd1cmd2行ずつ。

見事な

これは-dスイッチで調整できます:

paste -d : <(printf "%s\n" "${!foo[@]}") <(printf "%s\n" "${foo[@]}")
12:bar
35:baz
42:foo bar baz

あるいは:

paste -d = <(printf "foo[%s]\n" "${!foo[@]}") <(printf "'%s'\n" "${foo[@]}")
foo[12]='bar'
foo[35]='baz'
foo[42]='foo bar baz'

連想配列は同じように機能します。

declare -A bar=([foo]=snoopy [bar]=nice [baz]=cool [foo bar]='Hello world!')
paste -d = <(printf "bar[%s]\n" "${!bar[@]}") <(printf '"%s"\n' "${bar[@]}")
bar[foo bar]="Hello world!"
bar[foo]="snoopy"
bar[bar]="nice"
bar[baz]="cool"

問題改行や特殊文字

残念ながら、これが機能しなくなる少なくとも1つの条件があります。変数に改行が含まれている場合:

foo[17]=$'There is one\nnewline'

コマンドpasteは行ごとにマージするため、出力は間違ったものになります。

paste -d = <(printf "foo[%s]\n" "${!foo[@]}") <(printf "'%s'\n" "${foo[@]}")
foo[12]='bar'
foo[17]='There is one
foo[35]=newline'
foo[42]='baz'
='foo bar baz'

この作業で%q%s、2番目のprintfコマンド(およびwhipe quoting)の代わりに使用できます。

paste -d = <(printf "foo[%s]\n" "${!foo[@]}") <(printf "%q\n" "${foo[@]}")

完璧にレンダリングされます:

foo[12]=bar
foo[17]=$'There is one\nnewline'
foo[35]=baz
foo[42]=foo\ bar\ baz

からman bash

          %q     causes  printf  to output the corresponding argument in a
                 format that can be reused as shell input.

私のprintf "%q\n" "${var[@]}" 改行への賛成票が私の問題でした!
テクノ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.