Issue
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 expansion:
$ set -u
$ arr=()
$ echo "foo: '${arr[@]}'"
bash: arr[@]: unbound variable
(declare -a arr
doesn't help either.)
A common solution to this is to use ${arr[@]-}
instead, thus substituting an empty string instead of the ("undefined") empty array. However this is not a good solution, since now you can't discern between an array with a single empty string in it and an empty array. (@-expansion is special in bash, it expands "${arr[@]}"
into "${arr[0]}" "${arr[1]}" …
, which makes it a perfect tool for building command lines.)
$ countArgs() { echo $#; }
$ countArgs a b c
3
$ countArgs
0
$ countArgs ""
1
$ brr=("")
$ countArgs "${brr[@]}"
1
$ countArgs "${arr[@]-}"
1
$ countArgs "${arr[@]}"
bash: arr[@]: unbound variable
$ set +u
$ countArgs "${arr[@]}"
0
So is there a way around that problem, other than checking the length of an array in an if
(see code sample below), or turning off -u
setting for that short piece?
if [ "${#arr[@]}" = 0 ]; then
veryLongCommandLine
else
veryLongCommandLine "${arr[@]}"
fi
Update: Removed bugs
tag due to explanation by ikegami.
Solution
The only safe idiom is ${arr[@]+"${arr[@]}"}
Unless you only care about Bash 4.4+, but you wouldn't be looking at this question if that were the case :)
This is already the recommendation in ikegami's answer, but there's a lot of misinformation and guesswork in this thread. Other patterns, such as ${arr[@]-}
or ${arr[@]:0}
, are not safe across all major versions of Bash.
As the table below shows, the only expansion that is reliable across all modern-ish Bash versions is ${arr[@]+"${arr[@]}"}
(column +"
). Of note, several other expansions fail in Bash 4.2, including (unfortunately) the shorter ${arr[@]:0}
idiom, which doesn't just produce an incorrect result but actually fails. If you need to support versions prior to 4.4, and in particular 4.2, this is the only working idiom.
Unfortunately other +
expansions that, at a glance, look the same do indeed emit different behavior. Using :+
instead of +
(:+"
in the table), for example, does not work because :
-expansion treats an array with a single empty element (('')
) as "null" and thus doesn't (consistently) expand to the same result.
Quoting the full expansion instead of the nested array ("${arr[@]+${arr[@]}}"
, "+
in the table), which I would have expected to be roughly equivalent, is similarly unsafe in 4.2.
You can see the code that generated this data along with results for several additional version of bash in this gist.
Answered By - dimo414 Answer Checked By - Gilberto Lyons (WPSolving Admin)