Issue
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?
Solution
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.
- To quote values, Bash version 4.4+ provides the
@Q
expansion parameter. - 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
Answered By - Léa Gris Answer Checked By - David Marino (WPSolving Volunteer)