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

Viewed 882

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.)

3 Answers

There is a difference if test/[ faces an error. Consider:

x=abc
if   [ ! "$x" -gt 0 ]; then echo "first true"; fi
if ! [   "$x" -gt 0 ]; then echo "second true"; fi

The output is:

bash: [: abc: integer expression expected
second true

Unlike [[ .. ]], test/[ works like regular utility and signals errors by returning 2. That's a falsy value, and with ! outside the brackets, the shell inverts it just the same as a regular negative result from the test would be inverted.

With [[ .. ]] the behaviour is different, in that a syntax error in the condition is a syntax error for the shell, and the shell itself exits with an error:

if [[ a b ]]; then echo true; else echo false; fi

prints only

bash: conditional binary operator expected
bash: syntax error near `b'

with no output from the echos.

On the other hand, arithmetic tests work differently within [[ .. ]] so [[ "$x" -gt 0 ]] would never give an error.

Related