== _test_ is surprisingly smart [[Via Hacker News https://news.ycombinator.com/item?id=6751517]] I would up reading [[Common shell script mistakes http://www.pixelbeat.org/programming/shell_script_mistakes.html]]. When I read this, I initially thought that it contained well-intentioned but mistaken advice about _test_ (aka '_[ ... ]_'). Then I actually checked what _test_'s behavior is and got a bunch of surprised. It turns out that _test_ is really quite smart, sometimes disturbingly so. .pn prewrap on Here's two different versions of a _test_ expression: > [ x"$var" = x"find" ] && echo yes > [ "$var" = "find" ] && echo yes In theory, the reason the first version has an 'x' in front of both sides is to deal with the case where someone sets _$var_ to something that is a valid _test_ operator, like '_-a_' or '_-x_' or even '_(_'; after all, '_[ -a = find ]_' doesn't look like a valid _test_ expression. But if you actually check, it turns out that the second version works perfectly well too. What's going on is that _test_ is much smarter than you might think. Rather than simply processing its arguments left to right, it uses a much more complicated process of actually parsing its command line. When I started writing this entry I thought it was just modern versions that behaved this way, but in fact the behavior is much older than that; it goes all the way back to [[the V7 version of _test_ http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/test.c]], which actually implements a little recursive descent parser (in quite readable code). This behavior is even specified in [[the Single Unix Specification page for _test_ http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html]] where you can read the gory details for yourself (well, most of them). (The exception is that the SuS version of _test_ doesn't include _-a_ for and or _-o_ for or. This is an interesting exclusion since it turns out they were actually in the V7 version of test [[per eg the manpage http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/man/man1/test.1]].) Note that this cleverness can break down in extreme situations. For example, '_[ "$var1" -a "$var2" -a "$var3" ]_' is potentially dangerous; consider what happens if _$var2_ is '_-r_'. And of course you still really want to use _"..."_ to force things to be [[explicit empty arguments EmptyArgumentVsNone]], because an outright missing argument can easily completely change the meaning of a _test_ expression. Consider what happens to '_[ -r $var ]_' if _$var_ is empty. (It reduces to '_[ -r ]_', which is true because _-r_ is not the empty string. You probably intended it to be false because a zero-length file name is considered unreadable.)