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
  • 563
  • 9
  • 21
  • 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).

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • @Nishant Where exactly? Is it always the 2nd argument? – Kusalananda Oct 01 '20 at 17:19
  • Yeah it is going to be the 2nd argument always. Does it change if it is the k-th argument? – Nishant Oct 02 '20 at 07:38
  • 1
    @Nishant This should be clear from the last piece of code in this answer. – Kusalananda Oct 02 '20 at 07:51
  • 1
    Note: `typeset -n` is not supported in `ksh88`, and neither is array assignment via `b=( b1 b2 b3 )`. – jrw32982 Oct 02 '20 at 14:55
  • @jrw, Yes, `typeset -n` is from ksh93 while `array=(...)` is from zsh, but the awkward array design is from ksh83 (probably even earlier from the time David Korn wrote some extensions on the Bourne shell for some DB project in the early 80s). – Stéphane Chazelas Oct 02 '20 at 14:58