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 thetest
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