Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a difference between negating before/after a test command?

I've been Bash scripting for a while and I'm wondering if there's any difference between these two forms of negation with the test command:

if [ ! condition ]; then
fi

if ! [ condition ]; then
fi

The first tells the shell to pass the arguments ! condition to test, letting the program take care of the negation itself. On the other hand, the second passes condition to test and lets the shell itself negate the error code.

Are there any pitfalls I should be aware of when choosing between these two forms? What values of $condition could make the results differ between them?

(I seem to remember reading an article a while ago discussing this, but I don't remember how to find it/exactly what was discussed.)

like image 994
James Ko Avatar asked Mar 26 '16 17:03

James Ko


2 Answers

To build on chepner's insightful comment on the question:

  • In [ ! condition ], the ! is just an argument to the [ builtin (an effective alias of the test builtin); it so happens that [ / test interprets argument ! as negation.

  • In ! [ condition ], the ! is a shell keyword that negates whatever command it is followed by (which happens to be [ in this case).

One thing that the ! [ condition ] syntax implicitly gives you is that negation applies to whatever [ condition ] evaluates to as a whole, so if that is the intent, you needn't worry about operator precedence.

Performance-wise, which syntax you choose probably doesn't make much of a difference; quick tests suggest:

  • If condition is literally the same in both cases, passing the ! as an argument to [ is negligibly faster.

  • If ! is used as a keyword, and you are therefore able to simplify the condition by not worrying about precedence, it may be slightly faster (e.g, ! [ 0 -o 1 ] vs. [ ! \( 0 -o 1 \) ]; note that the POSIX spec. discourages use of -a and -o due to ambiguity).

That said, if we're talking about Bash, then you should consider using [[ instead of [, because [[ is a shell keyword that parses the enclosed expression in a special context that:

  • offers more features
  • allows you to safely combine expressions with && and ||
  • comes with fewer surprises
  • is also slightly faster (though that will rarely matter in pratice)

See this answer of mine.

like image 82
mklement0 Avatar answered Nov 04 '22 14:11

mklement0


! negates the exit code of following command :

$ ! test 1 = 1 || echo $?  # negate command with true expression
1

As said in man page, test (and similar [ builtin) exit with the status determined by following expression :

$ test ! 1 = 1 || echo $?  # return false expression
1
$ [ ! 1 = 1 ] || echo $?
1

For a given expression :

  • on one side you negate the test command that exit with true expression status.
  • on the other side, you negate an expression and the test command (or [) exit with its false status

Both will have the same effect.

So I would say that these syntax are equivalent. With the advantage for external ! to allow negate compound tests :

$ ! [ 1 = 1 -a 2 = 2 ] || echo $?
1
like image 22
SLePort Avatar answered Nov 04 '22 16:11

SLePort