! [ "$var" -gt 0 ]
vs. [ ! "$var" -gt 0 ]
shouldn't matter with current shells and utilities as long as there are no errors.
But note that you wrote if ! [ $var -gt 0 ]
and if [ ! $var -gt 0 ]
without the quotes around $var
and that does matter!
That would break if $var
was empty or contained whitespace. You'd get e.g. if ! [ -gt 0 ]
where the [
test would print an error message and return a falsy exit code of 2. It would then be inverted by !
, and the condition as whole would be true!
$ var=
$ if ! [ $var -gt 0 ]; then echo yes; fi
bash: [: -gt: unary operator expected
yes
In that particular context, [ ! $var -gt 0 ]
could be seen as safer, as the falsy error return would stay falsy, and the branch not taken. Here, the immediate problem is in the lack of quotes, though, and that's easy to fix.
About quotes, see: When is double-quoting necessary?
However, even apart from the quoting issue, non-numeric values in $var
would also give an error from [
and make the condition false. Because of that, you should test that the inputs are valid beforehand or otherwise deal with the possibility. In some cases, the proper action in case of an error could be to take the branch, e.g. if it contained an error exit. Sadly, the shell doesn't really have a good way of detecting errors, as there are no exceptions or such. To tell an error from a false result, the raw value in $?
would need to be saved and consulted.
Or the test could be written so that the error case coincides with an appropriate result. For example, here the test is written with an extra negation instead of [ "$1" -le 0 ]
, but this has the advantage that an invalid input will trigger the error exit. This only works with the !
outside [
.
$ cat foo.sh
#!/bin/bash
if ! [ "$1" -gt 0 ]; then
echo "invalid input '$1'"
exit 1
fi
echo "doing work on '$1'"
$ bash foo.sh
foo.sh: line 3: [: : integer expression expected
invalid input ''
The variations of values of the variable and the different tests in a matrix:
test \ $n | '0' '1' 'x'
-------------------+------------------------------------
[ "$n" -le 0 ] | truthy falsy falsy (w/error)
[ ! "$n" -gt 0 ] | truthy falsy falsy (w/error)
! [ "$n" -gt 0 ] | truthy falsy truthy (w/error)
Perhaps somewhat relatedly, in times of yore, versions of [
were known to get confused when the operands within a test looked like operators. E.g. with something like [ ! = x ]
the !
could be taken as a negation and the expression as whole could not be parsed. One might fear that putting the !
inside [
might compound that issue, but I don't know if that ever would have happened in practice. In any case, POSIX-conforming versions of [
should be safe from that, at least as long as you don't use -a
and -o
.
See: What's the purpose of adding a prefix on both sides of a shell variable comparison to a string literal?, especially Stéphane's great answer there (from which I nicked the [ ! = x ]
example).
[[...]]
. My question here is about single brackets[...]
. – Josh May 16 '20 at 03:29[[...]]
and[...]
. – Andy Dalton May 16 '20 at 03:31[[ .. ]]
which is implemented quite differently from[ .. ]
in Bash. I don't see an answer about[ .. ]
there. That's also just a comment which are prone to disappearing without notice. Not that closing this would matter very much at this point either... – ilkkachu May 18 '20 at 18:15