As @Gairfowl said in comments, in zsh, you'd do:
argv[-1]=()
To remove the last argument (replace it with an empty list).
Even
argv[1]=()
Or even:
1=()
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:
function tail() {
shift 1
echo $@
}
Should rather be written:
tail() {
(( $# == 0 )) || shift
echo -E - "$@"
}
Or Korn-compatible:
function tail {
(( $# == 0 )) || shift
print -r - "$@"
}
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:
init() {
argv[-1]=()
print -r -- "$@"
}
Though you could also do¹:
init() print -r -- "$@[1,-2]"
Zsh also recently added the ${array:offset:length} alternative form of array slicing for compatibility with ksh93, so a ksh93-compatible variant could be:
function init {
print -r -- "${@:1:${#}-1}"
}
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=({1..5})
$ unset 'a[3]' 'a[-1]'
$ print -r - $a[4]
4
$ typeset -p a
typeset -a a=( 1 2 '' 4 '' )
$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:
$ a=({0..5})
$ unset 'a[3]' 'a[-1]'
$ print -r - "${a[4]}"
4
$ typeset -p a
typeset -a a=([0]=0 [1]=1 [2]=2 [4]=4)
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.
array[first,last]=(new values)
(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:
array[1,0]=(new elements) to insert elements at the front (like perl's unshift)
array[3,5]=() to remove elements in the middle.
array+=(more) or array[3]+=(more) to insert elements at the end or after a given index.
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.
set -- "${@:1:#-1}". LIke Don Adams you were 'this close'!zshthe positional parameters can be accessed as theargvarray, so methods for updating arrays should work. Tryargv[-1]=()orset -- "${(@)argv[1,-2]}". Also,typeset -p argvis a good way to view the results. More in this answer (part of one of your links).