8

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
  • 20,023
  • 1
    I'm not familiar with ksh88, but if you need to pass the entire array by value, have you tried func "${array[@]}"? If you only need to pass one element, just pass the element - no need to make it more convoluted by passing an array and an index. – jw013 Jun 22 '12 at 12:14
  • I tried the syntax you suggested, but it did not work :( – rahmu Jun 22 '12 at 12:29
  • 1
    I was tired and confused. I had tried "${array[$@]}. Your suggestion actually works. Mea culpa. – rahmu Jun 26 '12 at 15:11

3 Answers3

10

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

  • 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 – rahmu Jun 23 '12 at 10:16
  • 1
    @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.) – Gilles 'SO- stop being evil' Jun 23 '12 at 21:47
  • 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. – rahmu Jun 23 '12 at 22:40
  • 1
    @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.) – Gilles 'SO- stop being evil' Jun 23 '12 at 22:56
4

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.

1

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

mirabilos
  • 1,733