The Interactive Subshell: Emacs’ Killer Feature

I've been using Emacs exclusively for a couple months now.* Like any good tool, there are things I like and things I don't like. I'll save diving into all of those upsides and downsides for another post. In this post, I’ll talk about Emacs' killer feature: the interactive subshell. Perhaps the "real" killer feature is something more meta like Emacs' programmability but that wouldn't have made for such a juicy title would it?

*If you’ve never heard of Emacs and you are curious, prepare to spend the next hours, to weeks, to your lifetime learning and using one of the most extensible text editors. Once you’ve played around with it come back and read this.


Before I begin, you should know that:

  1. I have a bias towards interacting with my computer through the keyboard as much as possible. The full power of the interactive subshell can only be harnessed with the keyboard. So, this article will probably be most valuable to people who also prefer to use their keyboard, but even if you prefer to use your mouse you may still find something worthwhile here. And who knows, maybe you'll be so inspired by the interactive subshell that it will be your hook into using your keyboard more.
  2. I don't have very much experience programming on an OS besides macOS or Linux. I used the Windows Subsystem for Linux briefly but that's about as far out as I've ventured. I think this article has a bit of a Linux slant but I believe the message holds true regardless of what OS you use as long as you use a terminal.

Now that I've checked off the mouse vs keyboard and OS boxes on my flamewar bingo card, let's get into the good stuff.

What is an interactive subshell and why should you care?

The "interactive" in "interactive subshell" means that all of the text in the terminal can be interacted with just like the text in any other file. Everything you can do with text (delete, move, select, etc) you can do with the input and output of the shell.

Interactive means you just grep'ed for something and because your regex fu is so bad you don't know how to craft one that doesn't include a bunch of useless results — so you just delete those lines from the output.

Interactive means you just cat'ed a log file and you're sifting through the output and see two lines that you want to compare side by side. You just delete all of the lines in between.

Interactive means your test suite is running and you want to search for a keyword in the output but you forgot to clear the output of the last run so you just select the old output and delete it. No need to worry about possibly finding the keyword in the old run.

Interactive means you have an ENVIRONMENT_VARIABLE_THAT_IS_UNBELIEVABLY_LONG and you want to unset it. If you wanted to echo $ the variable then your shell would let you tab complete it but it can't tab complete with the word unset in front of it. In an interactive subshell, you can use the autocomplete of Emacs to tab-complete the variable name.

Interactive means you are trying to find a file and you can't remember the name so you run a few find . -name "filename". Each time you run the command you want to di" (delete all text in-between "", would delete filename). But oh wait set -o vi in Bash doesn't support that feature even though your editor does.

I could go on but I'm ranting and I didn't anticipate ranting in just my second blog post. Here’s what matters: Interactive means you are in control. You can do whatever you want with the text in the shell as if you had written each line by hand yourself.

Why should you care? Well, I'll describe why I care and maybe you'll agree. Like many programmers, I interact with my shell (Bash) regularly. There are countless tales on the web of why shells matter and how they are a powerful way of interacting with the os. I won't get into the shell/GUI debate (add it to your flamewar bingo;)) but I use my shell and I like my experience interacting with it to be comfortable. I want to interact with the shell input and output however I please. I’ve described a few scenarios where I want to manipulate the shell input and output and I don't think my terminal should get in the way of me doing that.

In addition to manipulating the text as I please, using all of the features I use elsewhere in my editor within my terminal reduces cognitive load. If I want to use vi keybindings in my editor why should I get some watered-down version of them in my terminal? If I want to autocomplete, I should be offered suggestions of strings found elsewhere in my program. In both places I'm just editing text so I'd like my workflow to be the same. My terminal should trust me to edit the content however I want.

IDE's and Terminals

VSCode, the JetBrains suite of IDE's, Eclipse and many other editors of the day have powerful text editing features. You can efficiently manipulate text; moving it here and there and customizing your editing experience late into the night. All of these editors support plugins to allow for vi or Emacs keybindings which further increases your ability to manipulate text without leaving your keyboard. What all of these editors lack (at least in my experience, please prove me wrong) is a terminal where you can work on the text as you do in the rest of the editor. They all have a terminal but the terminals offer no special text editing features. For example, in exactly zero of these terminals can you delete a line of output.

NeoVim and Vim 8 have a half-baked terminal mode where you can navigate text (hjkl, search, highlight, etc) but you can't manipulate (delete, autocomplete, etc) text using the same commands you use in the editor. Vim also offers a mode where you can take the command, go into a temporary buffer, edit the command with normal text editing capabilities, and then drop back in the terminal pasting the command in. This is clunky; you lose the context of everything else going on in your terminal, and it only works for the shell input, not the output.

In addition to the editors, the terminal emulator apps (iTerm, GNOME terminal, etc) suffer from the same lack of editability. All of them offer some amount of text manipulation for editing just the command but not anything else. Usually, they default to having Emacs keybindings and if you're so inclined you can set -o vi and hjkl to your heart's content. While the ability to edit the command is nice (mandatory!) the inability to edit anything else is suffocating. I'm a vi keybinding user (talking about how good Emacs is, gasp) so I can't speak to the Emacs keybindings but if they are like the vi ones then they are half-implemented at best.

For example, in iTerm with vi keybindings enabled gUw (make uppercase from cursor to the end of the word) doesn't work. The specific command isn’t so important, but what is important is that it’s muscle memory. It slows me down when I have to stop and say "ohh I'm in the terminal now and the terminal says I can't do that" (said in a whiny kid voice). Other things like copy and paste keybindings also don't work; I can't move text elsewhere in the terminal (or in another split pane). This can be done with NeoVim and it can be done using Tmux but it doesn’t feel natural, in my experience. In NeoVim I'm always forgetting whether I'm in insert mode, normal mode from set -o vi, or normal mode in NeoVim. Tmux uses different keys altogether; so more muscle memory where my wires get crossed. Ok, that's enough bloviating on why the terminal you use is awful and why my setup is just so much better:). Check off Emacs vs vi on your bingo cards (sheets? I've never actually played bingo...) and let's move on to why Emacs solves all of the issues elaborated so far.

Emacs Shells

If you’ve made it this far hopefully I’ve persuaded you a bit that your terminal isn't interactive but it should be. Thankfully Emacs has us covered. As with seemingly all things Emacs, there are multiple shells and modes of interaction. Any shell could conceivably be made interactive. The shell itself isn't what is important but the way its input and output are presented is. I describe a mixture of shells and terminals below. Try to ignore that I sort of conflate the two. The point is these are the default shell/terminal combos that come with Emacs and let you do the shell/terminal things you might expect.

There is Eshell, which is a shell implemented entirely in Elisp. The Eshell is presented in an interactive manner which is excellent but the rest of the shell is not my cup of tea. I don't know Elisp so I think I'm not the target audience. The skills learned using it are not very portable to other shells.

There is the Emacs terminal emulator which, as the name implies, is a terminal emulator running in Emacs. You can run whatever shell program you want and you can edit the text as you wish. This is all great but the term mode has one downfall. It is modal (A vi user bashing on something being modal? I can't get my story straight). There is line mode and there is char mode. Line mode is the fully interactive mode where you can manipulate the text displayed. Char mode is like any other non-interactive terminal emulator. I have no need for char mode so I find switching between the two annoying and pointless.

Finally, there is the Emacs shell mode. Shell mode is like the Winston Churchill quote about democracy, it is the worst shell experience except for all of the other ones. It is slow, C-c frequently doesn't work (I think that is an artifact of using Evil Mode), and just generally feels kind of clunky (I know, very scientific). Also, it doesn't support ANSI escape sequences. If you're like the guy I work for, you'll go all the way to building a website to remind people that in shell mode you $TERM=dumb and they should play nice. Programs like top won’t work at all. These defects are just minor inconveniences and the upside, interactivity, far outweighs them. If you want an interactive shell then this mode is what you want.

Interactivity or bust

An interactive subshell might be something you have to play with to grasp. If you're unconvinced by my yelling then fire up Emacs and type M-x shell. That's Emacs speak for "hold down the alt/option key (Meta) and x." Then release, type in shell, and hit enter. That will drop you into the Emacs shell. Try using it for a day and see what you think. At first, it will feel clunky. This feeling doesn't really wear off but at some point you'll be so addicted to editing the text in your shell that you won't even care.

NY style pizza is better than Chicago deep-dish. That's five across and I win flamewar bingo. Have a great day.