Linux Fu: Alternative Shells

On Unix — the progenitor of Linux — there was /bin/sh. It was simple, by comparison to today’s shells, but it allowed you to enter commands and — most importantly — execute lists of commands. In fact, it was a simple programming language that could make decisions, loop, and do other things to allow you to write scripts that were more than just a list of programs to run. However, it wasn’t always the easiest thing to use, so in true Unix fashion, people started writing new shells. In this post, I want to point out a few shells other than the ubiquitous bash, which is one of the successors to the old sh program.

Since the 7th Edition of Unix, sh was actually the Bourne shell, named after its author, Stephen Bourne. It replaced the older Thompson shell written in 1971. That shell had some resemblance to a modern shell, but wasn’t really set up for scripting. It did have the standard syntax for redirection and piping, though. The PWB shell was also an early contender to replace Thompson, but all of those shells have pretty much disappeared.

You probably use bash and, honestly, you’ll probably continue to use bash after reading this post. But there are a few alternatives and for some people, they are worth considering. Also, there are a few special-purpose shells you may very well encounter even if your primary shell is bash.

Two Philosophies

There are really two ways to go when creating a new shell. Unix and Linux custom, as well as several standards, assume you will have /bin/sh available to execute scripts. Of course, a shell script can also ask for a specific interpreter using the #! syntax. That’s how you can have scripts written in things like awk.

That leads to two different approaches. You can create a new shell that is compatible with sh, but extended. That’s the approach things like the Korn shell (ksh) or the Bourne Again shell (bash) take. On the other hand, you can completely replace the shell with something new like the C shell (practically, now, tcsh which has pretty much replaced C shell). These shells don’t look anything like the classic shell. Of course, neither does bash if you look at the details, but superficially, most things you can do with sh will work with bash, too, but bash adds a lot more.

Korn Shell

David Korn at AT&T wrote a shell that bears his name. If you only know bash, you’d be a lot more comfortable with ksh than with sh. It is a compatible shell, but offers things we take for granted today. For example, ksh provided command line editing, coroutines, and new control structures like select. It also borrowed ideas from the C shell such as history, functions, and aliases.

The only problem with ksh is that AT&T held it pretty close to its chest for years. So even though not many people use ksh today, the ideas in ksh spread to other shells and are widely used today. There is a public domain version, pdksh, if you want to try it out.

Ash and Dash

The Almquist shell, or ash, is basically a clone of the Bourne shell written by Kenneth Almquist. It doesn’t add a lot of features, but it is very small and fast. This makes it a popular choice for tiny Linux distributions like rescue disks or embedded systems. In 1997 Herbet Xu ported ash for use with Debian and it became Dash — the Debian Almquist shell. If you use any of the Debian-derived distributions, you’ll probably find that /bin/sh is a link to dash.

Fish

Fish isn’t named after anyone — not even a TV detective. It stands for Friendly Interactive Shell. Unlike ksh, ash, dash, and bash, fish doesn’t try to be compatible with the old classic shell programs. Instead, it tries to be very user friendly. For example, it automatically suggests things as you type.

A big feature of fish is that it doesn’t implicitly create subshells. Consider this (contrived) example:

SUCCESS=0; cat /etc/passwd | if grep ^kirk: ; then SUCCESS=1; fi

Change “kirk” out for a user in your passwd file and try this under bash. Then print out $SUCCESS and you will see it stays zero no matter what. The reason is the part of the command to the right of the pipe character spawned a new shell. You set the variable in that shell, which then exits and the shell you started in still has SUCCESS as zero. With fish, this doesn’t happen.

If you were setting up Linux for a new user, fish might be a good choice for their default shell. For most power users, though, they’ll want to stick to something more conventional. If you do want to learn more, check out the video, below.

Z Shell

The Z shell is newer, dating from 1990. This may be the most popular shell outside of bash on this list. One of the biggest reasons is that it has a plugin architecture that allows for lots of customization including themes and very sophisticated command line completion. You can edit multiline commands easily. Some plugins even provide things like an FTP client.

Many of the things you get out of the box with zsh can be added to bash, but it would be a lot of work. If you start zsh as sh, it pretends to be sh — a lot of advanced shells do that.

Because of the plugin architecture, there’s something like an app store for zsh called “Oh My ZSH.” If you browse through it, you’ll probably be tempted to try zsh. If you ask a seasoned Linux user — at least in the year 2020 — what shell they use, and they don’t answer bash, they’ll probably answer zsh. If you have an hour and a half to kill, you might enjoy the video below.

And There’s More

There are probably more shells, but ultimately it is a matter of personal preference. One we are watching is Nu shell. It has some interesting ideas about extending the idea of a pipe and stream in Linux. I haven’t tried it yet, but as it becomes more stable, I might. If you are an emacs fan, there is eshell — something I’ll talk about in a future post.

Wikipedia has a good comparison matrix of shells if you are curious. Personally? I use bash, but I am always tempted to learn zsh better. I’ve used all of these at some point except fish. How about you? Leave a comment with your favorite shell, especially if it isn’t on this list.

 

37 thoughts on “Linux Fu: Alternative Shells

  1. Let the shell wars commence….

    I used sh for years because it was the default (only) shell.

    Then I used csh for the next few decades because it added history. Surprised to find no mention of it here.

    bash for the last few years because aain, it’s more or less the default.

    You’re still not going to convince me that there is a need for python though. :)

    1. Yeah, just a tiny “this came from that” mention. Harrumph!
      Back in the early 1990s, I wrote a csh script that was over 10K lines long. It was for analyzing and adjusting the power-supply wire sizing and routing in IC chip designs, based on switching profiles extracted from simulation output. Very cool, and ran like a charm. Crazy stuff! Definitely taught me that you can do nearly anything in nearly any language…if you work at it! It also had a thousand-line sed script that did the netlist analysis.

      Today, yeah, I’m okay with modern IDEs!

  2. I got pretty deep in the Ash shell in an embedded program I wrote for automating hardware running on a tripplite console terminal server appliance (running BusyBox). It was interesting that many familiar command line arguments just simply wouldn’t work or had totally different syntax but with workarounds the whole thing worked great in the end. Even SNMP. one advantage of the Tripplite terminal server was it has hardware based watchdogs that automatically reboot in event of failure. I’ve had dozens of these embedded systems out in extremely remote locations (which would cost thousands of dollars for a truck roll) for many years without having a reported outage.

  3. tcsh? It was my preferred shell for a long time. Whenever I got a shell account anywhere I went to great lengths to make it my interactive shell, if it isn’t. Now, I just use bash or ksh.

  4. I’m also surprised that you didn’t mention csh and tcsh. I was an early user of BSD Unix, and later SunOS and Solaris, and used csh for a long time. I finally gave up on it for scripting because some things were just impossible, due to various design flaws and bugs.

    Also of note on zsh, it is now the default shell for new users on maOS Catalina. Apple has been shipping a very old version of bash, due to the change to GPL v3, and while it was possible to install newer versions via Homebrew, etc., that was more than most users wanted to bother with. I haven’t tried zsh yet, but I plan to give it a shot when I update my Macbook later this year..

  5. Bourne shell all the way, it is the only way to be sure than anything you script will function on any flavour of UNIX with minimal tweaks. And when you have scripted on DEC Ultrix, SunOS, SCO, Solaris, HP-UX, AIX, *BSD, Linux and a few less common flavours, having a well consistent beachhead helps a lot.

    1. And whale oil is truly the only legitimate way to read a book! You are right, having a common shell makes life easier and thank goodness every modern Unix system ships with bash (even AIX!) so we don’t have to work in 2020 with a shell that was written to run on PDP-11.

  6. You have a point, having to write scripts for Linux, *BSDs and AIX, I go with the lowest common denominator, which for me is ksh. AIX sh is now ksh, at least in the newer versions. SunOS was so long ago I barely remember it, but it was bourne sh there.

    Still there are even gottas between the various ksh flavors :)

    Interactive, tcsh for me. I tried bash/zsh many times, and though good, there are 2 little things tcsh does that I cannot get working 100% in other shells. But zsh came the closest.

    1. Big fan of fish too. There’s a learning curve for sure, but it works quite well. The only “problem” I’ve found is that it can be quite hard to find relevant info when googling for a problem (“fish shell” tends to turn up shellfish allergies).

  7. I just stick to the default which is bash usually. Any programs I write for bash are usually one liners. Say mounting a nfs share after booting, setting up a VNC session, or using rsync to backup a directory. Anything more than that and I’ll write it in Python. At work I changed over most all the batch files (windows) and helper vb scripts to unified, readable, and maintainable Python applications. Has worked out very well. I still use batch files of course, but they are now used to execute a couple commands to save time (like cd here, build this, copy result to there, cd back to where I was. Done) .

    1. I was just thinking whether to waste my time on Windows shell scripts or just go with Python which will also work on Linux. Thank you for sharing your experience.

  8. There’s no such extant thing as “pdksh” anymore; development has mostly been picked up by mksh, along with the OpenBSD pdksh fork. mksh can be found at: https://www.mirbsd.org/mksh.htm and some portable OpenBSD versions are available at https://github.com/dimkr/loksh and https://github.com/ibara/oksh – of these mksh is probably the most featureful and useful, and is a mostly drop-in replacement for any ksh88/POSIX sh script or interactive environment.

    Note, however, that pdksh derivatives are all more like the classical “ksh88” than “ksh93.” The Korn shell grew a number of very useful features – associative arrays, floating point math, etc. – in the newer ksh93 version. Bash has adopted some of these, but not all, and the pdksh continuations/clones will likely stick with ksh88 compatibility. The old AT&T AST repo has relatively up-to-date (and open source) ksh93: https://github.com/att/ast and the ksh2020 branch has a build system not stuck in 1993: https://github.com/ksh2020/ksh

    Regarding ash, if you’ve used embedded Linux, you’ve probably used Busybox, and the primary shell is a fork of ash. NetBSD has an ash derivative as well. Debian’s dash is fast – extremely so, compared to Bash. ash is a good option if you don’t need features or desire strict POSIX compliance/absolute performance.

    I started out using Bash 1.x, which didn’t have a *ton* of features over regular Bourne shell or ash, so I switched to csh then tcsh for a long while. Don’t recommend it, far too quirky. ksh93 was and is a great shell, but Bash is available basically everywhere, and with the 4.x release (over a decade ago) introduced associative arrays, which is the killer feature for scripting so Bash is what I use, as stock as possible. In the bad old days, commercial Unix vendors tended to add features to either ksh or (t)csh, so you used what they gave you; frankly, I’m glad that’s not the case anymore.

    zsh is very, very nice but in the same way I’d prefer to not (have to) use every module under the sun in a new piece of software, or every plugin in my Vim config, I’d honestly rather not entertain the idea of an “app store” in my shell. If that’s your thing, good on you! fish seems fine as well, some of my LISP-y pals use it but it’s not my bag.

  9. `eshell` definitely is a nice tool to explore Emacs from it’s inside using familiar shell syntax and for lots of stuff it is enough for occasionally launching make or other straight forward oneliner stuff. Optionally you can address your shellish problems in the 2nd nature of eshell: Being kind of a Lisp repl.

    Don’t expect TUI stuff tu run in it. But you are using Emacs, so you don’t need an editor in eshell, just add an alias from ‘find-file-other-window $1’ to a new name like… eeeeehhhhmm… let’s say `emacs`? You miss midnight-commander? `dired somedir` saves your day. Looking at eshell ends with fatal rating if you don’t look at Emacs as a whole. Just ditch the idea to handle eshell in a series about plain shells.

  10. Ash is the default shell in Alpine Linux, which is very common in cloud computing (e.g. Docker, Kubernetes, AWS, GCP, Azure…). It’s a little odd at first when you’re used to Bash, but it’s way nicer than traditional /bin/sh and it’s much lighter than Bash.

    Come to think of it, cloud computing shares a lot of characteristics with embedded computing: minimized image sizes, fast boot times, read-only file systems, remote deployments and upgrades…

    I suspect that many embedded developers can make the switch to the cloud easier than typical desktop or enterprise server developers can.

  11. * Comparison of command shells – Wikipedia (this a fun page)

    https://en.wikipedia.org/wiki/Comparison_of_command_shells

    * What are the default shells in FreeBSD?

    https://www.freebsd.org/doc/en/articles/linux-users/shells.html

    Linux users are often surprised to find that Bash is not the default shell in FreeBSD. In fact, Bash is not included in the default installation. Instead, FreeBSD uses tcsh(1) as the default root shell, and the Bourne shell-compatible sh(1) as the default user shell. sh(1) is very similar to Bash but with a much smaller feature-set. Generally shell scripts written for sh(1) will run in Bash, but the reverse is not always true. However, Bash and other shells are available for installation using the FreeBSD Packages and Ports Collection.

    * OpenBSD:

    https://www.reddit.com/r/openbsd/comments/6zr2ps/whats_the_default_shell_in_openbsd/

    $ echo $SHELL
    /bin/ksh
    $ ls -alh /bin/sh
    -r-xr-xr-x 3 root bin 464K Apr 1 21:38 /bin/sh
    $ ls -alh /bin/ksh
    -r-xr-xr-x 3 root bin 464K Apr 1 21:38 /bin/ksh

    * I think NetBSD is the same as FreeBSD, tcsh(1) as root, and sh(1) for users, or it might be just sh(1) for both. I’m not sure.

  12. `eshell` definitely is a nice tool to explore Emacs from it’s inside using familiar shell syntax and for lots of stuff it is enough for occasionally launching `make` or other straight forward one liner stuff. Optionally you can address your shellish problems in the 2nd nature of `eshell`: Being a Lisp repl.

    Don’t expect TUI stuff tu run in it. But you are using Emacs, so you don’t need an editor in `eshell`, just add an alias from ‘find-file-other-window $1’ to a new name like… eeeeehhhhmm… let’s say `emacs`? You miss `midnight-commander`? `dired somedir` saves your day. Looking at `eshell` ends with fatal rating if you don’t look at Emacs as a whole. Just ditch the idea to handle `eshell` in a series about plain shells.

  13. A pretty interesting shell I’ve come across a while ago is es. Roughly speaking, it’s a Plan9 rc-Scheme crossover quite innovative in its particular ways. It does lack a few things on the interactive side, though, if I remember correctly. Also, development effort is limited to keeping it usable on current Unix(-like) operating systems now. But then, there’s an interesting offshoot named XS, which is in “active development”, apparently.

  14. I’ve used fish quite extensively over the years, and I’ve worked at a lot of places that standardised on ksh, and got very comfortable with it. However, because I’m a gentoo user, it tends to work a lot better with bash, so I’ve normally ended up drifting back to bash after a while.

    I think that shells are often something that gets forced on you; by your workplace, by the choices of distribution maintainers, etc. If I had the choice, I’d stick with fish.

  15. At work I got tossed in to help people whose day-to-day was command line linux stuff. Until I got there they were just transposing commands verbatim without understanding what they did. I poked around a bit and found none of my normal tricks worked, no tab completion, no up arrow to the past command. I poked around some more and found they were running an original version of ksh on solaris 10. This was 2017. Invoking /bin/bash got me what I expected. a 3×5 card that said “/bin/bash, to complete a command, up arrow to see the last command” saved innumerable man hours, I never got to teach them much more before I was moved to another team.

  16. The largest one is with a file called x.txt with this record:
    1 2 3
    on AIX (and ksh93) this works
    cat x.txt | read a b c

    Variables a b and c is set. in bash other ksh shells that does not happen

  17. Well it was ^d and list only directories in my current directory, added a whole lot of things to ~/.zprofile and quite close, but once in a great while it will show a dir in $CDPATH. I even asked in reddit r/zsh and no one knwe how.

    ^K, for the longest time, what I yanked would get overwritten by whatever was last erased by ^H. Figured that out by changing the default binding to:

    bindkey “^?” backward-delete-char

    In my opinion, the default zsh value for that is nuts :)

    For bash, there are a few additions that drive me really nuts that I have forgotten about.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.