Time to pick a new shell: fish, xonsh, elvish, bash, zsh, ksh93
Why aren’t there any good alternatives to bash or zsh? Specifically, a OS CLI shell that does not suffer from the problems inherent in being compliant with the POSIX.2 (aka POSIX 1003.2) standard? And also doesn’t suffer from the other problem that bash and zsh have due to all the configurable behaviors that make it effectively impossible to predict how those shells will behave?
Two years ago I got fed up with zsh and wrote a blog post why. That caused me to look for a saner alternative. I considered xonsh because it is based on Python which is one of my two favorite programming languages (the other being C). But I gave up on Xonsh fairly quickly. I eventually settled on Fish.
I had some misgivings about the Fish project because, as of Sep 2015, it had numerous open pull-requests. Including several more than a year old and quite a few more older than three months. The source code (both C++ and functions written in its native script language) was also a mess with no coherent style and hundreds of lint errors. At least two lint errors identified actual bugs and most of the remainder pointing out code that was hard to understand. Too, there were a couple hundred open issues more than three years old which even a cursory review suggested were no longer relevant. Nonetheless, I chose it as my new day to day shell and started contributing changes to the project. Primarily because its explicit non-adherence to the POSIX 1003.2 standard for shells meant it had far fewer surprising behaviors. It also had several innovative ideas such as every variable being an array even if it contained only a single element.
Near the end of 2015 I was offered commit privileges; that is, becoming a member
of the core development team. I demurred at that time because I wasn’t
sure I was going to use Fish long term. But in late January 2016 I accepted the
invite to join the core dev team. Primarily because the development team
expressed no objections to my requirement that the code style be standardized
and changes to deal with oclint
and cpplint
errors would be merged without
debate.
I spent several months doing things like running the code through clang-format
and oclint
and fixing problems they found. As well as making it easy for
everyone else to do so by adding new build targets like make style
and
make lint
. Once the code was in a better state I felt more comfortable making
more substantive changes.
Fast-forward to today where I am looking for a different shell to be my day to
day interactive shell. Why? There are several reasons. Such as the fact that the
Fish model for I/O redirection and pipelines is broken and results in FAQs from
people who expect saner behavior provided by nearly every other shell. Broken
behavior I accepted till now because I hoped that such problems would be fixed
sooner rather than later. I no longer believe that will happen. Again, why?
Because there have been too many arguments over bike-shedding issues like
whether all uppercase var names like FISH_HISTORY
and FISH_VERSION
should be
renamed to lowercase. Or, in the case of FISH_VERSION, renamed to version
because one developer is a fan of csh (actually tcsh although they never use
that term). More importantly no one but myself seemed to be interested in
setting a consistent vision for future releases with milestones and a roadmap.
Another example: Fish issue #478 was opened five years ago by xiaq to
suggest improving how commands to options are handled. Keep in mind the ID of
the person who opened that issue as it’s important to this blog post. I
commented a year ago asking if we should implement a Fish compatible
getopt
command. Fast forward to today. I opened a new issue asking for
feedback on a different approach to issue #478. Mostly because it seemed
like the DocOpt based solution would never be implemented. Too, I was not
convinced the DocOpt idea was sound. I implemented the argparse
command and
the feedback was uniformly positive and resulted in several subsequent
improvements. The Fish developer I’ve clashed with from the day they were
granted commit privileges then reopened issue #478 and the leader of the
fish project stated they still wanted to try and implement that solution.
I’m wondering if I’ll die of old age before that happens. Especially
since it has been more than two years since the last substantive commit to the
Python DocOpt project which has been open five years, and was the basis for
the fish-shell implementation, and it still hasn’t reached v1.0 release
status.
I have no intention of going back to either bash or zsh as my day to day interactive shell. They have too much baggage and broken behaviors. Zsh in particular suffers from a lack of a coherent vision which has lead to the project incorporating too many incompatible behaviors via configurable options.
So I’m once again looking for an innovative shell that I could use on a
daily basis and would be willing to contribute non-trivial changes to the
project. I took another look at Xonsh but was put off by the fact it has several
pull-requests more than a year old and most of the others are more than a month
old. Too, it has recently implemented things like the cat
command as a builtin
which seems rather pointless.
The current candidate for my new day to day shell is Elvish. It is even
more extreme in its departure from traditional POSIX.2 shells than fish compared
to shells like ksh, bash, and zsh. But therein lies its strength. Elvish is
based on sound programming language principles rather than the adhoc mess that
is the original Bourne shell and all subsequent, POSIX.2, based shells. My
primary concern is how it handles external commands that exit with a non-zero
status. At present it turns that into an exception. Which means Elvish behaves
as if bash/ksh/zsh was running with set -o errexit
in effect. This is great
from a safety perspective. The problem is that there are a large number of
commands which exit with a non-zero status for non-fatal situations. The grep
command, for example, exits with status of one if no lines matched the pattern.
That is rarely a fatal error that deserves raising an exception.