Diziler üzerinde döngü, hem dizin hem de değer yazdırma


184

Böyle bir şey yapmak istiyorum:

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

Sonra için kullanarak kullanarak döngü için çalıştı:

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

ama burada indeks değerini bilmiyorum.

Gibi bir şey yapabileceğini biliyorum

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

ama başka bir şekilde yapamaz mısın?

Yanıtlar:


317

Dizi anahtarlarını "${!foo[@]}"( reference ) ile bulabilirsiniz , yani:

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

Bu $i, öğelerin kendilerine erişilmesi gerektiğinde endekslerin içinde olacağı anlamına gelir${foo[$i]}


6
Önemli not, yinelenebilir olsa da, boşlukla ayrılmış bir kelime listesi bir dizi değildir . (a b c)Bir diziye dönüştürmek için bu şekilde sarın .
Breedly

4
[@]Ve çift tırnak işareti kullanımı "boşlukla ayrılmış sözcük listesi" olmadığı anlamına gelir. Tek tek anahtarlar boşluk olsa bile, gerçek dizi anahtarlarının listesini alırsınız.
glenn jackman

@glennjackman bunu daha fazla açıklayabilir misinizThe use of [@] and double quotes means it's not a "space separated list of words"
Kasun Siyambalapitiya

1
"${foo[@]}"(dizi) değişkenini alır ve fooöğelerin kimliğini koruyarak, yani boşlukta ayırmamak için bir dizi olarak genişletir. If foo=(x 'y z'), o zaman iki argümanla f "${foo[@]}"çağırır ve . Bir dizi gibi meta veri sorguları benzer ve benzer şekilde işlem görür. fx'y z'"${!foo[@]}""${#foo[@]}"foo
BallpointBen

Doğru cevap, ama bu referans anlaşılmaz. Bu yanıt size yardımcı olur: " "${!foo[@]}"dizide ayarlanan tüm dizinlerin listesidir".
pjhsea

18

her zaman yineleme parametresini kullanabilirsiniz:

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

5
((ITER++))modern bash
David Tonhofer

Neden artış sonrası? Sadece değerin artırılmasını istersiniz, bu nedenle ((++ ITER)) daha doğrudan ne yapmak istediğinizin bir ifadesidir ....
MikeW

3
Hayır, "DAİMA" değil. Bir karma "deliklere" sahip olabilir, yani tüm sayılar bir indeks değildir. Örneğinizde, $ {ITER} her zaman $ {I} endeksi değildir.
Marco

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

Cevabınız kesinlikle biraz açıklamaya değer. Lütfen stackoverflow.com/help/how-to-answer adresine bakın . Yorumlar aranabilir içerik oluşturmaya yardımcı olur.
J. Chomel

3
Bu kod snippet'i soruyu çözebilir, ancak bir açıklama da dahil olmak üzere , yayınınızın kalitesini artırmaya yardımcı olur. Gelecekte okuyucular için soruyu cevapladığınızı ve bu kişilerin kod önerinizin nedenlerini bilmeyebileceğini unutmayın. Lütfen kodunuzu açıklayıcı yorumlarla doldurmamaya çalışın, bu hem kodun hem de açıklamaların okunabilirliğini azaltır!
kayess

2

Bash 4'te ilişkilendirilebilir diziler kullanabilirsiniz:

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'te bu çalışır (zsh olarak da çalışır):

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
Bu yanlış! İç döngü işe yaramaz ve yanlış sonuçlara yol açabilir!
F. Hauri

1

Damping dizisi için basit bir satır numarası

Boşluklarla bir değer ekledim:

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

Ben, hızlıca dökmek için diziler veya ilişkisel diziler I kullanımı

Bu tek satırlık komut:

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

Oluşturacak:

12  bar
35  baz
42  foo bar baz

Açıklaması

  • printf "%s\n" "${!foo[@]}"tüm tuşları yeni satırla ayrılmış olarak yazdıracak ,
  • printf "%s\n" "${foo[@]}"tüm değerleri bir satırsonu ile ayrılmış olarak yazdıracak ,
  • paste <(cmd1) <(cmd2)çıktı cmd1ve cmd2satır satır birleştirir .

tunning

Bu -danahtar ile ayarlanabilir:

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

ya da:

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

İlişkilendirilebilir dizi aynı şekilde çalışır:

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"

Yeni satırlarla veya özel karakterlerle ilgili sorun

Ne yazık ki, bunun artık çalışmamasını sağlayan en az bir koşul var: değişken newline içerdiğinde:

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

Komut pastesatır satır birleştirilir, böylece çıktı yanlış olur:

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'

Bu iş için, ikinci komut %qyerine (ve whipe quotes) kullanabilirsiniz:%sprintf

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

Mükemmel hale getirecek:

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

Gönderen man bash:

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

printf "%q\n" "${var[@]}" Newline için oylarım benim sorunumdu !
techno
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.