Hành vi kỳ lạ của mảng chưa được khởi tạo và mảng unset


7

Tôi đang viết một kịch bản và tôi phát hiện ra một số hành vi bất ngờ của các biến mảng chưa được khởi tạo và chưa đặt mà tôi không hiểu.

Trước hết, độ dài:

$ echo ${#notset[@]}
0
$ uninitialized=
$ echo ${#uninitialized[@]}
1

Tại sao lại là uninitializedchiều dài 1? Nó có nên bằng không? Có phải vì một biến null được coi là một mảng của một phần tử null?

Thực tế này dẫn đến một số vấn đề. Ví dụ: giả sử tôi muốn tạo một mảng và chèn một số thứ nhất định dựa trên các đối số dòng lệnh của người dùng. Tôi nghĩ rằng tôi có thể làm một cái gì đó như (+) :

myarray=

if [ some-condition ]
then
    myarray[${#myarray[@]}]=some-value
fi

if [ some-condition2 ]
then
    myarray[${#myarray[@]}]=some-value2
elif [ some-condition3 ]
then
    myarray[${myarray[@]}]=some-value3
    myarray[${myarray[@]}]=some-value4
fi

Nhưng điều này để lại vị trí đầu tiên thành null, điều mà tôi không thích và cũng phá vỡ một số mã mà tôi đã viết (*) và tại thời điểm này, giả sử rằng tôi muốn xem liệu mảng đó có chứa phần tử nào không. Tôi nên làm thế nào?

[ -z "${myarray[@]}" ]

Tăng lỗi nếu mảng chứa nhiều hơn một phần tử.

[ -z "$myarray" ]

Thất bại vì phần tử đầu tiên là null, ngay cả khi mảng không trống.

Vì vậy, làm thế nào tôi nên kiểm soát rằng một mảng là chưa được khởi tạo?

Và ai đó có thể giải thích chính xác những gì xảy ra khi xử lý các mảng và unset - các biến chưa được khởi tạo?


(+) Tôi biết rằng tôi có thể tránh "khai báo" biến và nó sẽ hoạt động, nhưng tập lệnh này sẽ được xem xét bởi một giáo sư và anh ấy không thích các biến được xác định tại các vị trí ngẫu nhiên.

(*) Trước khi thử điều này, tôi đã giữ độ dài của mảng trong một biến khác và vì vậy tôi không gặp vấn đề gì. Nhưng tôi muốn tránh việc xác định các biến phụ trợ này vì tôi biết tôi có thể có được độ dài mà không cần chúng.


Lưu ý rằng không phảiuninitializedchưa được khởi tạo; phần tử đầu tiên của nó là chuỗi rỗng. foo${foo[0]}gần như có thể hoán đổi cho nhau.
chepner

Câu trả lời:


11

Bạn có thể thấy sự khác biệt với declare -p:

unset foo
declare -a foo
declare -p foo
# prints declare -a foo='()'
foo=
declare -p foo
# prints declare -a foo='([0]="")'

Nếu bạn muốn khởi tạo một mảng trống, đầu ra của đầu tiên declare -plà một gợi ý hay về cách tốt nhất để khai báo nó:

declare -a array='()'

(Phần declare -anày có thể là tùy chọn, đơn giản array=()cũng sẽ hoạt động tốt.)

Nếu bạn muốn kiểm tra xem một mảng có 0 phần tử hay không, hãy sử dụng so sánh số trên ${#array[@]}; đừng cố gắng thực hiện test -zviệc mở rộng vì sẽ không cho kết quả chính xác trong nhiều trường hợp.


Tôi không biết declare -p. Cảm ơn bạn đã giải quyết những nghi ngờ của tôi. Điều duy nhất không rõ ràng với tôi là câu cuối cùng của bạn. Tôi luôn sử dụng ${#array[@]}ký hiệu, tại sao bạn bảo tôi không sử dụng ký hiệu kia?
Bakuriu

1
@Bakuriu Tôi chỉ cố gắng nói rằng để kiểm tra xem một mảng có trống hay không, sẽ dễ kiểm tra hơn nếu số lượng được cung cấp bằng ${#array[@]}0 so với cố gắng mở rộng nội dung mảng và kiểm tra nếu chuỗi kết quả trống.
jw013

8

Để khởi tạo một mảng trống, sử dụng

array=()

Để thêm một giá trị cho mảng, sử dụng

array+=(value)

Thật tuyệt, tôi không biết cú pháp này. Điều duy nhất là điều này đòi hỏi phải sử dụng declare -a varđể tránh có null ngay từ đầu.
Bakuriu

1
@Bakuriu: Khởi tạo nó là array=().
choroba
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.