Вопрос или проблема
В bash мы можем перебирать индексы массива так:
~$ for i in "${!test[@]}"; do echo $i; done
где test – это массив, скажем,
~$ test=(a "b c d" e f)
так что вывод будет выглядеть так:
0
1
2
3
Однако, когда я делаю то же самое в zsh, я получаю ошибку:
➜ ~ for i in "${!test[@]}"; do echo $i; done
zsh: событие не найдено: test[@]
Что происходит?
Каков правильный способ обхода индексов в zsh?
Массивы zsh
– это обычные массивы, как в большинстве других оболочек и языков, они не такие, как ассоциативные массивы в ksh/bash, где ключи ограничены положительными целыми числами (так называемые разреженные массивы). zsh
имеет отдельный тип переменной для ассоциативных массивов (с ключами, представляющими произвольные последовательности из 0 или более байтов).
Таким образом, индексы для обычных массивов всегда являются целыми числами от 1 до размера массива (при условии, что совместимость с ksh не включена, в этом случае индексы массива начинаются с 0 вместо 1).
Итак:
typeset -a array
array=(a 'b c' '')
for ((i = 1; i <= $#array; i++)) print -r -- $array[i]
Хотя обычно вы будете перебирать элементы массива, а не их индексы:
for element ("$array[@]") print -r -- $element
(синтаксис "$array[@]"
, в отличие от $array
, сохраняет пустые элементы).
Или:
print -rC1 -- "$array[@]"
чтобы передать все элементы в команду.
Теперь, чтобы перебрать ключи ассоциативного массива, синтаксис таков:
typeset -A hash
hash=(
key1 value1
key2 value2
'' empty
empty ''
)
for key ("${(@k)hash}") printf 'key=%s value=%s\n' "$key" "$hash[$key]"
(снова с @
внутри кавычек, используемым для сохранения пустых элементов).
Хотя вы также можете передавать как ключи, так и значения командам с помощью:
printf 'key=%s value=%s\n' "${(@kv)hash}"
Для получения дополнительной информации о различных конструкциях массивов в оболочках, подобных Bourne, смотрите Проверка поддержки массивов в оболочке
Как указано в этой статье Руководство пользователя по Z-Shell – Глава 5: Подстановки:
Это расширяется для других параметров следующим образом:
% array=(one two three)
% print -l "${array[@]}"
one
two
three
и более в общем для всех форм подстановки с использованием другого флага, (@):
% print -l "${(@)array}"
one
two
three
Так что, возможно, просто попробуйте использовать второй способ?
И с фигурными скобками { }
:
% test=(a "b c d" e f)
% for i in {1..$#test}; do echo $i; done
1
2
3
4
%
.
Ответ или решение
Вопрос о том, как перебрать индексы массива в zsh, становится актуальным для пользователей, которые привыкли к синтаксису bash, где эта процедура довольно проста. В zsh существуют некоторые отличия, которые важно учитывать. Рассмотрим все детали, которые вам понадобятся для корректной работы с массивами в zsh.
Различия в работе с массивами между bash и zsh
-
Индексация массивов:
В zsh индексы обычных массивов начинаются с 1, в то время как в bash они начинаются с 0. Если вы работаете в режиме совместимости с ksh, индексы также будут начинаться с 1. -
Синтаксис обращения к элементам массива:
В zsh для обращения к элементам обычного массива используется следующий синтаксис:array[n]
Здесь
n
— номер индекса. -
Объявление массива:
Чтобы объявить массив в zsh, используйте командуtypeset -a
. Например:typeset -a my_array my_array=(a "b c" d e)
Перебор индексов массива в zsh
Чтобы перебрать индексы массива в zsh, вы можете воспользоваться циклом for
и диапазоном индексов. Например, если ваш массив называется my_array
, вы можете использовать следующую конструкцию:
typeset -a my_array
my_array=(a "b c" d e)
for ((i = 1; i <= $#my_array; i++)); do
print "$i"
done
Перебор элементов массива
Часто более удобным оказывается перебор самих элементов массива, а не их индексов. Для этого можно воспользоваться другим подходом:
for element in "${my_array[@]}"; do
print "$element"
done
Этот метод более читабелен и удобен, особенно если вам не нужны индексы.
Работа с ассоциативными массивами
Если вам необходимо работать с ассоциативными массивами, вам нужно использовать другой синтаксис. Например:
typeset -A my_hash
my_hash=(
key1 value1
key2 value2
empty ''
)
for key in "${(@k)my_hash}"; do
printf 'key=%s value=%s\n' "$key" "${my_hash[$key]}"
done
Здесь мы используем специальный модификатор @
, который позволяет работать с массивами и сохранять пустые элементы.
Полезные советы
-
Использование конструкции
${(@k)array}
для получения ключей:
Это позволяет извлекать ключи ассоциативного массива, что может быть полезно, если вы хотите перебрать их. -
Печать всех элементов с помощью
print
:
Чтобы распечатать все элементы массива, можно использовать:print -rC1 -- "${my_array[@]}"
-
Использование диапазонов:
Также вы можете использовать фигурные скобки и диапазон для генерации последовательностей:for i in {1..4}; do echo $i done
Заключение
Работа с массивами в zsh требует понимания некоторых различий по сравнению с bash. Соблюдение описанных рекомендаций поможет избежать ошибок и повысить эффективность ваших скриптов. Надеюсь, данное руководство будет полезным в вашей дальнейшей работе с zsh!