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

前端 未结 3 1214
迷失自我
迷失自我 2021-01-14 05:20

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 [         


        
相关标签:
3条回答
  • 2021-01-14 05:40

    See the POSIX specification of test for details of the portable use of the [ or test utility.

    If you use [ ! $bar ] you are playing with fire.

    • If $bar is an empty string, it degenerates to the one-argument form of test, with that argument being !, and exits successfully because ! is not an empty string.

    • If $bar is not an empty string, then the arguments are ! and the value of $bar, which establishes that the non-empty string is non-empty, and then inverts the condition, so it exits with a failure status.

    You should normally write [ ! "$bar" ] which will then work correctly if $bar is empty. You could (arguably should) use the explicit tests:

    • [ -z "$bar" ] to test for an empty (zero-length) string, and
    • [ -n "$bar" ] to test for a non-empty string

    but the quotes are mandatory to avoid confusion. The non-empty and zero-length are mnemonic terms.

    $ bar=
    $ [ ! $bar ] || echo Test failed
    $ bar=xyz
    $ [ ! $bar ] || echo Test failed
    Test failed
    $ [ ! "$bar" ] || echo Test failed
    Test failed
    $ bar=
    $ [ ! "$bar" ] || echo Test failed
    $ [ -n "$bar" ] || echo Test failed
    Test failed
    $ [ -z "$bar" ] || echo Test failed
    $ 
    

    Some people prefer the Bash (Korn shell, …) operator [[ which behaves differently. You could experiment with variations on the theme of this code:

    set -x
    bar=
    [ ! $bar ] || echo Test failed
    bar=xyz
    [ ! $bar ] || echo Test failed
    [ ! "$bar" ] || echo Test failed
    bar=
    [ ! "$bar" ] || echo Test failed
    [ -n "$bar" ] || echo Test failed
    [ -z "$bar" ] || echo Test failed
    bar=xyz
    [ -n "$bar" ] || echo Test failed
    [ -z "$bar" ] || echo Test failed
    
    bar=
    [[ ! $bar ]] || echo Test failed
    bar=xyz
    [[ ! $bar ]] || echo Test failed
    [[ ! "$bar" ]] || echo Test failed
    bar=
    [[ ! "$bar" ]] || echo Test failed
    [[ -n "$bar" ]] || echo Test failed
    [[ -z "$bar" ]] || echo Test failed
    bar=xyz
    [[ -n "$bar" ]] || echo Test failed
    [[ -z "$bar" ]] || echo Test failed
    bar=
    [[ -n $bar ]] || echo Test failed
    [[ -z $bar ]] || echo Test failed
    bar=xyz
    [[ -n $bar ]] || echo Test failed
    [[ -z $bar ]] || echo Test failed
    set +x  # Unnecessary if you save this in a script file and don't dot (source) it
    

    It will (probably) help you understand what's going on. The output is quite verbose, though.

    0 讨论(0)
  • 2021-01-14 05:52

    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.
    0 讨论(0)
  • 2021-01-14 05:59

    if tests the results of commands and can't directly test the values of variables. In this case you're using it in conjunction with the test/[ command, which is an ordinary command and can be used anywhere.

    [ -f ./foo.txt ] && echo "foo.txt exists"
    

    or equivalently

    test -f ./foo.txt && echo "foo.txt exists"
    

    both test whether a file exists at the path ./foo.txt.

    Depending on what you mean by "true", you can use the test or [ shell builtin to determine whether a variable is equal to another, and for some other functions like testing whether a directory or file exists.

    You can use if [ -z "$variable" ]; then ...; fi to check whether a variable is the empty string.

    If you want to check whether the variable is set at all, even to an empty value, you can use parameter expansion [ -z "${var+x}" ]. It only evaluates to true (meaning has exit status zero) when var is undefined.

    If you want to compare a variable against a fixed string you can use [ "$var" = "some-string" ] or use case e.g.

    case "$var" in
       some-string)
         dostuff
       ;;
    esac
    

    Note that you can negate that the result of test/[ using !.

    if ! [ -z "$var" ]; then
        do-something
    fi
    

    Does negate the condition.

    However, ! is also valid as an argument to test/[

    if [ ! -z "$var" ]; then
        do-something
    fi
    

    means the same thing.

    0 讨论(0)
提交回复
热议问题