Emacs: Running Shell Commands on the Current File

I’ve long thought it was a little funny that Emacs doesn’t provide an easy way to run a shell command on the current file. It turns out it does, but it’s not as obvious as you might expect.

Emacs provides lots of great commands for conveniently running shell commands:

  • M-! (shell-command): executes the given shell command, displays output in a new buffer or the echo area (depending on output length). Optionally inserts the output into the current buffer when given a prefix argument.
  • M-& (async-shell-command): as above, but executed asynchronously.
  • M-| (shell-command-on-region): pass the current region as input to a shell command and display output. Optionally replaces the region when given a prefix argument.

Of course we can also run a shell within Emacs, via shell (the basic option), eshell (a shell implemented in Emacs Lisp, with access to Emacs Lisp functions), and term (a more fully-featured terminal emulator).

We also have compile, which will run a shell command (often make), streaming in the output and overlaying functionality to easily jump to files referenced in the output (compilation warnings, grep results, etc.).

But what has always been notably missing for me, is a quick way to run a shell command on the current file. Not buffer / file contents, not region, but file.

From dired-mode, we can easily do this with !, but invoking Dired just to run a shell command is more disruptive to my workflow than it needs to be when I just want to run open <current-file> or similar.

It turns out there is a built-in two-keystroke solution via the “future history” mechanism I wrote about on the CollBox Engineering blog:

If you use M-! (shell-command) and then press M-n (next-history-element), the current file name will be inserted into the minibuffer with point before it. Then you just need to type the command you want to run and hit return.

It’s a good reminder that with 46 years of history behind it, if something obvious appears to be missing from Emacs, there’s probably a good reason why.