Làm thế nào để tôi thay đổi một mảng bash tại một số chỉ mục ở giữa?


12
1  #!/bin/bash
2  # query2.sh
3
4  numbers=(53 8 12 9 784 69 8 7 1)
5  i=4
6
7  echo ${numbers[@]} # <--- this echoes "53 8 12 9 784 69 8 7 1" to stdout.
8  echo ${numbers[i]} # <--- this echoes "784" to stdout.
9
10 unset numbers[i]
11
12 echo ${numbers[@]} # <--- this echoes "53 8 12 9 69 8 7 1" to stdout.
13 echo ${numbers[i]} # <--- stdout is blank.

Tại sao, trong dòng 13, thiết bị xuất chuẩn trống, xem xét rằng mảng dường như đã được cập nhật đánh giá theo thiết bị xuất chuẩn của dòng 12?

Và do đó, tôi nên làm gì để có câu trả lời dự định, "69"?


1
Xem xét loại công việc mã hóa mà câu hỏi này ngụ ý, bạn nên đưa ra cảnh báo: xem Có gì đó không đúng với tập lệnh của tôi hay Bash chậm hơn nhiều so với Python?
tự đại diện

Câu trả lời:


21

unsetloại bỏ một yếu tố Nó không đánh số lại các yếu tố còn lại.

Chúng ta có thể sử dụng declare -pđể xem chính xác những gì xảy ra numbers:

$ unset "numbers[i]"
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

Quan sát numberskhông còn có một yếu tố 4.

Một vi dụ khac

Quan sát:

$ a=()
$ a[1]="element 1"
$ a[22]="element 22"
$ declare -p a
declare -a a=([1]="element 1" [22]="element 22")

Mảng akhông có phần tử 2 đến 21. Bash không yêu cầu các chỉ số mảng liên tiếp.

Phương pháp được đề xuất để buộc đánh số lại các chỉ số

Hãy bắt đầu với numbersmảng có phần tử bị thiếu 4:

$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

Nếu chúng ta muốn các chỉ số thay đổi, thì:

$ numbers=("${numbers[@]}")
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [4]="69" [5]="8" [6]="7" [7]="1")

Bây giờ có một số phần tử 4và nó có giá trị 69.

Phương pháp thay thế để loại bỏ một phần tử & đánh số lại mảng trong một bước

Một lần nữa, hãy xác định numbers:

$ numbers=(53 8 12 9 784 69 8 7 1)

Theo đề xuất của Toby Speight trong các bình luận, một phương pháp để loại bỏ phần tử thứ tư và đánh số lại các phần tử còn lại trong một bước:

$ numbers=("${numbers[@]:0:4}" "${numbers[@]:5}")
$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [4]="69" [5]="8" [6]="7" [7]="1")

Như bạn có thể thấy, phần tử thứ tư đã bị xóa và tất cả các phần tử còn lại được đánh số lại.

${numbers[@]:0:4}mảng lát numbers: phải mất bốn phần tử đầu tiên bắt đầu bằng phần tử 0.

Tương tự, ${numbers[@]:5}lát mảng numbers: nó lấy tất cả các phần tử bắt đầu bằng phần tử 5 và tiếp tục đến cuối mảng.

Lấy các chỉ số của một mảng

Các giá trị của một mảng có thể được lấy bằng ${a[@]}. Để tìm các chỉ số (hoặc khóa ) tương ứng với các giá trị đó, hãy sử dụng ${!a[@]}.

Ví dụ, hãy xem xét lại mảng của chúng tôi numbersvới phần tử bị thiếu 4:

$ declare -p numbers
declare -a numbers=([0]="53" [1]="8" [2]="12" [3]="9" [5]="69" [6]="8" [7]="7" [8]="1")

Để xem chỉ số nào được chỉ định:

$ echo "${!numbers[@]}"
0 1 2 3 5 6 7 8

Một lần nữa, 4bị thiếu trong danh sách các chỉ số.

Tài liệu

Từ man bash:

Nội dung unsetđược sử dụng để phá hủy các mảng. unset name[subscript]phá hủy phần tử mảng tại chỉ mục subscript. Các chỉ số âm cho các mảng được lập chỉ mục được diễn giải như mô tả ở trên. Phải cẩn thận để tránh các tác dụng phụ không mong muốn gây ra bởi việc mở rộng tên đường dẫn. unset name, Nơi namelà một mảng, hoặc unset name[subscript], nơi subscript* hay @, loại bỏ toàn bộ mảng.


1
Lưu ý rằng cú pháp mảng shell thực sự chỉ là một cách để làm cho nó dễ dàng xử lý các biến có tên tương tự. Không có mảng nào ; trong thực tế, sau khi bạn viết a=(), biến avẫn không được xác định cho đến khi bạn thực sự gán cho một trong các chỉ số của nó.
chepner

@ John1024: Cảm ơn bạn vì câu trả lời này. Bạn có thể mở rộng nó để bao gồm một câu trả lời được đề xuất để đạt được kết quả mong muốn không?
Anthony Webber

@AnthonyWebber Chắc chắn. Tôi đã thêm một phần vào câu trả lời để chỉ ra cách buộc đánh số lại các chỉ số.
John1024

2
Chỉ đề cập đến một cách tiếp cận khác (có thể phù hợp hơn với một số mã): thay vì unset numbers[4]chỉ định toàn bộ mảng bằng cách sử dụng lát, nghĩa là numbers=("${numbers[@]:0:4}" "${numbers[@]:5}")(tôi sẽ đăng dưới dạng câu trả lời, nhưng không có thời gian để giải thích chính xác).
Toby Speight

@ John1024: Đánh giá cao bạn làm điều đó. Và thnx Toby :)
Anthony Webber

5

bashcác mảng như trong ksh, không thực sự là các mảng, chúng giống như các mảng kết hợp với các khóa được giới hạn ở các số nguyên dương (hay còn gọi là mảng thưa thớt ). Đối với một vỏ với mảng sản, bạn có thể có một cái nhìn tại vỏ như rc, es, fish, yash, zsh(hoặc thậm chí csh/ tcshdù những vỏ có rất nhiều vấn đề mà họ đang tốt hơn tránh).

Trong zsh:

a=(1 2 3 4 5)
a[3]=() # remove the 3rd element
a[1,3]=() # remove the first 3 elements
a[-1]=() # remove the last element

(Lưu ý rằng trong zsh, unset 'a[3]'thực sự đặt nó thành chuỗi trống để cải thiện khả năng tương thích với ksh)

trong yash:

a=(1 2 3 4 5)
array -d a 3 # remove the 3rd element
array -d a 1 2 3 # remove the first 3 elements
array -d a -1 # remove the last element

trong fish(không phải là vỏ giống Bourne trái với bash/ zsh):

set a 1 2 3 4 5
set -e a[3] # remove the 3rd element
set -e a[1..3] # remove the first 3 elements
set -e a[-1] # remove the last element

trong es(dựa trên rc, không giống như Bourne)

a = 1 2 3 4 5
a = $a(... 2 4 ...) # remove the 3rd element
a = $a(4 ...) # remove the first 3 elements
a = $a(... `{expr $#a - 1}) # remove the last element
# or a convoluted way that avoids forking expr:
a = $a(... <={@{*=$*(2 ...); return $#*} $a})

trong kshbash

Bạn có thể sử dụng các mảng như các mảng bình thường nếu bạn làm:

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

sau mỗi thao tác xóa hoặc chèn có thể làm cho danh sách các chỉ mục không liền kề hoặc không bắt đầu từ 0. Cũng lưu ý rằng ksh/ bashmảng bắt đầu từ 0, không phải 1 (ngoại trừ $@(theo một số cách)).

Điều đó sẽ có hiệu lực dọn dẹp các yếu tố và di chuyển chúng đến chỉ số 0, 1, 2 ... theo thứ tự.

Cũng lưu ý rằng bạn cần trích dẫn number[i]trong:

unset 'number[i]'

Nếu không, điều đó sẽ được coi unset numberilà có một tệp được gọi numberitrong thư mục hiện tại.

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.