Gwen Avery Gwen Avery - 6 months ago 8
Bash Question

Creating a Bash command prompt with a red $ after failure of previous command

I'm new to Bash programming, and I'm working on creating a custom Bash command prompt. My goal is to create a prompt which only shows the login name and host name when they differ from what I normally use. I'm also looking to append the current Git branch to the command prompt when in a directory which is under Git version control.

I would like to color the login and host name section green, the directory path blue, the Git branch section pink, and separators (: and $ characters) white. However, when the previously executed command returns anything other than zero, I would like to color the $ separator red. The general format without colors looks like this:

loginname@hostname:~/current/path:branchname$


The only sections that are mandatory are the directory path and the $ character. Here's the code I've written for my .bashrc file:

MYNAME="gwen"
MYHOST="gwen-laptop"

RED="\[\033[31m\]"
WHITE="\[\033[0m\]"
GREEN="\[\033[01;32m\]"
BLUE="\[\033[01;34m\]"
PINK="\[\033[01;35m\]"

DOLLAR="if [ \$? = 0 ]; then echo ${WHITE}\$; else echo ${RED}\$${NORMAL}; fi"
GITBRN='$(__git_ps1 "\033[0m:\033[01;35m%s")'

USERNM="if [ \u != ${MYNAME} ]; then whoami; fi;"
HOSTNM="if [ \h != ${MYHOST} ]; then echo -n @; hostname; fi;"
COLONM="if [ \u != ${MYNAME} ] || [ \h != ${MYHOST} ]; then echo -n :; fi;"

PS1="${GREEN}\`${USERNM}\`\`${HOSTNM}\`${WHITE}\`${COLONM}\`${BLUE}\w${GITBRN}\`${DOLLAR}\` "


This code meets all of my requirements, except that it leaves the $ character white at all times, and does not color it red at the appropriate times. (I suspect the problem is that the "\$?" in DOLLAR references the previously executed command, but DOLLAR is executed last when constructing PS1, so the previously execute statement is no longer the command which was run before PS1 construction began; it's something which was executed in order to create the command prompt.) I'm not sure how to solve this problem.

This code is ugly and needs to be refactored. I was trying to move all the color codes into their own variables, but when I used these color variables in the code for GITBRN, thing went haywire, so I ended up using literal colors there instead.

I've spent an entire day trying to get this code working, and I think I'm going nowhere at this point. Any suggestions for how to get that dollar sign colored red at the appropriate time would be most appreciated. I'm also open to suggestion on refactoring the code to make it cleaner and more readable.

P.S. I'm a Ubuntu Linux (Lucid Lynx) user.

Answer

Use the PROMPT_COMMAND variable, which is executed before each primary prompt according to the bash man page.

For example (this doesn't work yet, I'm trying to get it working right, but I think it's possible):

PROMPT_COMMAND="if [ \$? = 0 ]; then DOLLAR="${WHITE}\$${NORMAL}"; else DOLLAR="${RED}\$${NORMAL}"; fi"

Edit: due to frustrations with executing commands and nonprinting characters inside PS1 (the \[ and \] sometimes get printed out literally instead of used as hints to PS1), I've come up with this (replace ... with whatever you want in your prompt):

PROMPT_COMMAND='if [ $? = 0 ]; then DOLLAR_COLOR="\033[0m"; else DOLLAR_COLOR="\033[31m"; fi'
PS1='...\[$(echo -ne $DOLLAR_COLOR)\]$\[\033[m\] '

Of course, using $() you could put whichever parts of this you like inside PS1 instead of using PROMPT_COMMAND, I just like it this way so that PROMPT_COMMAND contains the logic and PS1 contains the display commands.