ripat
1
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
ripat
3
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 :=)
ripat
4
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]}