The Shell
This document provides information about the command interpreter – or shell – that we use when logging into an Alpine Linux system via the command line interface. It also explains how to install and configure the Z Shell (zsh).
Purpose and Choices
Whenever you log into a Linux machine on the command line, you’re used to seeing a prompt at which you can type commands. The program that produces this prompt is called the shell, and multiple different shells are available. Each shell has its own unique features, and shells often operate a bit differently from one another.
So far, we have been using the Almquist Shell3 that is provided by BusyBox.4 On Alpine Linux, the Almquist Shell is the default shell, and it is located at /bin/ash (which is a symlink to the BusyBox binary). This shell is an improved variant of the original Bourne Shell, which was developed at Bell Labs in the late 1970s.5 Since Ash uses a superset of the original Bourne shell syntax, the /bin/sh (Bourne Shell program) is also linked to the BusyBox binary. Ash is capable of running scripts designed for the original Bourne shell in addition to scripts targeting it directly. Another variant of the Bourne shell is the KornShell, which was also developed at Bell Labs.6
Most mainstream Linux distributions ship the GNU Bourne Again Shell (bash)7 as the default shell. One side-effect of this choice is that a lot of otherwise Bourne-compatible shell scripts contain “Bashisms,” or specialized syntax that only really works with Bash. Bash is also a much slower shell than Ash when executing shell scripts, which is why the Debian distribution ships their own version of Ash – the Debian Almquist shell (Dash) – for running scripts.
In addition to the Bourne shell and its derivatives (such as Ash, Bash, and Dash), there are entirely different classes of shell available, which have little to no compatibility with the original Bourne shell. Most of these shells are based on the C Shell, which was developed at the University of California, Berkeley.1 C shell (/bin/csh) and its variants (like /bin/tcsh) are often found in commercial UNIX systems and BSD systems.
For day-to-day operations, I use and recommend the Z Shell, which was originally created at Princeton University.2 Zsh is a Bourne shell derivative and is therefore capable of running Bourne shell scripts. However, it adds a number of features from other shells (including KornShell and Bash) while retaining good performance. Zsh is also highly programmable and customizable.
Installing the Z Shell
For the purposes of this tutorial, I am assuming that you have already set up a regular user account and are running as your regular user. I also assume that you have the doas command configured. To install the Z Shell, run:
doas apk add zsh
Enter your regular account password (not the root password) when prompted by doas.
Switching to the Z Shell
In order to switch our user account to the Z Shell, we first need to install the shadow package in order to obtain the chsh command:
doas apk add shadow
Run the following command:
chsh
You will be prompted for the path of the new shell. Type in the following:
/bin/zsh
then press Enter.
Now log out of the existing ash shell by running:
exit
Log back into the system as your regular user. Confirm that you’re now running the Z Shell by executing:
echo ${SHELL}
Your change of shell has been successful if the value of this SHELL environment variable is /bin/zsh.
Configuring the Z Shell
Let’s begin with a simple Z Shell configuration. The zsh configuration file lives at $HOME/.zshrc, which makes it a hidden file (starts with a dot) at the root of our user’s home directory. We would typically pronounce the name of this file as “dot zee shark” or just “the zee shark.” The rc ending is an ancient convention from the Multics shell of the 1960s, in which commands that should be run every time the shell started would be included in the macro.8 Start editing this file by running:
vi $HOME/.zshrc
Let’s begin by allowing Zsh to save command history between sessions, which is useful for system administration purposes, since it allows you to go back later and see which commands you typed previously. Zsh uses several environment variables for this purpose, which we can set at the top of the file to save our last 1000 commands to $HOME/.histfile:
HISTFILE=~/.histfile
HISTSIZE=1000
SAVEHIST=1000
Now let’s add some more lines to turn on useful features, such as extended globbing and the completion system:
setopt extendedglob
autoload -Uz compinit
compinit
To make command lines easier to edit, we need to enable Emacs-like editing mode and bind our Home and End keyboard keys to do what we expect. Note that the Home and End keys produce escape sequences that can be rebound using the bindkey directive in the Zsh configuration, so the syntax looks a little odd:
bindkey -e
bindkey '^[[H' beginning-of-line
bindkey '^[[F' end-of-line
Now let’s set a shell alias for the ls command to enable colorized output. We can also set our PAGER variable to the less command. Programs that understand PAGER and produce too much output to fit on the screen will be run through less so that we can scroll up and down through the output. We can also set our default editor to vi.
alias ls='ls --color=auto'
export PAGER=less
export EDITOR=vi
Finally, let’s make a cool command prompt:
PROMPT=$'%(?.%F{green}\U221a.%F{red}%?)%f %B[%n@%m:%~]%#%b '
That prompt is a bit to unpack, but:
- The $’ indicates the start of a string that needs to interpret escape sequences. This string runs to the closing single quote (‘).
- The %(?. . ) is an if-then-else that uses the exit status of the previous command (?) to select a piece of the prompt. If the last exit status was zero, indicating a success, then the part after the first period (.) is used. Otherwise, if the exit status was non-zero, indicating an error, then the part after the second period is used.
- The %F{green} changes the foreground color to green.
- \U221a is a Unicode escape sequence that represents the square root sign (√). Since we turned on escape sequences with the initial $’, the √ will be displayed if this branch of the if-then-else is selected.
- On the else side of the if-then-else, the %F{red} changes the foreground color to red.
- The %? displays the last command exit status code (which is a number).
- After the end of the if-then-else, the %f restores the foreground color to its default setting.
- The space leaves a literal space.
- The %B changes the font weight to bold.
- The [ displays a literal [ symbol.
- %n displays the current user’s username.
- @ displays the literal @ sign.
- %m displays the hostname of the system.
- The : displays a literal colon.
- %~ displays the current working directory with the part that consists of the user’s home directory replaced by a tilde (~).
- The ] displays a literal closing bracket (]).
- The %# displays either a % sign for a regular user or a # sign for the root user.
- The %b removes the bold font weight and restores the normal font.
- The prompt ends with a literal space in order to leave a space between the prompt and the spot where commands are typed.
Putting the whole file together, our $HOME/.zshrc file should look something like this (I put blank lines between the sections):
HISTFILE=~/.histfile
HISTSIZE=1000
SAVEHIST=1000
setopt extendedglob
autoload -Uz compinit
compinit
bindkey -e
bindkey '^[[H' beginning-of-line
bindkey '^[[F' end-of-line
alias ls='ls --color=auto'
export PAGER=less
export EDITOR=nvim
PROMPT=$'%(?.%F{green}\U221a.%F{red}%?)%f %B[%n@%m:%~]%#%b '
Loading the Z Shell Configuration
To test your Zsh configuration, save and exit from vi Back at the command prompt, run (note that the command is a single dot!):
. $HOME/.zshrc
If you did everything correctly, your prompt should now begin with a green square root sign. To show the red error code for unsuccessful commands, you can run a command that always returns nonzero status:
false
The prompt that follows should have a red 1 at the beginning. This prompt setup is quite useful, since it explicitly shows when a command has failed to run properly. The prompt configuration I supplied is the one I use on my own systems.
Additional Customization
There are a wealth of possible customizations for Zsh, including even fancier command prompts, add-ons, and other functionality. Some resources include:
- Prompt Expansion
- Customizing Zsh Prompt
- Creating a custom Zsh prompt from scratch
- Character Highlighting explains colors
- List of Unicode Symbols - other symbols that can be used in place of the square root. Replace \U221a with \U followed by the number from this table. Note that the Linux console cannot represent all characters, so you won’t be able to use the poop emoji in your prompt.
- Oh My Zsh
You can also enable other useful shell features by installing additional packages. For example, completions for various programs can be added by running:
doas apk add zsh-completions
Other packages can be found by using the Alpine Package Index and searching for zsh*. Be sure to change the version from Edge to the release that you’re using in the first dropdown, and set the architecture to your system architecture in the third dropdown. Click on the package name to see a short description and a link to the upstream project.
Notes and References
-
William Joy. An Introduction to the C shell. ↩
-
Kenneth Almquist. v19i001: A reimplementation of the System V shell, Part01/08. Rich Salz (moderator). ↩
-
Stephen Bourne. Keynote. The Technical BSD Conference (BSDCan2015), Ottawa, ON, June 12, 2015. ↩
-
“Korn KSH - A Shell Programming Language.” USENIX Association, Toronto, ON, July 14, 1983. ↩
-
Louis Pouzin. “The Origin of the Shell.” Multicans. November 25, 2000. ↩