Compound if / logical XOR bash bug? - bash

#!/bin/bash
function logic_test()
{
left_bracket=$1
right_bracket=$2
if [[ ($left_bracket || $right_bracket) && ! ($left_bracket && $right_bracket) ]]
then
errEcho "Input error: insertIntoConfigFile arg1 does not contain matching []."
else
errEcho "Passed"
fi
}
logic_test true true
logic_test true false
logic_test false true
logic_test false false
Expected results, as per XOR's functioning:
Passed
Input Error
Input Error
Passed
Actual results in 4.3.11(1)-release
Passed
Passed
Passed
Passed
Am I failing to see something trivial in my XOR implementation, or is the if logic buggy? If it is a bug, how do I go about submitting said bug?
I'm thinking this might be a bug...if I change the if to
if [[ ($left_bracket && !$right_bracket) || (!$left_bracket && $right_bracket) ]]
then all 4 outputs become "input error"

You can use set -xv to turn on debugging. It will show you the problem: bash interprets the things in [[ .. ]] as strings and tests them with -n. None of them is emtpy, so it works as 'true true' everytime.
To fix it, just remove the [[ and ]]. Bash will correctly interpret true and false as commands to run and use their exit code in the logic.

Related

Booleans in shell script [duplicate]

I tried to declare a Boolean variable in a shell script using the following syntax:
variable=$false
variable=$true
Is this correct? Also, if I wanted to update that variable would I use the same syntax? Finally, is the following syntax for using Boolean variables as expressions correct?
if [ $variable ]
if [ !$variable ]
Revised Answer (Feb 12, 2014)
the_world_is_flat=true
# ...do something interesting...
if [ "$the_world_is_flat" = true ] ; then
echo 'Be careful not to fall off!'
fi
Original Answer
Caveats: https://stackoverflow.com/a/21210966/89391
the_world_is_flat=true
# ...do something interesting...
if $the_world_is_flat ; then
echo 'Be careful not to fall off!'
fi
From: Using boolean variables in Bash
The reason the original answer is included here is because the comments before the revision on Feb 12, 2014 pertain only to the original answer, and many of the comments are wrong when associated with the revised answer. For example, Dennis Williamson's comment about bash builtin true on Jun 2, 2010 only applies to the original answer, not the revised.
TL;DR
my_bool=true
if [ "$my_bool" = true ]
Issues with Miku's (original) answer
I do not recommend the accepted answer1. Its syntax is pretty, but it has some flaws.
Say we have the following condition.
if $var; then
echo 'Muahahaha!'
fi
In the following cases2, this condition will evaluate to true and execute the nested command.
# Variable var not defined beforehand. Case 1
var='' # Equivalent to var="". # Case 2
var= # Case 3
unset var # Case 4
var='<some valid command>' # Case 5
Typically you only want your condition to evaluate to true when your "Boolean" variable, var in this example, is explicitly set to true. All the other cases are dangerously misleading!
The last case (#5) is especially naughty because it will execute the command contained in the variable (which is why the condition evaluates to true for valid commands3, 4).
Here is a harmless example:
var='echo this text will be displayed when the condition is evaluated'
if $var; then
echo 'Muahahaha!'
fi
# Outputs:
# this text will be displayed when the condition is evaluated
# Muahahaha!
Quoting your variables is safer, e.g. if "$var"; then. In the above cases, you should get a warning that the command is not found. But we can still do better (see my recommendations at the bottom).
Also see Mike Holt's explanation of Miku's original answer.
Issues with Hbar's answer
This approach also has unexpected behavior.
var=false
if [ $var ]; then
echo "This won't print, var is false!"
fi
# Outputs:
# This won't print, var is false!
You would expect the above condition to evaluate to false, thus never executing the nested statement. Surprise!
Quoting the value ("false"), quoting the variable ("$var"), or using test or [[ instead of [, do not make a difference.
What I do recommend:
Here are ways I recommend you check your "Booleans". They work as expected.
my_bool=true
if [ "$my_bool" = true ]; then
if [ "$my_bool" = "true" ]; then
if [[ "$my_bool" = true ]]; then
if [[ "$my_bool" = "true" ]]; then
if [[ "$my_bool" == true ]]; then
if [[ "$my_bool" == "true" ]]; then
if test "$my_bool" = true; then
if test "$my_bool" = "true"; then
They're all pretty much equivalent. You'll have to type a few more keystrokes than the approaches in the other answers5, but your code will be more defensive.
Footnotes
Miku's answer has since been edited and no longer contains (known) flaws.
Not an exhaustive list.
A valid command in this context means a command that exists. It doesn't matter if the command is used correctly or incorrectly. E.g. man woman would still be considered a valid command, even if no such man page exists.
For invalid (non-existent) commands, Bash will simply complain that the command wasn't found.
If you care about length, the first recommendation is the shortest.
There seems to be some misunderstanding here about the Bash builtin true, and more specifically, about how Bash expands and interprets expressions inside brackets.
The code in miku's answer has absolutely nothing to do with the Bash builtin true, nor /bin/true, nor any other flavor of the true command. In this case, true is nothing more than a simple character string, and no call to the true command/builtin is ever made, neither by the variable assignment, nor by the evaluation of the conditional expression.
The following code is functionally identical to the code in the miku's answer:
the_world_is_flat=yeah
if [ "$the_world_is_flat" = yeah ]; then
echo 'Be careful not to fall off!'
fi
The only difference here is that the four characters being compared are 'y', 'e', 'a', and 'h' instead of 't', 'r', 'u', and 'e'. That's it. There's no attempt made to call a command or builtin named yeah, nor is there (in miku's example) any sort of special handling going on when Bash parses the token true. It's just a string, and a completely arbitrary one at that.
Update (2014-02-19): After following the link in miku's answer, now I see where some of the confusion is coming from. Miku's answer uses single brackets, but the code snippet he links to does not use brackets. It's just:
the_world_is_flat=true
if $the_world_is_flat; then
echo 'Be careful not to fall off!'
fi
Both code snippets will behave the same way, but the brackets completely change what's going on under the hood.
Here's what Bash is doing in each case:
No brackets:
Expand the variable $the_world_is_flat to the string "true".
Attempt to parse the string "true" as a command.
Find and run the true command (either a builtin or /bin/true, depending on the Bash version).
Compare the exit code of the true command (which is always 0) with 0. Recall that in most shells, an exit code of 0 indicates success and anything else indicates failure.
Since the exit code was 0 (success), execute the if statement's then clause
Brackets:
Expand the variable $the_world_is_flat to the string "true".
Parse the now-fully-expanded conditional expression, which is of the form string1 = string2. The = operator is bash's string comparison operator. So...
Do a string comparison on "true" and "true".
Yep, the two strings were the same, so the value of the conditional is true.
Execute the if statement's then clause.
The no-brackets code works, because the true command returns an exit code of 0, which indicates success. The bracketed code works, because the value of $the_world_is_flat is identical to the string literal true on the right side of the =.
Just to drive the point home, consider the following two snippets of code:
This code (if run with root privileges) will reboot your computer:
var=reboot
if $var; then
echo 'Muahahaha! You are going down!'
fi
This code just prints "Nice try." The reboot command is not called.
var=reboot
if [ $var ]; then
echo 'Nice try.'
fi
Update (2014-04-14) To answer the question in the comments regarding the difference between = and ==: AFAIK, there is no difference. The == operator is a Bash-specific synonym for =, and as far as I've seen, they work exactly the same in all contexts.
Note, however, that I'm specifically talking about the = and == string comparison operators used in either [ ] or [[ ]] tests. I'm not suggesting that = and == are interchangeable everywhere in bash.
For example, you obviously can't do variable assignment with ==, such as var=="foo" (well technically you can do this, but the value of var will be "=foo", because Bash isn't seeing an == operator here, it's seeing an = (assignment) operator, followed by the literal value ="foo", which just becomes "=foo").
Also, although = and == are interchangeable, you should keep in mind that how those tests work does depend on whether you're using it inside [ ] or [[ ]], and also on whether or not the operands are quoted. You can read more about that in Advanced Bash Scripting Guide: 7.3 Other Comparison Operators (scroll down to the discussion of = and ==).
Use arithmetic expressions.
#!/bin/bash
false=0
true=1
((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true
Output:
true
not false
Long story short:
There are no Booleans in Bash
The true and false commands
Bash does have Boolean expressions in terms of comparison and conditions. That said, what you can declare and compare in Bash are strings and numbers. That's it.
Wherever you see true or false in Bash, it's either a string or a command/builtin which is only used for its exit code.
This syntax...
if true; then ...
is essentially...
if COMMAND; then ...
where the command is true. The condition is true whenever the command returns exit code 0. true and false are Bash builtins and sometimes also standalone programs that do nothing but returning the corresponding exit code.
Conditions in if..then..fi
When using square brackets or the test command, you rely on the exit code of that construct. Keep in mind that [ ] and [[ ]] are also just commands/builtins like any other. So ...
if [[ 1 == 1 ]]; then echo yes; fi
corresponds to
if COMMAND; then echo yes; fi
and the COMMAND here is [[ with the parameters 1 == 1 ]]
The if..then..fi construct is just syntactic sugar. You can always just run the commands separated by a double ampersand for the same effect:
[[ 1 == 1 ]] && echo yes
When using true and false in these testing constructs you are actually only passing the string "true" or "false" to the testing command. Here is an example:
Believe it or not but those conditions are all yielding the same result:
if [[ false ]]; then ...
if [[ "false" ]]; then ...
if [[ true ]]; then ...
if [[ "true" ]]; then ...
TL;DR; always compare against strings or numbers
To make this clear to future readers, I would recommend always using quotes around true and false:
DO
if [[ "${var}" == "true" ]]; then ...
if [[ "${var}" == "false" ]]; then ...
if [[ "${var}" == "yes" ]]; then ...
if [[ "${var}" == "USE_FEATURE_X" ]]; then ...
if [[ -n "${var:-}" ]]; then echo "var is not empty" ...
DON'T
# Always use double square brackets in bash!
if [ ... ]; then ...
# This is not as clear or searchable as -n
if [[ "${var}" ]]; then ...
# Creates impression of Booleans
if [[ "${var}" != true ]]; then ...
# `-eq` is for numbers and doesn't read as easy as `==`
if [[ "${var}" -eq "true" ]]; then ...
Maybe
# Creates impression of Booleans.
# It can be used for strict checking of dangerous operations.
# This condition is false for anything but the literal string "true".
if [[ "${var}" != "true" ]]; then ...
Long ago, when all we had was sh, Booleans where handled by relying on a convention of the test program where test returns a false exit status if run without any arguments.
This allows one to think of a variable that is unset as false and variable set to any value as true. Today, test is a builtin to Bash and is commonly known by its one-character alias [ (or an executable to use in shells lacking it, as dolmen notes):
FLAG="up or <set>"
if [ "$FLAG" ] ; then
echo 'Is true'
else
echo 'Is false'
fi
# Unset FLAG
# also works
FLAG=
if [ "$FLAG" ] ; then
echo 'Continues true'
else
echo 'Turned false'
fi
Because of quoting conventions, script writers prefer to use the compound command [[ that mimics test, but has a nicer syntax: variables with spaces do not need to be quoted; one can use && and || as logical operators with weird precedence, and there are no POSIX limitations on the number of terms.
For example, to determine if FLAG is set and COUNT is a number greater than 1:
FLAG="u p"
COUNT=3
if [[ $FLAG && $COUNT -gt '1' ]] ; then
echo 'Flag up, count bigger than 1'
else
echo 'Nope'
fi
This stuff can get confusing when spaces, zero length strings, and null variables are all needed and also when your script needs to work with several shells.
Instead of faking a Boolean and leaving a trap for future readers, why not just use a better value than true and false?
For example:
build_state=success
if something-horrible; then
build_state=failed
fi
if [[ "$build_state" == success ]]; then
echo go home; you are done
else
echo your head is on fire; run around in circles
fi
How can I declare and use Boolean variables in a shell script?
Unlike many other programming languages, Bash does not segregate its variables by "type." [1]
So the answer is pretty clear. There isn't any Boolean variable in Bash.
However:
Using a declare statement, we can limit the value assignment to
variables.[2]
#!/bin/bash
declare -ir BOOL=(0 1) # Remember BOOL can't be unset till this shell terminates
readonly false=${BOOL[0]}
readonly true=${BOOL[1]}
# Same as declare -ir false=0 true=1
((true)) && echo "True"
((false)) && echo "False"
((!true)) && echo "Not True"
((!false)) && echo "Not false"
The r option in declare and readonly is used to state explicitly that the variables are readonly. I hope the purpose is clear.
My findings and suggestion differ a bit from the other posts. I found that I could use "booleans" basically as one would in any "regular" language, without the "hoop jumping" suggested...
There isn't any need for [] or explicit string comparisons... I tried multiple Linux distributions. I tested Bash, Dash, and BusyBox. The results were always the same. I'm not sure what the original top voted posts are talking about. Maybe times have changed and that's all there is to it?
If you set a variable to true, it subsequently evaluates as an "affirmative" within a conditional. Set it to false, and it evaluates to a "negative". Very straightforward! The only caveat, is that an undefined variable also evaluates like true! It would be nice if it did the opposite (as it would in most languages), but that's the trick - you just need to explicitly initialize your booleans to true or false.
Why does it work this way? That answer is two fold. A) true/false in a shell really means "no error" vs "error" (i.e. 0 vs anything else). B) true/false are not values - but rather statements in shell scripting! Regarding the second point, executing true or false on a line by itself sets the return value for the block you're in to that value, i.e. false is a declaration of "error encountered", where true "clears" that. Using it with an assignment to a variable "returns" that into the variable. An undefined variable evaluates like true in a conditional because that equally represents 0 or "no error encountered".
See the example Bash lines and results below. Test it yourself if you want to confirm...
#!/bin/sh
# Not yet defined...
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;
myBool=true
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;
myBool=false
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;
Yields
when set to
it evaluates to true
when set to true
it evaluates to true
when set to false
it evaluates to false
POSIX (Portable Operating System Interface)
I miss here the key point, which is portability. That's why my header has POSIX in itself.
Essentially, all of the voted answers are correct, with the exception they are Bash-specific too much.
Basically, I only wish to add more information about portability.
[ and ] brackets like in [ "$var" = true ] are not necessary, and you can omit them and use the test command directly:
test "$var" = true && yourCodeIfTrue || yourCodeIfFalse
Important note: I no longer recommend this as it's being slowly deprecated and more difficult to combine multiple statements.
Imagine what those words true and false mean to the shell, test it yourself:
echo $(( true ))
0
echo $(( false ))
1
But using quotes:
echo $(( "true" ))
bash: "true": syntax error: operand expected (error token is ""true"")
sh (dash): sh: 1: arithmetic expression: expecting primary: ""true""
The same goes for:
echo $(( "false" ))
The shell can't interpret it other than a string. I hope you are getting the idea of how good it is using proper keyword without quotes.
But no one said it in previous answers.
What does this mean? Well, several things.
You should get used to the Boolean keywords are actually treated like numbers, that is true = 0 and false = 1, remember all non-zero values are treated like false.
Since they are treated as numbers, you should treat them like that too, i.e. if you define variable say:
var_bool=true
echo "$var_bool"
true
you can create an opposite value of it with:
var_bool=$(( 1 - $var_bool )) # same as $(( ! $var_bool ))
echo "$var_bool"
1
As you can see for yourself, the shell does print true string for the first time you use it, but since then, it all works via number 0 representing trueor 1 representing false, respectively.
Finally, what you should do with all that information
First, one good habit would be assigning 0 instead of true; 1 instead of false.
Second good habit would be to test if the variable is / isn't equal to zero:
if [ "$var_bool" -eq 0 ]; then
yourCodeIfTrue
else
yourCodeIfFalse
fi
In many programming languages, the Boolean type is, or is implemented as, a subtype of integer, where true behaves like 1 and false behaves like 0:
Boolean in C
Boolean in Python
Boolean in Java
Mathematically, Boolean algebra resembles integer arithmetic modulo 2. Therefore, if a language doesn't provide native Boolean type, the most natural and efficient solution is to use integers. This works with almost any language. For example, in Bash you can do:
# val=1; ((val)) && echo "true" || echo "false"
true
# val=0; ((val)) && echo "true" || echo "false"
false
man bash:
((expression))
The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression".
Regarding syntax, this is a simple methodology that I use (by example) to consistently and sanely manage Boolean logic:
# Tests
var=
var=''
var=""
var=0
var=1
var="abc"
var=abc
if [[ -n "${var}" ]] ; then
echo 'true'
fi
if [[ -z "${var}" ]] ; then
echo 'false'
fi
# Results
# var= # false
# var='' # false
# var="" # false
# var=0 # true
# var=1 # true
# var="abc" # true
# var=abc # true
If the variable is never declared the answer is: # false
So, a simple way to set a variable to true (using this syntax methodology) would be, var=1; conversely, var=''.
Reference:
-n = True if the length of var string is non-zero.
-z = True if the length of var string is zero.
Bill Parker is getting voted down, because his definitions are reversed from the normal code convention. Normally, true is defined as 0 and false is defined as nonzero. 1 will work for false, as will 9999 and -1. The same with function return values - 0 is success and anything nonzero is failure. Sorry, I don't have the street credibility yet to vote or to reply to him directly.
Bash recommends using double brackets now as a habit instead of single brackets, and the link Mike Holt gave explains the differences in how they work. 7.3. Other Comparison Operators
For one thing, -eq is a numerical operator, so having the code
#**** NOTE *** This gives error message *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
will issue an error statement, expecting an integer expression. This applies to either parameter, as neither is an integer value. Yet, if we put double brackets around it, it will not issue an error statement, but it will yield a wrong value (well, in 50% of the possible permutations). It will evaluate to [[0 -eq true]] = success, but also to [[0 -eq false]] = success, which is wrong (hmmm.... what about that builtin being a numerical value?).
#**** NOTE *** This gives wrong output *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then
There are other permutations of the conditional which will give wrong output as well. Basically, anything (other than the error condition listed above) that sets a variable to a numerical value and compares it to a true/false builtin, or sets a variable to a true/false builtin and compares it to a numerical value. Also, anything that sets a variable to a true/false builtin and does a comparison using -eq. So avoid -eq for Boolean comparisons and avoid using numerical values for Boolean comparisons. Here's a summary of the permutations that will give invalid results:
# With variable set as an integer and evaluating to true/false
# *** This will issue error warning and not run: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
# With variable set as an integer and evaluating to true/false
# *** These statements will not evaluate properly: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
#
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" == true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then
# With variable set as an true/false builtin and evaluating to true/false
# *** These statements will not evaluate properly: *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = 0 ]; then
#
if [[ "${The_world_is_flat}" = 0 ]]; then
#
if [ "${The_world_is_flat}" == 0 ]; then
#
if [[ "${The_world_is_flat}" == 0 ]]; then
So, now to what works. Use true/false builtins for both your comparison and your evaluations (as Mike Hunt noted, don't enclose them in quotes). Then use either or single or double equal sign (= or ==) and either single or double brackets ([ ] or [[ ]]). Personally, I like the double equals sign, because it reminds me of logical comparisons in other programming languages, and double quotes just because I like typing. So these work:
# With variable set as an integer and evaluating to true/false
# *** These statements will work properly: *****
#
The_world_is_flat=true/false;
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then
There you have it.
My receipe to (my own) idiocy:
# setting ----------------
commonMode=false
if [[ $something == 'COMMON' ]]; then
commonMode=true
fi
# using ----------------
if $commonMode; then
echo 'YES, Common Mode'
else
echo 'NO, no Common Mode'
fi
$commonMode && echo 'commonMode is ON ++++++'
$commonMode || echo 'commonMode is OFF xxxxxx'
Another way of using booleans is to test the emptyness of values. This has the advantage of making shorter tests:
first=1 # A true value
second= # A false value
[ -n "$first" ] && echo 'First var is true'
[ -z "$first" ] && echo 'First var is false'
[ -n "$second" ] && echo 'Second var is true'
[ -z "$second" ] && echo 'Second var is false'
Output:
First var is true
Second var is false
Here is an alternative test syntax with bash: [[ -n $one ]]
Here is an improvement on miku's original answer that addresses Dennis Williamson's concerns about the case where the variable is not set:
the_world_is_flat=true
if ${the_world_is_flat:-false} ; then
echo "Be careful not to fall off!"
fi
And to test if the variable is false:
if ! ${the_world_is_flat:-false} ; then
echo "Be careful not to fall off!"
fi
About other cases with a nasty content in the variable, this is a problem with any external input fed to a program.
Any external input must be validated before trusting it. But that validation has to be done just once, when that input is received.
It doesn't have to impact the performance of the program by doing it on every use of the variable like Dennis Williamson suggests.
Here is a simple example which works for me:
temp1=true
temp2=false
if [ "$temp1" = true ] || [ "$temp2" = true ]
then
echo "Do something."
else
echo "Do something else."
fi
Here is an implementation of a short handed if true.
# Function to test if a variable is set to "true"
_if () {
[ "${1}" == "true" ] && return 0
[ "${1}" == "True" ] && return 0
[ "${1}" == "Yes" ] && return 0
return 1
}
Example 1
my_boolean=true
_if ${my_boolean} && {
echo "True Is True"
} || {
echo "False Is False"
}
Example 2
my_boolean=false
! _if ${my_boolean} && echo "Not True is True"
I found the existing answers confusing.
Personally, I just want to have something which looks and works like C.
This snippet works many times a day in production:
snapshotEvents=true
if ($snapshotEvents)
then
# Do stuff if true
fi
and to keep everyone happy, I tested:
snapshotEvents=false
if !($snapshotEvents)
then
# Do stuff if false
fi
Which also worked fine.
The $snapshotEvents evaluates the contents of value of the variable. So you need the $.
You don't really need the parentheses, I just find them helpful.
Tested on: GNU Bash, version 4.1.11(2)-release
Bash Guide for Beginners, Machtelt Garrels, v1.11, 2008
This is a speed test about different ways to test "Boolean" values in Bash:
#!/bin/bash
rounds=100000
b=true # For true; b=false for false
type -a true
time for i in $(seq $rounds); do command $b; done
time for i in $(seq $rounds); do $b; done
time for i in $(seq $rounds); do [ "$b" == true ]; done
time for i in $(seq $rounds); do test "$b" == true; done
time for i in $(seq $rounds); do [[ $b == true ]]; done
b=x; # Or any non-null string for true; b='' for false
time for i in $(seq $rounds); do [ "$b" ]; done
time for i in $(seq $rounds); do [[ $b ]]; done
b=1 # Or any non-zero integer for true; b=0 for false
time for i in $(seq $rounds); do ((b)); done
It would print something like
true is a shell builtin
true is /bin/true
real 0m0,815s
user 0m0,767s
sys 0m0,029s
real 0m0,562s
user 0m0,509s
sys 0m0,022s
real 0m0,829s
user 0m0,782s
sys 0m0,008s
real 0m0,782s
user 0m0,730s
sys 0m0,015s
real 0m0,402s
user 0m0,391s
sys 0m0,006s
real 0m0,668s
user 0m0,633s
sys 0m0,008s
real 0m0,344s
user 0m0,311s
sys 0m0,016s
real 0m0,367s
user 0m0,347s
sys 0m0,017s
You can use shFlags.
It gives you the option to define: DEFINE_bool
Example:
DEFINE_bool(big_menu, true, "Include 'advanced' options in the menu listing");
From the command line you can define:
sh script.sh --bigmenu
sh script.sh --nobigmenu # False
In most cases you need "boolean" for boolean operations, such as ! && or ||. In some languages special boolean type do not exists at all (because in fact you do not need it technically), in most of interpreted languages (almost) all types automatically converted to some kind of boolean in operations like ! && or ||.
Boolean always is something that works with boolean operations. In Bash such operations are (no "" around $var, it's very important!):
[[ $var ]] - check if var is true
[[ ! $var ]] - check if var is false
[[ $var1 || $var2 ]] - var1 or var2
[[ $var1 && $var2 ]] - var1 and var2
[[ $var ]] is false only if var='' (or unset). So the only one correct 'false' value of variable in bash is '' (empty string). For true you can select any value, but I prefer var=1 (like in other languages, where true is any not-null int, but for readable purposes programmers always use 1).
NOTE: for var='false' var in boolean checks is true. In Bash string 'false' is actually true (same as in all other languages!). The only one difference between Bash and, e.g. Python, Perl or PHP, is that in Bash 0 is also true. But again: there are boolean operations in Bash, and they works like in all other interpreted languages, except that 0 in Bash is true.
It's absolutely irrational to use strings 'true' and 'false' as boolean replacement in Bash. It's like using same strange concept in Python, or Perl, or PHP. It works slower (in Bash too), and nobody do it in other languages, but in Bash there are a lot of coders that think it's a good solution. No. There are no reasons not to use boolean operations in Bash directly, without absolutely strange comparisons like $var == 'true' or $var == 'false'.
So again, boolean replacements in Bash are:
var='' # false
var=1 # true (actually any non-empty string)
And direct usage of boolean checks in Bash also is a fastest way to do boolean-like checks. All other constructions will works much slower.
P.S. "old" style of "boolean" checks like a=true; if $a; then ... are absolutely idiotic, because:
you can't use them directly in conditions [[ ]]
they act as eval of bash code, stored in variable. Well, if you think it's a good idea, just do not ever write any program, please;)
P.P.S. If $var can be unset and you use very helpful set -u just replace $var in checks to ${var-}, e.g. [[ ${var-} ]] (also: no "" around, it's important for speed of code execution!)
[[ "$x" == 'true' || "$x" -ne 0 ]] && ...
Is enough simple and has no dependencies.
Bash really confuses the issue with the likes of [, [[, ((, $((, etc.
All treading on each others' code spaces. I guess this is mostly historical, where Bash had to pretend to be sh occasionally.
Most of the time, I can just pick a method and stick with it. In this instance, I tend to declare (preferably in a common library file I can include with . in my actual script(s)).
TRUE=1; FALSE=0
I can then use the (( ... )) arithmetic operator to test thusly.
testvar=$FALSE
if [[ -d ${does_directory_exist} ]]
then
testvar=$TRUE;
fi
if (( testvar == TRUE )); then
# Do stuff because the directory does exist
fi
You do have to be disciplined. Your testvar must either be set to $TRUE or $FALSE at all times.
In (( ... )) comparators, you don't need the preceding $, which makes it more readable.
I can use (( ... )) because $TRUE=1 and $FALSE=0, i.e. numeric values.
The downside is having to use a $ occasionally:
testvar=$TRUE
which is not so pretty.
It's not a perfect solution, but it covers every case I need of such a test.
Alternative - use a function
is_ok(){ :;}
is_ok(){ return 1;}
is_ok && echo "It's OK" || echo "Something's wrong"
Defining the function is less intuitive, but checking its return value is very easy.

The nature of Bash's false

I saw something unexpected today.
$ bash -c 'false'
$ echo $?
1
This is as expected.
$ bash -c '[ false ]'
$ echo $?
0
Wat.
Similarly:
$ if [ false ]; then echo 'true'; fi
true
I think I understand that test a.k.a. [ does not execute its argument. But... what does it really do with it?
This is because test (which is what [ runs) does not run its argument, it evaluates it according to the operators it understands -- ! = != -a -n etc. In particular, any string that doesn't match an operator is a test for not equal to the empty string. false is such an unrecognized string, so it is compared against the empty string (not equal), so results in a true exit code...
man test will show you all of the operators test does understand.
if { false ; } ; then echo TRUE; fi
if ( false ) ; then echo TRUE; fi
if false ; then echo TRUE; fi
All of these forms execute the false command.
if [ false ] ; then echo TRUE; fi
if test false ; then echo TRUE; fi
These don't execute anything.
The [ is a bash builtin command synonomous with the test builtin (which is different from /usr/bin/test). The most basic test is simply [ string ] which is true if string is not null or empty.
You can find extended examples here.
[ processes its arguments like any other program, it just happens to change its return value to more directly reflect what they contain. If you look in the man pages for it, you will see that when run with exactly two arguments (false and ] in this case), it will exit with status code zero (true) if and only if the first is nonempty and the second is ]. Both are true, so it returns success.

How does AND and OR operators work in Bash?

I tried the following command on bash
echo this || echo that && echo other
This gives the output
this
other
I didn't understand that!
My dry run goes this way :
echo this || echo that && echo other implies true || true && true
Since, && has more precedence than ||, the second expression evaluates first
Since, both are true, the || is evaluated which also gives true.
Hence, I conclude the output to be:
that
other
this
Being from a Java background where && has more precedence than ||, I am not able to relate this to bash.
Any inputs would be very helpful!
From man bash
3.2.3 Lists of Commands
A list is a sequence of one or more pipelines separated by one of the operators ‘;’, ‘&’, ‘&&’, or ‘||’, and optionally terminated by one of ‘;’, ‘&’, or a newline.
Of these list operators, ‘&&’ and ‘||’ have equal precedence, followed by ‘;’ and ‘&’, which have equal precedence.
So, your example
echo this || echo that && echo other
could be read like
(this || that) && other
In bash, && and || have equal precendence and associate to the left. See Section 3.2.3 in the manual for details.
So, your example is parsed as
$ (echo this || echo that) && echo other
And thus only the left-hand side of the or runs, since that succeeds the right-hand side doesn't need to run.
Boolean evaluation in bash is short-circuit: true || false will never evaluate the false operand, because the true operand is enough to determine the outcome of the operation. Likewise, false && true will not evaluate the true operand, because it cannot change the value of the expression.
Boolean evaluation in bash is actually used mainly for controlling the conditional evaluation of the operands, not their order. Typical usage is do_foo || do_bar_if_foo_fails or do_foo && do_bar_only_if_foo_has_succeeded.
In no situation will your echo that command be executed, because the echo this is true and determines the value of the entire echo this || echo that sub-expression.
From man bash:
Of these list operators, && and || have equal precedence, followed by
; and &, which have equal precedence.
So your output is expected.
I think you've already figured it out. Operator precedence doesn't work like that in bash. Everything just goes left to right in your example.
Let's explain what this does:
echo this || echo that && echo other
echo this || echo that -> echo that only if echo this FAILS.
&& echo other -> echo other only if the command before && SUCCEEDS.
So basically:
echo this---> SUCCESS ----> echo that ----> is not executed since echo this succeeded ---> echo other ---> is executed cause echo this || echo that was executed correctly.

Operations on boolean variables

In this question it has been shown how to use neat boolean variables in bash. Is there a way of performing logic operations with such variables? E.g. how to get this:
var1=true
var2=false
# ...do something interesting...
if ! $var1 -a $var2; then <--- doesn't work correctly
echo "do sth"
fi
This does work:
if ! $var1 && $var2; then
echo "do sth"
fi
Maybe somebody can explain why -a and -o operators don't work and &&, ||, ! do?
Okay boys and girls, lesson time.
What's happening when you execute this line?
if true ; then echo 1 ; fi
What's happening here is that the if command is being executed. After that everything that happens is part of the if command.
What if does is it executes one or more commands (or rather, pipelines) and, if the return code from the last command executed was successful, it executes the commands after then until fi is reached. If the return code was not successful the then part is skipped and execution continues after fi.
if takes no switches, its behavior is not modifiable in anyway.
In the example above the command I told if to execute was true. true is not syntax or a keyword, it's just another command. Try executing it by itself:
true
It will print nothing, but it set its return code to 0 (aka "true"). You can more clearly see that it is a command by rewriting the above if statement like this:
if /bin/true ; then echo 1 ; fi
Which is entirely equivalent.
Always returning true from a test is not very useful. It is typical to use if in conjunction with the test command. test is sometimes symlinked to or otherwise known as [. On your system you probably have a /bin/[ program, but if you're using bash [ will be a builtin command. test is a more complex command than if and you can read all about it.
help [
man [
But for now let us say that test performs some tests according to the options you supply and returns with either a successful return code (0) or an unsuccessful one. This allows us to say
if [ 1 -lt 2 ] ; then echo one is less than two ; fi
But again, this is always true, so it's not very useful. It would be more useful if 1 and 2 were variables
read -p' Enter first number: ' first
read -p' Enter second number: ' second
echo first: $first
echo second: $second
if [ $first -lt $second ] ; then
echo $first is less than $second
fi
Now you can see that test is doing its job. Here we are passing test four arguments. The second argument is -lt which is a switch telling test that the first argument and third argument should be tested to see if the first argument is less than the third argument. The fourth argument does nothing but mark the end of the command; when calling test as [ the final argument must always be ].
Before the above if statement is executed the variables are evaluated. Suppose that I had entered 20 for first and 25 for second, after evaluation the script will look like this:
read -p' Enter first number: ' first
read -p' Enter second number: ' second
echo first: 20
echo second: 25
if [ 20 -lt 25 ] ; then
echo 20 is less than 25
fi
And now you can see that when test is executed it will be testing is 20 less than 25?, which is true, so if will execute the then statement.
Bringing it back to the question at hand: What's going on here?
var1=true
var2=false
if ! $var1 -a $var2 ; then
echo $var1 and $var2 are both true
fi
When the if command is evaluated it will become
if ! true -a false ; then
This is instructing if to execute true and passing the arguments -a false to the true command. Now, true doesn't take any switches or arguments, but it also will not produce an error if you supply them without need. This means that it will execute, return success and the -a false part will be ignored. The ! will reverse the success in to a failure and the then part will not be executed.
If you were to replace the above with a version calling test it would still not work as desired:
var1=true
var2=false
if ! [ $var1 -a $var2 ] ; then
echo $var1 and $var2 are both true
fi
Because the if line would be evaluated to
if ! [ true -a false ; ] then
And test would see true not as a boolean keyword, and not as a command, but as a string. Since a non-empty string is treated as "true" by test it will always return success to if, even if you had said
if ! [ false -a yourmom ] ; then
Since both are non-empty strings -a tests both as true, returns success which is reversed with ! and passed to if, which does not execute the then statement.
If you replace the test version with this version
if ! $var1 && $var2 ; then
Then it will be evaluated in to
if ! true && false ; then
And will be processed like this: if executes true which returns success; which is reversed by !; because the return code of the first command was failure the && statement short circuits and false never gets executed. Because the final command executed returned a failure, failure is passed back to if which does not execute the then clause.
I hope this is all clear.
It is perhaps worth pointing out that you can use constructs like this:
! false && true && echo 1
Which does not use if but still checks return codes, because that is what && and || are for.
There is kind of a black art to using test without making any mistakes. In general, when using bash, the newer [[ command should be used instead because it is more powerful and does away with lots of gotchas which must, for compatibility reasons, be kept in [.
Since the original poster did not supply a realistic example of what he's trying to accomplish it's hard to give any specific advice as to the best solution. Hopefully this has been sufficiently helpful that he can now figure out the correct thing to do.
You have mixed here two different syntaxes.
This will work:
if ! [ 1 -a 2 ]; then
echo "do sth"
fi
Note brackets around the expressions.
You need the test command ([ in newer syntax) to use these keys (-a, -o and so on).
But test does nut run commands itself.
If you want to check exit codes of commands you must not use test.

How can I declare and use Boolean variables in a shell script?

I tried to declare a Boolean variable in a shell script using the following syntax:
variable=$false
variable=$true
Is this correct? Also, if I wanted to update that variable would I use the same syntax? Finally, is the following syntax for using Boolean variables as expressions correct?
if [ $variable ]
if [ !$variable ]
Revised Answer (Feb 12, 2014)
the_world_is_flat=true
# ...do something interesting...
if [ "$the_world_is_flat" = true ] ; then
echo 'Be careful not to fall off!'
fi
Original Answer
Caveats: https://stackoverflow.com/a/21210966/89391
the_world_is_flat=true
# ...do something interesting...
if $the_world_is_flat ; then
echo 'Be careful not to fall off!'
fi
From: Using boolean variables in Bash
The reason the original answer is included here is because the comments before the revision on Feb 12, 2014 pertain only to the original answer, and many of the comments are wrong when associated with the revised answer. For example, Dennis Williamson's comment about bash builtin true on Jun 2, 2010 only applies to the original answer, not the revised.
TL;DR
my_bool=true
if [ "$my_bool" = true ]
Issues with Miku's (original) answer
I do not recommend the accepted answer1. Its syntax is pretty, but it has some flaws.
Say we have the following condition.
if $var; then
echo 'Muahahaha!'
fi
In the following cases2, this condition will evaluate to true and execute the nested command.
# Variable var not defined beforehand. Case 1
var='' # Equivalent to var="". # Case 2
var= # Case 3
unset var # Case 4
var='<some valid command>' # Case 5
Typically you only want your condition to evaluate to true when your "Boolean" variable, var in this example, is explicitly set to true. All the other cases are dangerously misleading!
The last case (#5) is especially naughty because it will execute the command contained in the variable (which is why the condition evaluates to true for valid commands3, 4).
Here is a harmless example:
var='echo this text will be displayed when the condition is evaluated'
if $var; then
echo 'Muahahaha!'
fi
# Outputs:
# this text will be displayed when the condition is evaluated
# Muahahaha!
Quoting your variables is safer, e.g. if "$var"; then. In the above cases, you should get a warning that the command is not found. But we can still do better (see my recommendations at the bottom).
Also see Mike Holt's explanation of Miku's original answer.
Issues with Hbar's answer
This approach also has unexpected behavior.
var=false
if [ $var ]; then
echo "This won't print, var is false!"
fi
# Outputs:
# This won't print, var is false!
You would expect the above condition to evaluate to false, thus never executing the nested statement. Surprise!
Quoting the value ("false"), quoting the variable ("$var"), or using test or [[ instead of [, do not make a difference.
What I do recommend:
Here are ways I recommend you check your "Booleans". They work as expected.
my_bool=true
if [ "$my_bool" = true ]; then
if [ "$my_bool" = "true" ]; then
if [[ "$my_bool" = true ]]; then
if [[ "$my_bool" = "true" ]]; then
if [[ "$my_bool" == true ]]; then
if [[ "$my_bool" == "true" ]]; then
if test "$my_bool" = true; then
if test "$my_bool" = "true"; then
They're all pretty much equivalent. You'll have to type a few more keystrokes than the approaches in the other answers5, but your code will be more defensive.
Footnotes
Miku's answer has since been edited and no longer contains (known) flaws.
Not an exhaustive list.
A valid command in this context means a command that exists. It doesn't matter if the command is used correctly or incorrectly. E.g. man woman would still be considered a valid command, even if no such man page exists.
For invalid (non-existent) commands, Bash will simply complain that the command wasn't found.
If you care about length, the first recommendation is the shortest.
There seems to be some misunderstanding here about the Bash builtin true, and more specifically, about how Bash expands and interprets expressions inside brackets.
The code in miku's answer has absolutely nothing to do with the Bash builtin true, nor /bin/true, nor any other flavor of the true command. In this case, true is nothing more than a simple character string, and no call to the true command/builtin is ever made, neither by the variable assignment, nor by the evaluation of the conditional expression.
The following code is functionally identical to the code in the miku's answer:
the_world_is_flat=yeah
if [ "$the_world_is_flat" = yeah ]; then
echo 'Be careful not to fall off!'
fi
The only difference here is that the four characters being compared are 'y', 'e', 'a', and 'h' instead of 't', 'r', 'u', and 'e'. That's it. There's no attempt made to call a command or builtin named yeah, nor is there (in miku's example) any sort of special handling going on when Bash parses the token true. It's just a string, and a completely arbitrary one at that.
Update (2014-02-19): After following the link in miku's answer, now I see where some of the confusion is coming from. Miku's answer uses single brackets, but the code snippet he links to does not use brackets. It's just:
the_world_is_flat=true
if $the_world_is_flat; then
echo 'Be careful not to fall off!'
fi
Both code snippets will behave the same way, but the brackets completely change what's going on under the hood.
Here's what Bash is doing in each case:
No brackets:
Expand the variable $the_world_is_flat to the string "true".
Attempt to parse the string "true" as a command.
Find and run the true command (either a builtin or /bin/true, depending on the Bash version).
Compare the exit code of the true command (which is always 0) with 0. Recall that in most shells, an exit code of 0 indicates success and anything else indicates failure.
Since the exit code was 0 (success), execute the if statement's then clause
Brackets:
Expand the variable $the_world_is_flat to the string "true".
Parse the now-fully-expanded conditional expression, which is of the form string1 = string2. The = operator is bash's string comparison operator. So...
Do a string comparison on "true" and "true".
Yep, the two strings were the same, so the value of the conditional is true.
Execute the if statement's then clause.
The no-brackets code works, because the true command returns an exit code of 0, which indicates success. The bracketed code works, because the value of $the_world_is_flat is identical to the string literal true on the right side of the =.
Just to drive the point home, consider the following two snippets of code:
This code (if run with root privileges) will reboot your computer:
var=reboot
if $var; then
echo 'Muahahaha! You are going down!'
fi
This code just prints "Nice try." The reboot command is not called.
var=reboot
if [ $var ]; then
echo 'Nice try.'
fi
Update (2014-04-14) To answer the question in the comments regarding the difference between = and ==: AFAIK, there is no difference. The == operator is a Bash-specific synonym for =, and as far as I've seen, they work exactly the same in all contexts.
Note, however, that I'm specifically talking about the = and == string comparison operators used in either [ ] or [[ ]] tests. I'm not suggesting that = and == are interchangeable everywhere in bash.
For example, you obviously can't do variable assignment with ==, such as var=="foo" (well technically you can do this, but the value of var will be "=foo", because Bash isn't seeing an == operator here, it's seeing an = (assignment) operator, followed by the literal value ="foo", which just becomes "=foo").
Also, although = and == are interchangeable, you should keep in mind that how those tests work does depend on whether you're using it inside [ ] or [[ ]], and also on whether or not the operands are quoted. You can read more about that in Advanced Bash Scripting Guide: 7.3 Other Comparison Operators (scroll down to the discussion of = and ==).
Use arithmetic expressions.
#!/bin/bash
false=0
true=1
((false)) && echo false
((true)) && echo true
((!false)) && echo not false
((!true)) && echo not true
Output:
true
not false
Long story short:
There are no Booleans in Bash
The true and false commands
Bash does have Boolean expressions in terms of comparison and conditions. That said, what you can declare and compare in Bash are strings and numbers. That's it.
Wherever you see true or false in Bash, it's either a string or a command/builtin which is only used for its exit code.
This syntax...
if true; then ...
is essentially...
if COMMAND; then ...
where the command is true. The condition is true whenever the command returns exit code 0. true and false are Bash builtins and sometimes also standalone programs that do nothing but returning the corresponding exit code.
Conditions in if..then..fi
When using square brackets or the test command, you rely on the exit code of that construct. Keep in mind that [ ] and [[ ]] are also just commands/builtins like any other. So ...
if [[ 1 == 1 ]]; then echo yes; fi
corresponds to
if COMMAND; then echo yes; fi
and the COMMAND here is [[ with the parameters 1 == 1 ]]
The if..then..fi construct is just syntactic sugar. You can always just run the commands separated by a double ampersand for the same effect:
[[ 1 == 1 ]] && echo yes
When using true and false in these testing constructs you are actually only passing the string "true" or "false" to the testing command. Here is an example:
Believe it or not but those conditions are all yielding the same result:
if [[ false ]]; then ...
if [[ "false" ]]; then ...
if [[ true ]]; then ...
if [[ "true" ]]; then ...
TL;DR; always compare against strings or numbers
To make this clear to future readers, I would recommend always using quotes around true and false:
DO
if [[ "${var}" == "true" ]]; then ...
if [[ "${var}" == "false" ]]; then ...
if [[ "${var}" == "yes" ]]; then ...
if [[ "${var}" == "USE_FEATURE_X" ]]; then ...
if [[ -n "${var:-}" ]]; then echo "var is not empty" ...
DON'T
# Always use double square brackets in bash!
if [ ... ]; then ...
# This is not as clear or searchable as -n
if [[ "${var}" ]]; then ...
# Creates impression of Booleans
if [[ "${var}" != true ]]; then ...
# `-eq` is for numbers and doesn't read as easy as `==`
if [[ "${var}" -eq "true" ]]; then ...
Maybe
# Creates impression of Booleans.
# It can be used for strict checking of dangerous operations.
# This condition is false for anything but the literal string "true".
if [[ "${var}" != "true" ]]; then ...
Long ago, when all we had was sh, Booleans where handled by relying on a convention of the test program where test returns a false exit status if run without any arguments.
This allows one to think of a variable that is unset as false and variable set to any value as true. Today, test is a builtin to Bash and is commonly known by its one-character alias [ (or an executable to use in shells lacking it, as dolmen notes):
FLAG="up or <set>"
if [ "$FLAG" ] ; then
echo 'Is true'
else
echo 'Is false'
fi
# Unset FLAG
# also works
FLAG=
if [ "$FLAG" ] ; then
echo 'Continues true'
else
echo 'Turned false'
fi
Because of quoting conventions, script writers prefer to use the compound command [[ that mimics test, but has a nicer syntax: variables with spaces do not need to be quoted; one can use && and || as logical operators with weird precedence, and there are no POSIX limitations on the number of terms.
For example, to determine if FLAG is set and COUNT is a number greater than 1:
FLAG="u p"
COUNT=3
if [[ $FLAG && $COUNT -gt '1' ]] ; then
echo 'Flag up, count bigger than 1'
else
echo 'Nope'
fi
This stuff can get confusing when spaces, zero length strings, and null variables are all needed and also when your script needs to work with several shells.
Instead of faking a Boolean and leaving a trap for future readers, why not just use a better value than true and false?
For example:
build_state=success
if something-horrible; then
build_state=failed
fi
if [[ "$build_state" == success ]]; then
echo go home; you are done
else
echo your head is on fire; run around in circles
fi
How can I declare and use Boolean variables in a shell script?
Unlike many other programming languages, Bash does not segregate its variables by "type." [1]
So the answer is pretty clear. There isn't any Boolean variable in Bash.
However:
Using a declare statement, we can limit the value assignment to
variables.[2]
#!/bin/bash
declare -ir BOOL=(0 1) # Remember BOOL can't be unset till this shell terminates
readonly false=${BOOL[0]}
readonly true=${BOOL[1]}
# Same as declare -ir false=0 true=1
((true)) && echo "True"
((false)) && echo "False"
((!true)) && echo "Not True"
((!false)) && echo "Not false"
The r option in declare and readonly is used to state explicitly that the variables are readonly. I hope the purpose is clear.
My findings and suggestion differ a bit from the other posts. I found that I could use "booleans" basically as one would in any "regular" language, without the "hoop jumping" suggested...
There isn't any need for [] or explicit string comparisons... I tried multiple Linux distributions. I tested Bash, Dash, and BusyBox. The results were always the same. I'm not sure what the original top voted posts are talking about. Maybe times have changed and that's all there is to it?
If you set a variable to true, it subsequently evaluates as an "affirmative" within a conditional. Set it to false, and it evaluates to a "negative". Very straightforward! The only caveat, is that an undefined variable also evaluates like true! It would be nice if it did the opposite (as it would in most languages), but that's the trick - you just need to explicitly initialize your booleans to true or false.
Why does it work this way? That answer is two fold. A) true/false in a shell really means "no error" vs "error" (i.e. 0 vs anything else). B) true/false are not values - but rather statements in shell scripting! Regarding the second point, executing true or false on a line by itself sets the return value for the block you're in to that value, i.e. false is a declaration of "error encountered", where true "clears" that. Using it with an assignment to a variable "returns" that into the variable. An undefined variable evaluates like true in a conditional because that equally represents 0 or "no error encountered".
See the example Bash lines and results below. Test it yourself if you want to confirm...
#!/bin/sh
# Not yet defined...
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;
myBool=true
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;
myBool=false
echo "when set to ${myBool}"
if ${myBool}; then echo "it evaluates to true"; else echo "it evaluates to false"; fi;
Yields
when set to
it evaluates to true
when set to true
it evaluates to true
when set to false
it evaluates to false
POSIX (Portable Operating System Interface)
I miss here the key point, which is portability. That's why my header has POSIX in itself.
Essentially, all of the voted answers are correct, with the exception they are Bash-specific too much.
Basically, I only wish to add more information about portability.
[ and ] brackets like in [ "$var" = true ] are not necessary, and you can omit them and use the test command directly:
test "$var" = true && yourCodeIfTrue || yourCodeIfFalse
Important note: I no longer recommend this as it's being slowly deprecated and more difficult to combine multiple statements.
Imagine what those words true and false mean to the shell, test it yourself:
echo $(( true ))
0
echo $(( false ))
1
But using quotes:
echo $(( "true" ))
bash: "true": syntax error: operand expected (error token is ""true"")
sh (dash): sh: 1: arithmetic expression: expecting primary: ""true""
The same goes for:
echo $(( "false" ))
The shell can't interpret it other than a string. I hope you are getting the idea of how good it is using proper keyword without quotes.
But no one said it in previous answers.
What does this mean? Well, several things.
You should get used to the Boolean keywords are actually treated like numbers, that is true = 0 and false = 1, remember all non-zero values are treated like false.
Since they are treated as numbers, you should treat them like that too, i.e. if you define variable say:
var_bool=true
echo "$var_bool"
true
you can create an opposite value of it with:
var_bool=$(( 1 - $var_bool )) # same as $(( ! $var_bool ))
echo "$var_bool"
1
As you can see for yourself, the shell does print true string for the first time you use it, but since then, it all works via number 0 representing trueor 1 representing false, respectively.
Finally, what you should do with all that information
First, one good habit would be assigning 0 instead of true; 1 instead of false.
Second good habit would be to test if the variable is / isn't equal to zero:
if [ "$var_bool" -eq 0 ]; then
yourCodeIfTrue
else
yourCodeIfFalse
fi
In many programming languages, the Boolean type is, or is implemented as, a subtype of integer, where true behaves like 1 and false behaves like 0:
Boolean in C
Boolean in Python
Boolean in Java
Mathematically, Boolean algebra resembles integer arithmetic modulo 2. Therefore, if a language doesn't provide native Boolean type, the most natural and efficient solution is to use integers. This works with almost any language. For example, in Bash you can do:
# val=1; ((val)) && echo "true" || echo "false"
true
# val=0; ((val)) && echo "true" || echo "false"
false
man bash:
((expression))
The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression".
Regarding syntax, this is a simple methodology that I use (by example) to consistently and sanely manage Boolean logic:
# Tests
var=
var=''
var=""
var=0
var=1
var="abc"
var=abc
if [[ -n "${var}" ]] ; then
echo 'true'
fi
if [[ -z "${var}" ]] ; then
echo 'false'
fi
# Results
# var= # false
# var='' # false
# var="" # false
# var=0 # true
# var=1 # true
# var="abc" # true
# var=abc # true
If the variable is never declared the answer is: # false
So, a simple way to set a variable to true (using this syntax methodology) would be, var=1; conversely, var=''.
Reference:
-n = True if the length of var string is non-zero.
-z = True if the length of var string is zero.
Bill Parker is getting voted down, because his definitions are reversed from the normal code convention. Normally, true is defined as 0 and false is defined as nonzero. 1 will work for false, as will 9999 and -1. The same with function return values - 0 is success and anything nonzero is failure. Sorry, I don't have the street credibility yet to vote or to reply to him directly.
Bash recommends using double brackets now as a habit instead of single brackets, and the link Mike Holt gave explains the differences in how they work. 7.3. Other Comparison Operators
For one thing, -eq is a numerical operator, so having the code
#**** NOTE *** This gives error message *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
will issue an error statement, expecting an integer expression. This applies to either parameter, as neither is an integer value. Yet, if we put double brackets around it, it will not issue an error statement, but it will yield a wrong value (well, in 50% of the possible permutations). It will evaluate to [[0 -eq true]] = success, but also to [[0 -eq false]] = success, which is wrong (hmmm.... what about that builtin being a numerical value?).
#**** NOTE *** This gives wrong output *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then
There are other permutations of the conditional which will give wrong output as well. Basically, anything (other than the error condition listed above) that sets a variable to a numerical value and compares it to a true/false builtin, or sets a variable to a true/false builtin and compares it to a numerical value. Also, anything that sets a variable to a true/false builtin and does a comparison using -eq. So avoid -eq for Boolean comparisons and avoid using numerical values for Boolean comparisons. Here's a summary of the permutations that will give invalid results:
# With variable set as an integer and evaluating to true/false
# *** This will issue error warning and not run: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
# With variable set as an integer and evaluating to true/false
# *** These statements will not evaluate properly: *****
The_world_is_flat=0;
if [ "${The_world_is_flat}" -eq true ]; then
#
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" == true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then
# With variable set as an true/false builtin and evaluating to true/false
# *** These statements will not evaluate properly: *****
The_world_is_flat=true;
if [[ "${The_world_is_flat}" -eq true ]]; then
#
if [ "${The_world_is_flat}" = 0 ]; then
#
if [[ "${The_world_is_flat}" = 0 ]]; then
#
if [ "${The_world_is_flat}" == 0 ]; then
#
if [[ "${The_world_is_flat}" == 0 ]]; then
So, now to what works. Use true/false builtins for both your comparison and your evaluations (as Mike Hunt noted, don't enclose them in quotes). Then use either or single or double equal sign (= or ==) and either single or double brackets ([ ] or [[ ]]). Personally, I like the double equals sign, because it reminds me of logical comparisons in other programming languages, and double quotes just because I like typing. So these work:
# With variable set as an integer and evaluating to true/false
# *** These statements will work properly: *****
#
The_world_is_flat=true/false;
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" = true ]]; then
#
if [ "${The_world_is_flat}" = true ]; then
#
if [[ "${The_world_is_flat}" == true ]]; then
There you have it.
My receipe to (my own) idiocy:
# setting ----------------
commonMode=false
if [[ $something == 'COMMON' ]]; then
commonMode=true
fi
# using ----------------
if $commonMode; then
echo 'YES, Common Mode'
else
echo 'NO, no Common Mode'
fi
$commonMode && echo 'commonMode is ON ++++++'
$commonMode || echo 'commonMode is OFF xxxxxx'
Another way of using booleans is to test the emptyness of values. This has the advantage of making shorter tests:
first=1 # A true value
second= # A false value
[ -n "$first" ] && echo 'First var is true'
[ -z "$first" ] && echo 'First var is false'
[ -n "$second" ] && echo 'Second var is true'
[ -z "$second" ] && echo 'Second var is false'
Output:
First var is true
Second var is false
Here is an alternative test syntax with bash: [[ -n $one ]]
Here is an improvement on miku's original answer that addresses Dennis Williamson's concerns about the case where the variable is not set:
the_world_is_flat=true
if ${the_world_is_flat:-false} ; then
echo "Be careful not to fall off!"
fi
And to test if the variable is false:
if ! ${the_world_is_flat:-false} ; then
echo "Be careful not to fall off!"
fi
About other cases with a nasty content in the variable, this is a problem with any external input fed to a program.
Any external input must be validated before trusting it. But that validation has to be done just once, when that input is received.
It doesn't have to impact the performance of the program by doing it on every use of the variable like Dennis Williamson suggests.
In most cases you need "boolean" for boolean operations, such as ! && or ||. In some languages special boolean type do not exists at all (because in fact you do not need it technically), in most of interpreted languages (almost) all types automatically converted to some kind of boolean in operations like ! && or ||.
Boolean always is something that works with boolean operations. In Bash such operations are (no "" around $var, it's very important!):
[[ $var ]] - check if var is true
[[ ! $var ]] - check if var is false
[[ $var1 || $var2 ]] - var1 or var2
[[ $var1 && $var2 ]] - var1 and var2
[[ $var ]] is false only if var='' (or unset). So the only one correct 'false' value of variable in bash is '' (empty string). For true you can select any value, but I prefer var=1 (like in other languages, where true is any not-null int, but for readable purposes programmers always use 1).
NOTE: for var='false' var in boolean checks is true. In Bash string 'false' is actually true (same as in all other languages!). The only one difference between Bash and, e.g. Python, Perl or PHP, is that in Bash 0 is also true. But again: there are boolean operations in Bash, and they works like in all other interpreted languages, except that 0 in Bash is true.
It's absolutely irrational to use strings 'true' and 'false' as boolean replacement in Bash. It's like using same strange concept in Python, or Perl, or PHP. It works slower (in Bash too), and nobody do it in other languages, but in Bash there are a lot of coders that think it's a good solution. No. There are no reasons not to use boolean operations in Bash directly, without absolutely strange comparisons like $var == 'true' or $var == 'false'.
So again, boolean replacements in Bash are:
var='' # false
var=1 # true (actually any non-empty string)
And direct usage of boolean checks in Bash also is a fastest way to do boolean-like checks. All other constructions will works much slower.
P.S. "old" style of "boolean" checks like a=true; if $a; then ... are absolutely idiotic, because:
you can't use them directly in conditions [[ ]]
they act as eval of bash code, stored in variable. Well, if you think it's a good idea, just do not ever write any program, please;)
P.P.S. If $var can be unset and you use very helpful set -u just replace $var in checks to ${var-}, e.g. [[ ${var-} ]] (also: no "" around, it's important for speed of code execution!)
Here is a simple example which works for me:
temp1=true
temp2=false
if [ "$temp1" = true ] || [ "$temp2" = true ]
then
echo "Do something."
else
echo "Do something else."
fi
Here is an implementation of a short handed if true.
# Function to test if a variable is set to "true"
_if () {
[ "${1}" == "true" ] && return 0
[ "${1}" == "True" ] && return 0
[ "${1}" == "Yes" ] && return 0
return 1
}
Example 1
my_boolean=true
_if ${my_boolean} && {
echo "True Is True"
} || {
echo "False Is False"
}
Example 2
my_boolean=false
! _if ${my_boolean} && echo "Not True is True"
I found the existing answers confusing.
Personally, I just want to have something which looks and works like C.
This snippet works many times a day in production:
snapshotEvents=true
if ($snapshotEvents)
then
# Do stuff if true
fi
and to keep everyone happy, I tested:
snapshotEvents=false
if !($snapshotEvents)
then
# Do stuff if false
fi
Which also worked fine.
The $snapshotEvents evaluates the contents of value of the variable. So you need the $.
You don't really need the parentheses, I just find them helpful.
Tested on: GNU Bash, version 4.1.11(2)-release
Bash Guide for Beginners, Machtelt Garrels, v1.11, 2008
This is a speed test about different ways to test "Boolean" values in Bash:
#!/bin/bash
rounds=100000
b=true # For true; b=false for false
type -a true
time for i in $(seq $rounds); do command $b; done
time for i in $(seq $rounds); do $b; done
time for i in $(seq $rounds); do [ "$b" == true ]; done
time for i in $(seq $rounds); do test "$b" == true; done
time for i in $(seq $rounds); do [[ $b == true ]]; done
b=x; # Or any non-null string for true; b='' for false
time for i in $(seq $rounds); do [ "$b" ]; done
time for i in $(seq $rounds); do [[ $b ]]; done
b=1 # Or any non-zero integer for true; b=0 for false
time for i in $(seq $rounds); do ((b)); done
It would print something like
true is a shell builtin
true is /bin/true
real 0m0,815s
user 0m0,767s
sys 0m0,029s
real 0m0,562s
user 0m0,509s
sys 0m0,022s
real 0m0,829s
user 0m0,782s
sys 0m0,008s
real 0m0,782s
user 0m0,730s
sys 0m0,015s
real 0m0,402s
user 0m0,391s
sys 0m0,006s
real 0m0,668s
user 0m0,633s
sys 0m0,008s
real 0m0,344s
user 0m0,311s
sys 0m0,016s
real 0m0,367s
user 0m0,347s
sys 0m0,017s
You can use shFlags.
It gives you the option to define: DEFINE_bool
Example:
DEFINE_bool(big_menu, true, "Include 'advanced' options in the menu listing");
From the command line you can define:
sh script.sh --bigmenu
sh script.sh --nobigmenu # False
[[ "$x" == 'true' || "$x" -ne 0 ]] && ...
Is enough simple and has no dependencies.
Bash really confuses the issue with the likes of [, [[, ((, $((, etc.
All treading on each others' code spaces. I guess this is mostly historical, where Bash had to pretend to be sh occasionally.
Most of the time, I can just pick a method and stick with it. In this instance, I tend to declare (preferably in a common library file I can include with . in my actual script(s)).
TRUE=1; FALSE=0
I can then use the (( ... )) arithmetic operator to test thusly.
testvar=$FALSE
if [[ -d ${does_directory_exist} ]]
then
testvar=$TRUE;
fi
if (( testvar == TRUE )); then
# Do stuff because the directory does exist
fi
You do have to be disciplined. Your testvar must either be set to $TRUE or $FALSE at all times.
In (( ... )) comparators, you don't need the preceding $, which makes it more readable.
I can use (( ... )) because $TRUE=1 and $FALSE=0, i.e. numeric values.
The downside is having to use a $ occasionally:
testvar=$TRUE
which is not so pretty.
It's not a perfect solution, but it covers every case I need of such a test.
Alternative - use a function
is_ok(){ :;}
is_ok(){ return 1;}
is_ok && echo "It's OK" || echo "Something's wrong"
Defining the function is less intuitive, but checking its return value is very easy.

Resources