4

I have a function called install_cont that is called twice from my bash script. However, inside that function there is a code block that I want to execute only if the boolean parameter is true. In Python I would do:

def install_cont(expres=False):
    if expres:
         # Do some code...

install_cont(True)
install_cont() # Can call with default False

How do I achieve this in bash? Reading online, I understand that all variables are string. However, what is the best way to achieve a boolean-like parameter?

Gilbert Williams
  • 970
  • 2
  • 10
  • 24
  • `bash` doesn't have boolean values in the first place. It *only* has strings. Pick any string you like, and handle it appropriately inside the function. – chepner Feb 24 '21 at 19:01
  • make one the default case and for the other pass an optional argument and check the existence of that argument in the script. – karakfa Feb 24 '21 at 19:06

5 Answers5

8

Shell scripting doesn't have booleans, but it does have a convention for representing success as an integer. Confusingly for programmers coming from other languages, this is that 0 is success, and anything else is failure.

This is the basis of the if construct which runs a command, and tests its result for success. You can't test a variable directly with if, but you can use the test built-in, which "succeeds" if a particular test passes.

test is also spelled [ which is why you'll often see code with if [ ... ] - those brackets aren't part of the if syntax, they're just a command with a funny name. (Bash also has a [[ built-in with some extra features, but [ is more standard, so worth learning.)

The test command can perform various tests - examining numbers, checking the file system, etc. For a command-line option, you could test against the string true or false with the = binary operator.

So what you end up with is something like this:

install_cont() {
    # Name the parameter for convenience
    local expres="$1";

    if [ "$expres" = "true" ];
    then
         # Do some code...
    fi;
}

install_cont true
install_cont # default will not pass the test

If you want fancier parameters, you can have actual defaults with the slightly awkward ${foo:-default} and ${foo:=default} parameter expansion syntaxes. For example:

install_cont() {
    # Default the mode to "boring"
    local mode="${1:-boring}";

    if [ "$mode" = "exciting" ];
    then
         # Do some code...
    elif [ "$mode" = "boring" ];
    then
         # Do some code...
    else
         echo "Unrecognised mode";
    fi;
}
IMSoP
  • 89,526
  • 13
  • 117
  • 169
  • Upvoted for expanding in more detail my pitiful attempt at explaining this. – Yokai Feb 24 '21 at 19:35
  • Why the revert of the extraneous semicolon removal? No one should write scripts like this. – Jens Feb 24 '21 at 21:10
  • @Jens Meh, maybe a revert was unnecessary, but I'm generally more comfortable with semicolons where they aren't necessary than learning rules about when they might or might not be (*waves fist at JavaScript*). Consider it a reminder of where the semicolons _would_ go if you were writing this on one line for any reason, because it's not really obvious if you're used to other languages - I get syntax errors from missing `;` or putting it the wrong side of `then` or `do` *all the time*. – IMSoP Feb 24 '21 at 21:26
0

Like this perhaps.

your_func () {
    if [[ $1 ]]; then
        # do things if parameter was present
    fi
    # do unconditional things
}
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • So I assume that checks if parameter was present or not. That means I can pass anything? Can I call it like `your_func 1` or do I need quotes? – Gilbert Williams Feb 24 '21 at 19:12
  • It checks if the argument is a nonempty string. For a single token you don't need to quote it, though always quoting your strings is a good habit to get into; perhaps see also [When to wrap quotes around a shell variable?](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable) – tripleee Feb 24 '21 at 19:23
0

Bash on it's own does not have built-in boolean handling. What it does is handles strings and integers. However, you can define strings and numbers for definite exit status codes and make pseudo-boolean logic by design. Where ever you see true or false in bash, you are seeing constructs with specific exit codes. Since the default command exit codes are 0 or 1, unless you assign one to the contrary, you basically have a built-in method, if not inherently for the purpose, to perform boolean logic.

Using the test command with subsequent [] or [[]] will output a 0 for true or a 1 for false. But these are merely exit status codes.

Yokai
  • 1,170
  • 13
  • 17
0

One of the tricky things about bash and other Bourne-derived shells is that a condition used in an if or while statement is not an expression. It's a command, and the condition is treated as true or false depending on whether the command succeeded or failed. A command indicates success by returning an exit status of 0, and failure by returning anything else.

The built-in commands true and false do nothing other than returning an exit status denoting success or failure, respectively.

The [ ... ] and [[ ... ]] constructs can be used to simulate using an expression as a condition. [ is actually a command, very nearly equivalent to the test command.

If I need the equivalent of a Boolean variable in a bash script, I use a variable whose value is either the string true or the string false. If I use that variable as a condition, it expands to the command name true or false. I just have to be careful to ensure that such a variable never takes some value other than true or false.

And it's important to remember not to use the [ ... ] syntax. With a single argument, for example

if [ false ] ; then # This is incorrect!

the command succeeds because false is a non-empty string. Remember that the [ and ] are not part of the syntax of an if statement (though they're designed to look like they are).

An example:

#!/bin/bash

func() {
    if $1 ; then
        echo In func, the condition is true
    else
        echo In func, the condition is false
    fi
}

func false
func true

The output:

In func, the condition is false
In func, the condition is true

This technique does have some drawbacks. It can break if you assign a value other than exactly true or false to a variable, or even if you misspell the name of a variable. And unlike in languages with a built-in Boolean type and false and true literals, the shell is likely to fail to diagnose such errors, causing your script to silently misbehave.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • Just remember not to take arbitrary input from untrusted sources if you're going to treat them as booleans by executing them... – ilkkachu Feb 24 '21 at 21:03
  • @ilkkachu Sure -- but it's also easy to *accidentally* set a variable's value to something other than exactly `true` or `false`, or to misspell the name of a variable. And unlike in languages with a built-in Boolean type an `false` and `true` literals, the shell isn't likely to diagnose your error. That's a substantial drawback of this technique. – Keith Thompson Feb 25 '21 at 02:28
0

Unix provides two commands:

  • true: return true value (i.e. exit value is 0)
  • false: return false value (i.e. exit value is non-0)

You can use them in if, while and until constructs.

Example:

install_cont()
{
    local expres="${1:-false}"    # Default value is false

    if "$expres"; then
        # Do some code...
    else
        # Do something else...
    fi
}

install_cont true
install_cont          # false implied

You are using Bash. If you want booleans that are also valid in arithmetic operations, then you can also use arithmetic booleans:

declare -i false=0 true=1

cond1="$true"
cond2="$false"
if ((cond1 || cond2)); then
    # Do some code...
fi

Personally, I don't use arithmetic booleans as I'm perfectly happy with true and false commands.

xhienne
  • 5,738
  • 1
  • 15
  • 34
  • 2
    Just remember not to take arbitrary input from untrusted sources if you're going to treat them as booleans by executing them... – ilkkachu Feb 24 '21 at 21:03
  • @ilkkachu Very true, but there are so many security traps in Bash that sanitizing input is mandatory in all cases. – xhienne Feb 24 '21 at 21:15
  • 1
    You missed a great chance to include my favourite man page summaries: `true - do nothing, successfully` and `false - do nothing, unsuccessfully` – IMSoP Feb 24 '21 at 21:29
  • @IMSoP Sometimes, like at this very moment, I'm trying hard to do nothing but I'm not always successful :-) BTW, it's probably the first time I open the GNU manpages for true and false and I notice they even have provided the usual `--help` and `--version` options to their commands. those texts are probably much longer than the remaining source code... – xhienne Feb 24 '21 at 21:39
  • @xhienne Yeah, [the GNU version of `true.c`](https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/true.c) is sometimes used as an example of unnecessary bloat, running to a whopping 80 lines; to be fair, it also implements `false` via [a 2-line stub `false.c`](https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/false.c). – IMSoP Feb 24 '21 at 21:49