Array of arrays in bash

I struggled with this but found an uncomfortable compromise. In general, when faced with a problem whose solution involves using data structures in Bash, you should switch to another language like Python. Ignoring that advice and moving right along:

My use cases usually involve lists of lists (or arrays of arrays) and looping over them. You usually don't want to nest much deeper than that. Also, most of the arrays are strings that may or may not contain spaces, but usually don't contain special characters. This allows me to use not-to-confusing syntax to express the outer array and then use normal bash processing on the strings to get a second list or array. You will need to pay attention to your IFS delimiter, obvi.

Thus, associative arrays can give me a way to create a list of lists like:

declare -A JOB_LIST=(
   [job1] = "a set of arguments"
   [job2] = "another different list"
   ...
)

This allows you to iterate over both arrays like:

for job in "${!JOB_LIST[@]}"; do
  /bin/jobrun ${job[@]}
done

Ah, except that the output of the keys list (using the magical ${!...}) means that you will not traverse your list in order. Therefore, one more necessary hack is to sort the order of the keys, if that is important to you. The sort order is up to you; I find it convenient to use alphanumerical sorting and resorting to aajob1 bbjob3 ccjob6 is perfectly acceptable.

Therefore

declare -A JOB_LIST=(
   [aajob1] = "a set of arguments"
   [bbjob2] = "another different list"
   ...
)
sorted=($(printf '%s\n' "${!JOB_LIST[@]}"| /bin/sort))
for job in "${sorted[@]}"; do
   for args in "${job[@]}"; do
     echo "Do something with ${arg} in ${job}"
   done
done

Bash has no support for multidimensional arrays. Try

array=(a b c d)
echo ${array[1]}
echo ${array[1][3]}
echo ${array[1]exit}

For tricks how to simulate them, see Advanced Bash Scripting Guide.


Field nest box in bash but it can not circumvent see the example.

#!/bin/bash

# requires bash 4 or later; on macOS, /bin/bash is version 3.x,
# so need to install bash 4 or 5 using e.g. https://brew.sh

declare -a pages

pages[0]='domain.de;de;https'
pages[1]='domain.fr;fr;http'

for page in "${pages[@]}"
do
    # turn e.g. 'domain.de;de;https' into
    # array ['domain.de', 'de', 'https']
    IFS=";" read -r -a arr <<< "${page}"

    site="${arr[0]}"
    lang="${arr[1]}"
    prot="${arr[2]}"
    echo "site : ${site}"
    echo "lang : ${lang}"
    echo "prot : ${prot}"
    echo
done

Knowing that you can split string into "array". You could creat a list of lists. Like for example a list of databases in DB servers.

dbServersList=('db001:app001,app002,app003' 'db002:app004,app005' 'dbcentral:central')

# Loop over DB servers
for someDbServer in ${dbServersList[@]}
do
    # delete previous array/list (this is crucial!)
    unset dbNamesList
    # split sub-list if available
    if [[ $someDbServer == *":"* ]]
    then
        # split server name from sub-list
        tmpServerArray=(${someDbServer//:/ })
        someDbServer=${tmpServerArray[0]}
        dbNamesList=${tmpServerArray[1]}
        # make array from simple string
        dbNamesList=(${dbNamesList//,/ })
    fi

    # Info
    echo -e "\n----\n$someDbServer\n--"

    # Loop over databases
    for someDB in ${dbNamesList[@]}
    do
        echo $someDB
    done
done

Output of above would be:

----
db001
--
app001
app002
app003

----
db002
--
app004
app005

----
dbcentral
--
central

Tags:

Arrays

Shell

Bash