1

I wrote this code to loop through usernames and domains on my LAN. Sadly, the script prints nothing.

#!/bin/bash

construct_array_of_trgts() {
  declare -A usrs_n_dmns
  
  local -a guest_dmns
  local -a usrs=("j" "jim" "o" "root")
  local -a guest_dmns=("raspberrypi" "lenovo")
  for d in "${guest_dmns[@]}"; do 
    PS3="Select the users to include for sshing into $d. Q when done selecting."$'\n'
    local -a targt_usrs
    select u in "${usrs[@]}"; do
      if [[ "$u" ]]; then
        targt_usrs+=("$u")
     elif [[ "$REPLY" == 'q' ]]; then 
      break;
     fi
    done
    usrs_n_dmns["${d}"]="$targt_usrs"
  done
  
}
construct_array_of_trgts

for d in "${!usrs_n_dmns[@]}"; do
  targt_usrs=("${usrs_n_dmns["${d}"]}")
  echo "$usrs_n_dmns"
  for u in "${targt_usrs[@]}"; do
    echo "ssh ${u}@${d}" 
  done
done

Why doesn't this script print anything visible? Is it at all possible for an array to be a value in an associative array in Bash?

3
  • 5
    Only scalar values are possible in arrays and associative arrays. Commented Jan 24, 2024 at 12:26
  • 3
    Bash does not directly support arrays of arrays (either integer-indexed or hashed). See How to declare 2D array in bash and Multi-dimensional arrays in Bash. Commented Jan 24, 2024 at 12:30
  • 1
    Bash doesn't actually have any array data types, only special syntax to "simulate" arrays using similarly named variables. Setting the array attribute on foo, for example, essentially lets you create foo[0], foo[1], etc, as individual "names", as well as some special names (foo[@], e.g.) that will expand to all the values assigned to the special indexed names. Commented Jan 28, 2024 at 18:16

2 Answers 2

6

As @KamilCuk explained, there are no nested or multi-dimensional arrays in Bash.

Bash arrays can only store scalar or integer values.

What is possible though, is to pass a whole array as a space-delimited concatenation of individually quoted elements that are suitable to be reused as-is in a Bash declare statement.

  1. To quote values, Bash version 4.4+ provides the @Q expansion parameter.
  2. To join array elements into a single string with each element space-delimited it needs * as the array expansion such as ${array[*]}.

Both value quoting parameter and array to string join can be combined into a single expansion such as: ${array[*]@Q}

For example:

#!/usr/bin/env bash

declare -a array=(foo bar 'Hello World')

printf '(%s)\n' "${array[*]@Q}"

Fixed your code and now it works:

#!/usr/bin/env bash

construct_array_of_trgts() {
  local -a usrs=("j" "jim" "o" "root")
  local -a guest_dmns=("raspberrypi" "lenovo")
  for d in "${guest_dmns[@]}"; do
    PS3="Select the users to include for sshing into $d. Q when done selecting."$'\n'
    local -a targt_usrs=()
    while :; do
      select u in "${usrs[@]}"; do
        if [[ "$u" ]]; then
          targt_usrs+=("$u")
        elif [[ "$REPLY" == 'q' ]]; then
          break 2
        fi
      done
    done
    # Array to scalar of space-delimited quoted values
    usrs_n_dmns["${d}"]="${targt_usrs[*]@Q}"
  done
}

declare -A usrs_n_dmns

construct_array_of_trgts

for d in "${!usrs_n_dmns[@]}"; do
  # Scalar value is expanded into array delcare
  declare -a targt_usrs="(${usrs_n_dmns[$d]})"
  for u in "${targt_usrs[@]}"; do
    echo ssh "${u}@${d}"
  done
done
Sign up to request clarification or add additional context in comments.

1 Comment

Might be worth explaining what you're doing (replacing arrays with strings that eval to their contents when added as values, and relying on declare to do that evaluation) for the uninitiated.
5

How do i iterate through an associative array in Bash where the values are arrays?

It is impossible to do, as associative array values can't be arrays.

Why doesn't this script print anything visible?

construct_array_of_trgts() {
  declare -A usrs_n_dmns

declare within a function body is equal to local. The variable usrs_n_dmns is local within the construct_array_of_trgts function. After construct_array_of_trgts returns, usrs_n_dmns no longer exists.

If you want the variable to be global add -g to declare.

Is it at all possible for an array to be a value in an associative array in Bash?

No.

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.