[comp.unix.shell] Finding the last arg

lan_csse@netrix.nac.dec.com (CSSE LAN Test Account) (12/27/90)

Well, it should have been easy, but everything I've tried has failed, so
I'll go to the experts... (;-)

What I've been trying to do is write a script whose semantics are sort of
like the cp/mv/ln commands, in that the last argument is special.  If it
is a directory, I want to do something different that amounts to appending
character strings to its name to give file name[s] within the directory;
if it is an ordinary file, I just want to use its name.

The problem is that I can't figure out any Bourne-shell expression that
gives the last argument.  In C-shell, it's easy (argv[$#]).  But I need
to write Bourne shell scripts.  In fact, they need to be BSD bourne shell
scripts rather that ATT Bourne shell scripts.  The difference is probably
significant here, because of the differences in the way these two Bourne
shells handle variables.  Actually, it'd be really nice to find a solution
that is portable, but that may be just a dream.

The obvious thing to try is some sort of expression combining $# with ${},
e.g. ${$#}.  This gets a syntax error.  The simpler $$# is valid, but it
just gives the process id followed by a '#', not even close.  I've also 
tried assigning $# to a variable and using that, but all I've managed to
get is the number of the last arg, not its value.

At the moment I've kludged the job by writing a program lastfld.c that just
prints argv[argc-1].  But it seems incredibly stupid to have to pass all
the args to a subprocess which throws away all but the last one.  I mean,
the shell knows which arg is which; why go to the expense of starting up
a subprocess to learn something that the parent already knows?

The pseudo-code I'd like to use is:
	if [ -d last-command-line-arg ]
	then
		...
	fi
Any ideas?

rouben@math9.math.umbc.edu (Rouben Rostamian) (12/27/90)

In article <18476@shlump.nac.dec.com> lan_csse@netrix.nac.dec.com (CSSE LAN Test Account) writes:
 >What I've been trying to do is write a script whose semantics are sort of
 >like the cp/mv/ln commands, in that the last argument is special.  If it
 >is a directory, I want to do something different that amounts to appending
 >character strings to its name to give file name[s] within the directory;
 >if it is an ordinary file, I just want to use its name.
 >
 >The problem is that I can't figure out any Bourne-shell expression that
 >gives the last argument.  In C-shell, it's easy (argv[$#]).  But I need
 >to write Bourne shell scripts.

Here's what you need:
#!/bin/sh
eval lastarg=\${$#}
...etc...


--
Rouben Rostamian                            Telephone: (301) 455-2458
Department of Mathematics and Statistics    e-mail:
University of Maryland Baltimore County     bitnet: rostamian@umbc
Baltimore, MD 21228,  U.S.A.                internet: rostamian@umbc3.umbc.edu

bob@wyse.wyse.com (Bob McGowen x4312 dept208) (12/27/90)

In article <18476@shlump.nac.dec.com> lan_csse@netrix.nac.dec.com (CSSE LAN Test Account) writes:
>Well, it should have been easy, but everything I've tried has failed, so
>I'll go to the experts... (;-)
>
>What I've been trying to do is write a script whose semantics are sort of
>like the cp/mv/ln commands, in that the last argument is special.  If it
>is a directory, I want to do something different that amounts to appending
>character strings to its name to give file name[s] within the directory;
>if it is an ordinary file, I just want to use its name.
>
>The problem is that I can't figure out any Bourne-shell expression that
>gives the last argument.  In C-shell, it's easy (argv[$#]).  But I need
>to write Bourne shell scripts.  In fact, they need to be BSD bourne shell
>scripts rather that ATT Bourne shell scripts.  The difference is probably
>significant here, because of the differences in the way these two Bourne

  As an aside, could you explain this comment?  I have had minimal contact
  with BSD, but my experience does not seem to support this statement.

>shells handle variables.  Actually, it'd be really nice to find a solution
>that is portable, but that may be just a dream.
---deleted discussion---
>Any ideas?

Three solutions came to mind:  1) using eval; 2) using shift; 3) using a
combined while loop/if statement with the shift.

   1)  eval echo this is the last argument \$$#

       This works because the shell evaluates the right $ with the # to
       give the last argument (not really, but see later), then re-reads
       the line with the value of this number following the first $, which
       it now sees as an unprotected character and gives the substituted arg
       that matches.  Now for the "not really":  this will only work for
       a list of args that is not bigger than nine.  The tenth arg would
       result in output like:

		this is the last argument a0

       where the a is arg 1 ($10 is the concatenation of $1 and 0).

   2)  shift `expr $# - 1`
       echo the last arg is $1

       The Bourne shell on AT&T's SysV/386 and the Sun OS support a
       numeric argument to the shift.  This number of args are shifted
       out of the list.  So if there are ten args, expr gives nine for
       the shift, the last arg becomes $1.  The problem here is that
       all the preceding args are thrown away.

   3)  while [ ! -z "$1" ]
       do
	  if [ $# -eq 1 ]
	  then
	     last=$1
	     shift
	  else
	     rest="$rest $1"
	     shift
	  fi
       done

       Each argument is collected as encountered on the command line
       (except for the case where it contains white space -- "a b";
       this is a little hard to handle with this type of structure
       because the "arguments" are parsed three times: once as $1,
       again in the if to construct the $rest and later whenever $rest
       is used).  All except the last are collected into the variable
       rest, the last is in the variable last.  You can now manipulate
       the parts as needed.  For instance, to create a path name
       with the last arg as the first component:

       for item in $rest
       do
	  echo $last/$item
       done

I hope this slightly lengthy discussion helps.


Bob McGowan  (standard disclaimer, these are my own ...)
Product Support, Wyse Technology, San Jose, CA
..!uunet!wyse!bob
bob@wyse.com

jeff@onion.pdx.com (Jeff Beadles) (12/27/90)

In <18476@shlump.nac.dec.com> lan_csse@netrix.nac.dec.com writes:


...
>The problem is that I can't figure out any Bourne-shell expression that
>gives the last argument.
...
>Any ideas?


Well, there's nothing that I am aware of in /bin/sh that will ALWAYS allow
this to work.  The problem is that if you have more than 9 arguements, you
have to shift them off to get to what you want.  Here's one try that might
be of use:


#!/bin/sh

if [ $# -gt 0 -a $# -lt 10 ] ; then
	LAST=`eval echo \\$$#`		## 1-9 == Great!
elif [ $# -le 0 ] ; then
	LAST="NO_ARGS"			## 0 == Oops! None!
else
	LAST="TOO_MANY_ARGS"		## >9 == Oops!  Too many!
fi

echo "LAST= >$LAST<"
exit 0

However, this is not all that swift.  "foo a b c d e f g h i j k" won't work
with this.

The only other hope is to save off the arguements and loop until the end.  This
has the possibility of screwing up any special characters and/or white space
though, and is generally not-so-hot.


	-Jeff
-- 
Jeff Beadles		jeff@onion.pdx.com

cpcahil@virtech.uucp (Conor P. Cahill) (12/27/90)

In article <18476@shlump.nac.dec.com> lan_csse@netrix.nac.dec.com (CSSE LAN Test Account) writes:
>The obvious thing to try is some sort of expression combining $# with ${},
>e.g. ${$#}.  This gets a syntax error.  The simpler $$# is valid, but it

Try:
	eval \$$#

-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

maart@cs.vu.nl (Maarten Litmaath) (12/28/90)

# How to get the last argument of a shell script reliably.
# If you are sure the number of arguments is in [1-9], you can use this:
#
#	eval last=\$$#
#
# The general (backquote) solution works for ANY number of arguments.
# In POSIX-compatible shells this works for any $#:
#
#	eval last=\${$#}

set a b c d e f g h i j k l m -n	# For example.

case $# in
0)
	# So the user tried to be funny; let's give him a little surprise.
	exec kill -SYS $$
	;;
[1-9])
	eval last=\$$#
	;;
*)
	last=`
		n=$#
		set '' ${1+"$@"}
		shift $n
		echo -n "$1"
	`
esac

echo "last=|$last|"
--
  _ d         _\  _\      2
 ih -- Y = (c A . p  + m c B) Y
    dt                  0

davidsen@sixhub.UUCP (Wm E. Davidsen Jr) (12/31/90)

In article <1990Dec27.154917.14751@virtech.uucp> cpcahil@virtech.UUCP (Conor P. Cahill) writes:
| In article <18476@shlump.nac.dec.com> lan_csse@netrix.nac.dec.com (CSSE LAN Test Account) writes:
| >The obvious thing to try is some sort of expression combining $# with ${},
| >e.g. ${$#}.  This gets a syntax error.  The simpler $$# is valid, but it
| 
| Try:
| 	eval \$$#

  With sh there is no valid way to do this, it breaks when $# > 9. With
ksh the followinf is true:
  eval echo $# \$##		# uses only one digit of $#
  eval echo $# \${$#}		# works for all values

  Hope that clarifies it. I treid several shells, all flavors of sh seem
to stop at $#>9 and need evaluation via shift.
-- 
bill davidsen - davidsen@sixhub.uucp (uunet!crdgw1!sixhub!davidsen)
    sysop *IX BBS and Public Access UNIX
    moderator of comp.binaries.ibm.pc and 80386 mailing list
"Stupidity, like virtue, is its own reward" -me

lvc@cbnews.att.com (Lawrence V. Cipriani) (01/01/91)

In article <18476@shlump.nac.dec.com>, lan_csse@netrix.nac.dec.com (CSSE LAN Test Account) writes:

> The pseudo-code I'd like to use is:
> 	if [ -d last-command-line-arg ]
> 	then
> 		...
> 	fi

If you don't need to have the last command line argument in a variable you
can do this:

	if eval [ -d "\${${#}}" ]
	then
		...
	fi
-- 
Larry Cipriani, att!cbvox!lvc or lvc@cbvox.att.com
"I just love the smell of gunpowder!" - Bugs Bunny

mercer@npdiss1.StPaul.NCR.COM (Dan Mercer) (01/02/91)

In article <1990Dec27.154917.14751@virtech.uucp> cpcahil@virtech.UUCP (Conor P. Cahill) writes:
:In article <18476@shlump.nac.dec.com> lan_csse@netrix.nac.dec.com (CSSE LAN Test Account) writes:
:>The obvious thing to try is some sort of expression combining $# with ${},
:>e.g. ${$#}.  This gets a syntax error.  The simpler $$# is valid, but it
:
:Try:
:	eval \$$#
:
:-- 
:Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
:uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
:                                                Sterling, VA 22170 

Obviously this fails for $# > 9.  So you need some form of shift,
preferably forking and execing as few processes as possible (nice to
avoid expr).  You'd also like to preserve the current args.  So try
this:

args="$@"      # get arglist
argno=$#       # and number of current args
export args argno
last=`set -- spacer $args   # set as args - add spacer to eliminate expr
shift $argno          # shift over
echo $1`

This works if args contains no args with embedded whitespace - if that
is a possibility,  then expr must be used.

args="$@"
export args argno
last=` set -- spacer $args
shift \`expr $# - 1\`
echo $1`
echo $last

But this also fails if last arg may contain whitespace.  Oh well!

-- 
Dan Mercer
NCR Network Products Division      -        Network Integration Services
Reply-To: mercer@npdiss1.StPaul.NCR.COM (Dan Mercer)
"MAN - the only one word oxymoron in the English Language"

martin@mwtech.UUCP (Martin Weitzel) (01/02/91)

In article <1990Dec27.060903.1604@onion.pdx.com> jeff@onion.pdx.com (Jeff Beadles) writes:
>In <18476@shlump.nac.dec.com> lan_csse@netrix.nac.dec.com writes:
>>The problem is that I can't figure out any Bourne-shell expression that
>>gives the last argument.
>...
>>Any ideas?
>
>Well, there's nothing that I am aware of in /bin/sh that will ALWAYS allow
>this to work.  The problem is that if you have more than 9 arguements, you
>have to shift them off to get to what you want.

Not really. I have waited some time, but as Chris Torek seems to be in
vacation I'll bite the bullet this time :-) [%].

What ALLWAYS works in the Bourne-Shell is this:

	for last do :; done

Explanation: The for loop loops over all arguments of the current procedure,
if you leave out the `in'-clause. Every argument is put into the variable
you name after `for'. This leaves the last argument in last when the loop is
through.

You can also access the second last (third last, etc) argument if you
extend this trick a little:

	for i
	do
		last3=$last2
		last2=$last
		last=$i
	done

And please DON'T use `for i in $*' instead of leaving the `in'-clause out!
It's not the same - despite some UNIX books claim so!! (For the nit-pickers:
`for i' is the same as `for i in ${1+"$@"}' i.e. you will never run into
problems if there are no arguments or some arguments have embedded blanks.)

%: Shouldn't this be in the FAQ-list or is it? (I've no recent copy available)
-- 
Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83

chet@odin.INS.CWRU.Edu (Chet Ramey) (01/03/91)

>>In fact, they need to be BSD bourne shell
>>scripts rather that ATT Bourne shell scripts.  The difference is probably
>>significant here, because of the differences in the way these two Bourne
>
>  As an aside, could you explain this comment?  I have had minimal contact
>  with BSD, but my experience does not seem to support this statement.

The BSD /bin/sh is the one from v7, with minimal changes for the 4.2 BSD
signal semantics (restarted system calls, etc.) and # as a comment
character.  The AT&T /bin/sh changed drastically beginning with System V.2,
and further changes appeared in System V.3. 

Here's a short list of what was added to the v7 sh for the System V.2 sh:

shell functions and the `return' builtin
redirection of input/output for builtins (e.g. `read x < /dev/tty')
command hashing, `set -h', and the hash builtin
test/[, echo, pwd, ulimit builtins
exit status of a pipeline was defined to be the exit status of the last command
set -a, set -f
# as the comment character (this was put in at Berkeley and adopted by AT&T)
colon form of parameter substitution to test for nullness of a variable
CDPATH
MAILCHECK, MAILPATH
the use of `!' to negate a [] pattern for filename generation
the `<<-' form of here-document redirection (strip leading tabs)
set --
numeric parameter to the shift builtin
the unset builtin (!)
restricted shell mode

There were a few changes between the System V.2 sh and the V.3 sh, in
addition to bug fixes:

the shell is now 8-bit clean (that is, the shell no longer implements quoting
by turning on the 8th bit of characters)
function arguments no longer overwrite the global set of positional parameters
the getopts builtin

The BSD /bin/sh, which is still pretty much the V7 /bin/sh, includes
`login' as a builtin and allows `chdir' as a synonym for `cd'.  The
System V shell includes `newgrp' as a builtin.  As of 4.3 BSD, the BSD sh
accepts # as a comment only when non-interactive.

Chet
-- 
Chet Ramey				``I die, Horatio''
Network Services Group, Case Western Reserve University
chet@ins.CWRU.Edu
                My opinions are just those, and mine alone.

bob@wyse.wyse.com (Bob McGowen x4312 dept208) (01/03/91)

In article <1991Jan2.174157.21530@usenet.ins.cwru.edu> chet@po.CWRU.Edu writes:
>>>In fact, they need to be BSD bourne shell
>>>scripts rather that ATT Bourne shell scripts.  The difference is probably
>>>significant here, because of the differences in the way these two Bourne
>>
>>  As an aside, could you explain this comment?  I have had minimal contact
>>  with BSD, but my experience does not seem to support this statement.
>
>The BSD /bin/sh is the one from v7, with minimal changes for the 4.2 BSD
>signal semantics (restarted system calls, etc.) and # as a comment
>character.  The AT&T /bin/sh changed drastically beginning with System V.2,
>and further changes appeared in System V.3. 
>
>Here's a short list of what was added to the v7 sh for the System V.2 sh:

---description of differences deleted---

>accepts # as a comment only when non-interactive.
>
>Chet
>-- 

Thanks for the list of differences.  It will be useful, I'm sure!-)

However, the original posters statement was that there were differences
in how the versions of the shells handle "variables", which my 
experience seems to show is either the same or very nearly so, since
all of the scripts I have written work with both BSD and AT&T sh's.
So, even though the above list is very useful, the specific point of
possible differences in how the shells handle parameters (command
line arguments and variables) is still open.

Any information on this particular point would be helpful.

Thanks,

Bob McGowan  (standard disclaimer, these are my own ...)
Product Support, Wyse Technology, San Jose, CA
..!uunet!wyse!bob
bob@wyse.com

hansm@cs.kun.nl (Hans Mulder) (01/04/91)

In article <1991Jan2.174157.21530@usenet.ins.cwru.edu> chet@po.CWRU.Edu writes:
>Here's a short list of what was added to the v7 sh for the System V.2 sh:

[ stuff deleted ]

>exit status of a pipeline was defined to be the exit status of the last command

Nit-pick:
Both the v7 and the SVR2 sh manuals mention that
"the exit status of a pipeline is the exit status of the last command".

The problem is the the word "last" is ambiguous.

The v7 sh takes it to mean "last to terminate", SVR2 interprets it as
"rightmost".

Thus the pipeline "sleep 10 | false" would be considered successful by
the v7 shell (since the sleep terminates last, and successfully), while
the SVR2 sh considers it unsuccessful.

I think the latter behaviour is more useful.  For one thing, it makes
"if foo | grep -s bar" work right 100% of the time.


Happy New Year,

Hans Mulder	hansm@cs.kun.nl

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (01/04/91)

In article <2577@wn1.sci.kun.nl> hansm@cs.kun.nl (Hans Mulder) writes:
: I think the latter behaviour is more useful.  For one thing, it makes
: "if foo | grep -s bar" work right 100% of the time.

Presuming, of course, that your grep actually returns a reasonable
exit status.  Some don't, alas.

And the moral of the story?  It's easier to port a shell than a shell script.

Larry Wall
lwall@jpl-devvax.jpl.nasa.gov

stuart@amc-gw.amc.com (Stuart Poulin) (01/05/91)

Of course if you know the last arg will never have white space and
the command line wont have args like "=", you can use expr:
	Last=`expr "$*" : '.* \(.*\)' \| "$*"`

	-or-
Use awk, it can handle a "=" by itself but still no white space.
	Last=`Last=`echo "$*" | awk '{ print $NF }'`

    -or-
Something with sed or grep. 

I like the "for last do :; done" method - it's very clever and always works. 

Stuart Poulin                             DNS: stuart@amc.com
Applications Engineer/System Administrator
Applied Microsystems Corporation         UUCP: amc-gw!stuart
Redmond, Washington  98073               Dial: 800-ASK-4AMC,206-882-2000 

allbery@NCoast.ORG (Brandon S. Allbery KB8JRR) (01/05/91)

As quoted from <1991Jan2.174157.21530@usenet.ins.cwru.edu> by chet@odin.INS.CWRU.Edu (Chet Ramey):
+---------------
| Here's a short list of what was added to the v7 sh for the System V.2 sh:
| 
| colon form of parameter substitution to test for nullness of a variable
| set --
| numeric parameter to the shift builtin
| restricted shell mode
+---------------

Those four were in the System III /bin/sh.

+---------------
| System V shell includes `newgrp' as a builtin.  As of 4.3 BSD, the BSD sh
| accepts # as a comment only when non-interactive.
+---------------

Silliness.  I disliked this in csh, I devoutly hope AT&T didn't pick it up for
V.4 /bin/sh.  Why should # be a comment only when noninteractive?  There are
valid reasons to use comments during interactive sessions --- *especially*
under BSD, which has a working (read: pty-based) "script" command....

++Brandon
-- 
Me: Brandon S. Allbery			    VHF/UHF: KB8JRR on 220, 2m, 440
Internet: allbery@NCoast.ORG		    Packet: KB8JRR @ WA8BXN
America OnLine: KB8JRR			    AMPR: KB8JRR.AmPR.ORG [44.70.4.88]
uunet!usenet.ins.cwru.edu!ncoast!allbery    Delphi: ALLBERY

jc@minya.UUCP (John Chambers) (01/05/91)

> What ALLWAYS works in the Bourne-Shell is this:
> 
> 	for last do :; done

Wow! A one-liner that works for more than 9 args!  Of course, there's 
the question as to whether this loop is actually faster than starting 
a subprocess that just does puts(argv[artc-1]), but at least there's
a way to do it that is portable.

That comment isn't worth wasting the bandwidth, of course; my motive
for this followup is a bit of bizarreness that I discovered while
testing this command.  The usual format of a for loop is 3 lines:
	for last
	do :
	done
Usually when I want to collapse such vertical code into a horizontal
format, I follow the rule "Replace the newlines with semicolons", and
it works.  For instance,
	if [ <test> ]
	then <stuff>
	else <stuff>
	fi
reduces to
	if [ <test> ];then <stuff>;else <stuff>;fi
which I can do in vi via a series of "Jr;" commands.  With the above 
for-loop, this gives
	for last;do :;done
which doesn't work.  The shell gives a syntax error, complaining about
an unexpected ';' in the line.  Myself, I found this to be a somewhat 
unexpected error message.  It appears my simple-minded algorithm for 
condensing code doesn't work in this case.

So what's going on here?  What the @#$^&#( is the shell's syntax that 
makes the semicolon not only unneeded, but illegal in this case?

One of the real hassles I keep finding with /bin/sh (and /bin/csh is
even worse ;-) is that the actual syntax regarding things like white
space, newlines, and semicolons seems to be a secret.  It often takes 
a lot of experimenting to find a way to get these syntax characters 
right.  Is there any actual documentation on sh's syntax?  Is it truly 
as ad-hoc as the above example implies?  Is there perhaps some logical 
structure underlying it all that would explain why
 	for last do :; done
and
	for last
	do :
	done
both work but
	for last;do :;done
doesn't?
-- 
Zippy-Says: Imagine ... a world without clothing folds, chiaroscuro, or marital difficulties ...
Home: 1-617-484-6393 Work: 1-508-952-3274
Uucp: ...!{harvard.edu,ima.com,eddie.mit.edu,ora.com}!minya!jc (John Chambers)
Uucp-map: minya	adelie(DEAD)

Dan_Jacobson@ATT.COM (01/07/91)

>>>>> On 5 Jan 91 15:56:45 GMT, jc@minya.UUCP (John Chambers) said
	among many other things that I was too lasy to go downstirs to get the
	book to look up [it's cold down there]:

John> Is there any actual documentation on sh's syntax?

In the 4.3 BSD document set you'll find a syntax description in the sh
articles in [I think] the User's Supplementary Documents volume.
-- 
Dan_Jacobson@ATT.COM  Naperville IL USA  +1 708-979-6364

chet@odin.INS.CWRU.Edu (Chet Ramey) (01/08/91)

In article <DANJ1.91Jan6120607@cbnewse.ATT.COM> danj1@ihlpa.att.com writes:
>John> Is there any actual documentation on sh's syntax?

>In the 4.3 BSD document set you'll find a syntax description in the sh
>articles in [I think] the User's Supplementary Documents volume.

The article is ``An Introduction to the Unix Shell'', USD:3, by S. R. Bourne.
There is a grammar in the appendix.

Take it with a grain of salt, though; as described by that grammar, sh does
not accept ``who | wc'' as a legal command. 

Chet
-- 
Chet Ramey				``There's just no surf in
Network Services Group			  Cleveland, U.S.A. ...''
Case Western Reserve University
chet@ins.CWRU.Edu		My opinions are just those, and mine alone.

allbery@NCoast.ORG (Brandon S. Allbery KB8JRR) (01/08/91)

As quoted from <443@minya.UUCP> by jc@minya.UUCP (John Chambers):
+---------------
| as ad-hoc as the above example implies?  Is there perhaps some logical 
| structure underlying it all that would explain why
|  	for last do :; done
| and
| 	for last
| 	do :
| 	done
| both work but
| 	for last;do :;done
| doesn't?
+---------------

I suspect a /bin/sh bug.  I just tried it on an ancient ksh and an almost-as-
old [ ;-) ] Xenix; Xenix sh barfed, but ksh accepted it.  I'll try it under
System V 3.2 /bin/sh tomorrow.

++Brandon
-- 
Me: Brandon S. Allbery			    VHF/UHF: KB8JRR on 220, 2m, 440
Internet: allbery@NCoast.ORG		    Packet: KB8JRR @ WA8BXN
America OnLine: KB8JRR			    AMPR: KB8JRR.AmPR.ORG [44.70.4.88]
uunet!usenet.ins.cwru.edu!ncoast!allbery    Delphi: ALLBERY

Chuck.Phillips@FtCollins.NCR.COM (Chuck.Phillips) (01/14/91)

>>>>> On 5 Jan 91 15:56:45 GMT, jc@minya.UUCP (John Chambers) said:
John> With the above for-loop, this gives
John> 	for last;do :;done
John> which doesn't work.  The shell gives a syntax error, complaining
John> about an unexpected ';' in the line.  Myself, I found this to be a
John> somewhat unexpected error message.  It appears my simple-minded
John> algorithm for condensing code doesn't work in this case.

John> So what's going on here?  What the @#$^&#( is the shell's syntax that 
John> makes the semicolon not only unneeded, but illegal in this case?

A little sh history: sh comments originally went from a ':' to the end of a
line.  ('#'-style comments were added later.)  The funny thing about ':'
comments is they get evaluated and have a return value, unlike '#' comments
which are merely whitespace.

For example:

: Arbitrary text here > foop

This "comment" wipes out the file foop, if it exists, then creates a null
file called "foop".  (Wonder why they added '#' comments? ;^)  The shell
does not attempt to execute a command called "Arbitrary", and the comment
has a return value of zero (a.k.a. success).

':' statements have other uses besides a filler with a return value of '0'
and an obscure way to create null files.

: ${VARIABLE=value}

Sets variable VARIABLE to "value" unless VARIABLE is already set.  Lastly,
beginning a sh script with ":" instead of "#! /bin/sh" saves an unnecessary
fork and exec on some versions of UNIX.  (Note to bash users: In bash,
scripts beginning with ":" are interpreted by bash while "#! /bin/sh"
scripts are, of course, executed by "/bin/sh".)


To answer your original question:

for last;do :;done

The ";done" part of the statement is a comment, which yields a syntax
error.  (sh error messages are often misleading, IMHO.)

John> One of the real hassles I keep finding with /bin/sh (and /bin/csh is
John> even worse ;-) is that the actual syntax regarding things like white
John> space, newlines, and semicolons seems to be a secret.

Oh, that reminds me.  You're not to repeat anything I've said.  :-)
I agree that csh is worse, mostly due to its (mis)handling of newlines.  I
still use csh interactively, but program in sh after being burned a few
times by csh's unexpected syntax exceptions and variations.  (Just try
piping the result of one loop into another loop in csh without using a
temporary file, FIFO or multiple invocations of csh.)

#include <std/disclaimer.h>

	Hope this helps,
--
Chuck Phillips  MS440
NCR Microelectronics 			chuck.phillips%ftcollins.ncr.com
2001 Danfield Ct.
Ft. Collins, CO.  80525   		...uunet!ncrlnk!ncr-mpd!bach!chuckp