Does the syntax of not equal matter?
Beside the cosmetic/preference arguments, one reason could be that there are more implementations where [ ! "$a" = "$b" ]
fails in corner cases than with [ "$a" != "$b" ]
.
Both cases should be safe if implementations follow the POSIX algorithm, but even today (early 2018 as of writing), there are still implementations that fail. For instance, with a='(' b=')'
:
$ (a='(' b=')'; busybox test "$a" != "$b"; echo "$?")
0
$ (a='(' b=')'; busybox test ! "$a" = "$b"; echo "$?")
1
With dash
versions prior to 0.5.9, like the 0.5.8 found as sh
on Ubuntu 16.04 for instance:
$ a='(' b=')' dash -c '[ "$a" != "$b" ]; echo "$?"'
0
$ a='(' b=')' dash -c '[ ! "$a" = "$b" ]; echo "$?"'
1
(fixed in 0.5.9, see https://www.mail-archive.com/[email protected]/msg00911.html)
Those implementations treat [ ! "(" = ")" ]
as [ ! "(" "text" ")" ]
that is [ ! "text" ]
(test whether "text" is the null string) while POSIX mandates it to be [ ! "x" = "y" ]
(test "x" and "y" for equality). Those implementations fail because they perform the wrong test in that case.
Note that there's yet another form:
! [ "$a" = "$b" ]
That one requires a POSIX shell (won't work with the old Bourne shell).
Note that several implementations have had problems with [ "$a" = "$b" ]
(and [ "$a" != "$b" ]
) as well and still do like the [
builtin of /bin/sh
on Solaris 10 (a Bourne shell, the POSIX shell being in /usr/xpg4/bin/sh
). That's why you see things like:
[ "x$a" != "x$b" ]
In scripts trying to be portable to old systems.
The x != y
syntax is better because ! x == y
is error prone - requires knowledge of operators precedence which differs from language to language. The syntax ! x == y
could be interpreted as !(x == y)
or (!x) == y
, depending on priority of !
vs =
.
For example, in c++
negation !
comes before comparison/relational operator ==
, hence the following code:
#include<iostream>
using namespace std;
int main()
{
int x=1, y=2;
if( x != y ) cout<<"true"<<endl; else cout<<"false"<<endl;
if( ! x == y ) cout<<"true"<<endl; else cout<<"false"<<endl;
if( !( x == y ) ) cout<<"true"<<endl; else cout<<"false"<<endl;
if( (!x) == y ) cout<<"true"<<endl; else cout<<"false"<<endl;
}
returns
true
false
true
false
Similar behavior can be observed in many other languages, including e.g. awk
- a frequently used tool in Unix world.
On the other hand, gathering operators together via x != y
does not lead to any confusion as a well-established pattern. Moreover, technically speaking !=
is very often not two, but just one operator, so should be even marginally faster to evaluate than separate comparison and then negation. Hence, although both syntaxes work in bash I would recommend to follow x != y
as it is way easier to read and maintain code which follows some standard logic.
This sort of thing is very opinion-based, as the "answer" depends very strongly on the way an individual's brain happens to be wired. While it is true that semantically, NOT ( A == B )
is identical to (A != B )
, one might be clearer to one person and the other to another. It also is context-dependent. For example, if I have a flag set, the meanings might be more clear with one syntax over another:
if NOT ( fileHandleStatus == FS_OPEN )
as opposed to
if ( fileHandleStatus != FS_OPEN )