3

I am trying to evaluate a boolean variable in Bash scripting, but it always return false.

Here is the code:

DEVICE_FOUND=false
tmp=`adb devices | tail -n+2 | awk '{print $1}'`

echo "$tmp" | while read line
do
    if [ "$DEVICE_ID" = "$line" ]
    then
        echo "Found a device"
        DEVICE_FOUND=true
    fi
done

if ! $DEVICE_FOUND
then
    echo "ERROR: The device "$DEVICE_ID" is not connected"
    exit
fi

Whether or not "Found a device" gets executed or not, I always go into the if statement. When DEVICE_FOUND=true, it should not go in the last if but it does.

I do not understand why is that.

Does anyone please know?

Thank you very much.

4 Answers 4

7

That's because you are setting the true value after the |, i.e. in a subshell. Variable values from a subshell do not propagate back to the parent shell. Get rid of the subshell:

while read line
do
    ...
done <<< "$tmp"
0
3

The problem isn't the boolean, it's that the while loop is in a pipeline, and therefore executes in a subshell. Changing DEVICE_FOUND in the subshell has no effect on DEVICE_FOUND in the main shell, it just stays false. If you're using bash (i.e. if the shebang at the beginning of the script is #!/bin/bash, not #!/bin/sh), there are a couple of ways to eliminate the pipe:

while read line
do
    if [ "$DEVICE_ID" = "$line" ]
    then
        echo "Found a device"
        DEVICE_FOUND=true
    fi
done < <(echo "$tmp")

or:

while read line
do
    if [ "$DEVICE_ID" = "$line" ]
    then
        echo "Found a device"
        DEVICE_FOUND=true
    fi
done <<<"$tmp"

But in this particular case there's a better way (provided $DEVICE_ID doesn't have any regex metacharacters):

if echo "$tmp" | grep -q "^$DEVICE_ID\$"; then
    echo "Found a device"
else
    echo "ERROR: The device "$DEVICE_ID" is not connected"
    exit
fi
1

Bash lacks boolean variables. Hence DEVICE_FOUND=false assigns the string false to $DEVICE_FOUND.

You can get the usual boolean flag functionality by using the "empty variable" condition as as your "false", and "nonempty variable" as your "true" (or viceversa if you want), or otherwise check for arbitrary values. Example:

DEVICE_FOUND=""
tmp=`adb devices | tail -n+2 | awk '{print $1}'`

echo "$tmp" | while read line
do
    if [ "$DEVICE_ID" = "$line" ]
    then
        echo "Found a device"
        DEVICE_FOUND=true
    fi
done

if [ ! "$DEVICE_FOUND" ]
then
    echo "ERROR: The device "$DEVICE_ID" is not connected"
    exit
fi
3
  • Found a device ERROR: The device HT91MLD00464 is not connected
    – Jary
    Commented Nov 12, 2011 at 23:22
  • true and false are bash builtins. Your code contains the same bug as the original one, see my answer.
    – choroba
    Commented Nov 12, 2011 at 23:24
  • 1
    @choroba: never said they weren't, DEVICE_FOUND=true is still a string assignment. You're right about the bug though.
    – ata
    Commented Nov 13, 2011 at 1:04
0

There is no boolean type in bash (and any POSIX-compliant shell). What effectively happens is that you are running the true (exits with success status, not necessarily 0) or false (exits with failure status, not necessarily not 0) external commands in the if statement. The if statement executes the second list if the first list evaluates to 0. You might have encountered a system where neither the true or false commands are available, or where they work differently than expected. Use "integer" values (all values are internally strings) or empty/non-empty string values instead.

On a side note, consider this:

adb devices | tail -n +2 | awk '{print $1}' | while read …
do
  …
done
1
  • The subshell is the more likely explanation why this fails (+1), but I will leave my answer for further reference. Commented Nov 12, 2011 at 23:27

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