What is the most correct way to pass an array to a function?

17,277

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”.

Share:
17,277

Related videos on Youtube

rahmu
Author by

rahmu

Updated on September 18, 2022

Comments

  • rahmu
    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
    rahmu about 12 years
    Thanks. 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'
    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 parameters hello world and wibble; work_on_array ${myarray[@]} passes 2 parameters hello, world and wibble. And with myarray=(*), 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
    rahmu about 12 years
    Correct 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'
    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 and wibble make 3 parameters.)
  • mirabilos
    mirabilos over 9 years
    Huh, interesting. Yes, this came from ksh, and works in mksh unmodified.