A review of UNIX shells

✍️ → Written on 2020-08-16 in 1369 words. Part of cs software-development Linux

Motivation

During the vacation, I read the Linuxuser magazine copy from March 2018, I got from Grazer Linuxtage 2018. It mainly concerns shells and mentions some of them. I am unsatisfied with my current zsh setup, so I wanted to discover alternatives. I booted a current xubuntu 20.04 from USB stick and installed the shells to test them. I failed to identify criteria to compare them, but I made notes for each of them. So consider this as an unstructured like. The summary should be somehow helpful though.

Besides those introductory articles, I used awesome-shell and this curated list to get a list of software to review.

Fundamentally, POSIX' string-based argv was certainly appropriate in the 80s, but these days it fundamentally limits the way to pass arguments to CLI tools. On a basic level, each program needs to parse the given array of string into an associative array of key-value pairs where a type mismatch needs to return proper error messages. Hence, each programming language features 20 different CLI parsing APIs with varying degree of quality. In a POSIX shell, everything is considered as byte sequence. A pipe uses the newline character to split byte sequences. These days, I am not the only one missing proper data types, type checking, syntax highlighting and improved visualizations for command output like in general programming languages.

Before this review, I used the zsh in combination with grml’s config. De facto, I use about 0.5% of the features and cannot tell which parts are contributed by grml and which parts are contributed by zsh. I am not a big fan of studying DSLs (domain-specific languages) for every task. After this review I recognized that I barely use any features of the grml config. Helpful features for me are history search via Ctrl+r, hashes and functions.

Comparison notes

Table 1. Shell comparison notes
shell remarks

bash

advanced korn shell (ksh)

busybox

minimalistic shell

dash

minimalistic shell, provided as /bin/sh in debian

elvish

interactive shell, syntax highlighting, DSL, pipe semantics extended for structured data, awesome navigator mode, Ctrl+R history lookup, prefix notation arithmetical operations, functional immutable data structures, well-thought through, implemented in Go

esh

minimalistic LISP-based shell

fish

interactive shell with syntax highlighting, tab completion for file & folder paths, syntax is POSIX-inspired but simpler, w/o Ctrl+R for history search, filepath is cd-per-default, unicode characters via \uXXXX, with Cartesian products for lists and proper range syntax for lists

ion

rust-based simplified syntax, tab completion for files/dirs broken in xfce4-terminal, neat brace expansion, useful ranges, suugestions by history, Ctrl+R history search, rust-like match statement, custom but POSIX-like redirections, optional type checking for functions

ksh93

shell giving the basis of POSIX

mksh

POSIX-like shell used by OpenBSD

nushell

shell with great representation skills for tabular data, syntax highlighting, auto suggestions for commands and filesystem paths, no tab selection, rust style error messages, no functions, no variables, implemented in rust

oksh

portable OpenBSD ksh, based on pdksh

osh

awkward POSIX-shell extensions with shitty documentation

posh

featureless shell with 9 custom builtin commands

pdksh

public domain implementation of korn shell

shell++

OOP shell with large C++ code stack to build, custom glob patterns, python/Go/Javascript-inspired syntax and functions, no tab-completion, broken documentation examples (' used but " required), lacks string representation for various types

tcsh

“C shell with file name completion and command line editing”, 12-step compilation instructions requiring adjustments for Ubuntu 20.04, stopped to look into compilation, feature-less POSIX-alternative shell

xonsh

very advanced python-based shell with autocompletion, pipes, I/O redirection, color syntax support, Ctrl+R history search

yash

Neat POSIX-compatible shell with many builtin functions and syntax highlighting, Ctrl+R search, tab completion for CLI arguments

zsh (ohmyzsh)

advanced DSL on top of POSIX shell, rjk & linuxonly & trapd00r themes are okay

So, what is my conclusion? Which shell shall I pick?

  • Do you need a POSIX shell or want a shell with few main memory requirements?

    • Yes ⇒ Really?

      • Yes ⇒ Do you want to study a lot of DSL syntax and thus be powerful?

      • No, not in all details ⇒ fish

    • No ⇒ What do you prefer?

      • consistent|functional ⇒ elvish

      • know-pythonfeature-rich|heavy-runtime ⇒ xonsh

      • intuitive-representation|unextendable ⇒ nushell

elvish examples

Technically I think, xonsh is best used for my work [cryptography research] (easy data manipulation with python), elvish is an interesting try and yash is best for my server systems. In short, I am going to try elvish. First of all, I had to migrate my zsh configuration to elvish. Here are some examples to give you an idea of elvish:

mem() { ps -eo rss,pid,euser,args:100 --sort %mem | grep -v grep | grep -i $@ | awk '{printf $1/1024 "MB"; $1=""; print }' }  # zsh
fn mem [@p]{ e:ps -eo rss,pid,euser,args:100 --sort %mem | grep -v grep | grep -i "$@p" | awk '{printf $1/1024 "MB"; $1=""; print }' } # elvish

hash -d X="PATH"  # zsh
X = "PATH"        # elvish, variable as fallback

export GOROOT="/opt/go/"
set-env GOROOT "/opt/go/"

And here I wrote a function to strip the file extension off a file path.

fn strip-file-extension [fp]{
  use str
  i = (str:last-index $fp ".")
  if (eq $i -1) { put $fp }
  else { put $fp[0:$i] }
}

Of course this exists in zsh with just two characters to add, but in elvish we only have string operations and the filepath semantics need to implemented separately. You can look up the details in the quite acceptable elvish documentation.

Outlook

In theory, I look for a shell which uses a strict type system. I was wondering which data types would be interesting to consider when designing a shell. My results (obviously heavily inspired by programming languages):

  1. bool

  2. integer

  3. float

  4. UTF-8 strings

  5. byte array

  6. file path

  7. URL

  8. file size (convertable to various units)

  9. exit code

  10. semantic version number

  11. n-dimensional table (where n=1 corresponds to a list)

  12. set

  13. JSON/TOML/CSV/INI/YAML/XML abstraction

Update 2020-09-01: I gave a lightning talk about xonsh at PyGraz.