What makes a shell?

I recently (re)discovered the Oil shell. The last time I saw it, it was trying to write a language to subsume Bash, Awk, and Make.

However, I came back to it doing nearly accomplishing two smaller goals:

  • Writing a fast, statically-parsed shell (osh) as close to Bash-compatible as possible.
  • Developing a stricter subset of Bash from which to build a new language (oil).

And I really like it.


As is probably obvious from this blog or my GitHub profile, I am a big fan of Zsh. I find it easier to script in and more flexible to interact with. My biggest go-to’s are the default behavior of $array, the numerous parameter expansion, subscripting, and globbing flags which are available, the ZLE and completion system, and the loadable modules like zsh/files, zsh/stat, and zsh/zutil.

But after looking at all of these (and more), I realized that Zsh is a pretty big launguage.

As the Oil project is finishing its journey to osh and beginning its journey to oil, I figure it is the perfect time to look at Zsh and ask, “What makes Zsh great?” “What features from this shell would be sorely missed were I to use Oil instead?”

I will rank each feature on a scale from 0 to 5.

Language Features

First, let’s look at features which are not exclusive to interactive sessions.

Globbing flags

I use globbing flags a fair bit. However, with some exceptions, they aren’t irreplaceable. To begin with, many globbing flags are too situational to be common in interactive sessions. For most flags and options, one of the following paradigms can be used instead:

# Oil extends Bash, so mapfile is a builtin
mapfile -d '' files < <(find -print0 . $find_options)
for file in "${files[@]}"; do
	...
done
while read -r -d '' file; do
	...
done < <(find -print0 . $find_options)

A single external command covers most globs and 70% of globbing flags. The notably absent are the (o) and (O) flags, which can sort the arguments in ascending or descending order by size, name, link count, or (a/m/c)time. Also missing is (Y) to shortcircuit the glob after a certain number of files and the (d) flag to restrict the device number to examine. The (e) and (+) flags take shell code to additionally filter the list.

For all of these flags, only (Y) is irreplaceable in all cases. In GNU find, the -printf option can allow find to print certain file stats. This can allow sorting after the fact with a simple | sort -z instead of expensively calling the program stat on every file. This can remove the need for (o) and (O), as well as (d) since device number is a printable property.

The (e) and (+) flags can be accounted for by filtering the resulting array in a loop if need be.

Conclusion:

  1. (Interactivity:) Flags serve a niche purpose in interactive sessions. Personally, I find myself using the filetype flags more than others, but a few find aliases would make the paradigm manageable in interactive mode. However the for file in $~glob; { ... } paradigm is much more natural.

  2. (Efficiency:) To make up the capability of globbing flags on a machine with GNU coreutils costs one or two external programs: one call to find, zero or one calls to sort.

Verdict: 1/5

Parameter Expansion Forms

Conclusion:

Verdict: ?/5

Parameter Expansion Flags

Conclusion:

Verdict: ?/5

Parameter Subscript Flags

Conclusion:

Verdict: ?/5

Builtins provided by modules

Conclusion:

Verdict: ?/5

Interactive Features

Deferred execution (zsh/async)

Conclusion:

Verdict: ?/5

ZLE Widget API

Conclusion:

Verdict: ?/5

Completion widgets

Conclusion:

Verdict: ?/5