Есть ли способ чтения последнего элемента массива с помощью bash?

68

Если у меня есть массив с 5 элементами, например:

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

Используя echo ${myarray[4]}я могу видеть, что он держит.

Но что, если я не знаю количество элементов в данном массиве? Есть ли способ прочитать последний элемент массива неизвестной длины? т.е. первое чтение элемента справа налево для любого массива?

Я хотел бы знать, как это сделать в Bash.

3kstc
источник
$@не совсем массив (не может быть подписан). Для этого см. Получение последнего аргумента, переданного скрипту оболочки .
Том Хейл

Ответы:

89

Вы можете просто использовать отрицательный индекс, ${myarray[-1]} чтобы получить последний элемент. Вы можете сделать то же самое для второго по счету и т. Д .; в Баш:

Если нижний индекс, используемый для ссылки на элемент индексированного массива, оценивается как число меньше нуля, он интерпретируется как относительный к одному, превышающему максимальный индекс массива, поэтому отрицательные индексы отсчитываются от конца массива, и Индекс -1 относится к последнему элементу.

То же самое также работает для назначения. Когда он говорит «выражение», это действительно означает выражение; вы можете написать любое арифметическое выражение для вычисления индекса, включая то, которое вычисляет с использованием длины массива в ${#myarray[@]}явном виде.

Майкл Гомер
источник
2
Вы можете сделать это в kshи zshтакже.
Янис
5
С zshхотя, по умолчанию массивы 1 индексированные, в отличие bashи kshгде они находятся 0 индексированные.
Стивен Китт
2
Да, конечно; краткий ответ на этот вопрос не меняется, но поскольку была упомянута полная форма, я счел необходимым указать на разницу в поведении.
Стивен Китт,
22
Отрицательный индекс работает только в bash 4.3 и выше.
cuonglm
10
Версия Bash, включенная в Mac OS X, начиная с версии 10.11.5, составляет всего 3.2, поэтому на Mac это не работает.
Доктор Дж
45

Modern Bash (v4.1 или лучше)

Вы можете прочитать последний элемент по индексу -1:

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

Поддержка доступа к численно индексированным массивам с конца с использованием отрицательных индексов началась с bash версии 4.1-alpha .

Старая версия (версия 4.0 или более ранняя)

Вы должны получить длину массива ${#a[@]}и затем вычесть единицу, чтобы получить последний элемент:

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

Поскольку bash рассматривает индексы массива как арифметическое выражение, нет необходимости в дополнительных обозначениях, таких как $((...))форсирование арифметической оценки.

John1024
источник
последний не работает для меня; Я использую Bash v4.1.2 (1): вместо печати последнего элемента он просто распечатывает весь массив.
Алексей Магура
Ответ @ cuonglm работает, однако.
Алексей Магура
Ответ был бы еще лучше, если бы вы могли претендовать modernна версию.
Самвин
1
Именно то, что было необходимо, чтобы довести до совершенства.
Самвин
1
Спасибо тебе за это. Я использовал echo $ {a [$ (($ {# a [@]} - 1]))}, потому что я не знал о том, что «bash рассматривает индексы массива как арифметическое выражение».
Бруно Броноски
15

bashприсваивание массива, ссылка, сброс с отрицательным индексом были добавлены только в bash 4.3 . С более старой версией bash, вы можете использовать выражение в индексеarray[${#array[@]-1}]

Другой способ также работает со старой версией bash(bash 3.0 или выше):

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

или же:

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

Используя отрицательное смещение, необходимо отделить :с , -чтобы избежать путать с :-расширением.

cuonglm
источник
1
Сделайте это, "${a[@]: -1}"и это будет работать (помимо bashи zsh) также в ksh.
Янис
Документы Kornshell ( www2.research.att.com/sw/download/man/man1/ksh.html ) определяют его полностью. (Не проверял документы zshили bash; но я проверил это во всех трех оболочках.)
Janis
@Janis: перечитайте документацию по bash, об этом тоже упоминалось. Еще раз спасибо.
cuonglm
4

массив

Самые старые альтернативы в bash (начиная с bash 3.0+):

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

Пробел необходим, чтобы избежать интерпретации с :последующим минусом -как расширением "${var:-abc}"(Использовать значения по умолчанию).

Это ~арифметическое побитовое отрицание (эквивалентное дополнению или переворачиванию всех битов ). От человека Баш:

АРИФМЕТИЧЕСКАЯ ОЦЕНКА

      ! ~         logical and bitwise negation  

Начиная с Баш-4.2 + также:

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

Начиная с bash 5.0+ также:

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

Для всех версий bash (более старая версия bash):

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

@

Для позиционных аргументов (начиная с bash 2.01):

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

Переносимым решением для всех оболочек является использование eval:

eval printf '"%s\n"' \"\${$#}\"
Исаак
источник
У вас есть ссылка на синтаксис bash 5+? Я искал все 58 экземпляров ~в руководстве и не видел его.
Том Хейл
... и как ты это делаешь $@? bash: ${@[@]:(-1)}: bad substitution
Том Хейл
1
Да, есть man bashссылка (проверьте расширенный ответ в @ заголовке). @ TomHale
Исаак
1
@Это не массив (ну, не полностью массив ) в Баш и не принимает индекс ( []) индекс для отдельных аргументов. Вы должны использовать ${@:(-1)}или эквивалентный. Проверьте расширенную запись в @заголовке. @ TomHale
Исаак
-2

Также вы можете сделать это:

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

Результат:

$ f

То, что вы делаете, это получает все количество элементов в массиве и вычитаете -1, потому что вы получаете все элементы, не начиная с индекса массива, который равен 0.

Хавьер Салас
источник