[BASH - KSH] Passing array to a function

Passing a array to a function, a basic feature in modern language, seems to be only possible in KSH. Not in BASH. Depite all my efforts I couldn't come to a solution. See the following examples:

It works perfectly in KSH:

#!/usr/bin/ksh

function print_array {
    # assign array by indirect variable reference (namerefs)
    typeset -n array=$1

    # display array content
    echo "${array[*]}"
}

colors=('Pink' 'Light Gray' 'Green')

print_array "colors"

But this one gave me headhache:

#!/bin/bash

function print_array {
    # trying to assign array by indirect variable reference
    declare -a array=${!1}
    # local array=${!1}         # doesn't work either

    # display array content
    echo "${array[*]}"
}

colors=('Pink' 'Light Gray' 'Green')

print_array "colors"

Some reference in the manual for using indirect reference in BASH:

This version works with ksh or bash.

#! /usr/local/bin/bash
#! /usr/bin/ksh

function print_array  {
        array_name=$1
        eval echo \${$array_name[*]}
        return
}

colors[0]="Pink"
colors[1]="Light Gray"
colors[2]="Green"

print_array colors
exit 0

Hi Perderabo,

With my GNU bash, version 3.2.25 it only returns the array name "colors" but not its contents. I have also tried this trick:

function print_array {
    array_name="$1[*]"
    echo ${!array_name}
}

It returns the array elements (on one line) but the local array doesn't exists as such as I can not access it by doing something like:

function print_array {
    array_name="$1"
    echo ${!array_name[1]}
}

This always return the first element "pink" [0]. Of course I could access the array elements by doing:

function print_array {
    array_name="$1[0]"
    echo ${!array_name}

    array_name="$1[1]"
    echo ${!array_name}

    array_name="$1[2]"
    echo ${!array_name}
}

Which is rather inelegant and clumsy. All I wanted is to create a *local* copy of the array whose name is passed to the function as positional parameter ($1 $2 ....). By the way, how can I copy an array without looping throught it and assigning values one by one. Of course, the following doesn't work:

colors=('Pink' 'Light Gray' 'Green')
echo ${colors[@]}

copy_colors=$colors
echo ${copy_colors[@]}

# returns:
# Pink Light Gray Green (3 elements)
# Pink

copy_colors=(${colors[*]})
echo ${copy_colors[*]}
echo ${copy_colors[1]}

# "Light Gray" is split:
# Pink Light Gray Green (4 elements!)
# Light

I think I start to understand why members of this forum perfer ksh :=)

Boy, this one wasn't easy!

OK, here is what works in bash. Remember, the question was to pass an array to a function and make sure that whatever is done by the function remains local. If the local scope isn't necessary, just remove the "local" declaration.

#!/bin/bash

function print_array {
    # Setting the shell's Internal Field Separator to null
    OLD_IFS=$IFS
    IFS=''

    # Create a string containing "colors[*]"
    local array_string="$1[*]"

    # assign loc_array value to ${colors[*]} using indirect variable reference
    local loc_array=(${!array_string})

    # Resetting IFS to default
    IFS=$OLD_IFS

    # Checking the second element "Light Gray" (the one with a space)
    echo ${loc_array[1]}
    
}

# create an array and display contents
colors=('Pink' 'Light Gray' 'Green')
echo ${colors[*]}

# call function with positional parameter $1 set to array's name
print_array colors 

# checking whether the function "local" loc_array is visible here
echo "Does the local array exist here? -->${loc_array[*]}<--"
exit 0

This returns:

Pink Light Gray Green
Light Gray
Does the local array exist here? --><--

We can also "copy" an array the same way: setting IFS to NULL permit to do variable expansion that preserves the possible spaces in array's elements.

colors=('Pink' 'Light Gray' 'Green')
OLD_IFS=$IFS
IFS=''
copy_colors=(${colors[*]})
IFS=$OLD_IFS

echo ${copy_colors[*]}
echo ${copy_colors[1]}