16
phone_missing=false
echo "missing $phone_missing"

if [ ! $phone_missing ]
then
        echo "Lost phone at $readabletime"
        $phone_missing=true
fi

I just can't understand this. The line

echo "missing $phone_missing"

echos missing false, I would expect the statement

if [ ! $phone_missing ]

to be true and enter the if clause, but it doesn't? What am I missing here!?

15

The variable $phone_missing is a string that happens to contain false. And a non-empty string evaluates to true. See also http://www.linuxintro.org/wiki/Babe#empty_strings

  • Ah alright. I would have declared that variable as boolean (at least not with the declare word) but the ash console won't let me. I also don't want to compare Strings. How is this done right? – asco Dec 19 '13 at 18:57
  • 1
    stackoverflow.com/questions/2953646/…, but take care: this guy EXECUTES a string variable, and as well false as true are unix commands. – Thorsten Staerk Dec 19 '13 at 19:01
16

I often use "true" and "false" since they are also commands that merely return success and failure respectively. Then you can do

if "$phone_missing"; then ...
5

Here's one way to do this, while retaining the true/false values.

phone_missing=false
if [ "$phone_missing" != false ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
if [ "$phone_missing" == true ]; then
    echo "phone_missing is true."
fi

The double quotes around $phone_missing are to protect against the case where variable phone_missing is not defined at all. Another common idiom to ward against this is [ x$phone_missing != xfalse ], but the quotes seem more natural to me.

The hint is in the bash help page for test:

  STRING      True if string is not empty.
  ...
  ! EXPR      True if expr is false.

So, basically [ $foo ] will be true if $foo is non-empty. Not true or false, just non-empty. [ ! $foo ] is true if $foo is empty or undefined.

You could always change your code to just set phone_missing to a non-empty value, which will denote true. If phone_missing is unset (or empty — phone_missing=""), it will be false. Otherwise, you should be using the string testing operators (= and !=).

The other slight issue is the assignment. You have it as $phone_missing=true, whereas it should be phone_missing=true (no dollar sign).

Sorry if this is a bit dense, it's because I am. It's been a long day. :)

3

Don't use string for boolean. Use integer.

Input:

val=1
((val)) && echo "true" || echo "false"
val=0
((val)) && echo "true" || echo "false"

Output:

true
false

Source:

((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".

1

I would have done this as a comment to support James Ko but didn't have the rep to comment or publicly up vote.

The issue here is that the [] brackets are notation for doing a comparison test such as value or string equality.

String truthiness in bash for an empty string is "" (empty string) evaluates to false (return value 1) and any non empty string "false" "true" or "bob's your uncle" evaluates to true (return value 0).

You can prove this to yourself with:

    if [ "foo" ]; then 
      echo true is $?; 
    else 
      echo false is $?; 
    fi

The $?above is a special variable that holds the last commands exit status (0 success, and any > 0 for error code) and will output true is 0. You can replace "foo" with anything you want and the result will be the same except if you replace it with an empty string "" or '' in which case it will evaluate the else condition and output false is 1.

When using the [] brackets the internal statement such as ! $phone_missing is evaluated and then returns a 0 for true or 1 for false to the if control statement. Since the bracket evaluates string and value comparisons the $phone_missing is first expanded to the non empty string "false" which is evaluated by the [] as non empty string (or true) and then the ! inverts the result which results in the if statement getting a false (or return value 1) and it skips the conditional body executing the else statement if present.

As James Ko said the notation would be to just pass the variable holding your 'boolean' to the if control statement. Note that in bash a boolean true and false must be lower case as in: bool_true=true bool_false=false Any other case will evaluate as strings and not boolean.

So using your example and James Ko's answer it would be:

phone_missing=false
echo "missing $phone_missing"

if ! $phone_missing 
then
  echo "Lost phone at $readabletime"
  $phone_missing=true
fi

or as I prefer for readability (syntactically the same and James Ko's)

phone_missing=false
echo "missing $phone_missing"

if ( ! $phone_missing ); then
  echo "Lost phone at $readabletime"
  $phone_missing=true
fi

Other answers such as by Alexios are literally checking the string content. So that either of the following would result in false:

phone_missing=false
if [ "$phone_missing" != false ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
if [ "$phone_missing" == true ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
if [ "$phone_missing" == Bobs_your_uncle ]; then
    echo "phone_missing is not 'false' (but may be non-true, too)"
fi
0

Other answers gave you solution, but I will explain what was wrong with the original thinking.

Bash variables don't have types, so there's no such thing as a boolean variable or value like true or false. Basically all bash variables are just strings.

When you test a variable/string in bash without specifying the type of test (-n or -z), it will default to a -n (nonzero length string) test.

So [ "$var" ] is equivalent to [ -n "$var" ]. As long as $var contains at least 1 character, it was evaluate to true.

Since you negated the expression, [ ! "$var" ] is equivalent to [ ! -n "$var" ]. So if $var contains at least 1 character, then the expression is false.

Since false (which is just a string) contains 5 characters, the expression is false. If doesn't matter what string you set phone_missing to (true, 0, 1), the expression will always be false because is phone_missing is nonzero length. The only way to make it true is phone_missing="" since that is zero length.

-1

The correct syntax is if ! $bool; then [statements]; fi.

Example:

bool=false

if ! $bool; then
    echo "This is correct!"
fi

if [ ! $bool ]; then
    echo "This is wrong!"
fi

Output: This is correct!

  • There’s a kernel of truth in your answer, but, lacking any explanation, it causes more confusion than it remedies.  Also, it is little more than a repeat of one of the previous answers.  If you don’t want your answer to be removed, explain why it is right. – G-Man Says 'Reinstate Monica' May 24 '15 at 6:17

Not the answer you're looking for? Browse other questions tagged or ask your own question.