Why does while [ 0 ] go into infinite loop?

13,564

Solution 1

Single square brackets in the shell is a synonym for test (either the separate command or the shell built-in), so [ 0 ] means the same thing as test 0. test is for doing comparisons and testing the attributes of files, as you can read about in its manpage. When it isn't given an expression that looks like a comparison, file test, or one of the other operations it can do, it will instead test if the argument is present and a non-empty string. Neither 0 or 1 are really appropriate inputs for test, and as non-empty strings test simply succeeds and your while loop loops forever.

You may want to try instead

while false; do
  echo "hello"
done

possibly replacing false with true. Or maybe what you want is to use (( )):

while (( 0 )); do
  echo "hello"
done

Which will behave like most languages, where 0 means failure/false and 1 means success/true.

Solution 2

The value 0 here is not acting as a numeric constant, but as a character string. These tests are all equivalent in their effect of producing a successful termination status:

[ A ]
[ "XYZ" ]
[ 0 ]

these produce a failed termination status:

[ ]
[ "" ]

there is a non-blank argument, which evaluates to logical true. This allows you to do things like:

if [ $UNDER_NUCLEAR_ATTACK ] ; then
  launch-missiles -a $DRY_RUN  # $DRY_RUN set to "-n" during testing
fi

The variable UNDER_NUCLEAR_ATTACK is set to any non-blank value to indicate true, or is unset or empty to indicate false.

We can apply the ! operator to reverse the logic:

[ ! a ]  # fails: a is nonblank so true; and not true is false.
[ ! ]    # succeeds: blank is false, not blank is true.

To evaluate a numeric condition, you have to use numeric test operators:

 while [ $A -gt $B ] ; do ...

If A and B contain strings that look like decimal integers, they are compared like numbers, and if A is greater than B, the loop executes. So suppose that UNDER_NUCLEAR_ATTACK is not a string-type boolean that is blank or nonblank, but actually a numeric boolean that is either 0 (false) or some other value (true). In that case, we would write the test like this:

 if [ $UNDER_NUCLEAR_ATTACK -ne 0 ] ; then ...
Share:
13,564

Related videos on Youtube

user13107
Author by

user13107

Updated on September 18, 2022

Comments

  • user13107
    user13107 almost 2 years

    I see the same behaviour for below loop as the loop with while [ 1 ]. Why is that so?

    while [ 0 ]; do
        echo "hello"
    done
    
  • Kevin
    Kevin over 10 years
    Yes, but not for the reason you seem to think. If [ gets only one argument (excluding the ], of course) it exits with status zero if the argument is not empty, nonzero if it is. So, for example, [ 1 ] will also return an exit code of 0.
  • ruakh
    ruakh over 10 years
    Re: "When it isn't given an expression that looks like a comparison, file test, or one of the other operations it can do, it simply succeeds": I don't think that's a good way to put it. After all, [ ] (with no argument) and [ "" ] (with a single empty argument) do not succeed.
  • Ian D. Allen
    Ian D. Allen over 10 years
    As @ruakh explains, this statement is wrong: When it isn't given an expression that looks like a comparison, file test, or one of the other operations it can do, it simply succeeds. Any single argument to test (any single argument inside the square brackets) is considered TRUE if the argument is not an empty string, and both 0 and 1 are not empty strings. New shell programmers often write if [ 1=2 ] instead of if [ 1 = 2 ] and wonder why the former is always true. It is true because it is a single argument and is not an empty string.
  • user1579506
    user1579506 over 10 years
    Good points, I've made a correction.