nested loops: '; done ; done' vs. '; done done'

nested loops: '; done ; done' vs. '; done done'

Post by Frank Terb » Sun, 03 Jun 2007 17:36:07


Hello,

When nesting two for-loops, why does the following work?

for i in a b c ; do for e in x y z ; do echo $i$e ; done done

I was sure it had to be '; done ; done' at the end (which, of course,
works as well). But I tried the former in various shells (old-bourne,
ash, ksh93, zsh, bash...) and it always works. :-)

My guess is that, as 'done' is a shell-reserved word, the shell knows
that they have to be read as two words. Is that the case, or am I on
the completely wrong trace?

Where is this behaviour documented?

Regards, Frank

--
There are no threads in alt.binaries.pictures. *** a,
so there's no gain in using a threaded news reader.
-- unknown source
 
 
 

nested loops: '; done ; done' vs. '; done done'

Post by bsh » Mon, 04 Jun 2007 08:50:09


ash and ksh93 are direct enhancements from the original
bourne shell source, but I'm surprised that the other
shells have managed to get this right, insofar as this
is not a well-documented behavior, as indeed bash does
its parsing with a munged flex/bison LALR(1) grammar,
which is not suited to the three-pass arcana of bournish
language syntax. See also my post at:

http://www.yqcomputer.com/ #8378d15b11134de6

On the other hand, you should see the syntactical gaffs
that pdksh makes, insofar it is based, IIRC, on an ad-hoc
parser!


Good guess, but read on....


Usually the right answer would have been "RTFM" but you
are correct insofar as this is not addressed in the manpage
but only in B&K ("The [New] Kornshell Command and
Programming Language"). Indeed, the manpage states:

"The following reserved words are only recognized as the
first word of a command and when not quoted: if then else
elif fi case esac for while until do done { } function
select time [[ ]]"

which is categorically incorrect: a terminating reserved
word (fi, esac, done, and }) is _also_ recognized as
a reserved word when _following_ another terminating
reserved word. Thus, one may play syntactic games like:

# not tested
{ if word
then while word
do case word in
esac done fi }

... as well as the special case (no pun intended) for the
case statement:

case ni in esac # no parenthesis... and longest palindromic statement!

For bourne shell, there is some serious goofiness possible:

_():&#\

and:

echo `echo no terminating backquote!
<end-of-file>

See also my post at:

http://www.yqcomputer.com/ #7551c9dab5bf9e30

=Brian

 
 
 

nested loops: '; done ; done' vs. '; done done'

Post by Frank Terb » Mon, 04 Jun 2007 19:07:10

bsh < XXXX@XXXXX.COM >:

[...]

Okay. I could not find this addressed in SUSv3 nor any of the involved
man pages or other official documentation of the said shells (correct
me if I am wrong). So, is this an undocumented "feature"? If so, that
would be a direct reason not to use ';done done' in my scripts.
Because scripts that are supposed to run in a POSIX shell, should only
use features that standard mentions, of course, as implementations
might drop undocumented features anytime.

[...]

Yes, I tried these as well. And the behaviour is the same. And yes,
mixing them like 'if true ; then while false ; do echo foo ; done fi'
does work as well.

Is that 'a terminating reserved...' sentence something that you found
documented somewhere (maybe in the book you mentioned)? Or is it
something you know from your personal experience.

Just to be clear: Is this an undocumented feature? Is it only working
"by accident"? Is this intended?

Regards, Frank

--
There are no threads in alt.binaries.pictures. *** a,
so there's no gain in using a threaded news reader.
-- unknown source
 
 
 

nested loops: '; done ; done' vs. '; done done'

Post by Stephane C » Mon, 04 Jun 2007 20:04:11

2007-06-02, 16:50(-07), bsh:

[...]

That's not true at least for ash. And that was the whole point
of ash: rewrite a public domain version of the System V shell,
so that it can be used in free Unices.

--
Sthane
 
 
 

nested loops: '; done ; done' vs. '; done done'

Post by Sven Masch » Tue, 05 Jun 2007 00:06:56


...and additionally: the original ash doesn't accept the above.
 
 
 

nested loops: '; done ; done' vs. '; done done'

Post by Geoff Clar » Tue, 05 Jun 2007 22:17:38


Looking at the shell grammar in SUSv3/POSIX, as far as I can see the
shell is required to accept it.

The syntax for "for ... do ... done" is:

for_clause : For name linebreak do_group
| For name linebreak in sequential_sep do_group
| For name linebreak in wordlist sequential_sep do_group
;

do_group : Do compound_list Done
;

(here "Do" is the token for "do" and "Done" is the token for "done").

So the question is whether a compound_list (which is what goes between
Do and Done) can end in a Done token.

The definition of compound_list is:

compound_list : term
| newline_list term
| term separator
| newline_list term separator
;

It can end with either "term" or "term separator" ("separator" is
basically a ';', '&' or newline, although that's not the full story).

Following the breakdown of "term" through several levels, one of the
things it can be is:

compound_command : brace_group
| subshell
| for_clause
| case_clause
| if_clause
| while_clause
| until_clause
;

I.e. another for_clause (or a while_clause or until_clause; they all
end in Done) is allowed to appear between Do and Done, with or
without a separator.

--
Geoff Clare < XXXX@XXXXX.COM >
 
 
 

nested loops: '; done ; done' vs. '; done done'

Post by Frank Terb » Wed, 06 Jun 2007 06:14:16

Geoff Clare:

[...]

That makes sense. Thank you for working this out.

Regards, Frank

--
There are no threads in alt.binaries.pictures. *** a,
so there's no gain in using a threaded news reader.
-- unknown source
 
 
 

nested loops: '; done ; done' vs. '; done done'

Post by Kaz Kylhek » Thu, 07 Jun 2007 02:23:20


This is analogous to closing nested if's with fi fi fi ...

These keywords terminate the command, making it lexically clear that
the following material is the start of another command without the
need for a separating semicolon.


It's perfectly clear in the grammar given in the Single Unix
Specification.

The specific phrase rule which generates your above for_clause is:

for name linebreak in wordlist sequential_sep do_group

Thus the nested for is generated by the do_group, which is:

do compound_list done

The compound_list isn't required to generate a terminating semicolon.
A simple loop terminated by done counts, grammatically, as a compound
list. The precise derivation chain is:

compound_list
-> and_or
-> pipeline
-> pipe_sequence
-> command
-> compound_command
-> for_clause

And so you have ``do for_clause done'', which generates the done done.

That's a wrap! :)