1

Trying to create a dynamic set of Key-Value pairs through a series of for loops that basically pull from a JSON file to create a series of arrays that are then iterated through to define the key-value pairs.

The issue I am having is that I keep getting the following error when I run the script:

array[key]=value: command not found

The information it should be writing to the array looks correct, just gives me that error.

Here's how I am trying to add to the arrays.

$array[$key]=$value

Not quite sure if I am trying to do something that simply cannot be done.

I've tried a few variations of the script, all with the same "command not found" error.

Here is the full script as written at the moment:

#!/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
1
  • Remove the $ in front of array. $ is a special character for the shell indicating parameter expansion. When you start a line with $, the first word is never interpreted as a variable assignement, but as a command. That's why you get the error command not found. Commented Jul 13, 2023 at 9:37

3 Answers 3

2

The relevant part of your code is basically:

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

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

Bash does not support variable name indirection written like this.

Assignments are only recognized if the LHS is a valid variable name, optionally followed by something in brackets, followed by an equal sign, followed by the value to be assigned. However $node before expansion is not a valid variable name. (See: assignment() in the Bash source-code.)

Because Bash does not recognise your assignment as being one, it tries to execute the word as a command, which gives the errors you have seen.


There is almost certainly a better way to structure the logic of your code so that this type of indirection is not needed but a quick-and-dirty fix is to use a nameref in the second loop (the first loop remains unchanged):

for node in "${nodeArray[@]}"; do
    declare -n ref=$node
    ref[$host]=$tags
done
Sign up to request clarification or add additional context in comments.

4 Comments

This seems to have at least gotten rid of the errors, however it doesn't seem to create the output I need, seemingly changing every time the script is run when I do a quick echo ${foo[@]} where foo is a known node name created by the declare -n ref-$node Looks like I will have to find a better structure for the logic after all. If there are any suggestions on where to start looking or reading about a better way to do this, I always appreciated a good push in the right direction.
@DavisSayer I assume ref-$node is a typo? Note that you have to perform a declare -n every time you change the destination of the reference - it has to be inside the loop
Ach, yeah, that was supposed to read ref=$node and it is being declared at the beginning of the loop for node in "${nodeArray[@]}" - basically I removed the declare_variables function from the script and integrated the nameref into the beginning of the get_backupVMs function.
@DavisSayer I expected you to retain the first for loop unchanged (I elided it to focus on the change). You still need to declare -A your actual associative arrays, otherwise the ref[$host] assignment will be treated as applying to an indexed array and $host will be cast to an integer.
0

First, you need to declare the associative array:

declare -A array

In the assignment, don't use the dollar sign on the left hand side:

array[$key]=$value

Also, make sure you're running the script in bash, not some other shell. Associative arrays aren't supported everywhere (e.g. in dash).

3 Comments

I suggest to use double quotes: array[$key]="$value"
@Cyrus: They aren't needed in assignments.
@choroba The arrays are declared earlier in the script based on the contents of another loop. The name of the array itself is a variable in the for loop, thus the $ in $array. Is there a better way of looping and adding to an array as a variable?
0

I guess you need a pseudo multidimensional array (as bash supports only 1 dimension you have to create special keys)

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.