Improvements over other shells

Luish improves on the traditional shells in ways small and large.

A modern plugin architecture

In luish, plugins are part of the shell and managed in a modern way: declaratively and reproducibly, with a lock file that records the exact commits of each plugin.

  • plugin add adds a plugin from GitHub, another git repository or a path to config.toml and fetches it.

  • A plugin’s plugin.toml lists its dependencies, which are loaded first, and can set options, aliases and key bindings.

  • A plugin can have an extension, code written in Rhai that runs inside the shell, avoiding the overhead of starting a separate process (see Extensions).

  • Plugins are opt-in: a shell that loads none, as every script, pays nothing for them, and a plugin written only in shell doesn’t start Rhai.

Remote shells that feel local

Over ssh, every key goes to the server and back before it shows, so on a slow connection typing lags and the shell feels sluggish. luish --ssh HOST splits the shell in two: the line editor runs on your machine, and the commands run on HOST. Typing, moving in the line, searching the history and the completion menu are instant, while Tab completion, the prompt and everything your startup files set up still come from HOST. Full-screen programs such as vim work as over ssh.

luish --ssh myserver                  # copies luish to myserver, if needed
luish --ssh -p 2222 me@myserver       # ssh options go before the host

luish needn’t be installed on HOST: luish --ssh copies itself there the first time (see Remote shells over SSH). It uses ssh as it is, with your keys and ~/.ssh/config, and needs no daemon or extra ports.

As fast as dash, with zsh’s features

bash and zsh are up to five times slower than dash on scripts that run mostly inside the shell, which is why many systems use dash as /bin/sh. luish runs scripts as fast as dash.

On the other hand, luish has many of zsh’s functionality when running interactively and can replace it for interactive use.

bash’s and zsh’s scripting extensions, at dash’s speed

POSIX sh has no arrays, [[ ... ]] or ${x/pattern/replacement}, so scripts that need them are written for bash or zsh, which are slow. luish runs them, with the same syntax and mostly the same behaviour, about five times faster than bash or zsh (see Performance). Indexed and associative arrays, typeset, [[ ... ]], zsh’s parameter flags and bash’s ${!name} are always available, since they give a meaning to what is a syntax error in dash, so POSIX scripts keep working. See Shell language extensions.

Instant startup with cached startup files

Startup files tend to grow: conda init, nvm, pyenv, completion systems and prompt frameworks each add something that runs whenever a shell starts. This can add seconds to the startup time. Lazy-loading tricks can reduce the time, but they add complexity and fricton. Luish instead caches the environment so that new shells can start in a few milliseconds.

Modern configuration

Instead of a script full of setopt lines with names such as hist_ignore_space, luish’s settings can go in a TOML file, ~/.config/luish/config.toml, with its options, aliases, key bindings and plugins:

[options]
autosuggest = true

[options.history]
file = "~/.histfile"
share = true
ignore_space = true

[alias]
ll = "ls -l"

[bindkey]
Up = "up-line-or-history"

[plugins.enabled]
std.completion = "*"         # completion for common commands, and git

luish’s options are hierarchical: they are named in groups (history.share, glob.star, cd.auto), which fits nicely with into TOML groups (zsh old names are still supported as synonyms).

Newly installed commands are found

Shells remember where they found each command in PATH, so they don’t have to search every directory each time you run it. The hash built-in shows that list. In dash, bash and zsh, the list stays as it is until you change PATH or run hash -r (rehash in zsh), even when the directories change on disk. Some cases that go wrong:

  • A new command shadows an old one. You ran python (found in /usr/bin), then installed another one in ~/.local/bin, which comes earlier in your PATH. The shell keeps running /usr/bin/python.

  • A profile is switched. Nix puts a directory in PATH that is really a symlink to the current version of your profile. Switching profiles can add a command that shadows a remembered one.

luish checks whether PATH directories have changed after each command line you enter (not its contents, which can be slow, just mtimes) and automatically runs hash -r if they have. You should never need to run hash -r manually.

The same check keeps Tab completion of command names up to date: new commands in PATH are offered as soon as they are installed.