Some Bashisms are faster and more readable
There may be good reasons to stick to POSIX. But if instead you end up using Bash, and you're already sprinkling Bashisms all over your script, then you might as well make good use of it.
I keep stumbling on things like [ $x -gt 2 ] in Bash scripts, and I see no good reason for that. I think this happens mostly because people don't know Bash's arithmetic evaluation syntax exists. After all, ((x > 2)) is immediately more readable. And about twice as fast. And safer: that $x should have been quoted, even at the inevitable price of more noise.
It seems that (( is faster than [[, which is faster than test, which is faster than [. See below.
Granted, unless you're running long loops like that, speed shouldn't make much of a difference. But readability matters, and you can get readability and speed and safe variables that don't need $ or quoting.
Behold as it gets progressively simpler and more readable:
([ "$x" -lt 4 ] && [ "$y" -ne 3 ] && [ "$z" -ge "$(echo "$x + $y" | bc)" ]) && echo "Good!"
([[ "$x" -lt 4 ]] && [[ "$y" -ne 3 ]] && [[ "$z" -ge "$x + $y" ]]) && echo "Good!"
[[ "$x" -lt 4 && "$y" -ne 3 && "$z" -ge "$x + $y" ]] && echo "Good!"
[[ x -lt 4 && y -ne 3 && z -ge "x + y" ]] && echo "Good!"
((x < 4 && y != 3 && z >= x+y)) && echo "Good!"
All things considered, I prefer (( when dealing with integers, and [[ for everything else.
You can try these tests yourself:
Numerical comparison
time for i in {1..2000000}; do (( 4 > 2 )); done # | 41% | Fastest time for i in {1..2000000}; do [[ 4 -gt 2 ]]; done # | 52% | time for i in {1..2000000}; do test 4 -gt 2 ; done # | 93% | time for i in {1..2000000}; do [ 4 -gt 2 ] ; done # | 100% | Slowest
Files
time for i in {1..2000000}; do [[ -e /dev/stdin ]]; done # | 58% | Fastest time for i in {1..2000000}; do test -e /dev/stdin ; done # | 97% | time for i in {1..2000000}; do [ -e /dev/stdin ] ; done # | 100% | Slowest
Strings
time for i in {1..2000000}; do [[ -n "foo" ]]; done # | 43% | Fastest time for i in {1..2000000}; do test -n "foo" ; done # | 70% | time for i in {1..2000000}; do [ -n "foo" ] ; done # | 100% | Slowest
By the way, we don't really need -n with [[ to confirm that a string isn't empty:
some1-p() if [[ "$1" ]]; then echo some; else echo empty; fi some2-p() if [[ -n "$1" ]]; then echo some; else echo empty; fi some1-p "quux" #⇒ some some2-p "quux" #⇒ some some1-p "" #⇒ empty some2-p "" #⇒ empty a="foobar" b="" c= some1-p "$a" #⇒ some some2-p "$a" #⇒ some some1-p "$b" #⇒ empty some2-p "$b" #⇒ empty some1-p "$c" #⇒ empty some2-p "$c" #⇒ empty some1-p "$d" #⇒ empty some2-p "$d" #⇒ empty
I found no difference in speed between some1-p and some2-p.
📆 2025-11-02