Passing arrays to bash functions

Short version: yes, you can pass an array to a Bash function. You can also manipulate the array contents within the function to pass information back. It is easy and has been supported since Bash version 4.3, I believe.

Long version: I recently watched a talk by some very knowledgeable people about Bash, where they delved deep into its internals and quirks. At one point, they discussed passing information to and from functions without creating subshells. The solution became quite convoluted, and I was surprised because the whole time I was thinking, “just use nameref“.

Out of curiosity, I searched online, but unfortunately, the internet is full of responses like “doesn’t work,” “Bash can’t do that” and many variations of “just pass all the values of the array to the function as arguments and piece them together again inside the function” (which is a terrible solution since you lose the keys). There are a few posts here and there suggesting local -n as a solution, but they are rare, and especially on sites like Stack Overflow, they are not the top answers.

In a nutshell what we are going to do is pass a reference to an array to a function (think “pointers” or “symlinks” if that helps).

Relevant parts of the bash man page for declare and local:

declare -n

Give each name the nameref attribute, making it a name reference to another variable. That other variable is defined by the value of name. All references, assignments, and attribute modifications to name, except for those using or changing the -n attribute itself, are performed on the variable referenced by name’s value. The nameref attribute cannot be applied to array variables.

local

For each argument, a local variable named name is created, and assigned value. The option can be any of the options accepted by declarelocal can only be used within a function; it makes the variable name have a visible scope restricted to that function and its children.

So, in summary, we can use local to define variables with a scope limited to the function they are defined in, and local accepts all the options that declare supports.

It seems the “The nameref attribute cannot be applied to array variables.” part of the declare definition causes a lot of confusion or deters people from trying to use it for referencing arrays.
What it means is that you can’t do a local -n my_array=() (i.e. applying the nameref attribute to an array), but local -n my_array is fine (where my_array is a variable with the nameref attribute which can also point to an array).

Enough theory, let’s get down to practical examples.

We will create a function called do_stuff that:

  • takes the name of an array as argument $1
  • reads the length key from the array
  • add a random key to the array with a random number the length of the length key previously read

Then we will create an array outside of the function with some keys/values, pass it to the do_stuff function, and then output the contents

So this shows us that the do_stuff function could read from the array (the length value), and write to the array (add the random number key/value), and the changes were applied to the array outside of the function. (where we did the declare -p). Bonus points for not needing a subshell.

Using this “trick” allows us to pass more complex information to a function, and especially receive more complex information from a function.

There is one caveat: you can’t use the same name for the array inside the function as well as outside. I wouldn’t advise doing this anyway for readability reasons, as the variable’s scope can become confusing. If you try it you get the following output:

Oddly I often noticed the following statement on Stack Overflow about nameref:

This only works if the array is defined as a global
Nope, works just as fine passing an array locally scoped to a function to another function this way.

(I prefer using a main() function like in this example to avoid global variables unless explicitly defined)

So, there you have it: an easy way to pass an array to a function in bash, no weird looping over values. And a better way to receive information from an array than the byte return value and parsing the output of the function.