Taking Laziness Seriously
Or you can call this post "Going nuts with shortcuts". A developer need to have a degree of healthy laziness, Neal Ford wrote a book about it if you're interested. Part of this laziness is expressed in the refusal of writing the same command line twice, Aravind S.V. (my colleague at TW and friend) is a master in making/coding shortcuts and aliases so he doesn't type a long, error prone, command line more than the first time. So, if you use bash or other shell daily here's some of my current ones.
My new NFS is called Dropbox
Yup, I'm old enough to be an old NFS user. A good thing is to keep you files, specially ad hoc scripts available all the time (that's what I did using NFS). Using a USB to copy them every time is a hassle, I confess that, before Dropbox, I just stopped using a lot of my custom scripts and settings files like .vimrc because the lack of a fast way to deploy them. But now is possible to store these files in Dropbox and keep them around. I just have to do this in my local profile (~/.bash_profile):
bash_load_root=~/Dropbox/
. ${bash_load_root}/load_bash
Different settings for each machine?
In my case I have two computers that I usually work on, a Dell laptop and a Mac laptop, they are a bit different specially the folders structure. Still, having a profile for each machine (and maybe duplicate settings) is not a good idea. The trick is using a folder with the specific settings by machine that will override the generic ones if necessary. Here how it's coded the load of my settings:
machine=${HOSTNAME:-$(hostname -s)}
loadtypes="variables profile functions aliases"
for type in ${loadtypes}; do
file="bash_${type}"
[ -f ${bash_load_root}/${file} ] && . ${bash_load_root}/${file}
[ -f ${bash_load_root}/${machine}/${file} ] && . ${bash_load_root}/${machine}/${file}
done
This script loads in order (generic to specific) settings according to the
machine I'm on. Note that bash_variables, bash_profile,
bash_functions
and bash_aliases
are loaded in this order
so that a generic function has the right context as a specific one (variables
set first).
Now we have all that is needed to load the files, arguments, aliases, paths and such. It's up to you to code them as soon you caught yourself typing the same command over and over again. But first how about check what command you type the most? Aravind S.V. came with this simple history filtering that shows what you've been typing in the shell lately grouped by duplicates:
history | sed 's/^[[:space:]]*[0-9]*[[:space:]]*//' | sort | uniq -c | sort -n
So, after contemplating how many time you typed the same command line, what about creating some clever shortcuts and aliases?
Some aliases and shortcuts
In my case, given the size of the current code base I'm working on finding
files is something I do very often. If you're familiar with the
find
command you know that is a bit large command to type 50 times
during the day. In my bash_aliases
I have the following to find
files:
myfind() {
find ./ -name "*$2*" -type "$1" | tee ${TMPDIR}/my_ffound | cat -n
FFOUND_PWD=${PWD}
FFOUND=($(cat ${TMPDIR}/my_ffound))
}
ff() { myfind "f" "$1"; }
fd() { myfind "d" "$1"; }
Don't worry much about FFOUND
right now I will explain it
later. Now I have a neat function to find files and I added two other ones that
I actually call ff
(mnemonic "find files") and fd
(mnemonic "find directories"). If I type ff FileName
it calls
myfind
trying to find a file with FileName
in part of
the name. Just to give one example on how is the output let me find some files
in my eclipse installation folder (note the number index before each file):
eclipse$ ff junit
1 ./plugins/org.eclipse.jdt.junit4.runtime_1.1.100.v20100526-0800.jar
2 ./plugins/org.junit_4.8.1.v4_8_1_v20100427-1100/junit.jar
3 ./plugins/org.apache.ant_1.7.1.v20100518-1145/lib/ant-junit.jar
4 ./plugins/org.apache.ant_1.7.1.v20100518-1145/etc/junit-noframes.xsl
5 ./plugins/org.apache.ant_1.7.1.v20100518-1145/etc/junit-frames-xalan1.xsl
6 ./plugins/org.apache.ant_1.7.1.v20100518-1145/etc/junit-frames.xsl
7 ./plugins/org.junit_3.8.2.v3_8_2_v20100427-1100/junit.jar
8 ./plugins/org.eclipse.jdt.junit.runtime_3.4.200.v20100526-0800.jar
9 ./plugins/org.eclipse.jdt.junit_3.6.0.v20100526-0800.jar
10 ./plugins/org.eclipse.jdt.junit.core_3.6.0.v20100526-0800.jar
11 ./plugins/org.eclipse.pde.junit.runtime_3.4.100.v20100601.jar
But after finding a file, what you do next? Copy the path and open it? List
the size? Change to the directory where the file is? And after all of that,
what happens with the results inside the terminal? Do you lost them and have to
issue the find again? If so, what about keeping a history of this find and
refer to it later? That's what the FFOUND
is about. What happens
is that the results are dumped into a file that's overwritten if another find
is issued. So, let's see some cool things that we can do with this history.
Do you want open the file in vim the file indexed as 6? Let's make a shortcut for that:
v() { vim $(echo ${FFOUND_PWD}/${FFOUND[$1-1]}); }
Now after the find a simple v 6
will open the file. What else
can we do? Copy the file? Well, since it seems that we will refer to the list
of files found a lot, so let's extract to a simpler function that returns the
file path if it exists and refactor your v()
function plus add
another alias to change to the directory where the file is:
fn() {
[ ! -z ${FFOUND[$1-1]} ] && echo ${FFOUND_PWD}/${FFOUND[$1-1]};
}
v() { vim $(fn "$1"); }
c() { cd $(dirname $(fn "$1")); }
And now, c 6
will go to the file directory. There's one more
thing we're missing, list back our results in case we forget or lose them,
so:
lf() { echo ${FFOUND[*]} | tr -s ' ' '\n' | cat -n; }
And now you can extend this as you wish, I have for aliases for
git and grep also. Moreover, there's another
hidden benefit, imagine that you don't have an alias for a command (maybe
because you don't use it too often), since fn
function is always
available inside the shell you can invoke the function for any command
like:
eclipse$ anyCommand $(fn 6)
And last but not least, here's an alias for a grep
where the
results are opened in vim with the quickfix window where I can
navigate the results (cool if you dig around lot of files):
vgr() { tmp=$(egrep -Rl "$1" * | xargs ) && vim -c ":vimgrep /$1/ ${tmp} | :copen "; }
The advantage of a simple :vimgrep
is that the linux
grep
is much faster, so I launch vim just with
the list of files already found.
That's it. If you have some cool aliases please share, and thanks Aravind
S.V. for some ideas and specially the help with the bash
functions.