arraysbashfunctionargumentspositional-parameter

Bash - How to pass array to function, with index defined in new function


I have this current function used to create an array in a menu style (associative arrays can't be used in the scenario).

declare -a array1=("host1" "host2" "host3")
declare -a array2=("ip1" "ip2" "ip3")

function menusetup {

  iter=0
  for ((i=0; i<(count*2); i+=2)); do
    menu[$i]=${array1[$iter]}
    menu[$i+1]=${array2[$iter]}
    ((iter++))
  done
}

menusetup

which gives me the menu array with {"host1" "ip1" "host2" "ip2" "host3" "ip3" This is working perfect for what I needed, but now I want to reuse this function to pass any 2 arrays, something like this.

function menusetup {

  iter=0
  for ((i=0; i<(count*2); i+=2)); do
    menu[$i]=${$1[$iter]}
    menu[$i+1]=${$2[$iter]}
    ((iter++))
  done
}

menusetup "${array3[@]}" "${array4[@]}"

Edit: I know the second example passes the entire list of elements of the array. I want to know if there is a way to pass and substitute the array


Solution

  • While you can use bash namereferences:

    array3=(1 2 3)
    array4=(a b c)
    menusetup() {
       declare -g menu
       declare -n count1=$1[@]
       count=$(( ${#count1} ))
       iter=0
       for ((i=0; i<count; i+=2)); do
           declare -n el1=$1[$iter]
           declare -n el2=$2[$iter]
           menu[$i]=$el1
           menu[$i+1]=$el2
           ((iter++))
        done
    }
    
    menusetup array3 array4
    declare -p menu
    # will output:
    # declare -a menu=([0]="1" [1]="a" [2]="2" [3]="b" [4]="3" [5]="c")
    

    And this is valid and useful programming style, It's way better and easier to think of bash as pipelines. You don't need state, you don't need to "iterate" over arrays.

    Think of bash as streams. What you are doing here is you are "zip"-ing two arrays. So first print each array element on newline separated items, then use paste to join two newline separated streams together, specify the paste separator to newline. Then read it to an array, either with readarray < <(...) or IFS=$'\n' menu=( $(...) ).

    Assuming there are no newline characters in array values, the whole work your function is doing can be simply done with:

    readarray -t menu < <(paste -d$'\n' <(printf "%s\n" "${array3[@]}") <(printf "%s\n" "${array4[@]}") )