il_mix
il_mix

Reputation: 571

Fill array passed as argument in bash function

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

Answers (4)

clt60
clt60

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

EDIT

Decomposition of the function - works as code generator

addtoarray () { var="$1"; shift 1; eval "$var+=($(printf "'%s' " "$@"))"; }
  • the function's 1st argument is the variable name(!!!) (not value)
  • storing the name (in $1) it into $var and shifting it out from the arguments
  • now the arguments contains only new elements for the array like val1 val2
  • now constructing an bash command:
somearrayname+=('elemnts1' 'element2' .... )
  • where the somearrayname is the variable name what we passed as the 1st arg
  • the printf "'%s '" "$@" creates the single-quoted array members from the arg-list
  • so in case of calling the function as addtoarray arr val1 "spaced val2" we generating the next string
arr+=('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)

  • finally the eval executes the above generated string
  • in the result you got an initialized/modified array $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

chepner
chepner

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

glenn jackman
glenn jackman

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

anubhava
anubhava

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

Related Questions