One of my favourite technical presentations I’ve read online has been Hal
Pomeranz’s Unix Command-Line Kung Fu, a catalogue of shortcuts and
efficient methods of doing very clever things with the Bash shell. None of
these are grand arcane secrets, but they’re things that are often forgotten in
the course of daily admin work, when you find yourself typing something you
needn’t, or pressing up repeatedly to find something you wrote for which you
could simply search your command history.
I highly recommend reading the whole thing, as I think even the most
experienced shell users will find there are useful tidbits in there that would
make their lives easier and their time with the shell more productive, beyond
simpler things like tab completion. Here, I’ll recap two of the things I
thought were the most simple and useful items in the presentation for general
shell usage, and see if I can add a little value to them with reference to the
Bash manual.
History with Ctrl+R
For many shell users, finding a command in history means either pressing the up
arrow key repeatedly, or perhaps piping a history
call through grep
. It
turns out there’s a much nicer way to do this, using Bash’s built-in history
searching functionality; if you press Ctrl+R and start typing a search pattern,
the most recent command matching that pattern will automatically be inserted on
your current line, at which point you can adapt it as you need, or simply press
Enter to run it again. You can keep pressing Ctrl+R to move further back in
your history to the next-most recent match. On my shell, if I search through my
history for git
, I can pull up what I typed for a previous commit:
(reverse-i-search)`git': git commit -am "Pulled up-to-date colors."
This functionality isn’t actually exclusive to Bash; you can establish a
history search function in quite a few tools that use GNU Readline, including
the MySQL client command line.
You can search forward through history in the same way with Ctrl+S, but it’s
likely you’ll have to fix up a couple of terminal annoyances first.
Additionally, if like me you’re a Vim user and you don’t really like having to
reach for the arrow keys, or if you’re on a terminal where those keys are
broken for whatever reason, you can browse back and forth within your command
history with Ctrl+P (previous) and Ctrl+N (next). These are just a few of the
Emacs-style shortcuts that GNU Readline provides; check here for a more
complete list.
Repeating commands with !!
The last command you ran in Bash can be abbreviated on the next line with two
exclamation marks:
$ echo "Testing."
Testing.
$ !!
Testing.
You can use this to simply repeat a command over and over again, although for
that you really should be using watch
, but more interestingly it turns
out this is very handy for building complex pipes in stages. Suppose you were
building a pipeline to digest some data generated from a program like
netstat
, perhaps to determine the top 10 IP addresses that are holding open
the most connections to a server. You might be able to build a pipeline like
this:
# netstat -ant
# !! | awk '{print $5}'
# !! | sort
# !! | uniq -c
# !! | sort -rn
# !! | sed 10q
Similarly, you can repeat the last argument from the previous command line
using !$
, which is useful if you’re doing a set of operations on one file,
such as checking it out via RCS, editing it, and checking it back in:
$ co -l file.txt
$ vim !$
$ ci -u !$
Or if you happen to want to work on a set of arguments, you can repeat all of
the arguments from the previous command using !*
:
$ touch a.txt b.txt c.txt
$ rm !*
When you remember to user these three together, they can save you a lot of
typing, and will really increase your accuracy because you won’t be at risk of
mistyping any of the commands or arguments. Naturally, however, it pays to be
careful what you’re running through rm
!