Which is generally the best practice in bash?
hotstrings=()
if [ ${#hotstrings[@]} != 0 ]; then return; fi
vs
if [ ${#hotstrings[@]} -gt 0 ]; then return; fi
vs
if [ "${hotstrings[@]}" != '' ]; then return; fi
vs
Something else?
Which is generally the best practice in bash?
hotstrings=()
if [ ${#hotstrings[@]} != 0 ]; then return; fi
vs
if [ ${#hotstrings[@]} -gt 0 ]; then return; fi
vs
if [ "${hotstrings[@]}" != '' ]; then return; fi
vs
Something else?
The comparison against an empty string looks brittle, and needlessly interpolates a potentially large array into a string just to compare it against the empty string.
I'm not sure whether numeric or string comparison against zero is more efficient in general, and in isolation, you would be hard pressed to perceive any difference even if there is one. My hunch is that strings are more efficient, and arithmetic requires some additional work by Bash.
The following quick test seems to bear this out:
bash$ array=({1..10000})
bash$ echo "${#array[@]}"
10000
bash$ time for i in {1..10000}; do [ "${#array[@]}" -gt 0 ]; done
real 0m0.109s
user 0m0.097s
sys 0m0.007s
bash$ time for i in {1..10000}; do [ "${#array[@]}" != 0 ]; done
real 0m0.100s
user 0m0.092s
sys 0m0.007s
For an apples-to-apples comparison, you should properly measure -ne
(numeric non-equivalence) instead of -gt
, but it doesn't seem to make any real difference. If anything, it seems slightly slower, somewhat to my surprise.
So with this rather ad-hoc measurement I would hesitantly recommend the string comparison != 0
over the arithmetic one. However, switching to [[
over [
makes a much bigger difference, and is more robust to boot.
bash$ time for i in {1..10000}; do [[ "${#array[@]}" != 0 ]]; done
real 0m0.065s
user 0m0.064s
sys 0m0.001s
(For the record, I did check that -gt 0
is slightly slower in this case too.)
Anyway, the test might obviously come out quite differently if your typical values are drastically different from what I guessed would be useful for a quick test, or if your Bash version is different from mine. (This was Bash 3.2.57(1)-release which is what MacOS still ships out of the box.) If this is really important, test with your own representative data.
All of these constructs are compatible back to Bash v2 at least; obviously, if you are not using Bash at all, your question is not well-defined (Zsh and ksh
have similar array syntax, but you would hardly want to try to maintain compatibility across them).
Rock-bottom baseline:
bash$ time for i in {1..10000}; do true; done
real 0m0.062s
user 0m0.047s
sys 0m0.006s
I'd use this:
[[ ${arr[@]} ]] || echo empty
Update, or this:
((${#arr[*]}<1)) && echo empty
Testing:
$ arr=()
$ [[ ${arr[@]} ]] || echo empty
empty
$ ((${#arr[*]}<1)) && echo empty
empty
$ arr=('')
$ [[ ${arr[@]} ]] || echo empty # fail (
empty
$ ((${#arr[*]}<1)) && echo empty # fine )