Invert boolean variable

There are two errors in your script. The first is that you need a space between ! and $flag, otherwise the shell looks for a command called !$flag. The second error is that -eq is for integer comparisons, but you're using it on a string. Depending on your shell, either you'll see an error message and the loop will continue forever because the condition [ "$x" -eq "true" ] cannot be true, or every non-integer value will be treated as 0 and the loop will exit if you enter any string (including false) other than a number different from 0.

While ! $flag is correct, it's a bad idea to treat a string as a command. It would work, but it would be very sensitive to changes in your script, since you'd need to make sure that $flag can never be anything but true or false. It would be better to use a string comparison here, like in the test below.

flag=false
while [ "$flag" != "true" ]
do
   read x
   if [ "$x" = "true" ]
   then
     flag=true
   fi
   echo "${x} : ${flag}"
done

There's probably a better way to express the logic you're after. For example, you could make an infinite loop and break it when you detect the termination condition.

while true; do
  read -r x
  if [ "$x" = "true" ]; then break; fi
  echo "$x: false"
done

While there are no Boolean variables in Bash, it is very easy to emulate them using arithmetic evaluation.

flag= # False
flag=0 # False
flag=1 # True (actually any integer number != 0 will do, but see remark below about toggling)
flag="some string" # Maybe False (make sure that the string isn't interpreted as a number)

if ((flag)) # Test for True
then
  : # do something
fi

if ! ((flag)) # Test for False
then
  : # do something
fi

flag=$((1-flag)) # Toggle flag (only works when flag is either empty or unset, 0, 1, or a string which doesn't represent a number)

This also works in ksh. I wouldn't be surprised if it works in all POSIX-compliant shells, but haven't checked the standard.


If you can be absolutely sure that the variable will contain either 0 or 1, you can use the bitwise XOR-equal operator to flip between the two values:

$ foo=0
$ echo $foo
0
$ ((foo ^= 1))
$ echo $foo
1
$ ((foo ^= 1))
$echo $foo
0

Tags:

Scripting

Bash