How to do the opposite of shift in zsh?
How can I write the opposite of this simple function?
I want to achieve
and get all but the last of the arguments in the $@ variable, so the function can be written as
I have tried unset '@[-1]', unset '[@:-1]', from https://unix.stackexchange.com/a/611717/696135 but these don't work
I have tried set -- "${@:1:$(($#-1))}", unset "@[${#@[@]}-1]", set -- "${@:1:$#-1}" from https://stackoverflow.com/questions/20398499/remove-last-argument-from-argument-list-of-shell-script-bash but these don't work
An example of a script that doesn't work:
shift -p will remove the last array element, as per man zshbuiltins:
shift [ -p ] [ n ] [ name ... ]
The positional parameters ${n+1} ... are renamed to $1 ..., where n is an arithmetic expression that defaults to 1. If any names are given then the arrays with these names are shifted instead of the positional parameters.
If the option -p is given arguments are instead removed (popped) from the end rather than the start of the array.
Your desired init function can be:
As @Gairfowl said in comments, in zsh, you'd do:
To remove the last argument (replace it with an empty list).
Even
Or even:
May sometimes be preferable to shift (itself short for shift 1, itself short for shift 1 argv, the latter being zsh-specific), as it doesn't complain if $argv has no element.
Note that your:
Should rather be written:
Or Korn-compatible:
If the point is to print the remaining arguments space separated and followed by a newline.
Without the quotes, empty arguments would be skipped, without -, that wouldn't work properly is the first argument started with -, without -E/-r, it wouldn't work properly for arguments containing backslashes.
For your init:
Though you could also do¹:
Zsh also recently added the ${array:offset:length} alternative form of array slicing for compatibility with ksh93, so a ksh93-compatible variant could be:
Note the brace around # without which in zsh (and unless in sh or ksh emulation), $#-1 would be taken as ${#-}1 that is the length of the $- parameter (using csh-style $#param operator) followed by 1. ${@:1:$# - 1} would also work.
zsh also supports ${var:offset:-length} Ã la bash, so you could also use print -r -- "${@:1:-1}".
Beware that those report an error if the list of arguments is empty, same as in ksh93 or bash.
${@:1:#-1} also works in current versions of zsh, just like echo $(( # )) outputs the same as echo $(( $# )), but I would avoid that as it's not documented, and # is used in several operators in arithmetic expressions. In particular $(( #var )), expands to the character value of the first character in $var (similar to $(( '$var' )) in ksh), so one might argue that $(( #- )) should expand to the character value of the first character in $-.
unset 'array[i]' does work in zsh for compatibility with ksh, but in ksh, arrays are not real arrays but more like associative arrays with keys limited to positive integers, while in zsh they are normal arrays like in all other non-Korn-like shells, so to emulate the ksh behaviour unset 'array[i]' doesn't unset the element (which wouldn't make sense for normal arrays), but sets it to the empty string, even if that's the last element in the array.
In zsh:
$a[4] is still 4, the unset elements have been replaced with the empty string.
Comparing with ksh93 or bash with their peculiar array design:
That (and the fact that array indices start at 0 instead of 1) is one reason why in ksh and its clones (such as bash), the positional parameters cannot be mapped to an array (like most other shells such as csh, fish, rc, zsh do), and I suspect that the fact that one can't unset 'argv[i]' in zsh except for the last element (though it still doesn't unset it but replaces with the empty string) might have something to do with ksh compatibility.
(if ,last is omitted, it's the same as array[first,first]) is itself an array slice assignment, similar to what you can achieve in perl with its splice function for instance, it's not limited to unsetting the last element (like perl's pop), you can also do:
What's missing compared to the perl equivalents is the ability to retrieve the elements that are removed at the same time they are removed.
For example, perl's @last2 = splice @array, -2 to pop the last two elements into @last2 has to be written: last2=( "${(A@)array[-2,-1]}" ); array[-2,-1]=() (which can be shortened to last2=( $array[-2,-1] ); array[-2,-1]=() if there's no empty element that needs to be preserved).
But I digress...
¹ Beware "$@[-2,-1]" expands to a list of one empty elements instead of an empty list. That makes no difference for print -r -- which prints an empty line in either case, but in the general case, that could be undesirable and would be worked around by using "${(A)@[-2,-1]}". If there's no empty element that needs to be preserved, $@[-2,-1] (without the quotes) is enough.