Using The Shell Right
The most powerful part of Unix/Linux/BSD is the command line. In stock trim, the Unix shells are all very effective, but your time can be more effective by customizing the shell. I use bash
, so I know that these things work in that shell, but they ought to be easily transferred to others as well.
Aliases
A good friend of mine, Jordan Sissel, once said that if you do something more than once, you’re doing it wrong. His conjecture applies as much to your shell as it does to your browser—computers are great at repetitive tasks, so you shouldn’t bore yourself with such things. Therein lies the most important thing you can do with your shell: make common tasks easier with aliases.
First things first, I use ls
a whole lot, and, despite it’s simple makeup, I often mistype the command. I don’t care about being able to easily run Steam Locomotive, and there’s no s
or l
command, so I replace those all with ls
:
alias sl="ls" alias l="ls" alias s="ls" alias ll="ls -l" alias lh="ls -lh" alias la="ls -la"Another thing I do all the time is descend into directories, which means I need to get out of them, too:
alias ..="cd .." alias ...="cd ../.." alias ....="cd ../../.." alias .....="cd ../../../.." alias ......="cd ../../../../.." alias .......="cd ../../../../../.."
I always want extended regular expressions, and there are tons of things I don’t want to search when I grep (though I use ack these days):
alias grep="egrep" alias G="grep -n --color=always --binary-file=without-match --exclude=tags \ --exclude=*-min.js --exclude-dir='.[a-zA-Z]*' --exclude-dir='external' \ --exclude-dir='blib'"
Furthermore, I often want to do recursive greps of a entire codebase, sometimes case insensitive, and like everything else, I mistype it:
alias GR="G -r" alias RG="GR" alias GRI="G -r -i" alias GIR="GRI" alias IGR="GRI" alias IRG="GRI"
If you do any sort of system administration, you need to grep the process list; make that easy:
alias paux="ps aux|grep -i"
Is someone shoulder surfing?
alias c="clear" alias logout="clear; logout"
Matt Behrens tipped me off to this one—type -a
tells you a lot more than the standard which
:
alias which='type -a'
When I’m writing in a language that requires compilation, I use cowsay to break up the output of each run, so that errors are easy to distinguish between this run and the previous one:
alias gcc='cowsay "Hello"; gcc' alias g++='cowsay "Hello"; g++' alias make='cowsay "Hello"; nice -n 10 make' alias javac='cowsay "Hello"; javac'
Machines that I SSH to often get their names as aliases; I’ve got a bunch more of these:
alias claudius="ssh -Y dinomite@dinomite.net" alias caligula="ssh -Y dinomite@caligula.dinomite.net"
Prompt
There are numerous articles about pimping out your shell’s prompt, many include previous command exit codes, the time, the current energy of the LHC, and the price of the S&P 500. I have a web browser, so I don’t need all that information—I only put in my prompt things that are pertinent to the task at hand. The things that make up my prompt are a bit complicated, so I build it in stages. First, since I work on a lot of different machines, I always have the hostname in my prompt. To make it easy to tell which machine I’m on, I assign colors to the systems that I use most often:
HOSTNAME=`hostname|sed -e 's/\..*$//'` if [ $HOSTNAME = 'Caligula' ] || [ $HOSTNAME = 'Caligula.local' ]; then export HOST_COLOR="\[\033[1;35m\]" fi if [ $HOSTNAME = 'claudius' ]; then export HOST_COLOR="\[\033[1;36m\]" fi if [ $HOSTNAME = 'dev1' ]; then export HOST_COLOR="\[\033[1;34m\]" fi if [ $HOSTNAME = 'svr-dev-rw1' ]; then export HOST_COLOR="\[\033[1;31m\]" fi if [ $HOSTNAME = 'drewfus' ]; then export HOST_COLOR="\[\033[1;30m\]" fi
Next, I capitalize the hostname and make the colon separating it from the path red if I’m currently acting as root via sudo -s
. This makes it very hard to forget that the consequences of actions are great at the current time:
COLON_COLOR='0m' if [ ${UID} -eq 0 ]; then COLON_COLOR='1;31m' fi if [ ${UID} -eq 0 ]; then HOSTNAME="`echo $HOSTNAME|tr '[a-z]' '[A-Z]'`" fi
Finally, build the actual prompt:
PS1=`echo -ne "$HOST_COLOR$HOSTNAME\[\033[00m\]\[\e[$COLON_COLOR\]:\[\033[33m\]\ w\[\033[00m\]\\[\033[01;33m\]\$\[\033[00m\] "`
What does this look like?
claudius:/usr/local$
And when root:
CLAUDIUS:/usr/local$
History
There are a lot of complicated commands that I only use occasionally; having a big history means if I’ve used it once, I can easily search and find the command later. The histappend
options tells bash to append rather than overwrite the history file and cmdhist
combines multi-line history commands into a single entry. It’s often useful to run the same command repeatedly, and I find myself typing ls
whenever I stop to think; setting HISTCONTROL
and HISTIGNORE
keeps those actions from filling up my history.
#"I know I've used that command before, but I can't remember the syntax" export HISTSIZE=50000 shopt -s histappend shopt -s cmdhist HISTCONTROL=ignoredups export HISTIGNORE="&:ls:ll:la:lh:sl" export HISTTIMEFORMAT='%Y-%m-%d %H:%M:%S - '
Environment Variables
A lot of linuxes come with lackluster program defaults (emacs, more, etc.); you can get better ones by setting environment variables:
export PAGER=/usr/bin/less export EDITOR='vim -X' export BROWSER='firefox' export CC=/usr/bin/gcc
Since we are in the 21st century, I use Unicode:
export LC_ALL="en_US.UTF-8" export LANGUAGE="en_US.UTF-8"
Functions
I use Perl a lot, and have to deal with keeping modules the same across different systems; this function makes getting the installed version of a module easy:
function perlmodver { perl -M$1 -e 'print "Version " . $ARGV[0]->VERSION . " of " . $ARGV[0] . " is installed.\n"' $1 }
The thing I use awk
for most often is ’{print $n}’, so I wrote fawk
which you give a number and it des just that:
function fawk { first="awk '{print " last="}'" cmd="${first}\$${1}${last}" eval $cmd }
awk
also does math:
function calc { awk "BEGIN{ print $* }"; }
Tying It All Together
To keep things organized, I separate the above mentioned things into a few different files, so my .bashrc
brings them all together. Additionally, I check for a .bash_local
file, which isn’t checked into subversion, so that I can have machine-specific alterations to my shell environment.
# .bashrc source ~/.bash_global source ~/.bash_aliases source ~/.bash_functions if [ -f ~/.bash_local ] then source ~/.bash_local fi