KJ wrote:
> perl's 'pop' builtin can be used to remove the last element of an
> array.
Ah, brings to mind the heady days of vituperative comparisons
between perl(1) and shell....
> Similarly, I want to be able to "pop" the last command-line argument
> to a shell script (that receives a variable number of arguments)
> in a way that allows me to capture the popped argument in a separate
> variable, and leaves all the remaining arguments in $*, exactly as
> they were originally passed to the script. (Just to be clear:
> after this operation the number of arguments in $* should decrease
> by exactly 1.)
> How can I do this?
(*Sigh!* Why do they never indicate the shell being used, or
the code desired?...)
A portable, robust, and reasonably quick programmatic idiom is:
eval "LASTARG=\${$#}\"; set -- \"\$@\" \"\$@\"; shift $#+1"
"shift $#+1" utilizes ksh(1)'s feature of implicit arithmetric
evaluation on the argument to "shift". Other shells will require
explicit arithmetric evaluation, e.g. "$(($#+1))" or "$[$#+1]"
or even "`expr $# + 1`". Also, remember the "--"!! (Can you
guess why?)
(This is "reasonably quick" because utilizing argv with a
non-trivial argc can get relatively inefficient -- but this
is not the shell's fault).
You would just not believe how I came to "invent" this idiom:
I actually presumed to accomplish LALR(1) parsing in
shellscript! What was I thinking! (Actually, I came rather
close).
Although I saw it retroactively, the above is reminiscent of
the solution that DGK himself uses in his demonstration code
for pushd:
#
http://www.kornshell.com/examples/pushpopdirs
# implementation of "push +n":
type=${1#+} i=_push_top-1
set -- "${_push_stack[@]}" "$dir" "${_push_stack[@]}"
shift $type
for dir
do (((i=i+1) < _push_max)) || break
_push_stack[i]=$dir
done
> (I was surprised by how difficult it is just to *read* the last
> argument of a shell script in such situations. I finally settled
> with: eval 'LASTARG=$'$#
Synonymous, but much more robust, is:
eval LASTARG=\${$#}
Because, even in ksh(1), positional parameters greater than 9
(that is, a two-or-more digit number) must be tokenized with
curly braces for backwards compatibility with bourne shell.
For instance, use "${10}" so as to distinguish it from being
parsed as "${1}0".
> which, crazy as it may look, is actually the *least* crazy-looking
> of all the alternatives I found, and it works in several shells.
> Unfortunately this solves only half of the "pop" problem: I have
> not yet removed the the last argument from $*. It crossed my mind
> to assign a new value to $# [perl supports such an idiom for resizing
> arrays], but I can't think of a way to do this!)
However, much better is the use of ksh93(1), which has much
enhanced array handling, requiring much less confabulation and
munging.
From the ksh(1) FAQ (
http://www.kornshell.com/doc/faq.html):
Q: How can I shift the elements of an array?
A: The "shift" special builtin command only works for
positional parameters. However, noting that array subscripts
start at 0, you can use:
set -A name "${name[@]:1}"
... to shift the array.
--
Firstly, one does not have to make side-effects upon argv,
and secondly, individual array elements (INCLUDING argv)
can be manipulated in place. It is worth checking out its
features in the manpage.
=Brian