Saher's page

Thoughts on shells

Shells are ubiquitous, yet I don’t see their design being discussed as frequently as I like. They have stayed roughly the same across the years, and I feel like we can address a few things about them.

Shells are almost always used through a terminal emulator (in canonical mode) 1. Terminals are portable and comfy, but they also leave a lot to be desired. You have a text-only environment that only uses mono space fonts, and it lacks proper UI elements which limits the type of user interfaces you can make: No images nor clickable elements like buttons. Many users object that this is actually a feature because it allows fast keyboard centric UIs, but they’re not mutually exclusive; you can have both. They’re useful in different contexts.

Shells provide little support for multitasking. You typically only run one command at a time that you can suspend through ctrl-z and resume through the fg builtin. However, nobody I know uses the job system, as most users prefer using one of:

Multiplexers are my preferred choice. I rarely ever use a shell outside of tmux. If users always reach out for external programs to use a shell, why can’t shells borrow some of those features and ideas? Multiplexers are powerful, but they also carry a lot of baggage and have performance implications. Why can’t we have a tabs or splits in shells as first class citizens?

Some shells stick to terminals but add a twist to their language. Shells like nushell or powershell introduce objects or structured data into the shell pipeline. This does provide a lot of advantages, but as I have highlighted in my previous post it comes with costs. Moreover, having rich objects increases the complexity of your shell’s language, and shell languages are notorious for being annoying. Small shell scripts can be elegant and concise, but nobody likes to write large shell scripts. There’s a reason why many Linux distros ship with python. Its common to start writing a shell script and switch to python midway once you realize the script has gotten too complicated. I’d like to see shells that optimize for the interactive use case rather than the language use case.

I created an experimental shell called browsh that doesn’t use terminals at all. Instead it uses the browser as its UI, allowing programs to render arbitrary HTML, this includes buttons, forms, etc. It allows us to handle asynchronous tasks for free. You can imagine how this can be extended to allow tabs or splits. Additionally, since writing complex shell logic is unpleasant browsh embeds Lua, a small language built for embedding, letting us write small Lua blocks as if they were any command. They can participate in pipelines and access stdin and stdout just fine, allowing our shell to be expressive without using a bespoke programming language.

Obviously I don’t expect anyone to switch to it, but it highlights the kind of stuff I want to see in newer shells. A kind of shell that bootstraps its own GUI and allows external programs to change the UI’s state. Until then, I’ll be using my trusty zsh + tmux + neovim setup.


  1. emacs’ M-x eshell comes to mind as an exception for a shell designed around a non-terminal ↩︎