Reputation: 571
I need to fill an array inside a function, passing it as an argument and NOT using a global variable (the function will be called here and there with different arrays).
I've read this discussion bash how to pass array as an argument to a function and used the pass-by-name solution, that ALMOST do the trick.
Here is my code
#!/bin/bash
function fillArray {
arrayName=$1[@]
array=("${!arrayName}")
for i in 0 1 2 3
do
array+=("new item $i")
done
echo "Tot items in function: ${#array[@]}"
for item in "${array[@]}"
do
echo $item
done
echo
}
myArray=("my item 0" "my item 1")
fillArray myArray
echo "Tot items in main: ${#myArray[@]}"
for item in "${myArray[@]}"
do
echo $item
done
Here is the output
Tot items in function: 6
my item 0
my item 1
new item 0
new item 1
new item 2
new item 3
Tot items in main: 2
my item 0
my item 1
So, the function correctly use the array passed as parameter (first two items are the one added in main declaration), and append the new items to the array. After the function call, in main, the added items are lost.
What am I missing? Probably some pass-by-reference/pass-by-copy things...
Thanks!
Upvotes: 3
Views: 4078
Reputation: 63942
In this answer using such technique. Modified, you can
addtoarray () { var="$1"; shift 1; eval "$var+=($(printf "'%s' " "$@"))"; }
arr=('my item 0' 'my item 1')
printf "%s\n" "${arr[@]}"
echo ====
addtoarray arr 'my new item 2' 'my new item 3'
printf "%s\n" "${arr[@]}"
prints
my item 0
my item 1
====
my item 0
my item 1
my new item 2
my new item 3
works also for initializing arrays
addtoarray secarr $(seq 5)
printf "%s\n" "${secarr[@]}"
prints:
1
2
3
4
5
Decomposition of the function - works as code generator
addtoarray () { var="$1"; shift 1; eval "$var+=($(printf "'%s' " "$@"))"; }
$1
) it into $var
and shifting it out from the argumentsval1
val2
somearrayname+=('elemnts1' 'element2' .... )
somearrayname
is the variable name what we passed as the 1st argprintf "'%s '" "$@"
creates the single-quoted array members from the arg-listaddtoarray arr val1 "spaced val2"
we generating the next stringarr+=('val1' 'spaced val2' )
this is an correct construction to adding members to the array named arr
- regardless if before contets, e.g. adds the new elements to its end. (if it was empty, the end is its start)
eval
executes the above generated string$arr
(the $arr is global variable, therefore is visible in the function too)And finally - brutally stole @chepner's answer ;) using his technique the addtoarray
si simple as:
addtoarray () {
declare -n arrname=$1
shift 1;
arrname+=("$@")
}
If you have bash 4.3 you should accepts @chepner's answer - it is the best.
Upvotes: 0
Reputation: 531625
For future reference, this becomes trivial with named references in bash
4.3:
function fillArray {
declare -n arrayName=$1
for i in 0 1 2 3
do
arrayName+=("new item $i")
done
echo "Tot items in function: ${#arrayName[@]}"
for item in "${arrayName[@]}"
do
echo $item
done
echo
}
Upvotes: 4
Reputation: 246992
You need to assign the local var to the global one:
function fillArray {
local arrayName=$1
local ref=$arrayName[@]
local array=("${!ref}")
local i item key
for i in 0 1 2 3
do
array+=("new item $i")
done
echo "Tot items in function: ${#array[@]}"
for item in "${array[@]}"
do
echo $item
done
echo
for key in "${!array[@]}"; do
declare -g ${arrayName}["$key"]="${array[$key]}"
done
}
Upvotes: 0
Reputation: 785376
You are adding elements into an array variable inside the function but arrays or any other variables are not passed as reference in BASH. So any changes made in those variables inside the function won't be visible outside the function.
As a workaround you can use a global variable like this example:
# initialize an array
myArray=("my item 0" "my item 1")
useArrayInFunc { myArray+=("my item 2")); }
useArrayInFunc
printf "%s\n" "${myArray[@]}"
my item 0
my item 1
my item 2
Upvotes: 0