Array of arrays in bash

前端 未结 6 1107
日久生厌
日久生厌 2020-12-24 05:09

I\'m attempting to read an input file line by line which contains fields delimited by periods. I want to put them into an array of arrays so I can loop through them later on

相关标签:
6条回答
  • 2020-12-24 05:41

    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
    
    0 讨论(0)
  • 2020-12-24 05:49

    You could make use of (de)referencing arrays like in this script:

    #!/bin/bash
    
    OFS=$IFS     # store field separator
    IFS="${2: }" # define field separator
    file=$1      # input file name
    
    unset a      # reference to line array
    unset i j    # index
    unset m n    # dimension
    
    ### input
    
    i=0
    while read line
    do
      a=A$i
      unset $a
      declare -a $a='($line)'
      i=$((i+1))
    done < $file
    # store number of lines
    m=$i
    
    ### output
    
    for ((i=0; i < $m; i++))
    do
      a=A$i
      # get line size
      # double escape '\\' for sub shell '``' and 'echo'
      n=`eval echo \\${#$a[@]}`
      for (( j = 0; j < $n; j++))
      do
        # get field value
        f=`eval echo \\${$a[$j]}`
        # do something
        echo "line $((i+1)) field $((j+1)) = '$f'"
      done
    done
    
    IFS=$OFS
    

    Credit to https://unix.stackexchange.com/questions/199348/dynamically-create-array-in-bash-with-variables-as-array-name

    0 讨论(0)
  • 2020-12-24 05:55

    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
    
    0 讨论(0)
  • 2020-12-24 05:56

    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.

    0 讨论(0)
  • 2020-12-24 06:03

    I use Associative Arrays and use :: in the key to denote depth. The :: can also be used to embed attributes, but that is another subject,...

    declare -A __myArrayOfArray=([Array1::Var1]="Assignment" [Array2::Var1]="Assignment")
    

    An Array under Array1

    __myArrayOfArray[Array1::SubArray1::Var1]="Assignment"
    

    The entries in any array can be retrieved (in order ...) by ...

    local __sortedKeys=`echo ${!__myArrayOfArray[@]} | xargs -n1 | sort -u | xargs`
    for __key in ${__sortedKeys}; do
        #
        # show all properties in the Subordinate Profile "Array1::SubArray1::"
        if [[ ${__key} =~ ^Array1::SubArray1:: ]]; then
            __property=${__key##Array1::SubArray1::}
            if [[ ${__property} =~ :: ]]; then
                echo "Property ${__property%%:*} is a Subordinate array"
            else
                echo "Property ${__property} is set to: ${__myArrayOfArray[${__key}]}"
            fi
        fi 
    done
    

    THE list of subordinate "Profiles" can be derived by:

    declare -A __subordinateProfiles=()
    local __profile
    local __key
    for __key in "${!__myArrayOfArray[@]}"; do
        if [[ $__key =~ :: ]]; then
            local __property=${__key##*:}
            __profile=${__key%%:*}
            __subordinateProfiles[${__profile}]=1
        fi   
    done
    
    0 讨论(0)
  • 2020-12-24 06:06

    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
    
    0 讨论(0)
提交回复
热议问题