how to format the path in a zsh prompt?

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).


After some going-around, I can provide a solution overriding chpwd:

doprompt() {
  local my_path=${(%):-%~}
  PROMPT="${yourstuff}${my_path//\//${PR_BOLD_RED}/${reset_color}}${otherstuff}"
}
doprompt

chpwd() {
  doprompt
  # unrelated: set window title
  [[ -t 1 ]] || return;
  print -Pn "\e]2;%n@%m: %~\a";
}
  • Is there is a way to improve this code to get rid of the temporary my_path var? I can't directly replace the / inside %~ ...
  • Any solution using a dynamic syntax to avoid calling on each directory change doprompt is probably cleaner.

A pure zsh solution:

PROMPT='%n@%m: %{$PR_BOLD_RED%}${${(%):-%~}//\//${PR_BOLD_RED}/%f}%f '
  • ${(%):-%~} is the current path.
  • ${xxxxx//\//${PR_BOLD_RED}/%f} replaces every / in xxxxx by a bold red colored one
  • and of course PROMPT_SUBST has to be on.

I had be using double quotes in my tests, which does not allow substitution. Everything works fine with single quotes.