Inline expansion in bash

bash has a lot of shortcuts to save you typing and thinking time— globs, shell aliases, and history expansion, for starters; and a bunch more.

When you use bash shortcuts extensively, though, you often end up with inputs that are rather, er, opaque, e.g.

$ !-3 -r [mnop]* !-3$

You have to be fairly brave to press ENTER without looking over that a few times. There is, though, a way out. Instead of trying to analyze what the hell it is you just wrote, you could just ask the computer to tell you exactly what it's going to do. bash has a number of features that facilitate this by expanding shortcuts for you to inspect before you execute them. I refer to these features collectively as inline expansion.

There are three really useful inline expansion commands you can use at a bash prompt that are essentially complementary to each other:

History expansion

If you add the following to your .inputrc,

$if Bash
  Space: magic-space

then whenever you press space, any history expansions (e.g. !!, !grep, !-2, !-3:1, etc.) are expanded.

You can also, at any time, press M-^ to perform history expansion (no configuration required). I prefer to set up magic space because you can just set it up and forget it about it, and subsequently you have one less thing to think about.

Glob expansion

C-x * expands the previous word into any filenames that match the glob. For example, /* gets expanded into /bin /boot /cdrom /dev ....

Shell expansion

C-M-e expands shell aliases, history, variables, commands, and arithmetic. For example:

  • ls into ls --color=auto (based on any alias directives you have)
  • !grep into grep 2010 mylogfiles (based on your history)
  • $DISPLAY into :0.0
  • `whoami` into phil
  • $((3+5)) into 8

* * * * *

I find these features useful for the same reason I use GNU Parallel the way I do: they really improve the interactivity of my work— in the sense that I get much faster empirical feedback on whether I'm doing a complex task correctly (the computer tells me what it thinks I'm trying to say), rather than me having to reason (unreliably) about what I wrote and recall a bunch of history and read the man page for the umpteenth time because I'm second-guessing myself.

Inline expansion is useful in a different scenario as well, namely, once you expand a glob or a shell alias you're free to (interactively) make changes to it. For example, if you want to delete all the files but one in a directory, start with rm *, press C-x * (which expands the glob into a list of all the files in the current directory), and edit out the name of the file you want to keep. Or if you want to run one of your shell-aliased commands, but without one of the flags, expand it with C-M-e and then edit it.

Further reading: A nice bash tips slide deck by Simon Myers; "Miscellaneous [Readline] Commands" in the bash manual

1 comment: