1

Well, the variable is not boolean until it has been set to true or false.

So when the script begins, it checks for a boolean that was not set yet as in bTest=''. So if I do this: if $bTest;then echo "is active";fi it will output "is active" when I would like it to show nothing...

I could use this: [ "$bTest" == true ], when before was just $bTest, but to me that is the problem, it is not a "boolean", it is a string comparison...

Also, if I misstype a boolean variable name, it will be empty, and the script execution will throw no error message... and for several times I discovered the real problem was just the misstyped variable... Could an IDE have get that? I use gedit so I must track my failures by myself.

I wonder if is there a clean and simple way to work with booleans instead of typing [ "$bTest" == true ] every time..?

3
  • 1
    I believe true here is the literal string "true", not a boolean value of true. So you can consider "" as false and any other value as true. To check, you can use the -z "$var" test operator (to check for a zero-length string).
    – aularon
    Feb 2, 2014 at 20:21
  • try this on terminal true;echo $?;false;echo $?, I think it is not just a string :). Also, my code would become this [ ! -z "$bTest" ] && $bTest and I would still be considering it as a string :( Feb 2, 2014 at 20:27
  • 1
    true and false are shell builtins. true returns a status of 0, while false returns a status of 1. When doing [ "$bTest" == true ] you are not executing true, but rather comparing the string true to the string content of $bTest.
    – aularon
    Feb 2, 2014 at 20:30

4 Answers 4

2

Based on your answer, it sounds like you're actually trying to avoid typos.

The best way to do this, is to put

set -u

at the top of your shell script.

Then, instead of a typo expanding to an empty string, it is a fatal error.

if $bTest; then
    echo "is active"
fi

# => is active

Versus

set -u
if $bTest; then
    echo "is active"
fi

# => typo.sh: line 4: bTest: unbound variable

Alternatively, if you want to assign defaults to variables, it's usually best to do all the assignments at the top of the script like this:

: ${bTest:=false}

Finally, if you really want to type out a default value every time, you could use

if ${bTest:-false}; then
    echo "is active"
fi
8
  • cool, this works great for unset variables! as I make it sure to initialize them all, this may work indeed thx! Feb 2, 2014 at 22:19
  • I am having trouble with empty parameters like $1 or ${1:0:1}, any tips? Feb 2, 2014 at 22:44
  • 1
    How about ${1:-false}?
    – samiam
    Feb 2, 2014 at 22:47
  • it is my options loop: while [[ "${1:0:2}" == "--" ]];do I am trying to find a workaround.. Feb 2, 2014 at 22:53
  • For $1, it's more common to handle that explicitly at the start, e.g. if test $# -eq 0; then echo "Usage..."; exit 2; fi. Or use getopts.
    – Mikel
    Feb 3, 2014 at 0:07
2

The simplest way is this:

if ${bTest:?};then echo "is active";fi

So I simply go from $bTest to ${bTest:?} and no need to deal with anything more complex like functions and aliases.

Be concerned that as @samiam pointed out, it may cause a problem to you if the variable was actually set to something and that was a command like in bTest='ls -l';if ${bTest:?};then echo "is active";fi (where ls -l will be executed).

But in my case, I treat all "booleans" as booleans, so I only put true or false on them; the biggest problem I have been having with them for years is misstyped variable names; so I always got an empty "boolean" troubling me until I found that I misstyped it..

So, if you use your "booleans" with other values than true or false, do not use it; as it may become unsafe.

5
  • 1
    -1: Inaccurate (I just tested it in Bash). That code expands the value of bTest in to a command run by Bash, and generates an error when bTest is not set. bTest='ls -l';if ${bTest:?};then echo "is active";fi will run ls -l. I'll undo my downvote as soon as the answer is corrected.
    – samiam
    Feb 2, 2014 at 20:53
  • 1
    thx, I understand your concern about safety, but in my case I only use true or false with booleans. And before ${bTest:?} I was already using $bTest and would already execute anything there! I still believe there may have some safer and still clean way to deal with it, I just couldnt figure out yet.. Feb 2, 2014 at 21:18
  • Is if [-z "$bTest" ]; then echo "is active"; fi clean enough for you? If you really want it to look more boolean-like, there is FALSE="";if[ "bTest" != "$FALSE" ] etc.
    – samiam
    Feb 2, 2014 at 21:25
  • if I set bTest=true it wont work with solely [ -z "$bTest" ]. I want to test the boolean in a way that if it is empty, it be evaluated as false or throw error message. ${bTest:?} worked perfectly, but has the flaw you said, but not in my case as I only use true or false for booleans! Feb 2, 2014 at 21:31
  • I have updated my answer to use functions called is_true and is_false. I don't like throwing errors in Bash scripts because it halts execution when a script is called with #!/bin/bash -e (-e: Halt on any error)
    – samiam
    Feb 2, 2014 at 21:33
1

Bash does not have a boolean datatype (/bin/true and /bin/false are commands which return 1 and 0, because Bash, unintuitively interprets a return code of 0 as 'all is good' and anything else as 'there was a problem')

Indeed, Bash does not have a numeric datatype (A=$(( $A + 1 )) converts $A in to an integer, adds one to it, then makes it a string again) and, more to the point, 0 does not have the same value as the empty string. BASH is not PHP (where var_dump(0 == ""); returns true).

Probably the best way to simulate booleans is as per aularon's suggestion: Use the -z operator, and consider empty strings false. e.g [ ! -z "$bTest" ] && $bTest

If that's not clean enough for you, how about having is_true and is_false functions?

#!/bin/bash

is_true() {
        if [ "$1" = "false" ]; then
                return 1 # false
        fi
        if [ -z "$1" ]; then
                return 1 # false
        else
                return 0 # true
        fi
}

is_false() {
        if [ "$1" = "false" ]; then
                return 0 # true
        fi
        if [ -z "$1" ]; then
                return 0 # true
        else
                return 1 # false
        fi
}

A="1"
B=""

if is_true "$A" ; then
        echo A is true
else
        echo A is false
fi

if is_false "$A" ; then
        echo A is false
else
        echo A is true
fi

if is_true "$B" ; then
        echo B is true
else
        echo B is false
fi
8
  • in other words, you mean everything in bash are strings right? I understand the arguments, but I am still concerned about the simplicity and clean coding, try this ${bTest:?} and the result will be what I was looking for! Feb 2, 2014 at 20:58
  • Exactly! Bash is a bit of a hack if you want super-clean code. It is not Python. As another example, one has to explicitly variables to be local in functions.
    – samiam
    Feb 2, 2014 at 21:00
  • my booleans are intended solely to true or false, this failed: bTest=false;if is_true $bTest;then echo "is active";fi Feb 2, 2014 at 21:44
  • Having the string "false" evaluate to false is not how I would do it, but I have added code to make the is_true() and is_false() functions work that way.
    – samiam
    Feb 2, 2014 at 21:56
  • the is_true is still failing for this bTest=false;if is_true $bTest;then echo "is active";fi :( Feb 2, 2014 at 22:08
0

Shells convert environment variables to shell variables. So you should never use a variable uninitialised as one day or another your script will break because for a reason or another a variable by the same name was in the environment that shell received on startup.

So:

#! /bin/sh -
bTest=false

while getopts ...;
  ... ) bTest=true
done

if "$bTest"; then

(don't forget the quotes, leaving out the quotes is the split+glob operator. Note that if $bTest is empty, you'll get an error about the empty string being not found as a command).

Now, if you want to ignore the advice. Another way using ksh syntax (works also in bash and zsh) is to use arithmetic expressions and 1 or any non-zero value to mean true.

0 and the empty string mean false in arithmetic expressions.

So you can do:

#! /bin/bash -
while getopts ...;
  ... ) bTest=1
done
if ((bTest)); then...

But beware that your script may stop working if for some reason there's a bTest in the environment (for instance because your script is called from another script that uses the same variable name and exported it (for instance because of a set -a or because it's a rc script where all variables are in the environment)).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .