What is the most correct way to pass an array to a function?
Solution 1
To pass the array elements as arguments to the function, use the ksh syntax to expand the elements of the array as a list.
work_on_array "${myarray[@]}"
The [@]
suffix makes this an array expansion. The double quotes protect each element from further expansion (splitting and globbing). The result of the expansion is not in general one word like it usually is with double quotes, but as many words as there are elements in the array.
The Nth element of the array is then ${N}
. To access it, you need to use eval
; see Use a variable reference "inside" another variable
Solution 2
There is a way in bash 4.3+, which probably comes from ksh:
echo_idx_array () # array index
{
local -n array=$1 # add nameref attribute
local idx=$2
echo "${array[idx]}"
}
$ names=(one two three four)
$ echo_idx_array names 2
three
$ days=([monday]=eggs [tuesday]=bread [sunday]=jam) # associative array
$ echo_idx_array days sunday
jam
See also declare -n
.
Solution 3
Depends on the Korn Shell… recent AT&T ksh93 and mksh versions support this:
function echo_idx_array {
nameref arr=$1
idx=$2
echo "${arr[idx]}"
}
set -A test -- a b c
echo_idx_array test 1
In my current shell, this does output “b”.
Related videos on Youtube
rahmu
Updated on September 18, 2022Comments
-
rahmu almost 2 years
Consider I have a very large array
$large_list
, is there a way to write a function that will take the array as an argument? For example:echo_idx_array () { arr="$1" idx="$2" echo "${arr[$idx]}" }
What is the usual strategy to do something like that? I tried giving the variable
$large_list
but it was empty.I am willing to modify the function to adapt it to any change in the argument list.
For the record, I am using ksh88, and I am looking for answers as portable as can be.
EDIT: So far the best I could come up with is to loop through the array and send each element as an argument to the function. This seems incredibly ugly and error-prone, not to mention that it is bound to hit some limit quickly. Here's what I did:
foo () { echo $* } cmd="foo " while [[ $i -lt $MAX_ARR_SIZE ]]; do cmd="$cmd ${large_list[$i]}" ((i=i+1)) done eval $cmd
Isn't there something better to do?
-
rahmu about 12 yearsThanks. Question: if the result of the expansion is not one word, why are the quotes needed? Can they be omitted? Are you just applying your advice of "always quote unless you have a good reason not to"? :p
-
Gilles 'SO- stop being evil' about 12 years@rahmu The quotes are needed to avoid splitting and globbing on individual elements. Consider
myarray=("hello world" wibble)
(2 elements, the first of which contains a space):work_on_array "${myarray[@]}"
passes 2 parametershello world
andwibble
;work_on_array ${myarray[@]}
passes 2 parametershello
,world
andwibble
. And withmyarray=(*)
,work_on_array ${myarray[@]}
passes the list of files in the current directory. (Hence this is one of the many cases where my advice makes a practical difference.) -
rahmu about 12 yearsCorrect me if I'm wrong, but I believe there's a typo in what you wrote: the unquoted expansion passes 3 params, not 2.
-
Gilles 'SO- stop being evil' about 12 years@rahmu There are two parameters: fear and surprise… and ruthless efficiency. (In other words, you're right, there's a typo:
hello
,world
andwibble
make 3 parameters.) -
mirabilos over 9 yearsHuh, interesting. Yes, this came from ksh, and works in mksh unmodified.