I tried to play around with bash, and even tried documentation and few threads though I cannot seem to get it right.
S=(true false)
if (( ${S[0]} || ${S[1]} ))
then
echo "true"
else
echo "false"
fi
how can the boolean expression be evaluated under bash? (any chance to fix the snippet above?)
is there any possibility to evaluate it without if, i.e. assign it to a variable directly (no if manipulation)?
Instead of S=(true false), you need to create your array like this:
S=(1 0)
Then this if block:
if (( ${S[0]} || ${S[1]} ))
then
echo "true"
else
echo "false"
fi
will output:
true
Please note that true/false are treated as literal strings "true" and "false" in BASH.
There isn't really such a thing as boolean in bash, only integral arithmetic expressions e.g. (( n )), which would return an exit code of 0 (no error or no failure code) if its value is greater than 1, or a nonzero code (has error code) if it evaluates to 0. if statements execute a then block if the command it calls returns 0 exit code, or on the else block otherwise. You can imitate boolean systems in bash like this though:
#!/bin/bash
true=1
false=0
A=true
B=false
# Compare by arithmetic:
if (( A || B )); then
echo "Arithmetic condition was true."
else
echo "Arithmetic condition was false."
fi
if (( (I > 1) == true )); then ## Same as (( I > 1 )) actually.
echo "Arithmetic condition was true."
else
echo "Arithmetic condition was false."
fi
# Compare by string
if [[ $A == true || $B == true ]]; then
echo "Conditional expression was true."
else
echo "Conditional expression was false."
fi
# Compare by builtin command
if "$A" || "$B"; then
echo "True concept builtin command was called."
else
echo "False concept builtin command was called."
fi
And I prefer the string comparison since it's a less hacky approach despite probably being a bit slower. But I could use the other methods too if I want too.
(( )), [[ ]], true, and false all are just builtin commands or expressions that return an exit code. It's better that you just think of the like that than thinking that they are really part of the shell's main syntax.
Related
In Bash 0 is true and any other number is false, like in C.
To test if a variable is true I'm currently doing:
is_on=0
if [ $is_on -eq 0 ]; then
echo "It's on!"
else
echo "It's off!"
fi
I want somethings more simple and readable, so I tried:
This doesn't because [ always returns 0:
is_on=0
if [ $is_on ]; then
echo "It's on!"
else
echo "It's off!"
fi
This also doesn't because [[ always returns 0:
is_on=0
if [[ $is_on ]]; then
echo "It's on!"
else
echo "It's off!"
fi
This also doesn't work:
is_on=0
if [ $is_on -eq true ]; then
echo "It's on!"
else
echo "It's off!"
fi
This inverses the logic:
is_on=0
if (( $is_on )); then
echo "It's on!"
else
echo "It's off!" # Prints this!
fi
This works, but it's a string comparison:
is_on=true
if [ $is_on = true ]; then
echo "It's on!"
else
echo "It's off!"
fi
Is there a simpler and more legible way to check if a variable is true or false?
The interpretation of a value depends on the context. So some facts:
When doing some logic:
0 means false,
nonzero means true.
When doing some action:
0 exit status means success,
nonzero exit status means failure.
The if body executes when the command is successful. The if body executes when command exits with zero exit status.
The exit status of (( ... )) arithmetic expansion depends on the logical value of the last expression inside. A value of true means success and a value of false means failure (read that twice).
See man test and inspect STRING equivalent to -n STRING part. Also research bash builtins, bash shell arithemtic, bash exit status.
Is there a simpler and more legible way to check if a variable is true or false?
In a controlled environment I just use the variable value and execute the shell builtins false and true (that return an exit status of nonzero (failure) and zero (success) respectively):
is_on=true
if "$is_on"; then
In not controlled environment it's best to compare the strings to be protected against strange user input:
is_on="$1"
if [[ "$is_on" = 'true' ]]; then
If you wish to handle complicated cases, then write your own logic for it. In bash all variable values are strings, there are no other variable types (well, there are arrays of strings):
shopt +extglob
user_boolean_value_to_my_boolean_convention() {
case "$1" in
# 0 and 0000000 are false
+(0)) echo false; ;;
# any nonzero number or string true is true
+([0-9])|T|t|[Tt][rR][uU][eE]) echo true; return; ;;
# all the rest is false
*) echo false; ;;
esac
}
is_on=$(user_boolean_value_to_my_boolean_convention "$1")
if "$is_on"; then
Not able to understand what this is doing
TEST_DEBUG=${TEST_DEBUG:-false}
[[ "$TEST_DEBUG" == false ]] || TEST_DEBUG=true
and in further down the code in multiple places..this code is present
$TEST_DEBUG && echo 1>&2
TEST_DEBUG=${TEST_DEBUG:-false}
This assing false to $TEST_DEBUG unless it already has a value (see Parameter Expansion in man bash).
[[ "$TEST_DEBUG" == false ]] || TEST_DEBUG=true
The condition inside [[ ... ]] returns true when $TEST_DEBUG has the value of false. If it returns false, i.e. the value is different, then $TEST_DEBUG is assigned true.
$TEST_DEBUG && echo 1>&2
This runs the echo only if $TEST_DEBUG is set to true.
echo without parameters just outputs an empty line to standard output. 1>&2 redirects standard output to standard error, so in this case, the empty line is printed to standard error.
In first statement, the operator :- Tests if TEST_DEBUG variable exists within the braces and is set, if it is set, it is assigned the value in the TEST_DEBUG if not set or has a null value, it is assigned false.
TEST_DEBUG=${TEST_DEBUG:-false}
in the second statement
[[ "$TEST_DEBUG" == false ]] || TEST_DEBUG=true
Bash checks if TEST_DEBUG is set to false, if not, it assigns it a value true.
for more information on how this works in detail, please check out GNU bash documentation.
https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html#Conditional-Constructs
x=23;
y=223;
if [[ $x < $y ]]
then
echo "TRUE"
else
echo "FALSE"
fi
So this always print FALSE even thought 23 is clearly less than 223. I am new to bash so I might be missing something obvious. I tried substituting $x and $y with their actually value and that means to work fine. Please send help
< performs lexicographic ordering of strings. To get numeric ordering, use -lt.
x=23;
y=223;
if [[ $x -lt $y ]]
then
echo "TRUE"
else
echo "FALSE"
fi
Or you can use an arithmetic expression instead of a conditional expression:
if (( $x < $y ))
I am really confused in understanding this. I presume, I can write a return code comparison as give below. Which is better? Why should it be?
RC=$?
if [[ $RC == '0' ]]; then
if [ $RC -eq '0' ]; then
if [ $RC == '0' ]; then
if [ "$RC" == "0" ]; then
if [ "$RC" -eq "0" ]; then
if [[ "$RC" == "0" ]]; then
if (( "$RC" == "0" )); then
if ( "$RC" == "0" ); then
if (( $RC == 0 )); then
if ( $RC == 0 ); then
And some more...
What if RC="A" -- String?
None of the above. In the vast majority of cases, you want to examine the exit code directly from within if. Any code which looks like
command
RC=$?
if [ $RC == 0 ]; then
is much more elegantly, robustly, and idiomatically written
if command; then
unless you specifically need to examine the precise nonzero value of the result code.
To separately address your question about which comparison operator to use, the only fully portable variant is [ with =. The syntax of this legacy Bourne shell operator does not portably support == for equality, so that's wrong (though some shells allow it as a syntactic safety net). The -eq operator compares for numeric equality, while = performs string comparison.
In modern shells, [[ and (( are both viable, depending on whether you care more about string comparisons (which is where [[ makes sense) or arithmetic (which is the domain of (( -- especially useful if the comparison involves some actual calculations on the numbers; but it is definitely the more fringe alternative, as it is not useful at all for string comparisons). [[ also supports pattern matching, so you are not confined to static string comparisons; unlike [, it requires a double equals sign for the string equality comparison. It works around a number of thorny issues with [, included but not limited to correct behavior with unquoted variables and arguments which begin with a dash.
The alternative with a single parenthesis does not do at all what you want. It attempts to run $RC as a command in a subshell.
If you are in Bash it does not segregate its variables by type, but ... a return code is a numeric value, so i will bet in:
rc=$?
if (( ! rc )); then
# rc == 0
Do something
fi
Reason: maintain coherence in relation with the expected type.
Check: http://tldp.org/LDP/abs/html/untyped.html
I'd like to use the result of a comparison in a subsequent comparison. I'm trying to do something like:
# $1 - expected result
# $2 - actual result
function print_result() {
if [[ [[ $1 -eq 0 ]] -eq [[ $2 -eq 0 ]] ]]; then # invalid
echo "Pass"
else
echo "Fail"
fi
}
I can get the desired behaviour with the more verbose form:
function print_result() {
if [[ (($1 -eq 0) && ($2 -eq 0)) || (($1 -ne 0) && ($2 -ne 0)) ]]; then
echo "Pass"
else
echo "Fail"
fi
}
but it seems like there should be a simpler way?
You need something which produces the result of the comparison operator as text rather than as a status return. That would be arithmetic expansion: $((expression)).
Note also that bash includes a numeric conditional compound statement -- (( expr )) -- which is often easier to use for numerical comparisons than the non-numerical [[ ... ]] conditional compound statement.
Putting that together, what you are looking for is:
if (( $(($1==0)) == $(($2==0)) )); then
Or just:
if (( ($1==0) == ($2==0) )); then
If you only want to know if $1 and $2 are both zero or both non-zero, then you can use the fact that boolean not (!) always evaluates to either 0 or 1. Consequently, the following even simpler expression is equivalent (but see warning below):
if ((!$1 == !$2)); then
Important: If you use that in a script, it will work fine, but at the command prompt you need to put a space after the ! to avoid it being interpreted as a history expansion character (unless you've turned history expansion off: if you don't actually use history expansion, turning it off is not a bad idea.)
A possibly slightly more readable (but not particularly simpler) way would be to store the result of each comparison in a variable and then compare the variables:
function print_result() {
[[ $1 -eq 0 ]]; arg_one_is_zero=$?
[[ $2 -eq 0 ]]; arg_two_is_zero=$?
if [[ $arg_one_is_zero -eq $arg_two_is_zero ]]; then
echo "Pass"
else
echo "Fail"
fi
}
There might be a better way to store the logical result (exit code) of a comparison in a variable but I couldn't find one after a quick look.