ZSH: Как динамически задать имя и содержимое ассоциативного массива?

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

Я устанавливаю свой ассоциативный массив следующим образом:

$ foo=test
$ set -A $foo "a b" "1 2" "c d" "3 4"
$ for key val in "${(@kv)test}"; do echo "$key -> $val" done
a b -> 1 2
c d -> 3 4

Как заменить содержимое массива "a b" "1 2" "c d" "3 4" переменной?

set -A array value1 value2 — это древний синтаксис ksh с начала 80-х¹ для определения обычных массивов. Он не определяет ассоциативные массивы и поддерживается только для совместимости с ksh88.

Флаги расширения параметров k и v имеют смысл только для переменных, являющихся ассоциативными массивами. Для других типов переменных, включая обычные массивы, они просто игнорируются. Таким образом, здесь "${(@kv)test}" то же, что и "${(@)test}" или "$test[@]" и разворачивается во все элементы обычного массива².

Чтобы объявить ассоциативный массив, вы должны использовать современный

assoc=(
  'key 1' 'value 1'
  'key 2' 'value 2'
)

синтаксис³ после объявления assoc как ассоциативного массива с помощью:

typeset -A assoc

С недавними версиями, теперь, когда typeset стал двойной командой / встроенной командой, вы также можете выполнить и объявление, и присвоение одновременно с помощью:

typeset -A assoc=(
  'key 1' 'value 1'
  'key 2' 'value 2'
)

Затем вы можете выполнить:

printf '"%s" => "%s"\n' "${(@kv)assoc}"

Или

for k v ("${(@kv)assoc}") print -r -- "$k => $v"

Или:

for k ("${(@k)assoc}") print -r -- "$k => $assoc[$k]"

Чтобы перебрать его ключи и значения.

Чтобы определить ассоциативный массив, имя которого хранится в переменной из списка ключей и значений, вы можете использовать:

setassoc() {
  typeset -gA $1; shift
  eval "$1"='( "$@[2,-1]" )'
}
foo=test
setassoc $foo 'key 1' 'value 1' 'key 2' 'value 2'

Или, если список ключей и значений хранится в обычной переменной массива:

array=('key 1' 'value 1' 'key 2' 'value 2')
setassoc $foo "$array[@]"

Кстати, set -A мог бы использоваться здесь, чтобы избежать eval (не то чтобы было что-то плохое в использовании eval таким образом здесь), но нужно убедиться, что переменная установлена как ассоциативный массив вначале:

foo=test
typeset -A $foo
set -A $foo "$array[@]"

И будьте осторожны, если включена эмуляция ksh (особенно, если включена опция ksharrays), ее нужно изменить на:

set -A $foo -- "$array[@]"

¹ Дэвид Корн выбрал -A, так как set -a из оболочки Bourne уже было занято. Это также объясняет, почему read -A используется в ksh93 или zsh для чтения строки в массив. bash не поддерживает set -A, хотя он основывает свой дизайн массивов на ksh88 (в то время как zsh ближе к дизайну массивов csh, чем ksh), и выбрал read -a вместо read -A. Возможный источник путаницы: typeset -a объявляет обычный массив, в то время как typeset -A используется для ассоциативных массивов (оба из ksh)

² и for k v присваивает k и v элементи массива поочередно, что объясняет вывод и почему он в том же порядке, как в присвоении (в то время как для ассоциативных массивов порядок не гарантируется).

³ этот синтаксис на самом деле из zsh в 1990 году, и позже появился в ksh93 (хотя там, как часть большого вариационного типирования, которое также включает составные переменные) и bash (который не имел массивов до версии 2.0 в 1996 году), хотя он в значительной степени вдохновлен csh (первая оболочка с массивами из конца 70-х), где синтаксис set array = (foo bar)

@Stéphane Chazelas: Поскольку у нас есть такой изящный синтаксис для объявления и назначения ассоциативного массива одновременно, я попробовал избежать “древней” команды set -A в вашем предложенном решении:

foo=test
typeset -A $foo
set -A $foo "$array[@]"

Однако, хотя typeset -A test=( "$array[@]" ) работает безупречно, возникает ошибка при использовании typeset -A $foo=( "$array[@]" ). Говорится: bad pattern: test=( key 1.

Ок, можно попробовать typeset -A $foo=( "${(qq)array[@]}" ), чтобы элементы массива были заключены в кавычки, но сейчас ошибка такая: bad pattern: test=( 'key 1'. Не намного лучше. Есть ли корректный способ сделать это?

.

Ответ или решение

Чтобы динамически задать имя и содержимое ассоциативного массива в ZSH, нужно правильно подходить к синтаксису и методам, которые используются для работы с переменными в этой оболочке. Вопрос поднимает тему создания и инициализации ассоциативного массива, используя переменную для имени массива и массив строк в качестве его содержимого.

Шаги для создания ассоциативного массива в ZSH

  1. Объявление ассоциативного массива:
    В ZSH ассоциативные массивы должны быть явно объявлены. Это можно сделать с помощью команды typeset -A. Например:

    typeset -A assocArray
  2. Инициализация ассоциативного массива с динамическим именем:
    Чтобы задать динамическое имя для вашего массива и наполнить его содержимым, вы можете использовать функцию setassoc. Эта функция объявляет массив и заполняет его данными:

    setassoc() {
     typeset -gA $1
     eval "$1"='( "$@[2,-1]" )'
    }
    • $1 – это имя переменной, которой будет присвоен массив.
    • eval используется для динамического создания массива с указанными парами ключ-значение.
  3. Передача данных в функцию:
    Допустим, у вас есть массив данных:

    array=('key 1' 'value 1' 'key 2' 'value 2')

    Вы можете вызвать функцию для создания ассоциативного массива с именем, хранящимся в переменной foo:

    foo=test
    setassoc $foo "$array[@]"
  4. Обход и печать ассоциативного массива:
    Чтобы вывести ключи и значения массива, можно использовать следующий синтаксис:

    for key value in "${(@kv)test}"; do
     echo "$key => $value"
    done

Практические замечания

  • Учитывайте версии ZSH: Новые возможности команд typeset и их использование могут зависеть от версии ZSH, с которой вы работаете.
  • Оптимизация и кросс-платформенность: Следует избегать устаревших команд вроде set -A, особенно если вы планируете переносить скрипты на другие системы или оболочки.
  • Использование eval: Хотя eval позволяет гибко и динамично задавать массивы, стоит использовать его осторожно и лишь в надежных контекстах, чтобы избежать ненамеренного выполнения вредоносного кода.

Таким образом, при помощи указанных методов вы сможете точно и гибко управлять именем и содержимым ассоциативных массивов в ZSH, что позволяет создавать более модульные и динамичные оболочечные скрипты.

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

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