I love using the shell; however so many people do not. My argument to others to use your shell more is simply that you see what is happening with your computer a lot easier than you can with a graphical user interface (GUI). Basically, your shell is somewhat like a glass bottom boat to see what your computer is actually doing. On linux, GUI's often have an option to pull up some of what your computer is doing, but for the most part they do not.

The other major argument for using your shell is that once you have it set up it is fast. Damn fast. I can do so much so quickly with my shell that takes me a lot longer with a GUI. This means that I spend last time on both billable and non-billable tasks – leaving more time to do other things.

Setting up a Drop Down Terminal in Ubuntu

Every POSIX system has a shell terminal application which you can use. But the first time you open it, it usually looks super scary – as in DOS scary. The best news is that once you get over that initial shock (perhaps by following this small tutorial) it can be a very pleasant experience. In Ubuntu you can get to your shell by typing ctrl+alt+t (for terminal). I don't like the main Gnome terminal application not because it is bad, indeed it is perfectly fine. I just prefer Guake because it is a terminal which stays hidden at the “top” of my screen completely out of sight. I start Guake on login, so it is always available. All I have to do is to press F12 and I'm instantly in a terminal which drops down over my open program. I set Guake to take up about 45% of my screen real estate which allows me to see the rest of whatever application I need. There is a newer terminal application, Terra Terminal, which performs similarly in some respects but adds additional features, such as terminal splitting which I never used. Terra is still in development so after using it for a while I switched back to Guake which is more stable.

Installing Guake could not be more simple. Drop into a terminal and type:

$ sudo apt-get install guake

Once the installation finishes, Guake will be in your applications. The next thing you will want to do is to add Guake to your startup applications. Open your applications and search for startup. When the GUI opens, click “Add” then in the “Name” field type Guake and in the “Command” field type guake. That's it. Next time you login Guake is ready for you. If you want to start guake without logging out and back in, go to your applications and search for guake and start guake terminal. Now you have your initial setup.

Guake and Gnome Terminal (what you get when you type ctrl+alt+t) both are simply terminals. They are like a portal to your shell program. Your shell program is distinct from you terminal program. You can use a terminal to run almost any shell program that you like. Shell programs live “below” your terminal in the stack between your eyeballs and your computer's processor. So with the same terminal, you can use different shells.

Shell Programs

Ubuntu's default shell (and indeed many the default shell for many POSIX systems) is bash. Bash is a good shell. Personally, I do not use it. I use zsh mainly because its community has a ton of plugins and installation scripts which I have used to automate the installation of my luxurious shell setup. There is a long history of (ahem) discourse between the supporters of the two shells and you can google to find it. Of course, POSIX being POSIX there are numerous other options such as fish shell and other newer competitors to bash and zsh. My recommendation is to use the shell that works for you and lets you do what you need to do in the most efficient way possible.

Ubuntu tries to keep its install wizards small so it does not install zsh by default (OSX on the other hand does I'm fairly sure), so if you want to follow the rest of this tutorial then you'll want to install zsh with the following command:

$ sudo apt-get install zsh

When the shell is installed then you need to tell Ubuntu that this is the shell you are interested in installing. You do this with two commands. The first command finds where the zsh binary is and the second tells Ubuntu to change your shell. You copy the output from the first command (ctrl+shift+c when you're in terminal) and paste it into the second command (ctrl+shift+v when you're in terminal).

$ which zsh
$ chsh -s $OUTPUT $USER

In the above command $OUTPUT is the output you get from the first command and $USER is your user name on ubuntu. After that you are all set. What I do generally at this point is to close the terminal window (in Guake this is ctrl+shift+w but that may be what I've set up to follow Chrome rather than the standard) and then open a new terminal window. Voila, you are all set up with zsh.

Making Your Shell YOUR Shell

Now we have all the basics setup with three simple commands in the terminal. You can easily put these into your install script to automate this process whenever you install or reinstall Linux on your machine. The next step is to make the shell YOUR shell by personalizing it.

The main file you will use to personalize the shell is your ~/.zshrc. This is the file that zsh will automatically load by default everytime a new instance of zsh is opened. You can use this file in turn to tell zsh to load other files as we will see. The reason why I like zsh is that there are a lot of plugin which you can use to make the shell your own in a very simple way. A while ago a framework for zsh was developed called oh-my-zsh which kicked off the frameworks in zsh in a big way. After a while this became complicated so another team developed a system which automated how this all works.

I use both of these systems. I use the automated install and updating system to install, remove, and update my zsh files and I use oh-my-zsh as the underlying framework (which I modify) to do much of the heavy lifting. If you want to follow along it will not be very painful I promise. The automatic installation and updater I use is called antigen. Here is how you set it up.

$ sudo apt-get install git
$ git clone https://github.com/zsh-users/antigen.git $LOCATION

When you run the git clone command it will pull down antigen but you will want to put it in whatever location you like. I do not recommend putting it in ~/.antigen because when you run the script it is going to put a bunch of other files in that location and this could cause conflicts. I put it in my ~/coding/antigen folder for instance. Now, when that finished you need to edit your .zshrc file in your favorite text editor. Here's what my file looks like.

#!/usr/bin/env zsh
# Antigen bundles
source ~/coding/antigen/antigen.zsh
antigen use oh-my-zsh
antigen bundles <<EOBUNDLES
zsh-users/zsh-completions src

#Now load the theme
antigen theme ~/Dropbox/Dot-Files/zsh-themes/csk-compact.zsh-theme
antigen apply

#Now load the custom files
source ~/Dropbox/Dot-Files/zsh-plugins/conf.zsh
source ~/Dropbox/Dot-Files/zsh-plugins/git-csk.plugin.zsh
source ~/Dropbox/Dot-Files/zsh-plugins/funcs.zsh
source ~/Dropbox/Dot-Files/zsh-plugins/aliases.zsh

You can read more about how to use antigen bundles on their github page linked to above, but I'll walk through it. The first thing that the file does is it sources (which in shell means load and use) the main antigen shell script. So point this to wherever you cloned their repo to. The next line tells antigen to use the oh-my-zsh framework (there are others which you could use and the github page shows how to do that). Then there is a long list of the bundles that I want to use. Bundles are simply plugins. At the end you will see a couple of non oh-my-zsh bundles sourced. Antigen knows that when you put a bundle that looks like user/plugin to get it from the github repo. Of course, you can not use the github defaults and you can easily point it to other bundles that you may have on a private git repo.

After the bundles are loaded then the file tells antigen to use a specific theme. The last thing the file does is to tell zsh to load a bunch of custom files which I keep in my dropbox. That's it. The way that zsh works is that when you pass it one command and then pass it a second command that conflicts with the first then the second one always wins. This is why I source my own custom files after the framework files. This allows me to override small things that the framework does which I do not like.

You can look on oh-my-zsh page for a lot of different themes. Here's the theme I use.

My theme tells me my username, hostname, current working directory and then tells me which rvm I'm using and how my git repo is looking (the git repo turns red if it is ‘dirty’ or green if it is not as you can see from the different repos). If you wanted to use my theme then here is the file:


local whoami="%{$fg[magenta]%}%n%{$reset_color%}@%{$fg[green]%}%m%{$reset_color%}"
local whereami=":%{$fg_bold[blue]%}%~%{$reset_color%}"
local git_branch="\$(git_prompt_info)"
local rvm_ruby="[%{$reset_color%}%{$fg[cyan]%}\$(~/.rvm/bin/rvm-prompt v g)%{$reset_color%}]"

PROMPT="${whoami}${whereami} ${rvm_ruby}${git_branch} >> "

On my servers I do not typically install Ruby Version Manager (rvm) and the result is that this theme will throw an error which is annoying, so on my servers I use a very slightly modified theme which I would recommend if you do not use ruby a lot. One thing to note is that the colors in the server theme are bolded (this makes it easy for me to remember that I am probably ssh'ing into a server in a specific terminal and not on my computer if you did not want the bold them simply delete ‘_bold’).


local whoami="%{$fg_bold[green]%}%n%{$reset_color%}@%{$fg_bold[magenta]%}%m%{$reset_color%}"
local whereami=":%{$fg_bold[cyan]%}%~%{$reset_color%}"
local git_branch="\$(git_prompt_info)"

PROMPT="${whoami}${whereami} ${git_branch} >> "

The next custom file my .zshrc file sources is my configuration file. This is full of a bunch of defaults I've found by stalking people's dot file repos on github. To be honest there is a lot of things in this file that I do not know what they do but solve specific problems for me. I recommend using one you find from repos and then googling as different problems arise and adding or deleting the specific parts of the config file to make it yours. That is more or less what I have done over the previous few years.

# Key bindings
bindkey -M viins '^[OH' vi-beginning-of-line  # home key
bindkey -M viins '^[OF' vi-end-of-line        # end key
bindkey -M viins '\e[3~' vi-delete-char       # use delete as forward delete
bindkey -M viins '^B' push-line-or-edit       # line buffer
bindkey '^[^[[C' forward-word
bindkey '^[^[[D' backward-word

# Shut up.
setopt NO_BEEP
setopt nullglob

# Changing Directories
setopt AUTO_CD

# History
bindkey "^R" history-incremental-pattern-search-backward
bindkey "^S" history-incremental-pattern-search-forward

# Resume vim after ctrl+z accidentally (or intentionally!) from vim
# just ctrl+z again to resume
foreground-vi() {
  fg %vi
zle -N foreground-vi
bindkey '^Z' foreground-vi


The next file sourced is a custom git alias file which I use. It is not terribly interesting but it makes things easier for me to remember because the oh-my-zsh git bundle kept changing for a while and I think of things slightly differently than they do so I just override a lot of their defaults which is fine.

# Aliases
alias git='hub'
alias g='git'
compdef g=git
alias ga='git add'
compdef _git ga=git-add
alias gaa='git add -A'
compdef _git gaa=git-add
alias gai='git add -i'
compdef _git gai=git-add
alias gb='git branch'
compdef _git gb=git-branch
alias gba='git branch -a'
compdef _git gba=git-branch
alias gbav='git branch -a -vv'
compdef _git gbav=git-branch
alias gc='git commit -v'
compdef _git gc=git-commit
alias gca='git commit -v -a'
compdef _git gca=git-commit
alias gc!='git commit -v --amend'
compdef _git gc!=git-commit
alias gcm='git commit -m'
compdef _git gcam=git-commit
alias gco='git checkout'
compdef _git gco=git-checkout
alias gcob='git checkout -b'
compdef _git gcob=git-checkout
alias gcom='git checkout master'
compdef _git gcom=git-checkout
alias gd='git diff'
compdef _git gd=git-diff
alias gds='git diff --cached'
compdef _git gds=git-diff
alias gdh='git diff HEAD'
compdef _git gdh=git-diff
alias gdelcom='git reset --hard HEAD~'
alias gl="git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%an %cr)%Creset' --abbrev-commit --date=relative"
alias glp='gl -p'
alias gl1='gl --pretty=oneline --abbrev-commit --decorate'
alias glg='git log --stat --max-count=5'
alias glt='gl | tig'
alias glu='git ls-files --other --exclude-standard'
alias glud='git ls-files --other --exclude-standard | xargs rm'
alias glua='git ls-files --other --exclude-standard | xargs git add'
alias grma='git ls-files --deleted | xargs git rm'
alias gcount='git shortlog -sn'
alias gm='git merge'
compdef _git gm=git-merge
alias gpl='git pull --rebase'
compdef _git gpl=git-pull
alias gup='git fetch && git rebase'
compdef _git gup=git-fetch
alias gps='git push'
compdef _git gps=git-push
alias gr='git remote'
compdef _git gr=git-remote
alias grv='git remote -v'
compdef _git grv=git-remote
alias grall='git remote -v show -n'
compdef _git grall=git-remote
alias grset='git remote set-url'
compdef _git grset=git-remote
alias grb='git rebase'
compdef _git grb=git-rebase
alias grba='git rebase --abort'
compdef _git grba=git-rebase
alias grbc='git rebase --continue'
compdef _git grbc=git-rebase
alias grm="git status | grep deleted | awk '{\$1=\$2=\"\"; print \$0}' | \
           perl -pe 's/^[ \t]*//' | sed 's/ /\\\\ /g' | xargs git rm"
alias grmd="git status | grep deleted | awk '{\$1=\$2=\"\"; print \$0}' | \
           perl -pe 's/^[ \t]*//' | sed 's/ /\\\\ /g' | xargs git rm -rf"
alias gs='git status'
compdef _git gs=git-status
alias gss='git status -s'
compdef _git gss=git-status
alias gst='git stash'
alias gsta='gst apply'
alias gundo='git reset --soft HEAD^'
alias gundoh='git reset HEAD'
alias gundohh='git reset HEAD --hard'
alias gwtfl='git wtf -l -a -A -r'
alias tigb='tig blame'
alias tigs='tig status'

# Will return the current branch name
# Usage example: git pull origin $(current_branch)
function current_branch()
  ref=$(git symbolic-ref HEAD 2> /dev/null) || return
  echo ${ref#refs/heads/}

function current_repository()
  ref=$(git symbolic-ref HEAD 2> /dev/null) || return
  echo $(git remote -v | cut -d':' -f 2)

# these aliases take advantage of the previous function
alias push='git push github $(current_branch) && git push wsl $(current_branch)'
alias pushpriv='git push wsl $(current_branch)'
alias pushprod='git push gandi master'
alias pushroku='git push heroku master'

function gtb() {
  git checkout -b $1 --track $2/$1

function gtrack() {
  branch=$(git rev-parse --abbrev-ref HEAD)
  git branch -u $1/$branch $branch

function gnuke() {
  if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1

  # remove all paths passed as arguments from the history of the repo
  git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD

  # remove the temporary history git-filter-branch otherwise leaves behind for a long time
  rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune

That file I've assembled over the years and to be honest I use maybe 60% of the aliases and functions in it, but the good thing is that when I'm trying to remember how to do something I just pull up that file and it is likely in there. It is probably overkill but it can be useful from time to time.

Next I source my custom functions files. This is a fun one that allows me to do a bit of coding in zsh and put these into my shell for easy use in the future. I use a lot fo these functions quite frequently.

## Process/System functions
#helper for pp
function my_ps() { ps $@ -u $USER -o pid,%cpu,%mem,bsdtime,command ; }

#do i own this process?
function pp() { my_ps f | awk '!/awk/ && $0~var' var=${1:-".*"} ; }

#install packages and log
function install()
  sudo apt-get install ${=packages} && echo "$packages" >> ~/.installd
apt_pref_compdef install  "install"
function add-repo()
  sudo add-apt-repository -y ppa:${=repos} && echo "sudo add-apt-repository -y ppa:$repos" >> ~/.installd
  sudo apt-get update

#colorize man output ... http://www.cyberciti.biz/faq/linux-unix-colored-man-pages-with-less-command/
man() {
  env \
    LESS_TERMCAP_mb=$(printf "\e[1;31m") \
    LESS_TERMCAP_md=$(printf "\e[1;31m") \
    LESS_TERMCAP_me=$(printf "\e[0m") \
    LESS_TERMCAP_se=$(printf "\e[0m") \
    LESS_TERMCAP_so=$(printf "\e[1;44;33m") \
    LESS_TERMCAP_ue=$(printf "\e[0m") \
    LESS_TERMCAP_us=$(printf "\e[1;32m") \
      man "$@"

## Networking functions
#get my ip
function myip-local()
  IP=`ifconfig  | grep 'inet addr:'| grep -v '' | cut -d: -f2 | awk '{ print $1}'`;
  echo $IP
function myip-global()
  dig +short myip.opendns.com @resolver1.opendns.com
function myips()
  echo -e "\nMy Local IPs:\n\033[1;32m`myip-local`\033[m"
  echo -e "\nMy External IPs:\n\033[1;32m`myip-global`${NC}\033[m\n"
function funsa()
  sudo ifconfig eth0 down
  sudo macchanger -r eth0
  sudo ifconfig eth0 up
  sudo ifconfig wlan0 down
  sudo macchanger -r wlan0
  sudo ifconfig wlan0 up
  google-chrome "file:///home/coda/Dropbox/Chrome-Extensions/Source%20Codes/funsa.html"
  sudo service network-manager restart

#summarize current network situation
function ii()
  echo -e "\nYou are logged on ${BLUE}$HOSTNAME${NC}"
  echo -e "\nAdditionnal information:$NC " ; uname -a
  echo -e "\n${RED}Users logged on:$NC " ; w -h
  echo -e "\n${RED}Current date :$NC " ; date
  echo -e "\n${RED}Machine stats :$NC " ; uptime
  echo -e "\n${RED}Memory stats :$NC " ; free
  myip > /dev/null 2>&1
  echo -e "\n${RED}Local IP Address :$NC" ; echo ${IP:-"Not connected"}

## Files and Directories
#special file create
function touch {
  dir=`expr "$1" : '\(.*\/\)'`
  if [ $dir ]
    mkdir -p $dir
/usr/bin/touch $1

#home folder
function h
  cd ~/$1;

#find a file with the string in the name
function ff() { find . -name '*'$1'*' ; }

#find a file with the string in the name and exec $2 on it
function fe() { find . -name '*'$1'*' -exec $2 {} \; ; }

#find a file ending in $2 and search for string $1 in it
function fstr() # find a string in a set of files
  if [ $# -ne 2 ]; then
    echo "Usage: fstr \"pattern\" [files] "
  SMSO=$(tput smso)
  RMSO=$(tput rmso)
  find . -type f -name "*${2}" -print | xargs grep -sin "$1" | \
  sed "s/$1/$SMSO$1$RMSO/gI"

#remove spaces from filename
function nospace()
    mv "$1" "`echo $1 | tr ' ' '_'`"

#filenames to lowercase
function lowercase()
  for file ; do
    case "$filename" in
      */*) dirname==${file%/*} ;;
      *) dirname=.;;
    nf=$(echo $filename | tr A-Z a-z)
    if [ "$nf" != "$filename" ]; then
      mv "$file" "$newname"
      echo "lowercase: $file not changed."

## Get the commands and aliases
#list all the aliases
function list_aliases
    ALIASES=`alias | cut -d '=' -f 1`
    echo "$ALIASES" | sort -u

#list all the commands
function list_commands
    COMMANDS=`echo -n $PATH | xargs -d : -I {} find {} -maxdepth 1 \
        -executable -type f -printf '%P\n'`
    echo "$COMMANDS" | sort -u

#find an alias
function lag
  for a in $(list_aliases | grep $1); do which $a; done

#find a command
function lcg
  for c in $(list_commands | grep $1); do which $c; done

## Git & Deployment
#ship the current branch
function ship()
  CURRENT=`git branch | grep '\*' | awk '{print $2}'`
  git checkout master
  git merge ${CURRENT}
  git push github master
  git push wsl master
  git checkout ${CURRENT}

## Application Specific
#use hub browser to get at the github and shut chrome up
function gbrow()
  git browse $@ &>/dev/null;

#cabal updater
function cabal-upgrade()
  cabal update;
  cabal list --simple-output --installed | awk '{print $1}' | uniq | xargs -I {} cabal install {};
#npm global installer
function npmig()
  sudo npm install -g ${=packages} && echo "npm $packages" >> ~/.installd

Some highlights of this file include an installation script which allows me to both install a package from apt-get and also to lot that in my install script so that I can then remember to reinstall it on a new system. I also have a collateral package for installing new ppas on my computer. I have built a bunch of network functions that I use every once in a while to figure out what the heck is happening. I also have built a f-u-nsa script which I run when I get to a new country. By changing my mac address and clearing all my cookies I think I can slightly reduce my chances of being flagged within NSA's systems – this is important when you run a law firm and spend lots of time in the Horn of Africa. Also I have some functions that make working with files and modifying files easier as well as a few specific functions for assisting with different programs like npm (node package manager and cabal which is haskell's package manager and I use to keep on the bleeding edge of pandoc).

Lastly my .zshrc file sources my aliases which are below and not terribly interesting.

#!/usr/bin/env zsh
# zsh aliases
# included by .zshrc, this is for my custom aliases

# allow aliases to be sudoed
alias sudo='nocorrect sudo'

# Set Colors in Terms & Dumb Terms
eval $(dircolors)
if [ "$TERM" != "dumb" ]; then
  eval "`dircolors -b`"
  alias ls='ls --color=always'

# Directory & File Management
alias ll='ls -l'
alias la='ls -A'
alias l='ls -G'
alias lla='ls -la'
alias cat='colorize'
alias grep='grep --color'
alias grepi='grep --color -i'
alias mkdir='mkdir -p'
alias less='less --raw-control-chars'

# EDiTor & Clipboard stuff
alias e='vim'
alias se='sudo vim'
alias pbcopy='xclip -selection clipboard'
alias pbpaste='xclip -selection clipboard -o'

# show sorted directory sizes for all directories
alias duh='du -s ~/* | cut -f1 | spark && du -sch ~/*'
alias duhere='du -s ./* | cut -f1 | spark && du -sch ./*'
alias dua='du --max-depth=1 ~ | cut -f1 | spark && du -ch --max-depth=1 ~'
alias dfh='df -h'

# system monitoring & updating
alias dpkgl="dpkg -l | grep"
alias psg='ps auxf | grep'
alias local_ports='sudo nmap -sT -O localhost'
alias traffic="sudo tcpdump -i wlan0 -n -s0 -e"
alias adga="adg && sudo npm update -g && antigen update && vundle-update && rvm use 2.0.0@global && gemup && gem cleanup && cabal-upgrade"
alias acr="aac && sudo $apt_pref -y autoremove"
alias aps="apt-cache search"
alias apsw="apt-cache show"

# Ruby
alias b='bundle exec'
alias bi='bundle install'
alias br='bundle exec rake'
alias birb='bundle exec irb'
alias lr='rake -T'
alias tests='bundle exec rake test'
alias gemi='gem install --no-rdoc --no-ri'
alias geml='gem list'
alias gemla='gem list -a'
alias gemun='gem uninstall'
compdef _gem_installed gemun=installed_gems
alias gemup='gem update --no-rdoc --no-ri'
compdef _gem_installed gemup=installed_gems

# Node
alias npmi='npm install --save'
alias npml='npm list'
alias npmla='npm list --global'
alias npms='npm search'
alias npmsw='npm show'
alias npmup='npm update'
alias npmun='npm uninstall --save'
alias bower='noglob bower'
alias bowi='bower install --save'
alias bowl='bower list'
alias bows='bower search'
alias bowsw='bower info'
alias bowup='bower update'
alias bowun='bower uninstall --save'
compdef _bower_installed_packages bowun
alias nw='~/sites/desktop/node-webkit/nw'

# Environmental Establishment
alias cli="cd ~/Insync/caseykuhlman@watershedlegal.com/A.Clients"
alias clear!="cd ~/ && source .zshrc && clear"
alias reload!="source ~/.zshrc && cd .. && cd -"

# Command Line Tools
alias decrypter="ghostscript -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=unencrypted.pdf -c .setpdfwrite -f"
alias subl="/home/coda/.bin/sublime"
alias lt="/opt/LightTable/LightTable"

One thing which should be mentioned is the difference between functions and aliases. There is a subtle difference between the two. Basically an alias is like a contraction. It is just a shorthand thing for a specific other command. So there is not much you can do with it AFTER the alias. If you need to pass the function a variable (like a file or a folder or a switch) then you should use a function. If you do not need to pass a variable of any time then you can use aliases if you are not trying to do something terribly complex. If there are a lot of steps to what you are trying to accomplish then a function is usually the better way to go even if it does not require any variables. Those are my two cents, am sure others may feel differently.

And that is it. Type a few commands, copy and paste some files and you'll be good to go with a dynamic, fast, and totally beautiful and useable shell at the push of a button.

Happy Hacking!

~ # ~