BASH supports a $PROMPT_COMMAND environment variable that defines a command to be executed before any first-level interactive prompt. I'm looking for a ZSH equilvalent of that.

The documentation says that there's a function precmd I can define to achive that; however, I have no idea how to define it from an environment variable.

I've considered passing an environment variable that would make ZSH read a file containing the definition of that function, but ZSH doesn't seem to support such things: it only reads global files and then per-user files. I can replace them but I cannot add to them without modifying the files, which I cannot do.

So how do I define a pre-prompt hook in ZSH via an environment variable, like I'd do using $PROMPT_COMMAND in BASH?

  • Truth to be told, I need a post-interactive-command-execution hook, but neither shell provides one so I have to resort to pre-prompt hooks - they seem to be as close as I can get. – Shnatsel Mar 31 '14 at 2:13
  • 1
    Hm, I am wondering, what the difference between post-interactive-command-execution and pre-prompt is. Apart from a conceptual difference, where do you observe actually a difference. (Let's omit the commands exit and exec, ok ;) ) – mpy Mar 31 '14 at 17:21
  • @mpy there's a difference when running a background job, because background jobs are independent from the prompt sequence. – Shnatsel Mar 31 '14 at 19:52
  • 1
    Ok, I got that point. So, how about something like that: start() { eval "$@"; echo post-command-code } and then use a zle-binding to execute the command line with start prepended? – mpy Mar 31 '14 at 20:14
  • 1
    The DEBUG trap is a nice find, but you still have the problem how to define it. I've extended my answer once more, but I leave it to you to write your own answer regarding the DEBUG trap solution. :) – mpy Apr 3 '14 at 17:23
up vote 17 down vote accepted

The simplest approach to emulate bash's $PROMPT_COMMAND which comes to my mind is to use the precmd hook, as you already figured out. Define it as

precmd() { eval "$PROMPT_COMMAND" }

and you can do something like that:

$ PROMPT_COMMAND='echo Hello, it is now $(date)'
Hello, it is now Mon, Mar 31, 2014 7:08:00 PM
$ whoami      
user
Hello, it is now Mon, Mar 31, 2014 7:08:21 PM     
$

Please note the single quotes in that example, otherwise $(date) will get expanded too early, i.e. already when defining $PROMPT_COMMAND and not when called before the prompt.


If you want to preserve (and don't want to alter) the existing definition, you can use that approach:

$ prmptcmd() { eval "$PROMPT_COMMAND" }
$ precmd_functions=(prmptcmd)

With that the prmptcmd functions is executed after the existing precmd() function.


Finally, here is a way which is suitable for use in a program package, which neither should modify user or system files nor can enter the commands interactive.

An example to spawn a bash session could be

PROMPT_COMMAND="echo foo" bash

To spawn zsh you can use

ZDOTDIR=/program/dir zsh

which causes /program/dir/.zshrc to be sourced. In this file the precmd() hook can be defined as explained above. If you want the user's settings in addition include source $HOME/.zshrc etc. in the program's .zshrc, too. This setup is maintainable, as no files outside the program directory are modified.


As a last addition, here is a proof of concept how to keep the newuser welcome, too. Use the following code in your /program/dir/.zshenv rc config file:

echo define precmd, traps, etc.

autoload -Uz zsh-newuser-install

if [[ ! -e "$HOME/.zshrc" ]]; then
  zsh-newuser-install -f
  mv $ZDOTDIR/.zshrc $HOME/.zshrc
else
  builtin source $HOME/.zshrc
fi
  • That much I figured. The problem is - how do I define the precmd hook via an environment variable? Is there a mechanism for adding hooks or code without modifying files? Or how do I do it at least without writing to the global and user-global ".zprofile" and similar files? Like, can I add my own .zprofile that won't replace existing ones? – Shnatsel Mar 31 '14 at 19:56
  • 1
    Also your use of precmd hook here would replace any alread existing precmd hooks; zsh docs mention I can make an array of functions that will coexist but I have no idea how to do that. – Shnatsel Mar 31 '14 at 19:58
  • 1
    (1) What do you mean with how do I define the precmd hook via an environment variable? The example I presented works IMHO like bash mechanism. (2) You can add the hook via command-line, but then it's not permanent. What's the problem with the modification of your .zshrc? (3) An example: foo() { echo foo }; bar() { echo bar }; precmd_functions=(foo bar) This executes foo() and bar() in addition to precmd(). – mpy Mar 31 '14 at 20:20
  • 2
    Ok, that clarifies a lot -- a minimal example for bash would then be PROMPT_COMMAND="echo foo" bash, right? Is this a possibility for spawning zsh: ZDOTDIR=/program/dir zsh. Then /program/dir/.zshrc is sourced upon start where you can define the precmd() hook. If you want the user's in addition include source $HOME/.zshrc etc. in the program's zshrc. This should be easy to maintain, as no files outside the program dir are modified. – mpy Apr 1 '14 at 21:27
  • 1
    @Shnatsel : I extended my answer. Perhaps you can also edit your question to include the additional info from your comments. – mpy Apr 2 '14 at 18:40

As @mypy states, Zsh's precmd works similarly to Bash's PROMPT_COMMAND.

Here's an example that works for Bash or Zsh and doesn't use eval:

## ~/myprompt.sh

# 'ZSH_VERSION' only defined in Zsh
# 'precmd' is a special function name known to Zsh

[ ${ZSH_VERSION} ] && precmd() { myprompt; }

# 'BASH_VERSION' only defined in Bash
# 'PROMPT_COMMAND' is a special environment variable name known to Bash

[ ${BASH_VERSION} ] && PROMPT_COMMAND=myprompt

# function called every time shell is about to draw prompt
myprompt() {
  if [ ${ZSH_VERSION} ]; then
    # Zsh prompt expansion syntax
    PS1='%{%F{red}%}%n%{%f%}@%{%F{red}%}%m %{%F{cyan}%}%~ %{%F{white}%}%# %{%f%}'
  elif [ ${BASH_VERSION} ]; then
    # Bash prompt expansion syntax
    PS1='\[\e[31m\]\u\[\e[0m\]@\[\e[31m\]\h \[\e[36m\]\w \[\e[37m\]\$ \[\e[0m\]'
  fi
}

Run from shell init scripts:

## ~/.bashrc
. ~/myprompt.sh

and:

## ~/.zshrc
. ~/myprompt.sh

The prompts here are just examples. One can definitely do much trickier stuff.

For details of setting prompt functions, see: http://zsh.sourceforge.net/Doc/Release/Functions.html#index-precmd and http://www.gnu.org/software/bash/manual/bashref.html#Printing-a-Prompt.

For details of prompt expansions, see http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html and http://www.gnu.org/software/bash/manual/bashref.html#Printing-a-Prompt.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.