Skip to content

Instantly share code, notes, and snippets.

@laggardkernel
Last active September 17, 2020 11:50
Show Gist options
  • Save laggardkernel/b2cbc937aa1149530a4886c8bcc7cf7c to your computer and use it in GitHub Desktop.
Save laggardkernel/b2cbc937aa1149530a4886c8bcc7cf7c to your computer and use it in GitHub Desktop.
Trigger chpwd Hook on Startup #zsh #hook #direnv

By default, ZSH hook chpwd is not triggered on shell startup. The tutorial here provides some ideas to fix this.

Trigger all chpwd_functions on startup

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.

Trigger specific chpwd_functions on 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)

Credit

The self-destruct function is borrowed from robobenklein/zinc the ZSH prompt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment