PS1 for Bash prompt showing last exit status, written mostly in Python I had most of this functionality accomplished in BASH, but I was having trouble getting the last process's return value (even though I had a minimal working example). Plus, the escape codes made everything unreadable and I wasn't sure how to factor them out appropriately. 

Since I know Python pretty well, I decided to rewrite it in Python, since I have now figured out enough shell that I felt confident embedding some Python into it.

The __future__ import should allow this to work in Python 2 or 3. 

I do not know if this is cross-compatible with other shells, or if this is only likely to work in BASH, but another driver that pushed me in this direction was that desire. I am aware of the PROMPT_COMMAND for BASH. 

Perhaps I could use a  similar technique along with a BASH function to keep it all in shell and avoid the Python process altogether. However, I find this to be incredibly easy to read, work on, and modify without fear of breaking it - especially relative to shell scripting.

Another perhaps: maybe these escape codes are already available in the Python standard library. I looked at ncurses - I don't think that's the one I want to focus on.

I'm not sure if I factored the escape codes properly - perhaps I should have the constants point to the formatted escapes instead. But theoretically I could embed multiple codes into a single escape, instead of doing it multiple times for every tweak. Maybe there's a drawback to doing that my way, too.

PS1="\$(export LAST_RETURN_VALUE="\$?" WHOAMI="\$\(whoami\)" HOSTNAME ; python -c \"
from __future__ import print_function
import os 
import datetime

ESC = '\033[{0}m'
esc = ESC.format

OFF, BOLD, UNDERSCORE, BLINK = 0, 1, 4, 5
BLACK, BLACKBG = 30, 40
RED, REDBG = 31, 41
GREEN, GREENBG = 32, 42
YELLOW, YELLOWBG = 33, 43
BLUE, BLUEBG = 34, 44
MAGENTA, MAGENTABG = 35, 45
CYAN, CYANBG = 36, 46
WHITE, WHITEBG = 37, 47

LAST_RET = os.environ['LAST_RETURN_VALUE']

print(esc(YELLOW) + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
      esc(GREEN) + os.environ['WHOAMI'] + esc(RED) + '@' +
      esc(CYAN) + os.environ['HOSTNAME'],
      esc(BLUE) + esc(BOLD) + os.getcwd() +
      esc(OFF))
if LAST_RET != '0':
    print(esc(MAGENTABG) + LAST_RET + esc(OFF), end='')
print('$ ', end='')
\" "


I'm getting impatient and want to go ahead and post this. I usually do more due diligence on code I write, but I'm up way past my bedtime already, and I think this is too clever not to share. 

Here's the result:
The typo

If we look at the beginning and the of the posted code:

PS1="\$(export LAST_RETURN_VALUE="\$?" WHOAMI="\$\(whoami\)" HOSTNAME ; python -c \"
from __future__ import print_function
... more python code
\" "


The end part is incorrect.
The \" closes the python -c \",
and the final " closes the PS1=",
but a ) is missing to close the \$(export.
(Pro tip: if you edit the script in vim, and press % on an opening (, it shows where's the closer, or emits an error beep if missing.)

I guess the last line was meant to be:

\")"


An easier way to escape stuff

Escaping things correctly within "..." can be quite painful in very long strings as in this example.
In this particular example there's easy alternative using literal here documents:

PS1=$(cat &lt;&lt; "EOF"
...
EOF
)


All the code you previously had within the "..." could go there in place of the ..., and it will be taken literally as if enclosed within single-quotes, but no need to escape ", or ' or $.

PS1=$(cat &lt;&lt; "EOF"
$(export LAST_RETURN_VALUE="$?" WHOAMI="$(whoami)" HOSTNAME ; python -c "
from __future__ import print_function
import os 
import datetime

ESC = '\033[{0}m'
esc = ESC.format

OFF, BOLD, UNDERSCORE, BLINK = 0, 1, 4, 5
BLACK, BLACKBG = 30, 40
RED, REDBG = 31, 41
GREEN, GREENBG = 32, 42
YELLOW, YELLOWBG = 33, 43
BLUE, BLUEBG = 34, 44
MAGENTA, MAGENTABG = 35, 45
CYAN, CYANBG = 36, 46
WHITE, WHITEBG = 37, 47

LAST_RET = os.environ['LAST_RETURN_VALUE']

print(esc(YELLOW) + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
      esc(GREEN) + os.environ['WHOAMI'] + esc(RED) + '@' +
      esc(CYAN) + os.environ['HOSTNAME'],
      esc(BLUE) + esc(BOLD) + os.getcwd())
if LAST_RET != '0':
    print(esc(OFF) + esc(MAGENTABG) + LAST_RET + esc(OFF), end='')
print(esc(OFF) + '$ ', end='')
"
)
EOF
)


Safety

If PROMPT_COMMAND is set, PS1 will be ignored.
To avoid confusion, I would add unset PROMPT_COMMAND.

What's wrong with doing this in Python?

It's an additional dependency to worry about.
If your friend asks you how to do that cool prompt,
it gets less cool when you explain how he needs Python to get it.
And running a Python script for every single command is a bit of a waste.

Why not do it in Bash?

The script basically sets variables, formats the date and prints stuff.
That's easy enough to do in Bash.
The script gets all its critical elements from Bash.
It uses the Bash variable $? to get the exit code of the last command,
the whoami command to get the current user,
and the HOSTNAME variable that's already defined in the environment.

In other words,
you already know everything it takes to make this work in Bash,
and Python doesn't bring real benefits,
the only thing left to do is to translate it to Bash.

This is pretty much a word-for-word translation of Python to Bash,
with some minor improvements, such as \u instead of calling whoami,
and unused variables dropped.
I don't see the readability suffering significantly compared to the Python solution.

PS1=$(cat &lt;&lt; "EOF"
$(
LAST_RET=$?
WHOAMI='\u'
HOSTNAME='\h'
DATE=$(date +'%Y-%m-%d %H:%M:%S')

OFF='\033[0m'
BOLD='\033[1m'
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BLUE='\033[34m'
MAGENTABG='\033[45m'
CYAN='\033[36m'

echo "$YELLOW$DATE $GREEN$WHOAMI$RED@$CYAN$HOSTNAME $BLUE$BOLD$PWD"
test $LAST_RET = 0 || printf "$OFF$MAGENTABG$LAST_RET$OFF"
printf "$OFF\$ "
)
EOF
)