Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!sun-barr!apple!mips!wyse!bob From: bob@wyse.wyse.com (Bob McGowen x4312 dept208) Newsgroups: comp.unix.shell Subject: Re: Finding the last arg Keywords: Bourne shell arguments Message-ID: <3074@wyse.wyse.com> Date: 26 Dec 90 22:44:52 GMT References: <18476@shlump.nac.dec.com> Sender: news@wyse.wyse.com Reply-To: bob@wyse.UUCP (Bob McGowen x4312 dept208) Organization: Wyse Technology Lines: 84 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