Czy istnieje sposób odczytu ostatniego elementu tablicy za pomocą bash?

68

Jeśli mam tablicę z 5 elementami, na przykład:

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

Za pomocą echo ${myarray[4]}Widzę, co trzyma.

Ale co, jeśli nie znam liczby elementów w danej tablicy? Czy istnieje sposób odczytu ostatniego elementu tablicy o nieznanej długości? tj. Pierwszy element odczytuje od prawej do lewej dla dowolnej tablicy?

Chciałbym wiedzieć, jak to zrobić w bash.

3kstc
źródło
$@nie jest dokładnie tablicą (nie można go zapisać w indeksie). W tym celu zobacz Pobieranie ostatniego argumentu do skryptu powłoki .
Tom Hale,

Odpowiedzi:

89

Możesz po prostu użyć indeksu ujemnego, ${myarray[-1]} aby uzyskać ostatni element. Możesz zrobić to samo dla przedostatniego i tak dalej; w Bash:

Jeśli indeks dolny użyty do odwołania do elementu tablicy indeksowanej ma wartość mniejszą od zera, jest interpretowany jako relatywny do jednego większego niż maksymalny indeks tablicy, więc indeksy ujemne liczą się od końca tablicy, a indeks -1 odnosi się do ostatniego elementu.

To samo działa również w przypadku przypisania. Kiedy mówi „wyrażenie”, naprawdę oznacza wyrażenie; możesz tam napisać dowolne wyrażenie arytmetyczne, aby obliczyć indeks, łącznie z tym, który oblicza, używając ${#myarray[@]}jawnie długości tablicy .

Michael Homer
źródło
2
Można to zrobić w kshi zshtak dobrze.
Janis
5
Z zshchoć domyślnie tablice są indeksowane 1, w przeciwieństwie do bashi kshgdzie są 0-indeksowane.
Stephen Kitt
2
Tak oczywiście; krótka odpowiedź na to pytanie się nie zmienia, ale ponieważ wspomniano o długiej formie, uznałem, że konieczne jest wskazanie różnicy w zachowaniu.
Stephen Kitt
22
Indeks ujemny działa tylko w wersji bash 4.3 i nowszych.
cuonglm
10
Wersja Bash zawarta w systemie Mac OS X od wersji co najmniej 10.11.5 to tylko 3.2, więc to nie działa na komputerach Mac.
Doktor J
44

Nowoczesne bash (wersja 4.1 lub nowsza)

Ostatni element możesz przeczytać na indeksie -1:

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

Obsługa dostępu do tablic indeksowanych numerycznie od końca przy użyciu indeksów ujemnych rozpoczęła się od wersji bash 4.1-alfa .

Starsza wersja bash (wersja 4.0 lub wcześniejsza)

Musisz uzyskać długość tablicy od, ${#a[@]}a następnie odjąć jedną, aby uzyskać ostatni element:

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

Ponieważ bash traktuje indeksy tablicowe jako wyrażenie arytmetyczne, nie ma potrzeby stosowania dodatkowej notacji, na przykład w $((...))celu wymuszenia oceny arytmetycznej.

John1024
źródło
ostatni nie działa dla mnie; Używam Bash v4.1.2 (1): zamiast drukować ostatni element, po prostu drukuje całą tablicę.
Alexej Magura
Odpowiedź @ cuonglm działa jednak.
Alexej Magura
Odpowiedź byłaby jeszcze lepsza, gdybyś mógł zakwalifikować modernsię do wersji.
Samveen
1
Dokładnie to, co było potrzebne, aby anwser był idealny.
Samveen
1
Dziękuję Ci za to. Używałem echa $ {a [$ (($ {# a [@]} - 1]))}, ponieważ nie wiedziałem o „bash traktuje indeksy tablic jako wyrażenie arytmetyczne”.
Bruno Bronosky,
15

bashprzypisanie tablicy, odwołanie, rozbrojenie z indeksem ujemnym dodano tylko w bash 4.3 . W starszych wersjach bashmożesz używać wyrażeń w indeksiearray[${#array[@]-1}]

Innym sposobem jest także praca ze starszą wersją bash(bash 3.0 lub lepsza):

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

lub:

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

Korzystanie ujemny offset, trzeba oddzielić :ze -aby uniknąć pomylenia z :-ekspansji.

Cuonglm
źródło
1
Zrób to "${a[@]: -1}"i będzie działać (oprócz bashi zsh) również w ksh.
Janis
Dokumenty Kornshell ( www2.research.att.com/sw/download/man/man1/ksh.html ) określają to całkowicie. (Nie sprawdziłem dokumentów zshlub bash; ale przetestowałem to we wszystkich trzech powłokach).
Janis
@Janis: ponownie przeczytaj dokumentację bash, wspomniałem także o tej. Dzięki jeszcze raz.
cuonglm
4

szyk

Najstarsze alternatywy w bash (od wersji bash 3.0+) to:

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

Spacja jest wymagana, aby uniknąć interpretacji :następującego po niej znaku minus -jako rozwinięcia "${var:-abc}"(Użyj wartości domyślnych).

~Jest arytmetyka negacja bitowe (odpowiada jednemu uzupełnień lub odwrócić wszystkie bity ). From man bash:

OCENA ARYTMETYCZNA

      ! ~         logical and bitwise negation  

Od wersji bash-4.2 + również:

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

Od wersji bash 5.0+:

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

Dla wszystkich wersji bash (starsza bash):

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

@

Dla argumentów pozycyjnych (od wersji bash 2.01):

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

Przenośnym rozwiązaniem dla wszystkich powłok jest użycie eval:

eval printf '"%s\n"' \"\${$#}\"
Izaak
źródło
Czy masz odniesienie do składni bash 5+? Przeszukałem wszystkie 58 wystąpień ~w instrukcji i nie widziałem tego.
Tom Hale
... i jak to robisz $@? bash: ${@[@]:(-1)}: bad substitution
Tom Hale
1
Tak, istnieje man bashodniesienie (sprawdź rozszerzoną odpowiedź w tytule @). @TomHale
Isaac
1
@To nie tablica (no, nie w pełni tablicą ) w bash i nie akceptują index ( []) indeks dla poszczególnych argumentów. Musisz użyć ${@:(-1)}lub ekwiwalentu. Sprawdź rozwinięty wpis przy @tytule. @TomHale
Isaac
-2

Możesz także to zrobić:

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

Wynik:

$ f

To, co robisz, to uzyskanie całej liczby elementów w tablicy i odjęcie -1, ponieważ otrzymujesz wszystkie elementy, nie zaczynając od indeksu tablicy, który wynosi 0.

Javier Salas
źródło