as the subject says, at least the chpwd and precmd hooks have the shell's argv[0] in $0. seen in both zsh-5.8-0-g77d203f and zsh-5.8-462-g765bc14. put the reproducer below in argzero.zsh and source it into a fresh shell (zsh -f). start the shell using its absolute path, that's what you'll see. % /usr/local/bin/zsh -f % . ./argzero.zsh ZSH_PATCHLEVEL: zsh-5.8-462-g765bc14 top: nofunctionargzero off top: posixargzero off precmd output should be below this message. 'cd .' will utilize all three hooks. precmd: nofunctionargzero off precmd: posixargzero off precmd: /usr/local/bin/zsh <------ % cd . preexec: nofunctionargzero off preexec: posixargzero off preexec: preexec <------ chpwd: nofunctionargzero off chpwd: posixargzero off chpwd: /usr/local/bin/zsh <------ precmd: nofunctionargzero off precmd: posixargzero off precmd: /usr/local/bin/zsh <------ argzero.zsh: print ZSH_PATCHLEVEL: $ZSH_PATCHLEVEL set -o > >(sed -n "/argzero/s/^/top: /p") function chpwd { set -o > >(sed -n "/argzero/s/^/chpwd: /p") print chpwd: "$0" '<------' } function precmd { set -o > >(sed -n "/argzero/s/^/precmd: /p") print precmd: "$0" '<------' } function preexec { set -o > >(sed -n "/argzero/s/^/preexec: /p") print preexec: "$0" '<------' } cat <<EOF precmd output should be below this message. 'cd .' will utilize all three hooks. EOF -- roman
On Mon, Aug 9, 2021 at 1:16 PM Roman Neuhauser <neuhauser@sigpipe.cz> wrote:
>
> as the subject says, at least the chpwd and precmd hooks
> have the shell's argv[0] in $0.
This is because (most) hook functions are called with an empty (NULL)
argument list, as opposed to e.g. all commands (external, builtin, or
function) run from a command line, which have a non-empty argument
list starting with the command name itself.
In the particular case of preexec, the argument list is empty any time
history is not active, which is the case when loading a script with
"source" or "." builtins. If you actually try it from the interactive
command line, you'll see that $0 is "preexec" in the call to every
function in the preexec_functions list.
I'm not sure what the desired behavior is here. callhookfunc() could
dummy up a LinkNode any time its lnklst argument is empty, and then
all the hooks would behave in the manner preexec behaves when history
is enabled; or doshfunc() could do the same, in which case every
hook-array function would have its own name in $0; or neither of the
above is appropriate and we should document the current behavior.
# schaefer@brasslantern.com / 2021-08-09 14:26:58 -0700: > On Mon, Aug 9, 2021 at 1:16 PM Roman Neuhauser <neuhauser@sigpipe.cz> wrote: > > > > as the subject says, at least the chpwd and precmd hooks > > have the shell's argv[0] in $0. > > This is because (most) hook functions are called with an empty (NULL) > argument list, as opposed to e.g. all commands (external, builtin, or > function) run from a command line, which have a non-empty argument > list starting with the command name itself. > > In the particular case of preexec, the argument list is empty any time > history is not active, which is the case when loading a script with > "source" or "." builtins. If you actually try it from the interactive > command line, you'll see that $0 is "preexec" in the call to every > function in the preexec_functions list. how did this happen? was it the shortest patch that wouldn't crash the shell? you give no hints of benefits this should have for users, and i can't think of any either. come to think of it, if so taxing to implement fully, why is it built into the shell in the first place? and is it too late to reverse the course, gut the half-assed implementation with one written using shell functions? so much in zsh is done that way, why is the loop over foo_functions written in C? > I'm not sure what the desired behavior is here. callhookfunc() could > dummy up a LinkNode any time its lnklst argument is empty, and then > all the hooks would behave in the manner preexec behaves when history > is enabled; or doshfunc() could do the same, in which case every > hook-array function would have its own name in $0; or neither of the > above is appropriate and we should document the current behavior. what are the downsides of (2) for users of the shell? both (1) and (3) result in loss of information and deviation from standard behavior, and what is lost in behavior is gained in length of the manual. (2) preserves information, gets rid of a special case, and nullifies the need for more documentation. that is, i'm assuming that functions plugged into any hook behave as actual functions, IOW function a b { ... } > ab.log precmd_functions=(a) preexec_functions=(b) calls the function with $0 == "a" in precmd, "b" in preexec, and that their runtime output goes to ab.log. hooks do neither and TRAPNAL functions only the first. :( -- roman
On Tue, Aug 10, 2021 at 4:33 AM Roman Neuhauser <neuhauser@sigpipe.cz> wrote: > > how did this happen? I return to what I told Marlon the other day: The answer to "why doesn't feature A take advantage of feature B" is often "because A was developed long before B". The code for calling the hook functions has been around since long before the FUNCTION_ARGZERO option was added, and nobody thought to update all the entry points. When the code for the arrays of hooks was further added, it just passed through what the base function was already getting. > come to think of it, if so taxing to implement fully, why is it built > into the shell in the first place? It's not "taxing", it's just forgotten. It was built into the C code so that people's existing hook functions (precmd, preexec, chpwd, etc.) wouldn't have to be changed/overwritten to "know about" the new special arrays, and so that e.g. module writers could plug into those arrays without fear that a user could change the calling protocol. > and is it too late to reverse the > course, gut the half-assed implementation with one written using shell > functions? There's no need to be offensive about it, but yes, it's too late. The implementation actually has all the interfaces it needs to populate FUNCTION_ARGZERO properly, it was just never noticed/found necessary to do so. > > I'm not sure what the desired behavior is here. callhookfunc() could > > dummy up a LinkNode any time its lnklst argument is empty, and then > > all the hooks would behave in the manner preexec behaves when history > > is enabled; or doshfunc() could do the same, in which case every > > hook-array function would have its own name in $0; or neither of the > > above is appropriate and we should document the current behavior. > > what are the downsides of (2) for users of the shell? Theoretically, a function might prefer to know that it was being called as a consequence of being found in a given hook list, vs. being called directly. That might argue for (1). However, since the current situation is (3), the only instance where that could exist in practice is for the preexec hooks, so we're probably safe choosing (2). I will note, to avoid yet again having an "A forgot B" situation, that Zftp module hooks also behave like (3) and would begin to have their own name in $0 if we change doshfunc(). > function a b { ... } > ab.log > precmd_functions=(a) > preexec_functions=(b) > > calls the function with $0 == "a" in precmd, "b" in preexec, > and that their runtime output goes to ab.log. hooks do neither > and TRAPNAL functions only the first. :( I'm not sure what "the first" is here? Anyway, having output redirections on function definitions is another thing that was added quite late in the long history of the shell -- originally, the redirect would have been connected to the "function" command itself -- so there may be circumstances where you have to write function a b { { ... } > ab.log } to be sure of getting what you meant there. PWS may have more to say about it.
# schaefer@brasslantern.com / 2021-08-10 10:31:00 -0700: > On Tue, Aug 10, 2021 at 4:33 AM Roman Neuhauser <neuhauser@sigpipe.cz> wrote: > > and is it too late to reverse the course, gut the half-assed implementation > > with one written using shell functions? > > There's no need to be offensive about it, but yes, it's too late. The > implementation actually has all the interfaces it needs to populate > FUNCTION_ARGZERO properly, it was just never noticed/found necessary > to do so. sorry, i didn't think much about the wording, and didn't mean to be offencisve. if you (someone) can finish it i'll be grateful. > > > I'm not sure what the desired behavior is here. callhookfunc() could > > > dummy up a LinkNode any time its lnklst argument is empty, and then > > > all the hooks would behave in the manner preexec behaves when history > > > is enabled; or doshfunc() could do the same, in which case every > > > hook-array function would have its own name in $0; or neither of the > > > above is appropriate and we should document the current behavior. > > > > what are the downsides of (2) for users of the shell? > > Theoretically, a function might prefer to know that it was being > called as a consequence of being found in a given hook list, vs. being > called directly. That might argue for (1). yes, but there's other ways to let a function know it's being called as part of a particular hook array. there's only one reasonable way of letting a function know the name it was called with. > > function a b { ... } > ab.log > > precmd_functions=(a) > > preexec_functions=(b) > > > > calls the function with $0 == "a" in precmd, "b" in preexec, > > and that their runtime output goes to ab.log. hooks do neither > > and TRAPNAL functions only the first. :( > > I'm not sure what "the first" is here? use the correct name for a function defined with multiple names. > Anyway, having output redirections on function definitions is another > thing that was added quite late in the long history of the shell -- i know, it was me who asked for it back then. Date: Thu, 24 Jul 2014 14:23:31 +0200 From: Roman Neuhauser <neuhauser@sigpipe.cz> To: zsh-users@zsh.org Subject: io-redirect in function definitions > originally, the redirect would have been connected to the "function" > command itself -- so there may be circumstances where you have to > write > > function a b { { ... } > ab.log } > > to be sure of getting what you meant there. PWS may have more to say about it. sure, a workaround exists and it's just a pair of braces and an indentation level away. my bitching here isn't motivated by difficulty to achieve the effect with existing means. i want the shell to be simpler, conceptually smaller by having one kind of functions: those that have their name in $0 and which don't forget redirections defined after their body. requests for changes "just" for the sake of some principles may be annoying so i'll add that this conversation exists because i wanted to use a single function in several hooks and modify its behavior based on the hook name: function generic-hook { local hooks=${1}_functions work with ${(P)hooks} } -- roman
On Wed, Aug 11, 2021 at 9:40 PM Roman Neuhauser <neuhauser@sigpipe.cz> wrote:
>
> [...] i want the
> shell to be simpler, conceptually smaller by having one kind of
> functions: those that have their name in $0 and which don't forget
> redirections defined after their body.
Just a couple of thoughts about the latter.
First, it doesn't work with autoload. You end up having to do some
equivalent of adding the extra set of braces, inside the source file.
Second, just worth noting:
% func() { print $SECONDS } > /tmp/funcout
% setopt noclobber
% func
% func
zsh: file exists: /tmp/funcout
%
Third, I'm not convinced that the semantics of memoized redirections
in this way is actually simpler or easier for a user to understand.
It's the only case where you can write a redirection in (what appears
to be) a "simple command" (doc term) and NOT have it immediately take
effect.
Fourth, related, why is redirection different from pipelining in this
particular case? Why not parse the entire pipeline as part of the
function definition? (Which would have to be done by treating it as
part of the function Eprog, see $0 patch discussion on zsh-workers.)
The "workaround" for this one is to redirect to a command
substitution.
% function f2 { print $SECONDS } | cat -n
% f2
zsh: command not found: f2
% function f2 { print $SECONDS } >>(cat -n)
% f2
1 186
%
I'll conclude by wondering if there are well-known semantics for
execution of what appears inside the command substitution in that last
example. DO NOT TRY THIS:
% f3() { print $SECONDS } >>(f3)