# History configuration # c.f. http://www.catonmat.net/blog/the-definitive-guide-to-bash-command-line-history/ shopt -s histappend shopt -s histreedit # Set the title of a Terminal window function history_settitle() { if [ -n "$STY" ] ; then # We are in a screen session echo "Setting screen titles to $@" printf "\033k%s\033\\" "$@" screen -X eval "at \\# title $@" "shelltitle $@" else printf "\033]0;%s\007" "$@" fi } mkdir -p $HOME/.history mkdir -p $HOME/.terminal TTY=$(tty | tr / _) HOSTNAME=$(hostname -s) # If $HISTFILE is in the home directory, then make a new history file unique to this tty if [ $(dirname "$HISTFILE") == "$HOME" ] then export HISTFILE="$HOME/.history/bash_${HOSTNAME}${TTY}" if [ -f "$HISTFILE" ] then history -r fi fi history_settitle "${HOSTNAME}${TTY}" # It takes a bit before wmctrl will see the result of the settitle; # once we have the window title, write the window id into the # corresponding .terminal file. sleep 0.1 w=$(wmctrl -l | grep "${HOSTNAME}${TTY}" | sed -e 's/ .*//') echo $w > $HOME/.terminal/"${HOSTNAME}${TTY}" export HISTIGNORE="&:[ ]*:ls:hist.*:exit" export PROMPT_COMMAND="$PROMPT_COMMAND"'; HISTTIMEFORMAT="[%m/%d@%H:%M:%S] " history -a' # Print command history including execution times, for this run of 'history' only alias histt='HISTTIMEFORMAT="[%m/%d@%H:%M:%S] " history' # Make a note in command history # Usage: # $ note COMMAND // comment # command output # $ recall comment # command output # The 'note' command embeds the output of the supplied command # into the bash history file. This command output may be printed # out again verbatim later with the 'recall' command. function note { t=$(date "+%s") p=(); c=false for a in "$@"; do if $c || [ "x$a" == "x//" ] then c=true else p[${#p[@]}]="$a"; fi done echo "#$t >>" $* >> $HISTFILE COLUMNS=$((COLUMNS-5-${#t})) "${p[@]}" | sed -e "s/^/#$t :: /" | tee -a $HISTFILE | sed -e 's/^#[^:]*:: //' } # Begin a new task: switch to a new command history # file, and make a note in it. We'll copy existing # history over, just in case we did some stuff relevant # to the current task before declaring it. # Usage: # $ task Install whizzy-fu pro # begin task: Install whizzy-fu pro # $ sudu apt-get install whizzyfu libwhizzyfu-dev # $ ... lots of other stuff # $ finished # finished task: Install whizzy-fu pro # # ... a few days later, in a different terminal: # $ recall whizzy # # Fri Oct 12 11:06:43 PDT 2012 # recalled task: Install whizzy-fu pro # $ ^Rapt-get # (reverse-i-search)`sudo apt-get': sudo apt-get install whizzyfu libwhizzyfu-dev function task { t=$(date "+%s") task=$(echo $* | tr A-Z a-z | sed -e 's/ /_/g' -e 's/[^a-z0-9_]//g') taskfile="$HOME/.history/task_$task" history -a histpush "$taskfile" note echo "begin task: $*" // $HISTFILE echo "HISTFILE is now $HISTFILE" } # Complete a task and go back to previous HISTFILE. Optional. function finished { l=$(grep "^#[0-9]\+ :: begin task:" $HISTFILE | tail -n 1) if [ -z "$l" ] then echo "finished: no task in progress" else m=$(echo $l | sed -e 's/^#[^>]*:: begin/finished/') echo $m histpop fi } # Show all active nested tasks in this terminal function tasks { for f in $HISTFILE $(echo $HISTFILES | tr : ' ') ; do if [ ${f:0:1} != '/' ] then f="$HOME/.history/$f" fi l=$(grep -Hm 1 "^#[0-9]\+ >>.*$*" $f) t=$(echo $l | sed -e 's/^[^#]*#\([0-9]\+\).*/\1/') c=$(echo $l | sed -e 's/^[^>]*>> *//') d=$(date --date="@$t") n=${c#*:} sn=${n%% //*} echo "$(basename $f):$sn // $d" done } # grep all history files for a command pattern function hgrep { ( cd $HOME/.history grep $* $(ls -rt | grep -v $(basename $HISTFILE)) $(basename $HISTFILE) | grep -v '^[^:]*:#' ) } # Focus on the terminal specified by the user. function terminal() { HOSTNAME=$(hostname -s) p="${HOSTNAME}"'_.*_'"$1" t=$(ls "$HOME/.terminal" | grep "$p") if [ -f "$HOME/.terminal/$t" ] ; then w=$(cat "$HOME/.terminal/$t") else w=$(wmctrl -l | grep "$p"'$' | sed -e 's/ .*//') fi if [ -n "$w" ] ; then wmctrl -i -a "$w" else echo "Cound not find terminal $1; perhaps it is no longer open." fi } # Show the output of a command passed to 'note', or switch back to a # task (and corresponding history file) from the past. function recall { l=$(cd $HOME/.history && grep "^#[0-9]\+ >>.*$*" $HISTFILE $(ls -t) | tail -n 1) if [ -z "$l" ] then echo "recall: cannot find $*: No such note or task" else f=$(echo $l | sed -e 's/^\([^:]*\):#.*/\1/') if [ ${f:0:1} != '/' ] then f="$HOME/.history/$f" fi t=$(echo $l | sed -e 's/^[^#]*#\([0-9]\+\).*/\1/') c=$(echo $l | sed -e 's/^[^>]*>> *//') d=$(date --date="@$t") if [ "${c%%:*}" == "echo begin task" ] then n=${c#*:} if [ "$HISTFILE" == "$f" ] then echo "# $d" echo "${c#* }" else echo "# $d" echo "resume task: $n" histpush "$f" echo "HISTFILE is now $HISTFILE" fi else if [ "$HISTFILE" != "$f" ] then echo "recall: found note in $f" fi if [ "${c%% *}" == "echo" ] then echo "# $d" echo "${c#* }" else echo "# $d" echo "\$ $c" grep "#$t :: " $HISTFILE | sed -e 's/^#[0-9]* :: //' fi fi fi } # Start using a new history file. Keep a stack of previous # history files so they may be returned to at a later time. function histpush { if [ -n "$1" ] then if [ -n "$HISTFILES" ] then export HISTFILES="$HISTFILE" else export HISTFILES="$HISTFILES:$HISTFILE" fi export HISTFILE="$1" history -r fi } # Resume using a history file that was in use in the past. function histpop { if [ -n "$HISTFILES" ] then export HISTFILE="${HISTFILES%%:*}" history -r if [ "${HISTFILES/:/}" == "$HISTFILES" ] then export HISTFILES= else export HISTFILES="${HISTFILES#*:}" fi else echo "histpop: HISTFILES is empty" fi echo "HISTFILE restored to $HISTFILE" }