Saturday, June 18, 2011

Efficiently Navigating Directories on UNIX

I find myself, like most developers, spending a lot of time navigating directories. Flipping back and forth between logs and application directories with long names can be quite tedious. So, with the help of a few new functions, aliases and config tweaks I've made the navigation process easier and more efficient. You no longer need to remember long paths because you can jump straight to them using their names. You can also choose to bookmark your favourite directories. Here is my setup:

1. Go up to a specific directory
I have a function called upto which allows you to jump up to any directory, on the current path, just by name. This is very useful if you are deep in a directory. I also have autocompletion for this function, so that it shows me valid directory names and completes them for me.

#
# Go up to the specified directory
#
upto(){
  if [ -z $1 ]; then
      echo "Usage: upto [directory]"
      return 1
  fi
  local upto=$1
  cd "${PWD/\/$upto\/*//$upto}"
}

#
# Completion function for upto
#
_upto(){
  local cur=${COMP_WORDS[COMP_CWORD]}
  d=${PWD//\//\ }
  COMPREPLY=( $( compgen -W "$d" -- $cur ) )
}
complete -F _upto upto
Example:
[/www/public_html/animals/hippopotamus/habitat/swamps/images] $ upto h[TAB][TAB]
habitat       hippopotamus
[/www/public_html/animals/hippopotamus/habitat/swamps/images] $ upto hippopotamus
[/www/public_html/animals/hippopotamus] $
2. Go up a specific number of directories
If you know how many levels you want to go up, you can use the up function e.g. up 5 will move you up 5 directories.
#
# 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
}
3. Go down to a specific directory
Sometimes you want to change to a directory but can't remember the path, or the path name is too long to type. I have a function called jd which allows you to jump down to a directory any level below the current one. It uses Bash's globstar feature so make sure you have it enabled (using shopt -s globstar). (Warning: this may be slow on large directory structures because of the searching involved.)
#
# Jumps to a directory at any level below.
# using globstar
#
jd(){
  if [ -z $1 ]; then
      echo "Usage: jd [directory]";
      return 1
  else
      cd **/$1
  fi
}
Example:
[/www/public_html/animals/hippopotamus/habitat/swamps/images] $ upto hippopotamus
[/www/public_html/animals/hippopotamus] $ jd images
[/www/public_html/animals/hippopotamus/habitat/swamps/images] $
4. CDPATH
The CDPATH variable is a colon-separated list of directories in which the shell looks for destination directories specified by the cd command. Mine is shown below. No matter what directory I am currently in, I can quickly jump to a project in my dev directory with cd <project> because it is on my path.
export CDPATH=".::..:../..:~:~/dev/"
5. Shell Options
I have set the following useful shell options in my .bashrc. The autocd option allows you to change to a directory without using the cd command and cdspell automatically corrects typos in directory names.
shopt -s cdspell     # correct dir spelling errors on cd
shopt -s autocd      # if a command is a dir name, cd to it
shopt -s cdable_vars # if cd arg is not a dir, assume it is a var
6. Quick Aliases
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
alias .....='cd ../../../..'
alias ......='cd ../../../../..'
7. Keeping a history of visited directories
I came across a useful post on Linux Gazette: History of visited directories in BASH. It contains a script which maintains a history of directories you have visited and then allows you to switch to them easily using a reference number. The command cd -- shows you your history and cd -2 would take you to the second item in your history list. For example:
[/www/public_html/animals] $ cd --
 1  /tmp
 2  /www/public_html/animals/hippopotamus/habitat/swamps/images
 3  /www/public_html/animals/lion
[/www/public_html/animals] $ cd -2
[/www/public_html/animals/hippopotamus/habitat/swamps/images] $
8. Bookmarks
I spend a lot of time moving between different directories especially between logs and application directories. I have implemented a bookmarking feature which allows you to bookmark your favourite directories and then change to them easily.
  • bm: bookmark the current directory
  • bcd: change to the specified bookmark
  • brm: remove a bookmark
  • bcl: clear all bookmarks
  • bll: list all bookmarks
#-------------------------------
# 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 bll 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
}

If you have any useful directory-related functions, share them in the comments below!

Related Posts:

No comments:

Post a Comment