3

I'd like to pass an array to a bash function but I get a bad substitution error

Example


mapfile -t ray < <(parallel -j 0 echo ::: {1..10})

declare -p ray

declare -a ray=([0]="2" [1]="1" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10")

arrLen() {
  echo "${#$1[@]}"
 }

arrLen ray

-bash: ${#$1[@]}: bad substitution

So is it impossible to pass params to bash arrays?

8

1 Answer 1

7

With recent versions of bash, you could use namerefs:

arrLen() {
  typeset -n __Var="$1"
  echo "${#__Var[@]}"
}

Here we choose __Var as the nameref variable name, as one that is unlikely to be used otherwise within your script. arrLen __Var fails with circular name reference errors.

Namerefs (like typeset, and bash's array design in general) is a feature that bash borrowed from the Korn shell. In ksh (ksh93 where namerefs were introduced), you'd write:

function arrLen {
  typeset -n var="$1"
  echo "${#var[@]}"
}

(ksh namerefs are able to reference a variable with the same name from the caller's scope (or the global scope), but scoping (static scoping) is only done in functions declared with the Korn syntax, not with the Bourne syntax)

Or you can always use eval to construct code dynamically.

arrLen() {
  eval 'echo "${#'"$1"'[@]}"'
}

With zsh:

arrLen() echo ${(P)#1}

bash Nameref resolution, zsh's P parameter expansion flag also do some form of eval (dynamic code evaluation) under the hood, so all those approaches are equally unsafe if the argument passed to arrLen is not guaranteed to be a valid variable name, but equally safe if they are.

6
  • Thanks a lot @StéphaneChazelas works perfectly
    – Nickotine
    Aug 15 at 10:39
  • @StéphaneChazelas are the underscores necessary? This worked fine for me arrLen() { typeset -n Var="$1"; echo "${#Var[@]}"; }
    – Nickotine
    Aug 15 at 10:42
  • Any reason, why use typeset instead of declare?
    – annahri
    Aug 15 at 10:47
  • @Nickotine, see edit. Aug 15 at 10:51
  • 2
    @annahri, typeset is the name used by the Korn shell from the earlier 80s (where bash copied that feature) and most other shells, so I prefer that one. I also prefer readarray over mapfile as that's not doing a mapping, only a reading (and zsh had a (real) mapfile feature long before bash introduced its mapfile / readarray builtin) Aug 15 at 10:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.