Why isn't there a ";" after "do" in sh loops?

That is the syntax of the command. See Compound Commands

for name [ [in [words …] ] ; ] do commands; done

Note specifically: do commands

Most people put the do and commands on a separate line to allow for easier readability but it is not necessary, you could write:

for i in thing
do something
done

I know this question is specifically about shell and I have linked to the bash manual. It is not written that way in the shell manual but it is written that way in an article written by Stephen Bourne for byte magazine.

Stephen says:

A command list is a sequence of one or more simple commands separated or terminated by a newline or ; (semicolon). Furthermore, reserved words like do and done are normally preceded by a newline or ;... In turn each time the command list following do is executed.


I don't know what the original reason for this syntax is, but let's consider the fact that while loops can take multiple commands in the condition section, e.g.

while a=$(somecmd);
      [ -n "$a" ];
do
    echo "$a"
done

Here, the do keyword is necessary to tell apart the condition section and main body of the loop. do is a keyword like while, if and then (and done), and it seems to be in line with the others that it doesn't require a semicolon or newline after it but appears immediately before a command.

The alternative (generalized to if and while) would look somewhat ugly, we'd need to write e.g.

if; [ -n "$a" ]; then
    some command here
fi

The syntax of for loops is just similar.


Shell syntax is prefix based. It has clauses introduced by special keywords. Certain clauses have to go together.

A while loop is made out one or more testing commands:

test ; test ; test ; ...

and by one or more body commands:

body ; body ; body ; ...

Something has to tell the shell that a while loop begins. That's the purpose of the while word:

while test ; test ; test ; ...

But then, things are ambiguous. Which command is the start of the body? Something has to indicate that, and that's what the do prefix does:

do body ; body ; body ; ...

and, finally, something has to indicate that the last body has been seen; a special keyword done does that.

These shell keywords don't require semicolon separation, even on the same line. For instance, if you close several nested loops, you can just have done done done ....

Rather, the semicolon is between ... test ; body ... if they are on the same line. That semicolon is understood to be a terminator: it belongs with the test. Therefore, if a do keyword is inserted between them, it has to go between the semicolon and body. If it were on the other side of the semicolon, it would be wrongly embedded inside the test command's syntax, rather than placed between commands.

The shell syntax was originally designed by Stephen Bourne, and is inspired by Algol. Bourne loved Algol so much that he used lots of C macros in the shell source code to make C look like Algol. You can browse the 1979-dated shell sources from Version 7 Unix. The macros are in mac.h, and they are used all over the place. For instance if statements are rendered as IF ... ELSE ... ELIF ... FI.

Tags:

Shell