Bash $PROMPT_COMMAND messing up scroll history display

bashprompt

I set up a PROMPT_COMMAND to display the current branch while I'm in a git repo.

The relevant part of my .bashrc is:

export PS1=" \[\033[34m\]\$\[\033[0m\] "
export PROMPT_COMMAND="$HOME/bin/myprompt"

where ~/bin/myprompt is:

#!/usr/bin/env ruby
green = "\033[32m"
bold = "\033[1m"
underline = "\033[4m"
reset = "\033[0m"

home_regex = Regexp.new "^#{ENV["HOME"]}"
Dir.chdir ENV["PWD"]

out = 
  underline + bold + green +
  `pwd`.gsub(home_regex, "~").chomp +
  reset

if `git log 2>&1 | grep ^fatal`.chomp == ""
  out += "#{underline}#{green} (#{`git branch | grep ^\* | sed s/^..//g`.chomp})#{reset}"
end

print out

It works fine for the most part, the only problem is that when I use the arrow keys to scroll through command history, sometimes the command gets written over my prompt. Suppose the following session:

~ $ pwd
/Users/Adrian
~ $ cd sibilant
~/sibilant (master) $ pwd
/Users/Adrian/sibilant
~/sibilant (master) $

Then I press the up arrow to scroll through command history. each line shows the result of one press of the arrow:

~/sibilant (master) $ pwd
~/sibilant (master) $ cd sibilant
~/spwd

as you can see, the last time I pressed the arrow the command was written over my prompt.

Has anyone experienced this and/or know how to fix it?

Best Answer

Instead of putting it in PROMPT_COMMAND, put it in PS1:

PS1='$($HOME/bin/myprompt) \[\033[34m\]\$\[\033[0m\] '

When it's in PS1, bash counts the number of characters that get printed so that it can correctly redraw when you scroll through your history. That's why bash has the \[ and \] special characters—they tell bash that the enclosed characters are not printable, which helps bash figure out how to redraw the prompt when necessary.

Note that the command substitution is inside the single quotes. This prevents expansion at assignment time. If the command substitution was in double quotes or unquoted, the output of myprompt would be captured once at assignment time and remain static. In order to support variable values that change over time, the shell expands the value of PS1 each time the prompt is displayed. This means that expansion should be prevented at assignment time by quoting. It also means that you need to be careful about speed: if the command substitution takes 1 second to complete, then your prompt will take 1 second to display.

Related Question