113

As we know, in bash programming the way to pass arguments is $1, ..., $N. However, I found it not easy to pass an array as an argument to a function which receives more than one argument. Here is one example:

f(){
 x=($1)
 y=$2

 for i in "${x[@]}"
 do
  echo $i
 done
 ....
}

a=("jfaldsj jflajds" "LAST")
b=NOEFLDJF

f "${a[@]}" $b
f "${a[*]}" $b

As described, function freceives two arguments: the first is assigned to x which is an array, the second to y.

f can be called in two ways. The first way use the "${a[@]}" as the first argument, and the result is:

jfaldsj 
jflajds

The second way use the "${a[*]}" as the first argument, and the result is:

jfaldsj 
jflajds 
LAST

Neither result is as I wished. So, is there anyone having any idea about how to pass array between functions correctly?

Michael
  • 8,362
  • 6
  • 61
  • 88
Red Lv
  • 1,369
  • 4
  • 13
  • 12
  • 3
    possible duplicate of [Passing arrays as parameters in bash](http://stackoverflow.com/questions/1063347/passing-arrays-as-parameters-in-bash) – chepner May 09 '13 at 12:49
  • 1
    @chepner thanks for reminding. I will check it later for some ideas – Red Lv May 09 '13 at 16:36
  • http://stackoverflow.com/questions/1063347/passing-arrays-as-parameters-in-bash – meso_2600 Apr 13 '15 at 12:51

8 Answers8

129

You cannot pass an array, you can only pass its elements (i.e. the expanded array).

#!/bin/bash
function f() {
    a=("$@")
    ((last_idx=${#a[@]} - 1))
    b=${a[last_idx]}
    unset a[last_idx]

    for i in "${a[@]}" ; do
        echo "$i"
    done
    echo "b: $b"
}

x=("one two" "LAST")
b='even more'

f "${x[@]}" "$b"
echo ===============
f "${x[*]}" "$b"

The other possibility would be to pass the array by name:

#!/bin/bash
function f() {
    name=$1[@]
    b=$2
    a=("${!name}")

    for i in "${a[@]}" ; do
        echo "$i"
    done
    echo "b: $b"
}

x=("one two" "LAST")
b='even more'

f x "$b"
Mike D
  • 5,984
  • 4
  • 31
  • 31
choroba
  • 231,213
  • 25
  • 204
  • 289
  • pass the array by name? can you make it more clear. I found you just make it possible – Red Lv May 09 '13 at 12:44
  • 1
    @RedLv: Just pass the name of the array and use parameter expansion with `!` to get the array. – choroba May 09 '13 at 12:48
  • 13
    note that the value of the "name" variable is merely the string "x[@]". The magic occurs with the `${!...}` – glenn jackman May 09 '13 at 12:53
  • @choroba great this idea is perfect when the function to be called is in the same file with the caller. unfortunately, I need to pass the array and other value as arguments to a script to be executed in the remote host by the ssh user@host cmd command. Do you have any suggestion about how to make it possible. By the way, do you have any reference about the "by name" mechanism. – Red Lv May 09 '13 at 13:13
  • @RedLv: The first solution should work, you just have to escape the cmd properly. Parameter expansion is described in `man bash`. – choroba May 09 '13 at 13:19
  • 5
    Just a note: Passing array by name => passing by reference. Any change in the called function will change the global value. Besides, arrays local to function cannot be passed. – anishsane May 09 '13 at 13:51
  • In function `f`, `a` is a copy of the array. – Edouard Thiel Oct 19 '14 at 08:42
  • @choroba If I declare name as `name=$1` instead of `name=$1[@]`m this doesn't. That part really looks like magic. Can you explain what's going on there? Also, how will this extend to passing associative arrays? – shahensha Jun 12 '20 at 03:08
  • @shahensha: See [here](/questions/4069188/how-to-pass-an-associative-array-as-argument-to-a-function-in-bash) on how to pass an associative array by name. It's even uglier than passing a normal array :-) – choroba Jun 12 '20 at 08:26
  • 1
    @anishsane Posted today [a robust way using recent nameref and an alternative field separator](https://stackoverflow.com/a/75033037/1765658) on a similar SO question. – F. Hauri - Give Up GitHub Jan 06 '23 at 16:09
  • Is `name` needed? Can `$1[@]` be used in `("${!name}")` instead of `name`? – user2023370 May 23 '23 at 11:06
  • @user2023370 What happened when you tried? – choroba May 23 '23 at 11:56
  • @choroba If I tried and it fails, there may still be another way to make it work. Perhaps you know already? – user2023370 May 23 '23 at 12:12
  • 1
    I used the trick with `name` because it's the only way I know it can be done. – choroba May 23 '23 at 14:11
  • I've just noticed the answer below from @EdouardThiel. I am using a version of bash newer than 4.3 so I am going for that. – user2023370 May 23 '23 at 14:59
80

You can pass an array by name reference to a function in bash (since version 4.3+), by setting the -n attribute:

show_value () # array index
{
    local -n myarray=$1
    local idx=$2
    echo "${myarray[$idx]}"
}

This works for indexed arrays:

$ shadock=(ga bu zo meu)
$ show_value shadock 2
zo

It also works for associative arrays:

$ declare -A days=([monday]=eggs [tuesday]=bread [sunday]=jam)
$ show_value days sunday
jam

See also nameref or declare -n in the man page.

Edouard Thiel
  • 5,878
  • 25
  • 33
  • 2
    Best answer if your shell supports it. Lightest code. Straight-forward. Thank you :> – MrPotatoHead Jan 31 '19 at 16:45
  • 1
    The drawback is that you must use in the function another name for the array that in the calls. – Edouard Thiel Mar 10 '19 at 10:43
  • 1
    CentOS 7 (centos-release-7-7.1908.0.el7.centos.x86_64) does not support this due to its default bash version is 4.2.26 – Iridium Cao Dec 04 '19 at 07:26
  • 1
    In `show_value`, one of `myarray` or `array` is probably a typo. They should probably be the same name. – mpb Feb 09 '21 at 01:13
  • Well seen, fixed. Thanks :) – Edouard Thiel Feb 11 '21 at 15:56
  • This is the best way! It would be even better if referring to [the online manual for declare builtin](https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html#index-declare). Unfortunately this neat trick doesn’t work in Zsh, which lacks the `-n` flag for [`declare`](http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html#index-typeset). – Franklin Yu Feb 15 '21 at 05:30
19

You could pass the "scalar" value first. That would simplify things:

f(){
  b=$1
  shift
  a=("$@")

  for i in "${a[@]}"
  do
    echo $i
  done
  ....
}

a=("jfaldsj jflajds" "LAST")
b=NOEFLDJF

f "$b" "${a[@]}"

At this point, you might as well use the array-ish positional params directly

f(){
  b=$1
  shift

  for i in "$@"   # or simply "for i; do"
  do
    echo $i
  done
  ....
}

f "$b" "${a[@]}"
whoan
  • 8,143
  • 4
  • 39
  • 48
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
4

This will solve the issue of passing array to function:

#!/bin/bash

foo() {
    string=$1
    array=($@)
    echo "array is ${array[@]}"
    echo "array is ${array[1]}"
    return
}
array=( one two three )
foo ${array[@]}
colors=( red green blue )
foo ${colors[@]}
Lavitha
  • 51
  • 3
3

Try like this

function parseArray {
    array=("$@")

    for data in "${array[@]}"
    do
        echo ${data}
    done
}

array=("value" "value1")

parseArray "${array[@]}"
andranikasl
  • 1,242
  • 9
  • 10
0

Pass the array as a function

array() {
    echo "apple pear"
}

printArray() {
    local argArray="${1}"
    local array=($($argArray)) # where the magic happens. careful of the surrounding brackets.
    for arrElement in "${array[@]}"; do
        echo "${arrElement}"
    done

}

printArray array
AndrewD
  • 4,924
  • 3
  • 30
  • 32
0

Here is an example where I receive 2 bash arrays into a function, as well as additional arguments after them. This pattern can be continued indefinitely for any number of bash arrays and any number of additional arguments, accommodating any input argument order, so long as the length of each bash array comes just before the elements of that array.

Function definition for print_two_arrays_plus_extra_args:

# Print all elements of a bash array.
# General form:
#       print_one_array array1
# Example usage:
#       print_one_array "${array1[@]}"
print_one_array() {
    for element in "$@"; do
        printf "    %s\n" "$element"
    done
}

# Print all elements of two bash arrays, plus two extra args at the end.
# General form (notice length MUST come before the array in order
# to be able to parse the args!):
#       print_two_arrays_plus_extra_args array1_len array1 array2_len array2 \
#       extra_arg1 extra_arg2
# Example usage:
#       print_two_arrays_plus_extra_args "${#array1[@]}" "${array1[@]}" \
#       "${#array2[@]}" "${array2[@]}" "hello" "world"
print_two_arrays_plus_extra_args() {
    i=1

    # Read array1_len into a variable
    array1_len="${@:$i:1}"
    ((i++))
    # Read array1 into a new array
    array1=("${@:$i:$array1_len}")
    ((i += $array1_len))

    # Read array2_len into a variable
    array2_len="${@:$i:1}"
    ((i++))
    # Read array2 into a new array
    array2=("${@:$i:$array2_len}")
    ((i += $array2_len))

    # You can now read the extra arguments all at once and gather them into a
    # new array like this:
    extra_args_array=("${@:$i}")

    # OR you can read the extra arguments individually into their own variables
    # one-by-one like this
    extra_arg1="${@:$i:1}"
    ((i++))
    extra_arg2="${@:$i:1}"
    ((i++))

    # Print the output
    echo "array1:"
    print_one_array "${array1[@]}"
    echo "array2:"
    print_one_array "${array2[@]}"
    echo "extra_arg1 = $extra_arg1"
    echo "extra_arg2 = $extra_arg2"
    echo "extra_args_array:"
    print_one_array "${extra_args_array[@]}"
}

Example usage:

array1=()
array1+=("one")
array1+=("two")
array1+=("three")

array2=("four" "five" "six" "seven" "eight")

echo "Printing array1 and array2 plus some extra args"
# Note that `"${#array1[@]}"` is the array length (number of elements
# in the array), and `"${array1[@]}"` is the array (all of the elements
# in the array) 
print_two_arrays_plus_extra_args "${#array1[@]}" "${array1[@]}" \
"${#array2[@]}" "${array2[@]}" "hello" "world"

Example Output:

Printing array1 and array2 plus some extra args
array1:
    one
    two
    three
array2:
    four
    five
    six
    seven
    eight
extra_arg1 = hello
extra_arg2 = world
extra_args_array:
    hello
    world

For further examples and detailed explanations of how this works, see my longer answer on this topic here: Passing arrays as parameters in bash

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
0

You can also create a json file with an array, and then parse that json file with jq

For example:

my-array.json:

{
  "array": ["item1","item2"]
}

script.sh:

ARRAY=$(jq -r '."array"' $1 | tr -d '[],"')

And then call the script like:

script.sh ./path-to-json/my-array.json
Paula T
  • 1,212
  • 11
  • 13