Có cách nào để đọc phần tử cuối cùng của một mảng với bash không?


68

Nếu tôi có một mảng với 5 phần tử, ví dụ:

[a][b][c][d][e]

Sử dụng echo ${myarray[4]}tôi có thể thấy những gì nó giữ.

Nhưng nếu tôi không biết số lượng phần tử trong một mảng nhất định thì sao? Có cách nào để đọc phần tử cuối cùng của một mảng có độ dài không xác định không? tức là phần tử đầu tiên đọc từ phải sang trái cho mảng nào?

Tôi muốn biết làm thế nào để làm điều này trong bash.


$@không chính xác là một mảng (không thể được đăng ký). Đối với nó, xem Lấy đối số cuối cùng được chuyển đến một tập lệnh shell .
Tom Hale

Câu trả lời:


89

Bạn chỉ có thể sử dụng một chỉ số tiêu cực ${myarray[-1]} để có được yếu tố cuối cùng. Bạn có thể làm điều tương tự cho lần thứ hai, v.v. trong Bash:

Nếu chỉ mục được sử dụng để tham chiếu một phần tử của mảng được lập chỉ mục ước tính thành một số nhỏ hơn 0, thì nó được hiểu là tương đối với một lớn hơn chỉ số tối đa của mảng, vì vậy các chỉ số âm đếm ngược từ cuối mảng và chỉ số -1 đề cập đến yếu tố cuối cùng.

Điều tương tự cũng làm việc cho nhiệm vụ. Khi nó nói "biểu thức", nó thực sự có nghĩa là một biểu thức; bạn có thể viết bất kỳ biểu thức số học nào ở đó để tính chỉ số, bao gồm cả biểu thức tính toán sử dụng độ dài của mảng ${#myarray[@]}một cách rõ ràng.


2
Bạn có thể làm điều đó trong kshzshlà tốt.
Janis

5
Với zshmặc dù, bởi các mảng mặc định là 1 lập chỉ mục, không giống bashkshnơi họ đang 0-lập chỉ mục.
Stephen Kitt

2
Phải, tất nhiên; câu trả lời ngắn cho câu hỏi này không thay đổi, nhưng vì hình thức dài đã được đề cập, tôi nghĩ cần phải chỉ ra sự khác biệt trong hành vi ở đó.
Stephen Kitt

22
Chỉ số tiêu cực chỉ hoạt động trong bash 4.3 trở lên.
cuonglm

10
Phiên bản Bash đi kèm với Mac OS X ít nhất là v10.11.5 chỉ là 3.2, vì vậy, phiên bản này không hoạt động trên máy Mac.
Doktor J

45

Bash hiện đại (v4.1 trở lên)

Bạn có thể đọc phần tử cuối cùng tại chỉ mục -1:

$ a=(a b c d e f)
$ echo ${a[-1]}
f

Hỗ trợ truy cập các mảng được lập chỉ mục số từ cuối bằng cách sử dụng các chỉ mục âm bắt đầu với bash phiên bản 4.1-alpha .

Bash cũ hơn (v4.0 trở về trước)

Bạn phải lấy chiều dài mảng từ ${#a[@]}rồi trừ đi một phần tử để lấy phần tử cuối cùng:

$ echo ${a[${#a[@]}-1]}
f

Vì bash coi các chỉ số mảng là một biểu thức số học, không cần ký hiệu bổ sung, chẳng hạn như $((...)), để buộc đánh giá số học.


cái cuối cùng không làm việc cho tôi; Tôi đang sử dụng Bash v4.1.2 (1): thay vì in mục cuối cùng, nó chỉ in ra toàn bộ mảng.
Alexej Magura

câu trả lời của @ cuonglm, tuy nhiên.
Alexej Magura

Câu trả lời sẽ tốt hơn nữa nếu bạn có thể đủ điều kiện modernvới một phiên bản.
Samveen

1
Chính xác những gì cần thiết để làm cho anwser hoàn hảo.
Samveen

1
Cảm ơn vì điều này. Tôi đã sử dụng echo $ {a [$ (($ {# a [@]} - 1]))} vì tôi không biết về "bash coi các chỉ số mảng như một biểu thức số học".
Bruno Bronosky

15

bashgán mảng, tham chiếu, bỏ đặt với chỉ số âm chỉ được thêm vào bash 4.3 . Với phiên bản cũ hơn bash, bạn có thể sử dụng biểu thức trong chỉ mụcarray[${#array[@]-1}]

Một cách khác, cũng hoạt động với phiên bản cũ hơn bash(bash 3.0 trở lên):

$ a=([a] [b] [c] [d] [e])
$ printf %s\\n "${a[@]:(-1)}"
[e]

hoặc là:

$ printf %s\\n "${a[@]: -1}"
[e]

Sử dụng tiêu cực bù đắp, bạn cần phải tách biệt :với -để tránh bị nhầm lẫn với các :-mở rộng.


1
Làm điều đó "${a[@]: -1}"và nó sẽ làm việc (bên cạnh bashzsh) cũng trong ksh.
Janis

Các tài liệu Kornshell ( www2.research.att.com/sw/doad/man/man1/ksh.html ) chỉ định nó hoàn toàn. (Không kiểm tra tài liệu của zshhoặc bash; nhưng tôi đã kiểm tra nó trong cả ba vỏ.)
Janis

@Janis: đọc lại tài liệu bash, nó cũng đề cập đến cái này nữa. Cảm ơn một lần nữa.
cuonglm

4

mảng

(Các) thay thế cũ nhất trong bash (Kể từ bash 3.0+) là:

$ a=(aa bb cc dd ee)
$ echo "${a[@]:(-1)}   ${a[@]: -1}   ${a[@]:(~0)}   ${a[@]:~0}"
ee   ee   ee   ee

Không gian được yêu cầu để tránh việc giải thích :theo sau là một điểm trừ -khi mở rộng "${var:-abc}"(Sử dụng các giá trị mặc định).

Đây ~là một phủ định bit bit số học (tương đương với phần bù của một hoặc lật tất cả các bit ). Từ người đàn ông bash:

ĐÁNH GIÁ ARITHMETIC

      ! ~         logical and bitwise negation  

Kể từ bash-4.2 + cũng:

$ echo "${a[-1]}   ${a[(~0)]}"
ee   ee

Kể từ bash 5.0+ cũng:

$ echo "${a[~0]}"
ee

Đối với tất cả các phiên bản bash (bash cũ hơn):

$ echo "${a[   ${#a[@]}-1   ]}"    # spaces added **only** for readability
ee

@

Đối với các đối số vị trí (kể từ bash 2.01):

$ set aa bb cc dd ee
$ echo "${@:(-1)} ${@:~0} ${@: -1} ${@:$#}   ${!#}"
ee ee ee   ee

Một giải pháp di động cho tất cả các vỏ là sử dụng eval:

eval printf '"%s\n"' \"\${$#}\"

Bạn có một tài liệu tham khảo cho cú pháp bash 5+ không? Tôi đã tìm kiếm tất cả 58 trường hợp ~trong hướng dẫn và không thấy nó.
Tom Hale

... và làm thế nào để bạn làm điều đó với $@? bash: ${@[@]:(-1)}: bad substitution
Tom Hale

1
Có, có một man bashtài liệu tham khảo (kiểm tra câu trả lời mở rộng tại tiêu đề @). @TomHale
Isaac

1
Các @không một mảng (tốt, không đầy đủ một mảng ) trong bash và nó không chấp nhận các chỉ số ( []subscript) cho các đối số cá nhân. Bạn cần sử dụng ${@:(-1)}hoặc tương đương. Kiểm tra mục mở rộng tại @tiêu đề. @TomHale
Isaac

-2

Ngoài ra, bạn có thể làm điều này:

$ a=(a b c d e f)
$ echo ${a[$(expr ${#a[@]} - 1)]}

Kết quả:

$ f

Những gì bạn đang làm là lấy tất cả số phần tử trong mảng và trừ -1 do bạn nhận được tất cả các phần tử, không bắt đầu từ chỉ số mảng bằng 0.

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.