Hacking at Relevance: Agile Development, Consulting and Training

Monday, June 16

bash: don't make me think

Last week I figured out a way to make my life a little bit easier by abstracting the scm I was using and having the prompt indicate whether I was in a Subversion or Git. Thanks to Mike Hommey whose script I tweaked for my needs.

For a long time, I've wanted my iTerm tab title to be more useful. I started by having it display the last process I executed in that tab (this also shows the full path in iTerm's window title bar):


PS1='\[\e]2;\h::\]${PWD/$HOME/~}\[\a\]\[\e]1;\]$(history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g")\a\]\$ '

Next up I wanted to show the currently running command. Google showed me how:


trap 'echo -e "\e]1;$BASH_COMMAND\007\c"' DEBUG

Instead of trying to explain this code I'll quote the Bash man page:

BASH_COMMAND
The command currently being executed or about to be executed, unless the shell is executing a command as the result of a trap, in which case it is the command executing at the time of the trap.

Some more tweaks followed:

  • Distinguish between a previously executed command and a currently executing command by decorating them (I chose surrounding the former with braces and the latter with >/<. e.g. [ls] and >vi<).
  • Show the context of the executing command in the tab. This is just the repository where I executed the command.
  • Make this coexist with TextMate.

And a screenshot to summarize:

The code is in github/relevance/etc/tree/master/bash/bash_vcs.sh.

Install instructions:


curl -L http://github.com/relevance/etc/tree/master%2Fbash%2Fbash_vcs.sh?raw=true > ~/.bash_dont_think.sh
echo "source ~/.bash_dont_think.sh" >> ~/.bash_profile

Enjoy! Tested with iTerm on Leopard.

5 comments:

Philipp Meier said...

Thanks for the nice prompt script. I, personally, like color and adjusted it. Hail to the bash.

nikolaj said...

This is a very useful post.
But as I use bazaar I added a new function:

bzr_dir() {
  base_dir=$(bzr root 2>/dev/null) || return 1
  if [ -n "$base_dir" ]; then
    base_dir=`cd $base_dir; pwd`
  else
    base_dir=$PWD
  fi
  sub_dir="/$(sub_dir "${base_dir}")"
  ref=$(bzr revno 2>/dev/null)
  vcs="bzr"
  alias pull="bzr pull"
  alias commit="bzr commit"
  alias push="bzr push"
  alias revert="bzr revert"

}

ottercat said...

I found that bash choked some on your PS1 -> it wrapped far earlier than it should have. I believe it is do to an escaping issue; things were being "printed" which went to the title bar, but bash thought was on the line so it wrapped prematurely. This I was able to get to work:

PS1='\[\e]2;\h::${PWD/$HOME/~}\a\e]1;$WORKING_ON[$(history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g")]\a\]\u:$__vcs_prefix\[${_bold}\]${__vcs_base_dir}\[${_normal}\]${__vcs_ref}\[${_bold}\]${__vcs_sub_dir}\[${_normal}\]\$ '

Andreas said...

This is great, thanks.

To make it work on ubuntu, I had to change the parameters to stat from -f to --printf="%n". Different versions of stat I guess.

Simon Harris said...

Love it! Only thing I had to do was use -L with curl to get it to follow redirects. Without it my .bash_dont_think.sh file ended up as html telling me that the "page has moved" :)