Looping over arrays, mencetak indeks dan nilai

184

Saya ingin melakukan sesuatu seperti ini:

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

Lalu saya mencoba untuk mengulanginya menggunakan untuk di:

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

tapi di sini saya tidak tahu nilai indeksnya.

Saya tahu Anda bisa menyukai sesuatu

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

tetapi, tidak bisakah Anda melakukannya dengan cara lain?

Tyilo
sumber

Jawaban:

317

Anda akan menemukan kunci array dengan "${!foo[@]}"( referensi ), jadi:

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

Yang berarti bahwa indeks akan berada $isementara elemen itu sendiri harus diakses melalui${foo[$i]}

glenn jackman
sumber
6
Catatan penting, meskipun dapat diubah, daftar kata yang dipisahkan spasi bukanlah sebuah array. Bungkus seperti itu (a b c)untuk mengubahnya menjadi sebuah array.
Breedly
4
Penggunaan [@]dan tanda kutip ganda berarti itu bukan "daftar kata-kata yang dipisahkan ruang". Anda mendapatkan daftar kunci array yang sebenarnya, bahkan jika masing-masing kunci berisi spasi.
glenn jackman
@glennjackman dapatkah Anda menjelaskan hal ini lebih lanjutThe use of [@] and double quotes means it's not a "space separated list of words"
Kasun Siyambalapitiya
1
"${foo[@]}"mengambil variabel (array) foodan memperluasnya sebagai array, mempertahankan identitas elemen-elemennya, yaitu, tidak membaginya di whitespace. Jika foo=(x 'y z'), maka f "${foo[@]}"panggil fdengan dua argumen, xdan 'y z'. Metadata kueri suka "${!foo[@]}"dan "${#foo[@]}"juga bertindak foosebagai array.
BallpointBen
Jawaban yang benar, tetapi referensi itu tidak bisa dipahami. Jawaban ini sangat membantu menjelaskan: " "${!foo[@]}"adalah daftar semua indeks yang diatur dalam array".
pjhsea
18

Anda selalu dapat menggunakan parameter iterasi:

ITER=0
for I in ${FOO[@]}
do  
    echo ${I} ${ITER}
    ITER=$(expr $ITER + 1)
done
Eyal Ch
sumber
5
((ITER++))dalam pesta modern
David Tonhofer
Mengapa pasca kenaikan? Anda hanya ingin nilai bertambah, maka ((++ ITER)) lebih merupakan pernyataan langsung tentang apa yang ingin Anda lakukan ....
MikeW
3
Tidak, tidak "SELALU". Hash dapat memiliki "lubang", yang berarti tidak semua angka adalah indeks. Dalam contoh Anda, $ {ITER} tidak selalu indeks dari $ {I}.
Marco
10
INDEX=0
for i in $list; do 
    echo ${INDEX}_$i
    let INDEX=${INDEX}+1
done
Aruy Aruy
sumber
Jawaban Anda tentu perlu sedikit penjelasan. Silakan merujuk ke stackoverflow.com/help/how-to-answer . Komentar akan membantu membuat konten yang dapat ditelusuri.
J. Chomel
3
Sementara potongan kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda. Tolong juga cobalah untuk tidak membuat kerumunan kode Anda dengan komentar penjelasan, ini mengurangi keterbacaan kode dan penjelasan!
kayess
2

Di bash 4, Anda dapat menggunakan array asosiatif:

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.

Di bash 3, ini berfungsi (juga berfungsi di zsh):

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

for keyvalue in "${map[@]}" ; do
    key=${keyvalue%%:*}
    value=${keyvalue#*:}
    echo "key: $key, value $value."
done
mattmc3
sumber
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
code4mk
sumber
1
Ini salah! Loop dalam tidak berguna dan bisa mengarah ke hasil yang salah!
F. Hauri
1

Sederhana satu barisTrik untuk membuang array

Saya telah menambahkan satu nilai dengan spasi:

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

Saya, untuk membuang dengan cepat array atau array asosiatif yang saya gunakan

Perintah satu baris ini:

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

Will render:

12  bar
35  baz
42  foo bar baz

Dijelaskan

  • printf "%s\n" "${!foo[@]}"akan mencetak semua kunci yang dipisahkan oleh baris baru ,
  • printf "%s\n" "${foo[@]}"akan mencetak semua nilai yang dipisahkan oleh baris baru ,
  • paste <(cmd1) <(cmd2)akan menggabungkan output cmd1dan cmd2baris demi baris.

Tunning

Ini bisa disetel dengan -dsakelar:

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

atau bahkan:

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

Array asosiatif akan bekerja sama:

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"

Masalah dengan baris baru atau karakter khusus

Sayangnya, setidaknya ada satu kondisi yang membuat ini tidak berfungsi lagi: ketika variabel mengandung baris baru:

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

Perintah pasteakan menggabungkan baris demi baris, sehingga output akan menjadi salah:

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'

Untuk pekerjaan ini, Anda bisa menggunakan %qalih-alih %sdi printfperintah kedua (dan mengutip whipe):

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

Akan membuat sempurna:

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

Dari man bash:

          %q     causes  printf  to output the corresponding argument in a
                 format that can be reused as shell input.
F. Hauri
sumber
printf "%q\n" "${var[@]}" Suara saya untuk baris baru adalah masalah saya!
techno