All these are equivalent:
[ $(vagrant --version > /dev/null) ]
[ ]
test
shells are not like other programming languages. They have 2 outputs: The return value, and stdout. You are passing stdout to test
, but stdout is the empty string, as it has been redirected.
What you were looking for is.
[ $(vagrant --version > /dev/null; echo $? ) ]
However [ 0 ]
and [ 1 ]
etc all return true because [ … ]
with a single word inside tests if the word is non-empty.
So you need
[ $(vagrant --version > /dev/null; echo $? ) = 0 ]
or more simply
vagrant --version > /dev/null
That's:
if
first list of commands
then
second list of commands
else
third list of commands
fi
That's to run the second list of commands if the first list of commands returns with a true/success (zero) exit status, that is if the last run command in there returns with a zero exit status.
In:
var=-2 && ((var += 2))
It's cmd1 && cmd2
where cmd2
is only run if cmd1
is successful.
var=-2
Will typically be successful as long as $var
has not been made read-only, so the ((var += 2))
command will be run:
((arithmetic expression))
Returns success/true as long as the expression is correctly evaluated (no syntax error) and the result of the expression is non-zero.
((123))
, ((1 + 1))
, ((1 == 1))
return true
((0))
, ((-2 + 2))
, ((2 == -2))
return false.
((4294967296 * 4294967296))
return false in most shells because of 64 bit integer wrapping
var += 2
as an arithmetic expression, performs the assignment and resolves to the value being assigned, here 0, hence the false exit status.
You can see the value upon which is based the exit status, by using the $((...))
arithmetic expansion syntax:
$ echo "$((1 + 1)) $((2 == 2)) $((2 == -2)) $((var = -2)) $((var += 2))"
2 1 0 -2 0
Or assigning it to a variable:
$ var=-2; ((result = (var += 2)))
$ echo "$? $result $var"
1 0 0
$?
contains the exit status of the previous command. As far as if
/then
/else
/fi
is concerned, 0 means true, anything else means false.
The confusion here comes from the fact that for arithmetic expressions, it's the other way round: 0
means false and anything else means true (for instance, 2 == 2
is 1
while 2 < 1
is 0
).
To avoid worrying about the difference, just forget about $?
and its possible values. Just think in terms of boolean true/false, sucess/failure.
grep -q foo file
Returns true if foo
is found in file
.
[ "$a" = "$b" ]
Returns true if $a
contains the same thing as $b
.
((6 * 3 - 12))
((4 == 1))
Return true if the result of the arithmetic expression is a non-zero number.
It doesn't matter whether those true/false are expressed in terms of 0 or 1 of the exit status of those grep
/[
commands or ((...))
construct.
Best Answer
The examples in the post you linked to are things like:
In that context, with the exit status of a processes, yes,
0
is truthy, and anything else is falsy. (Yes,true
andfalse
are programs here. Probably builtin to the shell, but that works the same.)From the POSIX definition (
||
is similar, as is what Bash's documentation says):Yes, that's opposite to the conventions used in pretty much all other contexts and programming languages. Then again, it is also somewhat common in C for functions to return a zero on success, and a non-zero error code on error. That way, you can tell different sorts of error apart(*). That, of course, doesn't really work if your function needs to return some useful value, like a pointer, and as far as the C language is concerned, 0 is falsy. Anyway, different it is.
I don't think it's a good idea to assume anything about the implementation. Just remember that in this context zero is true, and e.g. (true && true) is (true).
(* Like families, happy system calls are all similar. It's the unhappy ones that are unhappy each in their own way.)
Then your examples:
Here, you're using
&&
in an arithmetic context, which doesn't obey the same rules as that of exit statuses. Here, 0 is false, and 1 is true. So1 && 0
is (true AND false), which is (false), or 0.Similar to the above.
The utility called
false
exits with a status of 1 (or at least some non-zero value). Which, in that context, is falsy, so the right hand side of&&
is skipped by way of short-circuiting logic.Same thing.
You used
1 || 0
here, which is true either way, and the numerical value somewhat disappears there inside the arithmetic context. Let's try these:Now,
((...))
is an arithmetic construct (like$((...))
), and within it, 0 is falsy. Unlike$((...))
,((...))
is also a command, so has an exit status. It exits with zero (truthy), if the expression inside evaluates to non-zero (truthy); and with 1 (falsy), if the expression inside evaluates to zero (falsy). Ok, that may be confusing, but the end result is that C-like implicit comparisons against zero work inside it, and you get the same truth value out of it, when using it with the shell's conditionals.So,
while (( i-- )); do ...
loops untili
goes to zero.(
(( foo ))
is not standard, but supported by ksh/Zsh/Bash. The standard interpretation for it would be the commandfoo
inside two nested subshells, so would probably give a "command 'foo' not found" error.)It might also be worth pointing out that something like
(( true )) && echo maybe
probably doesn't print anything. In the arithmetic context, the plain word is taken as a name of a variable (recursively in many shells), so unless you have a variabletrue
set to a non-zero value,(( true ))
will be false.(From the department of obfuscation comes the idea of running
true=0; false=1; true() (exit 1); false() (exit 0);
. Now what doestrue && (( false )) || echo maybe && echo maybe not
print and why?.)