user3341592
user3341592

Reputation: 1561

Bash and Zsh prompt that ring a bell and do display error code of last command

I want to have the same prompt in both Bash and Zsh. And I want it to:

In Bash, I do have:

BLK="\[$(tput setaf 0; tput bold)\]"
RED="\[$(tput setaf 1; tput bold)\]"
grn="\[$(tput setaf 2)\]"
GRN="\[$(tput setaf 2; tput bold)\]"
yel="\[$(tput setaf 3)\]"
reset_color="\[$(tput sgr0)\]"

PS1='\n\
`if [[ $? -gt 0 ]]; then printf "\[\033[01;31m\]$?"; tput bel; else printf "\[\033[01;32m\]0"; fi`\
\[\033]0;$PWD\007\] \
\[\033[0;32m\]\u@\h\
\[\033[01;30m\]:\
\[\033[;;33m\]\w\
\[\033[36m\]`__git_ps1`\
\[\033[0m\]\n$ '

In Zsh, that's my config:

BLK=$(tput setaf 0; tput bold)
RED=$(tput setaf 1; tput bold)
grn=$(tput setaf 2)
GRN=$(tput setaf 2; tput bold)
yel=$(tput setaf 3)
reset_color=$(tput sgr0)

PROMPT="
%(?.$GRN.$RED)%?$reset_color $grn%n@%m$BLK:$reset_color$yel%~ $reset_color
%(!.#.$) "

And this is how it looks like in the terminal:

enter image description here

Both prompts do ring the bell when there is an error with the last command. But, in Bash, it prints 0 instead of the right return code of the command that failed.

How to fix that?

PS- Any better way to improve the above code is welcomed!

Upvotes: 1

Views: 1339

Answers (2)

chepner
chepner

Reputation: 532398

The command to test $? itself resets $? to the result of the test. You need to save the value you want to display first.

PS1='\n\
$(st=$?; if [[ $st -gt 0 ]]; then printf "\[\033[01;31m\]$st"; tput bel; else printf "\[\033[01;32m\]0"; fi)\
\[\033]0;$PWD\007\] \
\[\033[0;32m\]\u@\h\
\[\033[01;30m\]:\
\[\033[;;33m\]\w\
\[\033[36m\]`__git_ps1`\
\[\033[0m\]\n$ '

I would recommend building up the value of PS1 using PROMPT_COMMAND, instead of embedding executable code. This gives you more flexibility for commenting and separating any computations you need from the actual formatting. make_prompt doesn't need quite so many lines, but it's just a demonstration.

set_title () {
    printf '\033]0;%s%s' "$1" "$(tput bel)"
}

make_prompt () {
  local st=$?
  local c bell
  bell=$(tput bel)

  # Green for success, red and a bell for failure
  if [[ $st -gt 0 ]]; then
    c=31
  else
    c=32 bell=
  fi
 
  win_title=$(set_title "$PWD")
  git_status=$(__git_ps1)

  PS1="\n"
  PS1+="\[\e[01;${c}m$bell\]"  # exit status color and bell
  PS1+=$st
  PS1+="\[$win_title\]"  # Set the title of the window
  PS1+="\[\e[0;32m\]"    # Color for user and host
  PS1+="\u@\h"
  PS1+="\[\e[01;30m\]"   # Color for : separator
  PS1+=":"
  PS1+="\[\e[;;33m\]"    # Color for directory
  PS1+="\w"
  PS1+="\[\e[36m\]"      # Color for git branch
  PS1+=$git_status
  PS1+="\[\e[0m\]"       # Reset to terminal defaults
  PS1+="\n$ "
}

PROMPT_COMMAND=make_prompt

zsh already has terminal-agnostic escape sequences for adding color.

PROMPT="%B%(?.%F{green}.%F{red}$(tput bel))%?%f%b %F{green}%n@%m%F{black}%B:%b%F{yellow}%~ %f%(!.#.$) "

Upvotes: 3

tripleee
tripleee

Reputation: 189936

Any external command (such as printf or tput) resets the value of $?. You need to capture it in a variable before that happens.

rc=$?
if [ $rc -gt 0 ]; then
  printf "\[\033[01;31m\]$rc"
  tput bel
else
  printf "\[\033[01;32m\]0"
fi

(Unwrapped for legibility; this will replace the code inside the backticks.)

Notice that this will still overwrite the value of $?; perhaps add exit $? at the end to properly preserve the value.

Upvotes: 1

Related Questions