Ramin Honary: Emacs as a shell

6 min read Original article ↗
Home About Codeberg

This article is not about shell-mode, an Emacs wrapper around a your OS's shell REPL, nor is it about term-mode, the Emacs terminal emulator. It is about how Emacs is a shell in and of itself, and how it can replace a CLI shell.

Why a CLI shell is so useful

CLI terminals are nice because you can access any one of hundreds, if not thousands, of functions on your computer system with just a few keystrokes. If you are a regular CLI user, you probably have a favorite terminal emulator, and use it in the following ways:

  • Long-term, per-project terminals — You probably keep at least one terminal window open for every project you work on, where you can quickly create or edit a file, or inspect the state of your project. You might have your editor open in this window most of the time.

  • Short-lived, one-time use terminals — You probably also have a key binding set in your window manager configuration to quickly open a fresh new terminal window so you can quickly run a command once, like htop, df -h, ip addr, some command to check the status of something on your computer system. You quickly open a terminal window, run the command, check want you need, then Ctrl-D to be rid of the window.

I do this as an Emacs user, but in a slightly different way.

Long-term, per-project terminals

Start with dired

C-x d — prompts for a directory path to browse, then launches a dired (I pronounce it "Dir--ed"), the Emacs directory editor. I have one dired buffer open for every project I work on in Emacs, and I switch to this buffer as the starting point for anything I might do in the project I am working on.

dired commands

While in the dired buffer, several single-key commands are available to you:

  • g — will run ls -l, refreshes the current view so you can see what files are available.

  • i — when the cursor is over a directory, calls ls -l on that directory and appends the output of it to the dired text buffer. The g command will refresh these sub-directories as well.

    • C-M-n and C-M-p — navigate the cursor to the next/previous subdirectory entry appended by the i command.

    • C-u k — delete the text display of a subdirectory (does not delete anything in the filesystem), the cursor must be on the green subdirectory header text for this to work.

  • f or <Enter> — when the cursor is over a file, edits that file. This is like running vim on a file in the current directory.

  • o — is similar to f but will also split the screen into two windows and edits the file in the new split window, sort of like running tmux split-window 'exec vim ./filename', if your shell is always inside of a Tmux session.

  • & — prompt for a shell command to run on the file under the cursor. This is perfect for running commands like gzip, or tar xzf. If it is a script, you can also run bash or python on the file, output is captured in a buffer called *Async shell command*.

  • D — runs rm on the file under the cursor

  • M — runs chmod on the file under the cursor

M-x find-grep

Emacs provides a nice wrapper around the find and grep shell programs. It calls these programs, but captures and formats the output in a separate buffer that you can more easily navigate with the cursor. It is like always piping the output of grep to less but with hyperlinks to jump to the search results.

M-x compile

This is a make-shift IDE tool to run a compiler on code in the current directory. This will prompt you for a command to run, such as make or cmake. It is similar to pressing & in the dired buffer, but will use regular expressions to find and highlight file and line numbers, so if your compilation process discovers an error, you can navigate to location of the error in the source code using the cursor or mouse.

Browsing through source code

I wrote a more detailed walk-through comparing the specific actions I would take when starting work on a new long-term project, please refer to the Browsing source ccode article.

Short-term, one-time-use terminals

You might be in the middle of your work, then realize you need to run a command, such as htop to what program is using your CPU the most, or df -h to check how much disk space you have available. You could just launch a terminal emulator to do this, but it is good to get into the habit of using Emacs instead of a terminal emulator for this, since you never know when the text output by this command might be something you want to copy and paste elsewhere.

  • M-& — best for a command like df, ip addr, or some command that produces a non-interactive textual report. It prompts for a shell command to run, runs the command and captures it's output in a temporary buffer called *Async shell command*. When the command finishes, you can do C-x C-s in the temporary buffer to save the output to a file, or C-x C-k to discard it. Note that this command launches the child process with the current working directory set to the currently focused dired directory, or the parent directory of the currently focused file.

  • M-x term — best for use with a command like htop or mpv, because the M-x term command does terminal emulation, and can handle the ANSI terminal codes for text coloring, and responds to interactive key presses, like for pausing/resuming playback in mpv. I use this with htop often enough that I bind my own Emacs command to it:

    (global-set-key (kbd "C-c h t") (lambda () (term "htop")))
          

    Note that Emacs also has a TOP-like tool built-in: M-x proced.

  • M-x man — is a wrapper around the CLI man command. Running M-x man prompts you for a search term with tab completion so you don't have to know the name of the exact manual page you are looking for, it then captures the output of man just as the less command would do.

Further Reading

  • EINVAL blog: "Emacs as a Shell" — Wojciech Siewierski had published an article of the very same name as this one only two years before me, you may find their explanation with screen captures easier to understand than my article.


Emacs for Professionals

This article is part of my Emacs for Professionals series, in which I explain in a few paragraphs how I perform a specific common task using Emacs in ways that people already familiar with command line tools and Linux shell scripting can quickly understand.