28

When scripting, I usually write my ifs with the following syntax as it is easier for me to understand that what comes next is not true.

if [ ! "$1" = "$2" ]; then

Others say that the way below is better

if [ "$1" != "$2" ]; then

The thing is when I ask why and whether there are any differences no one seems to have any answer.

So, are there any differences between the two syntaxes? Is one of them safer than the other? Or is it just a matter of preference/habit?

Braiam
  • 35,991
Jimmy_A
  • 383
  • 9
    In the first case you need to know operators priority to distinguish !(x==y) from (!x)==y. – jimmij Jan 12 '18 at 16:23
  • @jimmij Do you mean that when comparing a string this if [ ! "$string" = "one" ] translates to if not the value of $string equals one. And this if [ "$string" != "one"] translates to if the value of $string does not equal with one? – Jimmy_A Jan 12 '18 at 16:33
  • 5
    @Jimmy_A I mean that you need to know how this "translates". Gathering operators together (!= syntax) is just more obvious. – jimmij Jan 12 '18 at 16:48
  • Dear close voters, Stéphane's answer shows that there is a material difference in behavior, the correct answer is not in any way opinion-based. – Kevin Jan 13 '18 at 18:08

3 Answers3

39

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/dash@vger.kernel.org/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.

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
  • So in other words, both syntax do the same, no differences whatsoever, but with ! "$a" = "b" you need to be more careful on how you write it to specify the comparison. From your example I understand that the second command returns not zero which could be both beneficial or troubling. It could be beneficial if you want to exit if they do not match, or troubling if you want to see whether the comparison crashed – Jimmy_A Jan 12 '18 at 16:52
  • 2
    @Jimmy_A, no, in those implementations [ ! "$a" = "$b" ] just fail when $a is ( and $b is ), it claims they are identical when they're not, ( is not the same string as ), but [ performs the wrong test in that case. – Stéphane Chazelas Jan 12 '18 at 16:59
  • Ahhh, I think I got it. The first and third mean to check if the condition is true, while the second and fourth if the condition is false. Thus, the exit status codes being different. Its basically saying, 1 & 4 variables are different and 2 & 3 not equal. – Jimmy_A Jan 12 '18 at 17:09
  • 4
    @Jimmy_A, no. You've completely missed the point. If these implementations followed POSIX, then all status codes would be 0. – Wildcard Jan 12 '18 at 23:39
  • To make sure I understand, this boils down to: [ ! "$a" = "$b" ] is sometimes handled incorrectly in buggy shell implementations (which I think is more or less restating the first sentence of the answer). – Michael Burr Jan 13 '18 at 19:41
  • @Wildcard missed the sentence mentioning that. – Jimmy_A Jan 15 '18 at 10:45
  • @StéphaneChazelas That is a really great explanation. Thank you – Jimmy_A Jan 15 '18 at 10:52
9

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.

jimmij
  • 47,140
4

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 )
DopeGhoti
  • 76,081
  • And even if ( FS_OPEN != fileHandleStatus ) as a result of the ease of accidentally typing = instead of == in languages where the former is assignment and the later equality test (like C)... – derobert Jan 12 '18 at 16:50