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
!