Как итерировать индексы массива в zsh?

Вопрос или проблема

В 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

  1. Индексация массивов:
    В zsh индексы обычных массивов начинаются с 1, в то время как в bash они начинаются с 0. Если вы работаете в режиме совместимости с ksh, индексы также будут начинаться с 1.

  2. Синтаксис обращения к элементам массива:
    В zsh для обращения к элементам обычного массива используется следующий синтаксис:

    array[n]

    Здесь n — номер индекса.

  3. Объявление массива:
    Чтобы объявить массив в 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

Здесь мы используем специальный модификатор @, который позволяет работать с массивами и сохранять пустые элементы.

Полезные советы

  1. Использование конструкции ${(@k)array} для получения ключей:
    Это позволяет извлекать ключи ассоциативного массива, что может быть полезно, если вы хотите перебрать их.

  2. Печать всех элементов с помощью print:
    Чтобы распечатать все элементы массива, можно использовать:

    print -rC1 -- "${my_array[@]}"
  3. Использование диапазонов:
    Также вы можете использовать фигурные скобки и диапазон для генерации последовательностей:

    for i in {1..4}; do
       echo $i
    done

Заключение

Работа с массивами в zsh требует понимания некоторых различий по сравнению с bash. Соблюдение описанных рекомендаций поможет избежать ошибок и повысить эффективность ваших скриптов. Надеюсь, данное руководство будет полезным в вашей дальнейшей работе с zsh!

Оцените материал
Добавить комментарий

Капча загружается...