Saturday, March 26, 2011

My Bash Profile - Part V: Prompt

This is what my prompt looks like:
Your Bash prompt is stored in the PS1 variable. In my prompt, I display the following items, colour coded where appropriate:
  • time (\t)
  • user (\u) - red if a production user, green otherwise
  • host (\H) - red if a production machine, green otherwise
  • working directory (_get_path) - trimmed to 80 characters
  • number of jobs currently running in the shell (\j)
  • history number of this command (\!)
  • exit status of the previous command (_get_exit_status) - green if success, red otherwise
I also have a useful function called title which allows me change my xterm's titlebar so that I can differentiate it from other xterm's.

Update: My dotfiles are now in Git. For the latest version, please visit my GitHub dotfiles repository.

Here is my prompt taken from ~/.bash_prompt.


# define some colours
GREY=$'\033[1;30m'
RED=$'\033[1;31m'
GREEN=$'\033[1;32m'
YELLOW=$'\033[1;33m'
BLUE=$'\033[1;34m'
MAGENTA=$'\033[1;35m'
CYAN=$'\033[1;36m'
WHITE=$'\033[1;37m'
NONE=$'\033[m'

# trims long paths down to 80 chars
_get_path(){
  local x=$(pwd | sed -e "s:$HOME:~:")
  local len=${#x}
  local max=80
  if [ $len -gt $max ]
  then
      echo ...${x:((len-max+3))}
  else
      echo ${x}
  fi
}

# prints a colour coded exit status
_get_exit_status(){
   local es=$?
   if [ $es -eq 0 ]
   then
       echo -e "${GREEN}${es}"
   else
       echo -e "${RED}${es}"
   fi
}

# change xterm title
title() {
   if [ $# -eq 0 ]
   then
      title=""
   else
      title="$* - "
   fi
}

# colour the host red if it is production
# all prod hostnames end with "prod"
if [[ $HOSTNAME =~ prod$ ]]
then
    HOST_COLOR=$RED
else
    HOST_COLOR=$GREEN
fi

# colour the user red if it is production
# all prod usernames end with "prod"
if [[ $USER =~ prod$ ]]
then
    USER_COLOR=$RED
else
    USER_COLOR=$GREEN
fi

#executed just before prompt
PROMPT_COMMAND='exitStatus=$(_get_exit_status);mydir=$(_get_path);'

PS1='\033]0;${title}\u@\h:`tty`>${mydir}\007\n\
\[${GREY}\][\[${BLUE}\]\t\[${GREY}\]]\
\[${GREY}\][\[${USER_COLOR}\]\u\[${GREY}\]@\[${HOST_COLOR}\]\H\[${GREY}\]] \
\[${WHITE}\]${mydir} \
\[${GREY}\](\
\[${YELLOW}\]+${SHLVL}\[${GREY}\]|\
\[${YELLOW}\]%\j\[${GREY}\]|\
\[${YELLOW}\]!\!\[${GREY}\]|\
\[${YELLOW}\]${exitStatus}\[${GREY}\])\[${NONE}\]\n\
\[${USER_COLOR}\]$\[${NONE}\] '

# continuation prompt
PS2='\[${USER_COLOR}\]>\[${NONE}\] '

#used by set -x for tracing
PS4='\[${USER_COLOR}\]+\[${NONE}\] '
References:

More posts on my Bash profile:

Saturday, March 19, 2011

My Bash Profile - Part IV: Functions

Bash functions store a series of commands for later execution. If you find yourself running a sequence of commands frequently, it would make sense to wrap them up in a function. Functions are a lot like aliases but you can also pass arguments to them.

Update: My dotfiles are now in Git. For the latest version, please visit my GitHub dotfiles repository.

Here is a list of my bash functions taken from ~/.bash_functions.

#
# Go up a specified number of directories
#
up(){
    if [ -z $1 ]
    then
      cd ..
      return
    fi
    local levels=$1
    local result="."
    while [ $levels -gt 0 ]
    do
        result=$result/..
        ((levels--))
    done
    cd $result
}

#
# Make a directory and change to it
#
mkcd(){
  if [ $# -ne 1 ]; then
         echo "Usage: mkcd <dir>"
         return 1
  else
         mkdir -p $1 && cd $1
  fi
}

#
# fast find, using globstar
#
ff(){
   ls -ltr **/$@
}

#
# Jumps to a directory at any level below.
# using globstar
#
jd(){
    if [ -z $1 ]; then
        echo "Usage: jd [directory]";
        return 1
    else
        cd **/$@
    fi
}

#
# moves file to ~/.Trash
# (use instead of rm)
#
trash(){
   if [ $# -eq 0 ]
   then
       echo Usage: trash FILE...
       return 1
   fi
   local DATE=$(date +%Y%m%d)
   [ -d "${HOME}/.Trash/${DATE}" ] || mkdir -p ${HOME}/.Trash/${DATE}
   for FILE in "$@"
   do
     mv "${FILE}" "${HOME}/.Trash/${DATE}"
     echo "${FILE} trashed!"
   done
}

#
# Calculate an expression e.g. calc 1+1
#
calc(){
    echo "$@"|bc -l;
}

#
# Calendar which starts on Monday
# Highlights current day
#
cal(){
    if [ $# -eq 0 ]
    then
        /usr/bin/cal -m  | sed "s/\($(date +%e)\)/${RED}\1${NONE}/"
    else
        /usr/bin/cal -m "$@"
    fi
}

#
# Email me a short note
#
emailme(){
    if [ $# -eq 0 ]
    then
        echo Usage: emailme text
        return 1
    fi
    echo "$*" | mailx -s "$*" fahds
    echo "Sent email"
}

#
# Prints out a long line. Useful for setting a visual flag in your terminal.
#
flag(){
    echo -e  "\e[1;36m[==============="$@"===\
              ($(date +"%A %e %B %Y %H:%M"))\
              ===============]\e[m"
}

#
# Swap two files
#
swap(){
    if [ $# -ne 2 ]
    then
        echo Usage: swap file1 file2
        return 1
    fi
    local TMPFILE=tmp.$$
    mv "$1" $TMPFILE
    mv "$2" "$1"
    mv $TMPFILE "$2"
}

#
# Backup file(s)
#
dbackup(){
    if [ $# -lt 1 ]
    then
        echo Please supply a file to backup
        return 1
    fi
    date=`date +%Y%m%d-%H%M`
    for i in "$@"
    do
        echo Backed up $i to $i.$date
        cp $i $i.$date
    done
}

#
# Extract an archive of any type
#
extract(){
   if [ $# -lt 1 ]
   then
       echo Usage: extract file
       return 1
   fi
   if [ -f $1 ] ; then
       case $1 in
           *.tar.bz2)   tar xvjf $1    ;;
           *.tar.gz)    tar xvzf $1    ;;
           *.bz2)       bunzip2 $1     ;;
           *.rar)       unrar x $1     ;;
           *.gz)        gunzip $1      ;;
           *.tar)       tar xvf $1     ;;
           *.tbz2)      tar xvjf $1    ;;
           *.tgz)       tar xvzf $1    ;;
           *.zip)       unzip $1       ;;
           *.war|*.jar) unzip $1       ;;
           *.Z)         uncompress $1  ;;
           *.7z)        7z x $1        ;;
           *)           echo "don't know how to extract '$1'..." ;;
       esac
   else
       echo "'$1' is not a valid file!"
   fi
}

#
# Creates an archive
#
roll(){
  if [ "$#" -ne 0 ] ; then
    FILE="$1"
    case "$FILE" in
      *.tar.bz2|*.tbz2) shift && tar cvjf "$FILE" $* ;;
      *.tar.gz|*.tgz)   shift && tar cvzf "$FILE" $* ;;
      *.tar)            shift && tar cvf "$FILE" $* ;;
      *.zip)            shift && zip "$FILE" $* ;;
      *.rar)            shift && rar "$FILE" $* ;;
      *.7z)             shift && 7zr a "$FILE" $* ;;
      *)                echo "'$1' cannot be rolled via roll()" ;;
    esac
  else
    echo "usage: roll [file] [contents]"
  fi
}

#
# XPath
#
xpath(){
    if [ $# -ne 2 ]
    then
       echo Usage: xpath xpath file
       return 1
    fi
    echo "cat $1" | xmllint --shell $2 | sed '/^\/ >/d'
}

#-------------------------------
# Directory Bookmark Functions
#-------------------------------

#
# Add a bookmark, if it doesn't exist
#
bm(){
  local val=$(pwd)
  for i in ${bookmarks[@]}
  do
     if [ "$i" == "$val" ]
     then
         return 1
     fi
  done
  num=${#bookmarks[@]}
  bookmarks[$num]=$val
}

#
# Goto specified bookmark
# or previous one by default
#
bcd(){
  index=$1
  if [ -z $index ]
  then
      index=$((${#bookmarks[@]}-1))
  fi
  local val=${bookmarks[$index]}
  if [ -z $val ]
  then
      echo "No such bookmark. Type blist to list bookmarks."
      return 1
  else
      cd "$val"
  fi
}

#
# Remove a bookmark
#
brm(){
  if [ $# -lt 1 ]
  then
      echo "Usage: brm <bookmark-index>"
      return 1
  fi
  if [ -z ${bookmarks[$1]} ]
  then
      echo "No such bookmark"
      return 1
  fi
  bookmarks=(${bookmarks[@]:0:$1} ${bookmarks[@]:$(($1 + 1))})
}

#
# Remove all bookmarks
#
bcl(){
    bookmarks=()
}

#
# List all bookmarks
#
bll(){
    if [ ${#bookmarks[@]} -ne 0 ]
    then
        local i=0
        while [ $i -lt ${#bookmarks[@]} ]
        do
            echo $i: ${bookmarks[$i]}
            ((i++))
        done
    fi
    return 0
}

#-------------------------------
# String manipulation functions
#-------------------------------

#
# substring word start [length]
#
substring(){
    if [ $# -lt 2 ]; then
        echo "Usage: substring word start [length]"
        return 1
    fi
    if [ -z $3 ]
    then
        echo ${1:$2}
    else
        echo ${1:$2:$3}
    fi
}

#
# length of string
#
length(){
    if [ $# -ne 1 ]; then
        echo "Usage: length word"
        return 1
    fi
    echo ${#1}
}

#
# Upper-case
#
upper(){
    if [ $# -lt 1 ]; then
        echo "Usage: upper word"
        return 1
    fi
    echo ${@^^}
}

#
# Lower-case
#
lower(){
    if [ $# -lt 1 ]; then
        echo "Usage: lower word"
        return 1
    fi
    echo ${@,,}
}

#
# replace part of string with another
#
replace(){
    if [ $# -ne 3 ]; then
        echo "Usage: replace string substring replacement"
        return 1
    fi
    echo ${1/$2/$3}
}

#
# replace all parts of a string with another
#
replaceAll(){
    if [ $# -ne 3 ]; then
        echo "Usage: replace string substring replacement"
        return 1
    fi
    echo ${1//$2/$3}
}

#
# find index of specified string
#
index(){
    if [ $# -ne 2 ]; then
        echo "Usage: index string substring"
        return 1
    fi
    expr index $1 $2
}

#
# surround string with quotes, for example.
#
surround () {
   if [ $# -ne 2 ]
   then
     echo Usage: surround string surround-with e.g. surround hello \\\"
     return 1
   fi
   echo $1 | sed "s/^/$2/;s/$/$2/" ;
}
If you have any useful functions, please share them in the comments section below.

More posts on my Bash profile:

Friday, March 18, 2011

My Bash Profile - Part III: Completions

Bash programmable completion is a powerful feature which allows you to specify how arguments to commands should be completed. You do this using the complete command. For example, you can set completion up so that when you type the unzip command and hit the TAB key, it only shows completions for files ending with the .zip extension. Similarly, the completion for the ssh command would display hosts taken from your known_hosts file.

The completion file I use can be downloaded from here and saved in ~/.bash_completion. It has a comprehensive range of completions covering commands such as java, cvs, ant, make and kill.

Update: My dotfiles are now in Git. For the latest version, please visit my GitHub dotfiles repository.

One of my favourites is shown below:

_longopt is a generic completion function which can be used to complete the options on a number of different commands. For example, if you type grep --[TAB] you will see the available options for grep.

_longopt()
{
  local cur opt
  cur=${COMP_WORDS[COMP_CWORD]}

  if [[ "$cur" == --*=* ]]; then
      opt=${cur%%=*}
      # cut backslash that gets inserted before '=' sign
      opt=${opt%\\*}
      cur=${cur#*=}
      _filedir
      COMPREPLY=( $( compgen -P "$opt=" -W '${COMPREPLY[@]}' -- $cur))
      return 0
  fi

  if [[ "$cur" == -* ]]; then
      COMPREPLY=( $( $1 --help 2>&1 | sed -e '/--/!d' \
        -e 's/.*\(--[-A-Za-z0-9]\+=\?\).*/\1/' | \
        command grep "^$cur" | sort -u ) )
  fi
}
for i in a2ps autoconf automake bc gprof ld nm objcopy objdump readelf strip \
  bison cpio diff patch enscript cp df dir du ln ls mkfifo mknod mv rm \
  touch vdir awk gperf grep grub indent less m4 sed shar date \
  tee who texindex cat csplit cut expand fmt fold head \
  md5sum nl od paste pr ptx sha1sum sort split tac tail tr unexpand \
  uniq wc ldd bash id irb mkdir rmdir; do
  complete -F _longopt $i
done
Here is a demo:
sharfah@starship:~> grep --[TAB][TAB]
--after-context=       --color                --exclude-from=
--basic-regexp         --colour               --exclude=
--before-context=      --context=             --extended-regexp
--binary               --count                --file=
--binary-files=        --devices=             --files-with-matches
--byte-offset          --directories=         --files-without-match

More posts on my Bash profile:

Friday, March 11, 2011

My Bash Profile - Part II: Aliases

An alias gives you the ability to run a long or cryptic command using a simple name. The syntax is alias name='command' which means that whenever you type name, Bash will substitute command in its place. For example: alias ll='ls -ltr'. You can't use arguments in an alias command. If arguments are needed, a shell function should be used.

To see what aliases are currently defined use the alias command. To disable an alias in your current shell, use unalias name. You can also disable an alias in your current command by prefixing the alias name with a \. For example: \ls.

Update: My dotfiles are now in Git. For the latest version, please visit my GitHub dotfiles repository.

Here is a list of my Bash aliases taken from ~/.bash_aliases

# reloads profile
alias reload='. ~/.bash_profile'

# edit and source aliases file
alias va='vi ~/.bash_aliases; source ~/.bash_aliases && echo "aliases sourced"'

# go up multiple levels
# (also see 'up' function)
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
alias .....='cd ../../../..'
alias ......='cd ../../../../..'
alias cdhist='dirs -v'

# concise date
alias d='date +%Y%m%d-%H%M'

# various ls shortcuts
alias ls='ls -F --color=auto'
alias l='ls'
alias la='ls -a'
alias ll='ls -ltr'
alias lu='ls -ltur'
alias lal='ls -altr'
alias sl='ls'

# list dirs only
alias ldir='ll -d */'

# less with ignore-case, long-prompt and quit-if-one-screen
alias less='less -iMF'

# more is less
alias more='less'
alias mroe='more'
alias m='more'

alias h='history'

# execute last command
# 'r cc' runs the last command beginning with "cc"
alias r='fc -s'

alias igrep='grep -i'
alias rgrep='grep -r'
alias ftail='tail -f'

# fast scp
alias scp='scp -o StrictHostKeyChecking=no -c arcfour -o Compression=no'

# ps with wide output so you can see full commands
alias fullps='ps -auxwww'

# shows all declared functions
alias functions='declare -F'

# autosys aliases. All start with "job".
alias jobls='autorep -J'
alias jobll='autorep -q -J'
alias jobstart='sendevent -E FORCE_STARTJOB -J'
alias jobhold='sendevent -E JOB_ON_HOLD -J'
alias jobice='sendevent -E JOB_ON_ICE -J'
alias jobkill='sendevent -E KILLJOB -J'
alias joboffhold='sendevent -E JOB_OFF_ICE -J'
alias joboffice='sendevent -E JOB_OFF_ICE -J'
alias jobhist='jobrunhist -j'
alias jobdepends='job_depends -c -J'
alias jobsu='sendevent -E CHANGE_STATUS -s SUCCESS -J'
alias jobterm='sendevent -E CHANGE_STATUS -s TERMINATED -J'
If you have any useful aliases, please share them in the comments section below.

More posts on my Bash profile:

Sunday, March 06, 2011

My Bash Profile - Part I

I wrote about my Bash profile a few years ago and since then I've made a quite a few changes to it such as adding a more powerful prompt and many more useful aliases and functions; some invented, others discovered. Over the next few posts, I will be sharing my current profile with you. Feel free to use and comment, but most importantly share any gems from your own profile that might be useful to the rest of us.

Note that I am using Bash version 4.1.2.

Update: My dotfiles are now in Git. For the latest version, please visit my GitHub dotfiles repository.

.bash_profile
This is executed by Bash for login shells. Quite simply, mine is:

if [ -f ~/.bashrc ]; then
   source ~/.bashrc
fi
.bashrc
This is executed by Bash for interactive non-login shells:
# don't save command history
unset HISTFILE

# don't save duplicates in history
HISTCONTROL=ignoredups

EDITOR=vi
VISUAL=vim
PAGER='less -i'

set -o notify   # Report status of terminated bg jobs immediately
set -o emacs    # emacs-style editing

shopt -s extglob   # extended pattern matching features
shopt -s cdspell   # correct dir spelling errors on cd
shopt -s lithist   # save multi-line commands with newlines
shopt -s autocd    # if a command is a dir name, cd to it
shopt -s checkjobs # print warning if jobs are running on shell exit
shopt -s dirspell  # correct dir spelling errors on completion
shopt -s globstar  # ** matches all files, dirs and subdirs
shopt -s cmdhist   # save multi-line commands in a single hist entry
shopt -s cdable_vars # if cd arg is not a dir, assume it is a var
shopt -s checkwinsize # check the window size after each command
shopt -s no_empty_cmd_completion # don't try to complete empty cmds

# enable coloured man pages
export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;31m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_se=$'\E[0m'
export LESS_TERMCAP_so=$'\E[01;44;33m'
export LESS_TERMCAP_ue=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;32m'

# define some colours
GREY=$'\033[1;30m'
RED=$'\033[1;31m'
GREEN=$'\033[1;32m'
YELLOW=$'\033[1;33m'
BLUE=$'\033[1;34m'
MAGENTA=$'\033[1;35m'
CYAN=$'\033[1;36m'
WHITE=$'\033[1;37m'
NONE=$'\033[m'

# random grep colour
export GREP_COLOR="1;3$((RANDOM%6+1))"
export GREP_OPTIONS='--color=auto'

# path for directories
export CDPATH=".:..:../..:~/:~/dev/"

# file containing hosts
export HOSTFILE=~/.hosts

# source everything else
. ~/.bash_prompt
. ~/.bash_completion
. ~/.bash_aliases
. ~/.bash_functions

# trap commands to display on the xterm titlebar. Must be last line.
trap 'echo -ne "\033]0;$BASH_COMMAND - $USER@${HOSTNAME}>$(pwd)\007"' DEBUG
Read about my prompt, aliases and functions in the following posts:

Saturday, March 05, 2011

Upgrading to SyntaxHighlighter 3.0

I have now upgraded this blog to use SyntaxHighlighter 3.0. This version allows you to add titles to your code blocks and select code without line numbers. Take a look at the What's new page to see a complete list of new features that have been added in this version.

This is how you can upgrade too:

1. Download SyntaxHighlighter v3.0
You can download it here.
If you don't have a place to upload, you can use the free hosted version listed here.

2. Link to CSS and Javascript
Open your webpage's HTML file and add links to the SyntaxHighlighter's CSS and JavaScript files. For optimal results, place these lines at the very end of your page, just before the closing body tag.

<link type="text/css" rel="stylesheet" href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css"></link>
<link type="text/css" rel="stylesheet" href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css"></link>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/current/scripts/shLegacy.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js"></script>
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js"></script>
<script type="text/javascript">
    SyntaxHighlighter.config.bloggerMode = true;
    SyntaxHighlighter.all();
</script>
It is not necessary to add the js files for all languages - just for the ones you will be using.

3. Add Code
Now add the code you wish to highlight in your webpage, surrounded by the <pre> tag. Set the class attribute to the language alias e.g. brush:java

<pre title="Test SyntaxHighlighter 3" class="brush: java;">
public void printHello(){
    System.out.println("Hello World");
}
</pre>
4. View Page
View the webpage in your browser and you should see your syntax highlighted code snippet as:
public void printHello(){
    System.out.println("Hello World");
}