Bash prompt that won’t clobber long commands

bashprompt

I'm trying to set up a bash prompt for - hostname(screen#):directory$ which is coloured green if the last command completed successfully, red if not. This is what I have so far, which actually does the job but seems to cause display problems if the command wraps a line:

 PS1="\[\`if [[ \$? = "0" ]]; then echo '\e[32m'; else echo '\e[31m' ; fi\` - \h(${WINDOW}):\W$\e[00m "

Googling the issue I found this helpful SO post with a comment that mentions wrapping nonprinting characters in \\[ and \\] to avoid this issue. Therefore I tried the following, but it did not solve the issue, and furthermore breaks the colour change:

PS1="\[\`if [[ \$? = "0" ]]; then echo '\e[32m'; else echo '\e[31m' ; fi\`\] - \h(${WINDOW}):\W$\[\e[00m\] "

How can I keep the structure of this prompt, with colours, but fix it so that long commands are displayed properly?

Best Answer

I have a fancy prompt with colors, and now bash doesn't seem to know how wide my terminal is. Lines wrap around incorrectly.


I have another proper way to do this, put this code in your ~/.bashrc or create a new file and source file :

PROMPT_COMMAND=$(
    cat<<-'EOF'

    retval=$?

    RED=$(tput setaf 1)
    GREEN=$(tput setaf 2)
    STOP=$(tput sgr0)

    # arithmetic using bash parameter expansion on a array
    if (($retval + ${PIPESTATUS[@]/%/+} + 0)); then
        PS1="\[$RED\]\u@\h:\w$ \[$STOP\]"
    else
        PS1="\[$GREEN\]\u@\h:\w$ \[$STOP\]"
    fi
EOF
)

That will do the trick =)

Bash will run the code inside PROMPT_COMMAND for each commands.

If you have copy/paste problem, you can download the script

tput

EXPLANATIONS

  • (( )) is arithmetic in bash, see http://wiki.bash-hackers.org/syntax/arith_expr
  • PROMPT_COMMAND : if set, the value is executed as a command prior to issuing each primary prompt. See man bash | less +/PROMPT_COMMAND
  • tput is better than hard coding ANSI escape codes. See http://wiki.bash-hackers.org/scripting/terminalcodes
  • PIPESTATUS : An array variable containing a list of exit status values from the processes in the most-recently-executed foreground pipeline (which may contain only a single command). See man bash | less +/PIPESTATUS
  • cat<<-'EOF' is a special here doc : the - character means I can indent code, and the single quotes on 'EOF' means to not interpolate variables
Related Question