How to format the path in a zsh prompt

zsh

I would love to have a readable colored path. For example, instead of simply using %~ to return ~/path/to/foo I would like to format it to ~$RED/$NOCOLORpath$RED/$NOCOLORto$RED/$NOCOLORfoo to highlight the path separators.

Can I define the content of PROMPT so that the path expression gets re-evaluated on each display? Something like ${${(%):-%~}//\//_some_format_expression/} obviously does not work.

Or should I hack this further, and force the PROMPT value to get reset each time we're changing a directory?

Any solution achieving the path formatting goal would be welcome.

Thanks 🙂

Best Answer

zsh

Try this:

setopt PROMPT_SUBST
PROMPT='%{$(pwd|grep --color=always /)%${#PWD}G%} %(!.%F{red}.%F{cyan})%n%f@%F{yellow}%m%f%(!.%F{red}.)%#%f '

Here's a breakdown of the prompt:

  • PROMPT_SUBST turns on command substitution in the prompt (and parameter expansion and arithmetic expansion)
  • %{...%} - an escape sequence
  • $(pwd|grep --color=always /) - print the current directory and highlight the / - the color will depend on the $GREP_COLORS environment variable (or its default value) - bold red is the default
  • %${#PWD}G - use the length in characters of the name of the current directory as a glitch value. This makes the shell consider this the length of the preceding character sequence ( after the "%{" ) instead of the actual length of the string which includes ANSI escape sequences. This keeps the shell from being confused about the position of the cursor relative to the end of the prompt.
    - - - - - - - this is the end of the part that answers your question - - - - - - -
  • %(!.%F{red}.%F{cyan}) - if this is a privileged shell (root) set the foreground color to red, otherwise cyan
  • %n - output the username
  • %f - reset the foreground color to the default
  • @ - a literal at sign
  • %F{yellow} - make the foreground color yellow
  • %m - output the hostname
  • %f - reset the foreground color to the default
  • %(!.%F{red}.) - if this is a privileged shell (root) set the foreground color to red
  • %# - output a # for a privileged shell or % for an unprivileged one
  • %f - reset the foreground color to the default

I put the path first in this prompt for emphasis, since the question concerns the path.

alt text

Here is a version for zsh that changes the slash color depending on whether you are root (privileged) by manipulating the $GREP_COLORS variable:

setopt PROMPT_SUBST
PROMPT='%{$(pwd|([[ $EUID == 0 ]] && GREP_COLORS="mt=01;31" grep --color=always /|| GREP_COLORS="mt=01;34" grep --color=always /))%${#PWD}G%} %(!.%F{red}.%F{cyan})%n%f@%F{yellow}%m%f%(!.%F{red}.)%#%f '

Bash

You can do a similar prompt in Bash. In this example, I've put the user and hostname first and the color of the slashes also changes when the UID is 0. Warning: this overwrites Bash's $PS1 prompt variable. That shouldn't be a problem unless you're doing something special or you expect the behavior to change when you set that variable directly and this is in effect. Also, this uses a variable called "usercolor" which may collide with something else, although this whole thing could be put in a function and the variable declared local.

PROMPT_COMMAND='usercolor="\[\033[0;36m\]";[[ $EUID == 0 ]] && usercolor="\[\033[1;31m\]";PS1="$(pwd)";PS1="$usercolor\u\[\033[0m\]@\[\033[0;33m\]\h\[\033[0m\]:${PS1//\//$usercolor/\[\033[0m\]}$usercolor\\$\[\033[0m\] "'

alt text

I took advantage of the fact that Bash doesn't have zsh's "glitch" feature to use parameter expansion substitution to conditionally color the slashes (instead of using grep).

Related Question