What's the ZSH equivalent of BASH's $PROMPT_COMMAND?
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?
Solution 1:
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
Solution 2:
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.