Babblings of an aging geek in love with the Absurd, his family, and his own hubris.... oh, and Lisp.

Death to the Shell

I hate the Shell


— Howard Abrams @howardabrams

Well, I kinda hate the Shell

As Emacsians, we have a love/hate relationship with shells.

  • shell, term, ansi-term, eshell, vterm, oh my…
  • Like dired, we have better options


I have two shell-related itches to scratch related to both shell uses:

  1. Interactive Data transformations through pipes are good and bad.
  2. Automative Personally, I want to replace 20+ years of shell scripts with Emacs Lisp.

Work in Progress


This project is for tinkers only (at least, at this point).

Let’s Start with a Story

  • Hey Howard, can you restart the Openstack service for me?
  • Uh, you mean, services?

    for S in $(systemctl --all | grep openstack | sed 's/\.service.*//' | cut -c3-)
        systemctl restart $S

    That was easy to bang out, right?

    Converting data by chaining small executables is incredibly flexible.

Bad Parts of Pipes

The flow of data through pipes is inscrutable.

Similar to function calls: a | b | c(c (b (a)))

Good thing we can use an editor to do the real dirty work, eh?

$ systemctl --all > /tmp/all-services
$ emacsclient /tmp/all-services
$ systemctl restart $(cat /tmp/all-services)

Wait a minute…

Merging Shell Pipes and Emacs

In the shell, we use commands, editors, and scripts based on personal preference.

This flexibility gives a shell its power.

But Emacs has this same flexibility.

First Idea:

Transform data flows through pipes from command to command to Transform a buffer of data from function to function

Sending Data to Commands

Transforming data in an Emacs buffer is easy.

How do we use that data in the shell?

  • As standard in to a command
  • As a series of command line arguments (aka xargs)
  • Repeatedly run command with line as argument (aka for loop)
  • Copy data to clipboard
  • Just to visually inspect it (aka less)

Demonstration: Piper

  • Primarily a user interface
  • Thin wrapper around existing Emacs functions


  • Cat /proc/acpi/wakeup
  • Pair it down to just the enabled device names
  • Write each device back to /proc/acpi/wakeup


  • Get a list of all services: service --status-all
  • Filter to just the service names.
  • Get a description of each service.

Note: We can talk about a better name later. ☻


  • Transforming data from standard in to standard out is better in an Emacs buffer
  • Sending data to an executable can be improved:
    • for loops
    • xargs, etc.
  • Any Lisp is better than Shell’s language:
    • Emacs Lisp is pretty hacky, and that’s a good thing
    • Calling Emacs functions is trivial and pretty flexible
    • Making Emacs Lisp as your go to scripting language is pretty fun


Some potential sources for inspiration:

(defun emacs-piper-presentation-title ()
  (find-file "/tmp/emacs-presentation-title-a.org")
  (demo-it-load-file "/tmp/emacs-presentation-title-b.org"))

(defun emacs-piper-presentation-reset ()
  (setq piper--command-history '())
  (setq history-delete-duplicates t)
  (add-to-history 'piper--command-history "cut -f1")
  (add-to-history 'piper--command-history "service --status-all"))

(use-package demo-it
  :load-path "~/Other/demo-it"
  (demo-it-create :advanced-mode :single-window
              (demo-it-show-image "~/Downloads/emacs-piper-presentation-title.png" :none)
              (demo-it-presentation (buffer-file-name) 3 :both)))