If I understand correctly you want to append a value to an array if this value is not yet in the array, but the tricky part is that the array name is passed to the function.
Method 1
One possibility is to see the problem from a different viewpoint: you can pass to the function the value to be inserted and the full array, and the function will set a global variable that you will recover after its execution. For our test example we'll use:
array=( hello world "how are you today?" )
and we'll try to insert test
and hello
(so that hello
will not be inserted):
f() {
# This function will set the global variable _f_out to an array obtained from
# the positional parameters starting from position 2, with $1 appended if and
# only if $1 doesn't appear in the subsequent positional parameters
local v=$1 i
shift
_f_out=( "$@" )
for i; do [[ $i = $v ]] && return; done
_f_out+=( "$v" )
}
Let's use it:
$ array=( hello world "how are you today?" )
$ f test "${array[@]}"
$ array=( "${_f_out[@]}" )
$ printf '%s\n' "${array[@]}"
hello
world
how are you today?
test
$ f hello "${array[@]}"
$ array=( "${_f_out[@]}" )
$ printf '%s\n' "${array[@]}"
hello
world
how are you today?
test
It works.
Remark. I used for i; do
. It's a nice shortcut for for i in "$@"; do
.
Method 2
You really want to fiddle with simili-pointers and do the appending in place (this is not really in the spirit of Bash—that's why it's a bit clumsy). The tool for that is to use printf
with the -v
option: from help printf
:
-v var assign the output to shell variable VAR rather than
display it on the standard output
The good thing is that it also works with array fields.
Warning: you might see other methods that use eval
. Avoid them like the plague!
f() {
local array_name=$2[@] i
local array=( "${!array_name}" )
for i in "${array[@]}"; do [[ $i = $1 ]] && return; done
# $1 was not found in array, so let's append it
printf -v "$2[${#array[@]}]" '%s' "$1"
}
and let's try it:
$ array=( hello world "how are you today?" )
$ f test array
$ printf '%s\n' "${array[@]}"
hello
world
how are you today?
test
$ f hello array
$ printf '%s\n' "${array[@]}"
hello
world
how are you today?
test
It works too.
Note. With both methods you can very easily have a return
code of the function, e.g., 0
(success) if the value was inserted, and 1
(failure) if the value was already there (or the other way round)—the adaptation is straightforward and left as an exercise. This might be useful in Method 1 to determine whether you need to update array
to the returned value _f_out
or not. In that case, you could even slightly modify f
so that it doesn't even set _f_out
when the value is already in the array.
Caveat. In both methods shown I assumed that your arrays have contiguous indices that start at 0 (i.e., non-sparse arrays). I think it's a safe assumption here; but if it is not the case, these methods are broken: the first one will (after reassignment) transform the array into a non-sparse one, and the second one might overwrite fields (as, for an array a
, ${#a[@]}
expands to the number of elements in the array, not the highest index + 1 found in the array).