Tue, 07. May 2013
For a long time, I could not bother putting more effort into a shell environment than defining a few aliases. That changed after reading through some inspiring dotfiles.
The first step in my attempt to build a more productive environment was to set up my own dotfiles project. I did not want to loose the setup or manually copy bits and pieces from one machine to another. I found http://dotfiles.github.io to be a good starting point.
I'm constantly trying to improve my working environment. If you already have a nice collection of dotfiles, I'd love to hear about your favorite bits.
Reading other peoples dotfiles was quite inspiring, but there is one very simple trick that I'd like to highlight here: Define the most commonly used commands as single letter functions.
A function allow you to do more than a simple alias g='git'
. For example, it
can define meaningful default behavior in case you invoke it without arguments:
function g {
if [[ $# > 0 ]]; then
git $@
else
git status --short --branch
fi
}
That prints a nice and compact git status summary. If you do provide arguments,
it behaves like an alias to git
. I've stolen the g
function from someone
elses dotfiles and I've used it so much, it inspired me to write more functions
myself.
Oftenly, I started working on something by opening vim, finding a source file,
finding the corresponding test case and then trying to remember what I actually
wanted to do. I guess you know what I'm talking about, regardless of which
editor you are using. I had v
as an alias for vim
, but I wanted to be able
to open files with similar names with a single command (e.g. calendar.js
and
calendar-test.js
. I changed v
to a function that uses find
if an argument
is given. Later, I extracted the f
function out of v
because it's already
useful by itself:
F_CMD='find . -type d \( -path "**/node_modules" -o -path "**/.*" \) -prune -o -wholename "$1"'
function f {
if [[ $# > 1 ]]; then
eval "$F_CMD -exec grep -nHF \"$2\" {} \; ;"
elif [[ $# > 0 ]]; then
eval "$F_CMD -print"
else
echo 'Usage: f file_name [file_content]'
fi
}
This will search all files in the current directory, ignoring node_modules
and hidden directories. You can specify any part of the file path. If you want
to search for somethign lile test-*.js
in the file name, remember to put it
in quotes or bash will expand it before searching. If a second argument is
given, it greps through all the files that matched.
This function is then used by v
. It passes the results of f
to vim. I
didn't try, but I guess this works as well with other editors like mate
and
subl
(let me know if you tried):
function v {
if [[ $# > 0 ]]; then
if [[ $1 == "*"* ]]; then
RESULTS=$(f "$1*")
COUNT=`echo $RESULTS | wc -w`
if [[ $COUNT > 5 ]]; then
read -p "Found $COUNT results. Open all? (Y/n) " -n 1
echo
if [[ $REPLY =~ ^[Nn]$ ]]; then
return
fi
fi
vim $RESULTS
else
vim $1
fi
else
vim
fi
}
If you pass one argument and it starts with a *
, it will use the f
function
to find matching files and open them in vim. This allows me to write v
*calendar
to open all files with calendar
in their name. If the argument
does not start with a *
, it behaves just like an alias for vim
.
Feel free to copy and tweak for your own needs. I'd be interested in the improvements you made.
What is your favorite shell function? I'd love to learn from your experiences.