I\'m writing a bash script which has set -u
, and I have a problem with empty array expansion: bash appears to treat an empty array as an unset variable during e
@ikegami's accepted answer is subtly wrong! The correct incantation is ${arr[@]+"${arr[@]}"}
:
$ countArgs () { echo "$#"; }
$ arr=('')
$ countArgs "${arr[@]:+${arr[@]}}"
0 # WRONG
$ countArgs ${arr[@]+"${arr[@]}"}
1 # RIGHT
$ arr=()
$ countArgs ${arr[@]+"${arr[@]}"}
0 # Let's make sure it still works for the other case...
The most simple and compatible way seems to be:
$ set -u
$ arr=()
$ echo "foo: '${arr[@]-}'"
this may be another option for those who prefer not to duplicate arr[@] and are okay to have an empty string
echo "foo: '${arr[@]:-}'"
to test:
set -u
arr=()
echo a "${arr[@]:-}" b # note two spaces between a and b
for f in a "${arr[@]:-}" b; do echo $f; done # note blank line between a and b
arr=(1 2)
echo a "${arr[@]:-}" b
for f in a "${arr[@]:-}" b; do echo $f; done
Here are a couple of ways to do something like this, one using sentinels and another using conditional appends:
#!/bin/bash
set -o nounset -o errexit -o pipefail
countArgs () { echo "$#"; }
arrA=( sentinel )
arrB=( sentinel "{1..5}" "./*" "with spaces" )
arrC=( sentinel '$PWD' )
cmnd=( countArgs "${arrA[@]:1}" "${arrB[@]:1}" "${arrC[@]:1}" )
echo "${cmnd[@]}"
"${cmnd[@]}"
arrA=( )
arrB=( "{1..5}" "./*" "with spaces" )
arrC=( '$PWD' )
cmnd=( countArgs )
# Checks expansion of indices.
[[ ! ${!arrA[@]} ]] || cmnd+=( "${arrA[@]}" )
[[ ! ${!arrB[@]} ]] || cmnd+=( "${arrB[@]}" )
[[ ! ${!arrC[@]} ]] || cmnd+=( "${arrC[@]}" )
echo "${cmnd[@]}"
"${cmnd[@]}"
@ikegami's answer is correct, but I consider the syntax ${arr[@]+"${arr[@]}"}
dreadful. If you use long array variable names, it starts to looks spaghetti-ish quicker than usual.
Try this instead:
$ set -u
$ count() { echo $# ; } ; count x y z
3
$ count() { echo $# ; } ; arr=() ; count "${arr[@]}"
-bash: abc[@]: unbound variable
$ count() { echo $# ; } ; arr=() ; count "${arr[@]:0}"
0
$ count() { echo $# ; } ; arr=(x y z) ; count "${arr[@]:0}"
3
It looks like the Bash array slice operator is very forgiving.
So why did Bash make handling the edge case of arrays so difficult? Sigh. I cannot guarantee you version will allow such abuse of the array slice operator, but it works dandy for me.
Caveat: I am using GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Your mileage may vary.
Turns out array handling has been changed in recently released (2016/09/16) bash 4.4 (available in Debian stretch, for example).
$ bash --version | head -n1
bash --version | head -n1
GNU bash, version 4.4.0(1)-release (x86_64-pc-linux-gnu)
Now empty arrays expansion does not emits warning
$ set -u
$ arr=()
$ echo "${arr[@]}"
$ # everything is fine