1

How do I pass an array to a function, especially if it is in the middle somewhere? Both "${b}" and "${b[@]}" seems to pass the first item only, so is there a way to both - call and retrieve it, correctly?

#/usr/bin/env bash

touch a b1 b2 b3 c

f() { local a="${1}" local b="${2}"
local c="${3}" ls "${b[@]}" # expected b1 b2 b3 }

a=a b=("b1" "b2" "b3") c=c

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

rm a b1 b2 b3 c

Nishant
  • 603
  • 1
    You can't really pass an array as an argument; arguments are just strings, not more complicated data structures. You can pass the strings that are in an array (each one as a separate argument), but you cannot pass the array itself. – Gordon Davisson Oct 02 '20 at 07:52
  • https://stackoverflow.com/questions/1063347/passing-arrays-as-parameters-in-bash is a similar question from SO. It uses an idea based on Bash's dynamic scoping from pre-Bash 4.3 (where the accepted answer works). – Nishant Oct 02 '20 at 13:29

1 Answers1

5

In the bash shell, like in the ksh shell whose array design bash copied, "${array[@]}" expands to all the distinct element of the array (sorted by array index), and "$array" is the same as "${array[0]}".

So to pass all the elements of an array to a function, it's f "${array[@]}".

Now, a function's arguments are accessed via "$@", so your functions should be something like:

f() {
  ls -ld -- "$@"
}

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

Another option is to pass your array by name and use named references (another feature bash copied from ksh (ksh93)):

f() {
  typeset -n array="$1"
  ls -ld -- "${array[@]}"
}

f b

Or for f to take 3 arguments: a filename, an array name and another filename:

f() {
  typeset -n array="$2"
  ls -ld -- "$1" "${array[@]}" "$3"
}

f "$a" b "$c"

In virtually every other shell with arrays (csh, tcsh, rc, es, zsh, yash, fish), you just use $array to expand to all the elements of the array. In every other shell, arrays are also normal (non-sparse) arrays. A few caveats though: in csh/tcsh and yash, $array would still be subject to split+glob, and you'd need $array:q in (t)csh and "${array[@]}" in yash to work around it, while in zsh, $array would be subject to empty-removal (and again "${array[@]}" or "$array[@]" or "${(@)array}" works around it).