By default, ZSH hook chpwd
is not triggered on shell startup. The tutorial here provides some ideas to fix this.
We can use a trick to define a function run only once on precmd
and destruct itself automatically.
function _self_destruct_hook {
local f
for f in ${chpwd_functions}; do
"$f"
done
# remove self from precmd
precmd_functions=(${(@)precmd_functions:#_self_destruct_hook})
builtin unfunction _self_destruct_hook
}
# prepend the hook, in case you want run the hook 1st
precmd_functions=(_self_destruct_hook ${precmd_functions[@]})
# or append the hook
(( $+functions[add-zsh-hook] )) || autoload -Uz add-zsh-hook
add-zsh-hook precmd _self_destruct_hook
The _self_destruct_hook
wraps chpwd_functions
and run every item within once on precmd
/startup.
Unlike the method above, the following change makes it possible to run specific item from chpwd_functions
.
# define an array to collect functions run only once
typeset -ag self_destruct_functions=()
function _self_destruct_hook {
local f
for f in ${self_destruct_functions}; do
"$f"
done
# remove self from precmd
precmd_functions=(${(@)precmd_functions:#_self_destruct_hook})
builtin unfunction _self_destruct_hook
unset self_destruct_functions
}
precmd_functions=(_self_destruct_hook ${precmd_functions[@]})
# example 1: hook direnv on chpwd and run it on startup/precmd once
if (( $+commands[direnv] )) && ! (( $+functions[_direnv_hook] )); then
_direnv_hook() {
eval "$(command "direnv" export zsh)";
}
typeset -ag chpwd_functions
chpwd_functions=(_direnv_hook ${chpwd_functions[@]})
# add the _direnv_hook into custom _self_destruct_hook
# prepend _direnv_hook cause we need env var changes before any other action
self_destruct_functions=(_direnv_hook ${self_destruct_functions[@]})
fi
# example 2: dynamic umask with direnv
function _umask_hook {
if [[ -n $UMASK ]]; then
umask "$UMASK"
elif [[ $OSTYPE == darwin* ]]; then
umask 0077
else
umask 0022
fi
}
add-zsh-hook chpwd _umask_hook
# run _umask_hook once on shell startup
# append _umask_hook to make sure env var change made by direnv is triggered 1st
self_destruct_functions=(${self_destruct_functions[@]} _umask_hook)
The self-destruct function is borrowed from robobenklein/zinc the ZSH prompt.