My Bash $PS1

07 September 2018

I spend a lot of time on the command-line, and every so often I run across a new way to make it show me more useful information. Today’s update: displaying the real time the last command took to run inline in the prompt. I can’t take credit for the idea - I found the approach on this excellent blog post - but I’ve got a few other interesting pieces in my $PS1 so I figure it’s worth sharing briefly. Here’s what my shell environment looks like:

archive layout

All of these functions use shell variables to pass info along to the $PS1. I’ve found that this approach along with $PROMPT_COMMAND leads to the fewest levels of shell quoting & interpolation hell. The only thing to remember is that in your $PS1 you need to escape the $ so that it’s interpolated when the prompt prints rather than when the $PS1 is set. Wrong: PS1=${my_dynamic_var}, Right: PS1=\${my_dynamic_var}.

current git branch

This one is pretty standard, ask git for your current branch. If it’s non-empty wrap and color it for easy visual grepping.

function vc_branch() {
    vc_branch=`git rev-parse --abbrev-ref HEAD 2>/dev/null`
    if [ "$vc_branch" != "" ]; then
        vc_branch="${LPURPLE}[${vc_branch}]${RESTORE} "

last command’s exit status

I’ve had this in my $PS1 for years now - who doesn’t love a smiley face to let you know that your last command didn’t explode?

# sets $cmd_face with a colored emoticon representing the exit status of the last command
function smile() {
    if [ $? -eq 0 ]; then

last command’s time elapsed

This is a little bit more involved - the basic idea here is that we start a timer before running every command with trap and stop it when the prompt refreshes, as the last thing to run in $PROMPT_COMMAND. I’ve updated Jake’s example code to format the timing a little bit more densely - 1m31s instead of 91s and 1hr31m0s instead of 91m0s.

# adapted from
function timer_start() {

# sets $timer_show with the runtime of the last command
# empty if the command ran in < 1s
# units switch at 1m31s and 1hr31m0s
function timer_stop() {
    secs=$(($SECONDS - $timer))
    unset timer

    if [ $secs -lt 1 ]; then
        timer_show=" "
        return 0

    hours=$(($secs / 3600))
    mins=$(($secs / 60))

    if [ $secs -gt 90 ]; then
        secs=$(($secs % 60))

        if [ $mins -gt 90 ]; then
            mins=$(($mins % 60))
    timer_show=" $timer_show "

trap 'timer_start' DEBUG


And finally it’s time to hook the functions up to $PROMPT_COMMAND so that they refresh as you navigate around in your terminal

# order matters here: smile needs to be first to check the last command and timer_stop
# needs to be last in order to time the next command rather than the prompt commands
if [ "$PROMPT_COMMAND" == "" ]; then
    PROMPT_COMMAND="smile; vc_branch; timer_stop"
    PROMPT_COMMAND="smile; vc_branch; $PROMPT_COMMAND; timer_stop"

and to set your $PS1 variable so that the whole show works. One last note: I’m a huge fan of a two-line terminal prompt so that it’s easy to see which directory you’re in and still have plenty of space to type out long commands without hitting line wrapping. Here’s the $PS1 I use on pretty much every machine I log into:

if [ "$PS1" ]; then
    # bash 3.0 and line-wrapping compatible version:
    # gives the following (where smiley indicates last cmd successfully ran)
    # powered by the functions above and $PROMPT_COMMAND
    # [HH:MM AM/PM] user@host: [current branch (if any)] $PWD
    # :-) / 8-( {last command runtime (s)} $

    PS1="${BOLD}[\@]${RESTORE} ${YELLOW}\u${RESTORE}@${GREEN}\h \${vc_branch}${BLUE}\w/${RESTORE} \n \${cmd_face}\${timer_show}\$ "

Finally, for easy copy-pasta, here’s a gist with all of this code in one place. Stick it in your ~/.bash_profile and never worry about running time by hand again!

Downloading Photos of You from Facebook

03 April 2018

Over the next couple weeks I’m going to work on tools to help transition all my data from Facebook to Diaspora*. Part of that is making sure that all the data I want to keep from my Facebook days is safely stored somewhere locally. First task up was to use...

Moving to Diaspora*

30 March 2018

I’m going to be moving my social networking over to Diaspora* over the next few weeks. I encourage you all to join me. Why Diaspora*? It’s open source, free and federated. What ‘federated’ means that there are lots of independent servers running the code, not just one. Think email. You...

2nd Annual Fantasy Football Drafting API

23 July 2014

There are only 43 days until the NFL starts back up again. This means it’s time for all of you data-loving football fans to start getting ready for the second annual Fantasy Football API League. The league is a yahoo-defaults (modified roster) fantasy league with a catch - instead of...

Fantasy Football Drafting API

30 August 2013

I had a lot of fun writing my poker bot, even though the competition only lasted for a single season. Inspired by my coworker’s excellent 2013 NFL Draft site, I decided to make a fantasy football drafting API. While the ultimate goal of having a fully automated fantasy football platform...

Optimizing Python

15 February 2013

Pretty soon after I had my poker bot mostly up and running I realized that nearly the entire runtime was dominated by a single function: finding the hand value (e.g. trip 8’s with a KJ kicker) of a five card hand. The code was being called again and again by...