Vòng lặp trên mảng, in cả chỉ số và giá trị


183

Tôi muốn làm một cái gì đó như thế này:

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

Sau đó, tôi đã cố gắng lặp qua nó bằng cách sử dụng cho:

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

nhưng ở đây tôi không biết giá trị chỉ số.

Tôi biết bạn có thể một cái gì đó như

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

Nhưng, bạn không thể làm điều đó theo cách khác?

Câu trả lời:


315

Bạn sẽ tìm thấy các khóa mảng với "${!foo[@]}"( tham chiếu ), vì vậy:

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

Điều đó có nghĩa là các chỉ số sẽ ở trong $ikhi các phần tử phải được truy cập thông qua${foo[$i]}


6
Lưu ý quan trọng, trong khi có thể lặp lại, một danh sách các từ được phân tách bằng dấu cách không phải là một mảng. Gói nó như vậy (a b c)để chuyển đổi nó thành một mảng.
Nhân giống

4
Việc sử dụng [@]và trích dẫn kép có nghĩa là nó không phải là "danh sách các từ được phân tách bằng dấu cách". Bạn nhận được danh sách các khóa mảng thực tế, ngay cả khi các khóa riêng lẻ chứa khoảng trắng.
glenn jackman

@glennjackman bạn có thể giải thích điều này nhiều hơnThe use of [@] and double quotes means it's not a "space separated list of words"
Kasun Siyambalapitiya

1
"${foo[@]}"lấy biến (mảng) foovà mở rộng nó thành một mảng, duy trì danh tính của các phần tử của nó, tức là không tách chúng trên khoảng trắng. Nếu foo=(x 'y z'), sau đó f "${foo[@]}"gọi fvới hai đối số, x'y z'. Các truy vấn siêu dữ liệu giống "${!foo[@]}""${#foo[@]}"tương tự hoạt động foonhư một mảng.
BallpointBen

Câu trả lời đúng, nhưng tài liệu tham khảo đó là không thể hiểu được. Câu trả lời này chỉ hữu ích giải thích: " "${!foo[@]}"là danh sách tất cả các chỉ mục được đặt trong mảng".
pjhsea

16

bạn luôn có thể sử dụng thông số lặp:

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

5
((ITER++))trong bash hiện đại
David Tonhofer

Tại sao phải tăng sau? Bạn chỉ muốn giá trị tăng lên, do đó ((++ ITER)) trực tiếp hơn là một tuyên bố về những gì bạn muốn thực hiện ....
MikeW

3
Không, không phải "LUÔN". Một hàm băm có thể có "lỗ", có nghĩa là không phải tất cả các số đều là một chỉ mục. Trong ví dụ của bạn, $ {ITER} không phải luôn là chỉ số của $ {I}.
Marco

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

Câu trả lời của bạn chắc chắn là giá trị một lời giải thích. Vui lòng tham khảo stackoverflow.com/help/how-to-answer . Nhận xét sẽ giúp tạo nội dung có thể tìm kiếm.
J. Chomel

3
Mặc dù đoạn mã này có thể giải quyết câu hỏi, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn. Xin vui lòng cố gắng không làm đông mã của bạn với các bình luận giải thích, điều này làm giảm khả năng đọc của cả mã và các giải thích!
chèo thuyền kayak

2

Trong bash 4, bạn có thể sử dụng mảng kết hợp:

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.

Trong bash 3, điều này hoạt động (cũng hoạt động trong 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
Cái này sai! Vòng lặp bên trong là vô ích và có thể dẫn đến kết quả sai!
F. Hauri

1

Thủ thuật một dòng đơn giản để đổ mảng

Tôi đã thêm một giá trị với khoảng trắng:

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

Tôi, để nhanh chóng đổ mảng hoặc mảng kết hợp tôi sử dụng

Lệnh một dòng này:

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

Sẽ kết xuất:

12  bar
35  baz
42  foo bar baz

Giải thích

  • printf "%s\n" "${!foo[@]}"sẽ in tất cả các khóa được phân tách bằng một dòng mới ,
  • printf "%s\n" "${foo[@]}"sẽ in tất cả các giá trị được phân tách bằng một dòng mới ,
  • paste <(cmd1) <(cmd2)sẽ hợp nhất đầu ra cmd1cmd2theo từng dòng.

Điều chỉnh

Điều này có thể được điều chỉnh bằng -dchuyển đổi:

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

hoặc thậm chí:

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

Mảng kết hợp sẽ hoạt động như nhau:

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"

Vấn đề với dòng mới hoặc ký tự đặc biệt

Thật không may, có ít nhất một điều kiện làm cho điều này không hoạt động nữa: khi biến có chứa dòng mới:

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

Lệnh pastesẽ hợp nhất từng dòng một, do đó đầu ra sẽ bị sai:

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'

Đối với công việc này, bạn có thể sử dụng %qthay vì %strong printflệnh thứ hai (và trích dẫn whipe):

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

Sẽ hoàn hảo:

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

Từ man bash:

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

Phiếu bầu của tôi cho printf "%q\n" "${var[@]}" dòng mới là vấn đề của tôi!
kỹ thuật
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.