Использование переменных для добавления в словарь Bash

Попытка создать динамический набор пар ключ-значение с помощью серии циклов for, которые в основном извлекают из файла JSON для создания серии массивов, которые затем повторяются для определения пар ключ-значение.

Проблема, с которой я сталкиваюсь, заключается в том, что я продолжаю получать следующую ошибку при запуске скрипта:

array[key]=value: command not found

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

Вот как я пытаюсь добавить в массивы.

$array[$key]=$value

Не совсем уверен, что я пытаюсь сделать что-то, что просто невозможно сделать.

Я пробовал несколько вариантов скрипта, все с той же ошибкой «команда не найдена».

Вот полный сценарий, написанный на данный момент:

#!/bin/bash 

backupTargets=()
#currently active nodes
nodeArray=()
#add proxmox api token
pveTOK=FOO

get_nodes () { 
#grab currently active nodes 
curl -s -k -H "Authorization: PVEAPIToken=$pveTOK" https:///api2/json/nodes | jq . > nodes
#use jq to extract inital data from the curl command, remove returns with tr and use sed to clean things up to make it ready to be placed 
#in an array
nodes=$(jq [.data[].node] nodes | tr -d '\n'| sed 's/\[//g; s/\]//g; s/ //g; s/,/ /g; s/\"//g')
nodeArray=($nodes)
}

declare_variables () {
for node in "${nodeArray[@]}"
do
declare -A $node
done
}

get_backupVMs () {
#some nested for loops that iterate over the nodes to create a dictionary that contains each vm and the associated tags
for node in "${nodeArray[@]}"
do
hosts=()
curl -s -k -H "Authorization: PVEAPIToken=$pveTOK" https:///api2/json/nodes/$node/qemu | jq . > $node
name=$(jq [.data[].name] $node | tr -d '\n'| sed 's/\[//g; s/\]//g; s/ //g; s/,/ /g; s/\"//g')
hosts+=($name)
for host in "${hosts[@]}"
do
tags=$(jq '.data[] | select(.name=='\"$host\"').tags' $node | sed 's/\[//g; s/\]//g; s/ //g; s/;/ /g; s/\"//g')
$node[$host]=$tags
done
done
}

get_nodes
declare_variables
get_backupVMs

Уберите $ перед array. $ — специальный символ оболочки, указывающий на расширение параметра. Когда вы начинаете строку с $, первое слово никогда не интерпретируется как присваивание переменной, а как команда. Вот почему вы получаете ошибку, команда не найдена.

user1934428 13.07.2023 11:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
51
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Сначала нужно объявить ассоциативный массив:

declare -A array

В задании не используйте знак доллара слева:

array[$key]=$value

Кроме того, убедитесь, что вы запускаете скрипт в bash, а не в какой-либо другой оболочке. Ассоциативные массивы поддерживаются не везде (например, в dash).

Я предлагаю использовать двойные кавычки: array[$key] = "$value"

Cyrus 12.07.2023 19:48

@Cyrus: они не нужны в заданиях.

choroba 12.07.2023 19:49

Вы правы, спасибо, что указали на это.

Cyrus 12.07.2023 19:51

@choroba Массивы объявляются в скрипте ранее на основе содержимого другого цикла. Имя самого массива является переменной в цикле for, таким образом, $ в массиве $. Есть ли лучший способ зацикливания и добавления в массив в качестве переменной?

Davis Sayer 12.07.2023 19:59
Ответ принят как подходящий

Соответствующая часть вашего кода в основном:

for node in "${nodeArray[@]}"; do
    declare -A $node
done

for node in "${nodeArray[@]}"; do
    $node[$host]=$tags
done

Bash не поддерживает косвенное обращение к имени переменной, написанное таким образом.

Присваивания распознаются только в том случае, если LHS является допустимым именем переменной, за которым может следовать что-то в скобках, за которым следует знак равенства, за которым следует присваиваемое значение. Однако $node перед расширением не является допустимым именем переменной. (См.: присваивание() в исходном коде Bash.)

Поскольку Bash не распознает ваше присваивание как единицу, он пытается выполнить слово как команду, что приводит к ошибкам, которые вы видели.


Почти наверняка есть лучший способ структурировать логику вашего кода, чтобы этот тип косвенности не требовался, но быстрое и грязное решение — использовать nameref во втором цикле (первый цикл остается неизменным) :

for node in "${nodeArray[@]}"; do
    declare -n ref=$node
    ref[$host]=$tags
done

Кажется, это, по крайней мере, избавило от ошибок, однако, похоже, он не создает нужный мне вывод, по-видимому, меняясь каждый раз, когда скрипт запускается, когда я делаю быстрый echo ${foo[@]}, где foo - известное имя узла, созданное declare -n ref-$node Похоже, мне все-таки придется найти лучшую структуру для логики. Если есть какие-либо предложения о том, с чего начать искать или читать о лучшем способе сделать это, я всегда ценю хороший толчок в правильном направлении.

Davis Sayer 12.07.2023 22:33

@DavisSayer Я полагаю ref-$node это опечатка? Обратите внимание, что вы должны выполнять declare -n каждый раз, когда вы меняете место назначения ссылки - оно должно быть внутри цикла.

jhnc 12.07.2023 22:42

Ах, да, это должно было читаться ref=$node, и оно объявляется в начале цикла for node in "${nodeArray[@]}" — в основном я удалил функцию declare_variables из скрипта и интегрировал nameref в начало функции get_backupVMs.

Davis Sayer 12.07.2023 22:48

@DavisSayer Я ожидал, что вы сохраните первый цикл for без изменений (я исключил его, чтобы сосредоточиться на изменении). Вам все еще нужно declare -A ваши фактические ассоциативные массивы, иначе присваивание ref[$host] будет рассматриваться как применение к индексированному массиву, а $host будет приведено к целому числу.

jhnc 12.07.2023 22:57

Я думаю, вам нужен псевдомногомерный массив (поскольку bash поддерживает только одно измерение, вам нужно создать специальные ключи)

declare -A array
for n in ${nodeArray[@]}; do
   for h in ${hosts[@]}; do
     array["$n,$h"] = "tags $n $h"
   done
done

Другие вопросы по теме