Reputation: 96827
A usual check if a binary exists:
if hash gdate 2>/dev/null; then
gdate "$@"
else
date "$@"
fi
When ran from the commandline:
vagrant@vagrant-ubuntu-saucy-32:~$ hash gdate 2>/dev/null
vagrant@vagrant-ubuntu-saucy-32:~$ echo $?
1
This means that if I run the script, date will be called since I don't have gdate on my system.
So, this would mean that the snippet above is something like this?
if 1; then
gdate "$@"
else
date "$@"
fi
I thought that 1 evaluates to true in bash, so, shouldn't the if branch be chosen, instead of the else? Could someone break down this snippet for me?
Upvotes: 2
Views: 100
Reputation: 39414
The code works as you've analyzed. Only issue is your last statement. In bash, true ("success") is a zero exit status, non-true ("failure") is non-zero. See http://tldp.org/LDP/abs/html/exit-status.html
So, in bash, if a command exits with zero, conditionals will see that as the "true" branch. If a command exits with anything other than zero, conditionals will treat that as the "false" branch.
Upvotes: 2
Reputation: 753805
In Unix systems, an exit status of 0 is success; anything else is failure.
This is independent of what goes on inside test
operations. The test
command too returns (exits with a status of) 0 when the condition is true and 1 when it is false.
pfnuesel asked:
Is this always true? Isn't there C-like evaluation of true/false in some specific cases?
Specify a counter-example...Yes, I believe it is always true. Run [ "$var" != "value" ]; echo $?
or [[ $var != value ]]; echo $?
or similar command combinations and see what you get.
i=0; (( i++ )); echo $?
Hmmm...interesting; that definitely made me pause for thought. It complicates things, but (assuming you agree that it produces 1 from echo $?
), I think that means that the ((
command is being careful to invert the logic. If you run a test on the ((
command for various values of $i
, you will find that when the expression evaluates to 0 (as it does in your example because it is a post-increment), then the exit status is 1, which is false, but otherwise, the exit status is 0, which is true. This applies to bash
and ksh
(and I think that is bash
following ksh
).
I used this script to help clarify things to myself:
for start in {-3..3}
do
for shell in bash ksh sh
do
output=$($shell -c "i=$start;"' if (( i++ )); then echo $? true $i; else echo $? false $i; fi')
printf "%-4s %4s %s %-5s %s\n" $shell "[$start]" $output
done
done
Output:
bash [-3] 0 true -2
ksh [-3] 0 true -2
sh [-3] 0 true -2
bash [-2] 0 true -1
ksh [-2] 0 true -1
sh [-2] 0 true -1
bash [-1] 0 true 0
ksh [-1] 0 true 0
sh [-1] 0 true 0
bash [0] 1 false 1
ksh [0] 1 false 1
sh [0] 1 false 1
bash [1] 0 true 2
ksh [1] 0 true 2
sh [1] 0 true 2
bash [2] 0 true 3
ksh [2] 0 true 3
sh [2] 0 true 3
bash [3] 0 true 4
ksh [3] 0 true 4
sh [3] 0 true 4
Interestingly, the POSIX shell syntax recognizes the possibility of ((
as a command:
If a character sequence beginning with "
((
" would be parsed by the shell as an arithmetic expansion if preceded by a '$
', shells which implement an extension whereby "((expression))
" is evaluated as an arithmetic expression may treat the "((
" as introducing as an arithmetic evaluation instead of a grouping command. A conforming application shall ensure that it separates the two leading '(
' characters with white space to prevent the shell from performing an arithmetic evaluation.
Upvotes: 3
Reputation: 263267
That snippet attempts to execute 1
as a command. Since you very probably don't have a command by that name, the condition is false. if 0 ; then ...
would do the same thing (unless you happen to have commands named 0
or 1
).
If you want a condition that's always true, there are several options, the simplest of which is:
if : ; then
...
fi
or, if you prefer:
if true ; then
...
fi
Upvotes: 1