5

I'm wondering how the while loop evaluates its loop condition. In the man page it is written:

while list-1; do list-2; done

The while command continuously executes the list list-2 as long as the last command in the list list-1 returns an exit status of zero.

and

Lists

A list is a sequence of one or more pipelines separated by one of the operators ;, &, &&, or ||, and optionally terminated by one of ;, &, or .

and

Pipelines

A pipeline is a sequence of one or more commands separated by one of the control operators | or |&. The format for a pipeline is:

         [time [-p]] [ ! ] command [ [|⎪|&] command2 ... ]

This means:

while <single-command>; do <list>; done;

is valid syntax. The list is executed as long as <single-command> returns 0. If I run a while loop, like this, I get (obviously) errors:

$while aaa; do echo "foo"; done;
If 'aaa' is not a typo you can use command-not-found to lookup the package that contains it, like this:
cnf aaa

$while ; do echo "foo"; done;
Absolute path to '' is '/usr/sbin/', so running it may require superuser privileges (eg. root).

$while ""; do echo "foo"; done;
Absolute path to '' is '/usr/sbin/', so running it may require superuser privileges (eg. root).

To my surprise, using an unitialized variable bar as <single-command>, I run into an infinite loop:

$while $bar; do echo "foo"; done;
foo
foo
foo
...

This means, bar gets parameter-expanded to an empty string(?), gets executed(?) and always returns 0. But why doesn't my second error-example work equivalently? Interestingly:

$while "$bar"; do echo "foo"; done;
Absolute path to '' is '/usr/sbin/', so running it may require superuser privileges (eg. root).

doesn't work. This is equivalent to my third error-example. $bar gets expanded to an empty string and the unescaped quotes remain.

So my question is: How does the shell(bash in my case) interpret the

$while $bar; do echo "foo"; done;

command which results in an endless-loop?

Update:

I found out that the null-command (does nothing, returns 0) isn't that hard to simulate. The null command corresponds to : . So the non-terminating while-loop can be written equivalently as:

while :; do echo "foo"; done;
  • Check the command_not_found_handle function which emits the "Abolute path to..." message. – choroba Jan 22 '17 at 14:48
  • 1
    Interesting discovery, and it's not bash-specific (even though you tagged the question with bash), it happens under dash as well. Using set -x, it seems that it treats it as an empty command. It's difficult to get the same kind of "empty command" effect using anything else (even eval won't do it). – Celada Jan 22 '17 at 15:00
  • 1
    I suppose this is the same difference as trying to execute the command $bar (with bar unset) and trying to execute an empty string. Executing $bar will be the same as just pressing return, while executing an empty string will result in a "command not found" (bash) or " cannot execute [Is a directory]" (ksh93). Doesn't quite explain it all though. – Kusalananda Jan 22 '17 at 15:07
  • @Celada: Semantically equivalent you can achieve this via the null-command, see the update. – FloHe Jan 24 '17 at 8:56
  • @FloHe yes, but the case of the : command is much less interesting. That's an actual, non-null (builtin) command that is just like true but spelled differently. Your discovery concerned a command with no name (not a blank name or length-0 name, none at all) – Celada Jan 27 '17 at 21:32
2

The while instruction is followed by the command from which to evaluate the return code to determine if it should loop or not. It proceeds if the return value is 0.

Referring to an uninitialized variable is identical to having it initialized to ''.

Since running an empty command causes no error, it returns the corresponding return code (0):

$ foo=''
$ $foo
$ echo $?
0

That is normal behavior since the shell would otherwise give an error each time you press enter without typing anything.

Note that this differs from specifying an empty string which the shell identifies as an issue:

$ ''
-bash: : command not found
$ echo $?
127
  • That sounds plausible. The crux of the matter seems to be the difference in execution of >foo=''; $foo and >''. – FloHe Jan 22 '17 at 16:31
  • But why is it different from specifying an empty string on the command line? That's the part that I can't adequately explain to myself. – Kusalananda Jan 22 '17 at 16:33
  • 2
    @Kusalananda: That's simply because variable substitution is done first and the resulting command is empty, as opposed to a delimited empty string which it attempts to run since it is not considered empty. If you play around with it, you'll see a similar opposite reaction when starting a command with a ;command which gets bypassed if you begin $foo;command because the syntax is checked before the variable substitution. – Julie Pelletier Jan 22 '17 at 16:44
  • It simply acts this way by design. I'm convinced no one decided that it should be like that but applying all the other specs, that is the result. – Julie Pelletier Jan 22 '17 at 16:46
  • @JuliePelletier Well explained. That satisfies my curiosity. – Kusalananda Jan 22 '17 at 16:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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