Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

exclamation mark to test variable is true or not in bash shell

Tags:

shell

I know in shell the exclamation mark can invert the outcome of condition. Here I wanna use it to test variable is true or false .

#! /bin/bash
bat=false
if [ ! $bar ]; then
    echo 'bar is false'
else
   echo 'bar is true'
fi

I was expecting 'bar is false'. However it turned out the other way. Then I use "$bar" == "false". It is right.

So what is the tips for using exclamation mark. Would it only invert outcome when we test about file ?

like image 632
nathan Avatar asked Dec 07 '22 20:12

nathan


1 Answers

tl;dr

[ ! $bar ] treats $bar as a string, and any nonempty string is considered "true" - including literal false; in other words: [ ! 'true' ] && [ ! 'false' ] both evaluate to "false", because the operands are nonempty strings in both cases and ! negates the outcome.

Therefore, you must use string comparison:

bar=false
if [ ! "$bar" = 'true' ]; then  # same as: [ "$bar" != 'true' ]
    echo 'bar is false'
else
    echo 'bar is true'
fi

In Bash, you can also use [[ ! $bar == 'true' ]] or [[ $bar != 'true' ]]; unless you need to remain POSIX-compliant, I suggest using [[ ... ]].


In the context of [ ... ] and [[ ... ]] (Bash-specific), variable values are strings by default, and, generally, POSIX-like shells have no explicit Boolean data type.

Unary operator ! interprets its operand implicitly as a Boolean, and a string in a Boolean context is interpreted as follows:

  • only an empty string is considered "false" (exit code 1(!))
  • any nonempty string - including literal false - is considered "true" (exit code 0(!))

Thus, ! $bar evaluates to "false", because $bar - containing nonempty string 'false' - evaluates to "true", and ! inverts that.


! can also be used outside of conditionals to directly negate the success status of commands (including [).
Since false and true also exist as command names (typically, as shell builtins), you could do the following, but do note that it only works as intended with variable values that are either the literals false or true:

bar=false
if ! "$bar"; then
   echo 'bar is false'
else
   echo 'bar is true'
fi

Background information

POSIX-like shells only have 2 basic (scalar) data types:

  • strings (by default):

    • In [ ... ] and [[ ... ]] conditionals, operators = (== in Bash),<,<=,>,>=` perform string comparison.
  • integers:

    • In [ ... ] and [[ ... ]] conditionals, distinct arithmetic operators (-eq, lt, le, gt, ge) must be used to perform numerical comparison

    • Alternatively, in an arithmetic expansion ($(( ... ))), ==, <, <=, >, >= have their usual, numeric meaning.
      In Bash, you can also use (( ... )) as an arithmetic conditional.

Note: Per POSIX, you cannot type a variable as an integer (because there is no way to declare a shell variable), so it is only implicitly treated as one in comparisons using arithmetic operators / in arithmetic contexts.
In Bash, however, you can declare integer variables with declare -i / local -i, but in conditionals / arithmetic contexts it is still the choice of operator / arithmetic context that determines whether the value is treated as a string or as an integer.


Booleans in shells are expressed implicitly as exit codes, in a reversal of the usual Boolean-to-integer mapping:

  • "true" maps to exit code 0(!)
  • "false" is expressed as exit code 1(!) or any other nonzero exit code.
like image 124
mklement0 Avatar answered Dec 11 '22 12:12

mklement0