In bash, I don't understand why the 3rd command isn't true:
[[ 1 -eq 1 ]] # $? is 0
[[ ! 1 -eq 1 ]] # $? is 1
[[ ! ! 1 -eq 1 ]] # $? is 1 (??)
[[ ! ( ! 1 -eq 1 ) ]] # $? is 0
It seems to do the same thing if I replace 1 -eq 1
with any true expression, and negate with any false expression.
$ ( (...)) is called arithmetic expansion, which is typical of the bash and ksh shells. This allows doing simple integer arithmetic, no floating point stuff though.
let is a bash and ksh keyword which allows for variable creation with simple arithmetic evaluation. If you try to assign a string there like let a="hello world" you'll get a syntax error. Works in bash and ksh93. $ (...) is command substitution, where you literally take the output of a command and assign to a variable.
Some of these are specific to bash shells, while others aren't. $ ( (...)) is called arithmetic expansion, which is typical of the bash and ksh shells. This allows doing simple integer arithmetic, no floating point stuff though. The result of the expression replaces the expression, as in echo $ ( (1+1)) would become echo 2
But that is not true. This is the correct way to implement this. The expression s+1 is evaluated by the shell and can be assigned to a variable. Here the expression will be calculated by the program expr, which isn't a shell builtin but an external Unix program.
[[
is an extended syntax that provides (mostly) a superset of [
. To understand its behavior, then, one should start from the standard for [
.
The POSIX test
specification describes how [
is expected to behave. In one place, it does provide a description in line with the expectations described in this question:
! expression
- True if expression is false. False if expression is true.
...but later on, the more detailed description of how parsing occurs based on number of primitives contradicts this expectation:
- 0 arguments: Exit false (1).
- 1 argument: Exit true (0) if $1 is not null; otherwise, exit false.
- 2 arguments: If $1 is
!
, exit true if $2 is null, false if $2 is not null. If $1 is a unary primary, exit true if the unary test is true, false if the unary test is false. Otherwise, produce unspecified results.- 3 arguments: If $2 is a binary primary, perform the binary test of $1 and $3. If $1 is '!', negate the two-argument test of $2 and $3. (Obsolescent XSI behavior: If $1 is '(' and $3 is ')', perform the unary test of $2.. On systems that do not support the XSI option, the results are unspecified if $1 is '(' and $3 is ')'. Otherwise, produce unspecified results.
- 4 arguments: If $1 is '!', negate the three-argument test of $2, $3, and $4. (Obsolescent XSI behavior: If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3.) On systems that do not support the XSI option, the results are unspecified if $1 is '(' and $4 is ')'. Otherwise, the results are unspecified.
- More than 4 arguments: The results are unspecified.
In the case of ! ! 1 -eq 1
, you have a five-argument case. The results are unspecified, as the standard does not specify that a five-argument case is the negation of a four-argument case if the first argument is !
.
As suggested by Zilog80: If you don't want to be subject to these restrictions, consider putting your !
outside the test syntax; ! [[ ... ]]
happens at a the shell command parsing layer instead of in bespoke test-syntax-specific logic, and ! ! [[ ... ]]
is perfectly valid there.
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