0

Edit: This has been flagged as a duplicate so let me explain how this is unique: 1. This uses for loops 2. This should be portable across all versions of bash

first_array=(1 2 3)
second_array=(4 5 6)

do_stuff () {
_array1=( "$1" )
_array2=( "$2" )
for i in "${_array1[@]}"; do
  for i in "${_array2[@]}"; do
    echo "$i"
  done
done
}

do_stuff "$(echo ${first_array[@]})" "$(echo ${second_array[@]})"

This outputs:

4 5 6

Instead of:

4 
5 
6 
4 
5 
6 
4 
5 
6

Why? It works fine if I don't pass arrays as parameters, such as:

_array1=(1 2 3)
_array2=(4 5 6)

do_stuff () {
for i in "${_array1[@]}"; do
  for i in "${_array2[@]}"; do
    echo "$i"
  done
done
}

do_stuff

Produces this output:

4
5
6
4
5
6
4
5
6

I would like to be able to get this result but passing different arrays of my choosing in order to avoid rewriting similar functions.

Fingers
  • 373
  • 3
  • 18
  • Possible duplicate of [Passing arrays as parameters in bash](https://stackoverflow.com/questions/1063347/passing-arrays-as-parameters-in-bash) – oliv Aug 22 '18 at 10:47
  • This was hard for me to understand and doesn't incorporate for loops. I did look at some of the other posts before posting – Fingers Aug 22 '18 at 10:50

2 Answers2

2

What is wrong with your code:

  1. echo ${first_array[@]} prints all the elements of first_array separated by a space. Since you are not quoting the array expansion, it will also be subject to word-splitting and filename expansion!

  2. The command substitution "$(...)" will result in one single string thanks to the double quotes and the string "1 2 3" is passed to the function

  3. You then assign this single string "1 2 3" to an array _array1=( "$1" ) which results in the array having just that one element

The same thing applies to the second array as well. Add declare -p _array1 _array2 into your function to see that both arrays indeed contain just one string:

declare -a _array1=([0]="1 2 3")
declare -a _array2=([0]="4 5 6")

How to solve it:

  1. Follow the linked question: Passing arrays as parameters in bash

  2. You had an idea to pass all the elements. That would work well if you had just one array. You could use arr=( "$@" ) then. With two arrays however, the problem is that you don't know how many elements belong to the first array. So, you need to pass the number of elements of the first array as well:

    #!/usr/bin/env bash
    
    foo=(1 2 3)
    bar=(4 5 6)
    
    do_stuff() {
        local -a arr1=( "${@:2:$1}" ) arr2=( "${@:$1+2}" )
        declare -p arr1 arr2
    }
    
    do_stuff "${#foo[@]}" "${foo[@]}" "${bar[@]}"
    

This code uses parameter expansion, see Bash Reference Manual or this great wiki.bash-hackers.org article.

PesaThe
  • 7,259
  • 1
  • 19
  • 43
1

You can try something like this:

#!/bin/bash

first_array=(1 2 3)
second_array=(4 5 6)

do_stuff () {
    declare -a _array1=("${!1}")
    declare -a _array2=("${!2}")
    echo "${_array1[@]}"
    echo "${_array2[@]}"
}

do_stuff first_array[@] second_array[@]

output:

$ ./script.sh
1 2 3
4 5 6
tripleee
  • 175,061
  • 34
  • 275
  • 318
Szczerba
  • 271
  • 1
  • 8
  • 1
    Requiring the `[@]` in the call is ... odd. – tripleee Aug 23 '18 at 06:49
  • @Szczerba where can I go to research your usage of the syntax ("${!1}") ? I can find this usage in any guides online such as http://tldp.org/LDP/abs/html/complexfunct.html http://wiki.bash-hackers.org/scripting/posparams http://linuxcommand.org/lc3_wss0120.php – Fingers Aug 26 '18 at 14:12
  • @Fingers Follow either link in my [answer](https://stackoverflow.com/a/51966748/6176817). It's called variable indirection, see for example: [wiki.bash-hackers.org](http://wiki.bash-hackers.org/syntax/pe#indirection). – PesaThe Aug 26 '18 at 17:45
  • 1
    @tripleee True! On top of that, since it is not quoted, should there be a file called `first_array@`, it will fail. – PesaThe Aug 26 '18 at 17:48