Apakah ada cara membaca elemen terakhir array dengan bash?

68

Jika saya memiliki array dengan 5 elemen, misalnya:

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

Menggunakan echo ${myarray[4]}saya bisa melihat apa yang ada di dalamnya.

Tetapi bagaimana jika saya tidak tahu jumlah elemen dalam array yang diberikan? Apakah ada cara membaca elemen terakhir dari array panjang yang tidak diketahui? yaitu pembacaan elemen pertama dari kanan ke kiri untuk array apa pun?

Saya ingin tahu bagaimana melakukan ini di bash.

3kstc
sumber
$@bukan array yang tepat (tidak dapat disalin). Untuk itu, lihat Mendapatkan argumen terakhir yang diteruskan ke skrip shell .
Tom Hale

Jawaban:

89

Anda bisa menggunakan indeks negatif ${myarray[-1]} untuk mendapatkan elemen terakhir. Anda dapat melakukan hal yang sama untuk yang kedua terakhir, dan seterusnya; di Bash:

Jika subskrip yang digunakan untuk mereferensikan elemen dari array yang diindeks mengevaluasi ke angka kurang dari nol, itu ditafsirkan sebagai relatif terhadap yang lebih besar dari indeks maksimum array, sehingga indeks negatif menghitung mundur dari akhir array, dan sebuah indeks -1 mengacu pada elemen terakhir.

Hal yang sama juga berfungsi untuk penugasan. Ketika dikatakan "ekspresi" itu benar-benar berarti ekspresi; Anda dapat menulis dalam ekspresi aritmatika apa pun di sana untuk menghitung indeks, termasuk yang menghitung menggunakan panjang array ${#myarray[@]}secara eksplisit.

Michael Homer
sumber
2
Anda dapat melakukannya di kshdan zshjuga.
Janis
5
Dengan demikian zsh, secara default array adalah 1-diindeks, tidak seperti bashdan di kshmana mereka diindeks 0.
Stephen Kitt
2
Ya tentu saja; jawaban singkat untuk pertanyaan ini tidak berubah, tetapi karena bentuk panjang disebutkan saya pikir perlu untuk menunjukkan perbedaan perilaku di sana.
Stephen Kitt
22
Indeks negatif hanya berfungsi di bash 4.3 ke atas.
cuonglm
10
Versi Bash yang disertakan dengan Mac OS X setidaknya v10.11.5 hanya 3.2, jadi ini tidak berfungsi pada Mac.
Doktor J
45

Bash modern (v4.1 atau lebih baik)

Anda dapat membaca elemen terakhir di indeks -1:

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

Dukungan untuk mengakses array yang diindeks secara numerik dari akhir menggunakan indeks negatif dimulai dengan bash versi 4.1-alpha .

Bash yang lebih lama (v4.0 atau sebelumnya)

Anda harus mendapatkan panjang array dari ${#a[@]}dan kemudian kurangi satu untuk mendapatkan elemen terakhir:

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

Karena bash memperlakukan subscript array sebagai ekspresi aritmatika, maka tidak perlu notasi tambahan, seperti $((...)), untuk memaksa evaluasi aritmatika.

John1024
sumber
yang terakhir tidak bekerja untuk saya; Saya menggunakan Bash v4.1.2 (1): alih-alih mencetak item terakhir, ia hanya mencetak seluruh array.
Alexej Magura
Namun, jawaban cuonglm bekerja.
Alexej Magura
Jawabannya akan lebih baik jika Anda bisa memenuhi syarat moderndengan versi.
Samveen
1
Persis apa yang dibutuhkan untuk membuat anwser sempurna.
Samveen
1
Terima kasih untuk ini. Saya menggunakan echo $ {a [$ (($ {# a [@]} - 1]))} karena saya tidak tahu tentang "bash treats subscript array sebagai ekspresi aritmatika".
Bruno Bronosky
15

bashpenugasan array, referensi, unsetting dengan indeks negatif hanya ditambahkan di bash 4.3 . Dengan versi yang lebih lama bash, Anda dapat menggunakan ekspresi dalam indeksarray[${#array[@]-1}]

Cara lain, juga bekerja dengan versi yang lebih lama bash(bash 3.0 atau lebih baik):

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

atau:

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

Menggunakan offset negatif, Anda harus memisahkan :dengan -untuk menghindari kebingungan dengan :-ekspansi.

cuonglm
sumber
1
Buat itu "${a[@]: -1}"dan itu akan berhasil (selain bashdan zsh) juga di ksh.
Janis
Dokumen Kornshell ( www2.research.att.com/sw/download/man/man1/ksh.html ) menetapkannya sepenuhnya. (Belum memeriksa dokumen dari zshatau bash; tetapi saya mengujinya dalam tiga kulit.)
Janis
@ Janis: baca kembali dokumentasi bash, juga disebutkan tentang yang ini. Terima kasih lagi.
cuonglm
4

array

Alternatif tertua di bash (Sejak bash 3.0+) adalah:

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

Ruang diperlukan untuk menghindari interpretasi :diikuti oleh minus -sebagai perluasan dari "${var:-abc}"(Gunakan Nilai Default).

Ini ~adalah negasi bitwise aritmatika (setara dengan komplemen seseorang atau membalikkan semua bit ). Dari man bash:

EVALUASI ARITHMETIK

      ! ~         logical and bitwise negation  

Sejak bash-4.2 + juga:

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

Sejak bash 5.0+ juga:

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

Untuk semua versi bash (versi lama):

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

@

Untuk argumen posisional (sejak bash 2.01):

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

Solusi portabel untuk semua cangkang adalah menggunakan eval:

eval printf '"%s\n"' \"\${$#}\"
Ishak
sumber
Apakah Anda memiliki referensi untuk sintaks bash 5+? Saya mencari semua 58 contoh ~dalam manual dan tidak melihatnya.
Tom Hale
... dan bagaimana Anda melakukannya $@? bash: ${@[@]:(-1)}: bad substitution
Tom Hale
1
Ya, ada man bashreferensi (periksa jawaban yang diperluas di @ title). @ TomHale
Isaac
1
The @adalah tidak array (baik, tidak sepenuhnya array ) di bash dan tidak menerima indeks ( []) subscript untuk argumen individu. Anda harus menggunakan ${@:(-1)}atau setara. Periksa entri yang diperluas di @judul. @ TomHale
Isaac
-2

Anda juga dapat melakukan ini:

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

Hasil:

$ f

Apa yang Anda lakukan adalah mendapatkan semua jumlah elemen dalam array dan kurangi -1 karena Anda mendapatkan semua elemen, tidak dimulai dari indeks array yaitu 0.

Javier Salas
sumber