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.)
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:
&&
and ||
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 :
test
command that exit with true expression status. test
command (or [
) exit with its false statusBoth 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With