I'd like to pass an array to a bash function but I get a bad substitution
error
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?
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.
arrLen() { typeset -n Var="$1"; echo "${#Var[@]}"; }
— Aug 15, 2022 at 10:42 typeset
instead of declare
? — Aug 15, 2022 at 10:47 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, 2022 at 10:53