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

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.


! 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