Zsh shell installation and configuration on Linux

The Z-shell (zsh) is a modern and very powerful shell: it incorporates and extends many feature of other shells, like Bash. Although it can be used as a powerful scripting language, it is mainly aimed at interactive use, since one of its more prominent feature is the advanced tab completion system. In this tutorial we see how to install zsh in the most commonly used Linux distributions, see what are its startup and shutdown files and how to perform the basic configurations.

In this tutorial you will learn:

  • How to install Zsh on the most used Linux distributions
  • What is the difference between interactive,non-interactive, login and non-login shells
  • What are the Z-shell startup and shutdown files and in which context they are invoked
  • How to perform the basic shell configurations
  • How to setup PATH when using zsh
  • How to set zsh as the default shell
Zsh shell tutorial with examples
Zsh shell tutorial with examples

Software requirements and conventions used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution-independent
Software zsh
Other Root privileges for installation
Conventions # – requires given linux-commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux-commands to be executed as a regular non-privileged user

Installing Zsh

Performing the installation of Zsh is a very easy task, since it is available in the official repositories of the most used Linux distributions. To install the package on Debian or one of the many systems based on it, we should run:

$ sudo apt-get update && sudo apt-get install zsh

To perform the installation on Fedora, instead, we would use the dnf package manager:

$ sudo dnf install zsh



On Archlinux, instead, to install software packages, we use pacman:

$ sudo pacman -Sy zsh

Few seconds and zsh should be installed. Before start talking about how to configure it, let’s take a moment to remark the distinction between the different “type” of shells we can use; this will help us understand the role of zsh startup files.

Type of shells

We can basically distinguish the following shell types:

  • interactive
  • non-interactive
  • login
  • non-login

An interactive shell, as its name suggests, is what we normally use when we launch a terminal emulator: its input and error outputs are connected to said terminal. A non-interactive shell, instead, doesn’t accept user input. When a command is launched from inside a script, for example, a non-interactive shell is used.

The further distinction we have, is between login and non-login shells. A login shell is what is invoked when we login into the system via a TTY or via ssh. In such cases we are working in an interactive login shell. A non-login shell, instead, is any other type of shell used once we are logged into the system (unless a login shell is invoked explicitly).

Grasping the above distinctions will help us better understand in what context zsh startup files are used. Let’s see what they are.

Zsh startup and shutdown files

The system-wide zsh startup files are the following:

  • /etc/zshenv
  • /etc/zprofile
  • /etc/zlogin
  • /etc/zshrc
  • /etc/zlogout

Each one of the files above have its per-user equivalent. User-level configuration files are located in the directory path set as value to the ZDOTDIR variable. If the value of this variable is an empty string, files are assumed to be in the user HOME directory. Per user configuration file names starts with a ., so they are hidden (dotfiles):

  • .zshenv
  • .zprofile
  • .zlogin
  • .zshrc
  • .zlogout

The /etc/zshenv and .zshenv configuration files are used to define environment variables. They are always invoked each time a zsh session is started, therefore they should contain the less possible content. Only commands that don’t produce output should be written in these files.

The /etc/zprofile and .zprofile startup files are read when a login shell session is started, and can be used to run commands to setup that specific context. When using interactive shells they are executed before /etc/zshrc and .zshrc.

The /etc/zlogin and .zlogin files, are invoked when login shell sessions are started too. When using interactive shells, however, they are executed after /etc/zshrc and /.zshrc. While they can be used together with the “profile” files, should be considered as an alternative to them.

The /etc/zshrc and .zshrc files are invoked when an interactive shell session is started. They are basically the counterpart of the /etc/bashrc and ~/.bashrc files for the BASH shell.

Finally, the /etc/zlogout and .zlogout files are run when a login shell session is closed. The latter is run before the former.

System-wide configuration files are read before their user-only counterpart, so here is the global order in which configuration files are read. When using interactive, non-login shell sessions:

  1. /etc/zshenv
  2. ~/.zshenv
  3. /etc/zshrc
  4. ~/.zshrc

When using interactive, login shell sessions:

  1. /etc/zshenv
  2. ~/.zshenv
  3. /etc/zprofile
  4. ~/.zprofile
  5. /etc/zshrc
  6. ~/.zshrc
  7. /etc/zlogin
  8. ~/.zlogin
  9. ~/.zlgout
  10. /etc/zlogout



When using non-interactive, non-login shell sessions (for example when a command is launched from a script):

  1. /etc/zshenv
  2. ~/.zshenv

First zsh configuration

The first time we start an interactive zsh shell session (for example by running zsh in a terminal), if no configuration files exist for our user, the zsh-newuser-install script is launched. It is meant to help us creating our first setup:

The zsh-newuser-install main menu
The zsh-newuser-install main menu

As we can see, to proceed we just should press (1). If we decide to do so, we will be prompted to select what aspect of the shell we want to configure:

The zsh-newuser-install configurations menu
The zsh-newuser-install configurations menu



My suggestion is to proceed with the basic setups, and than examine what is generated in the startup files in order to better understand how things work under the hood. Once this is done, we can always tweak things further, by, for example, looking at other users configuration files stored on github, or similar sites.

Configuring history

To configure how history is handled we would press (1) when the above menu is displayed. This would lead us the to the following screen:

Zsh-newuser-install history configuration menu
Zsh-newuser-install history configuration menu

The value of three environment variables is displayed. We talked about how to configure bash history in a previous tutorial, so those variables should appear familiar to us. The first one HISTSIZE, contains the number of lines of history which are kept in memory, the second, HISTFILE is used to define in what file history is saved when the shell session is closed. Finally, the value of the third, SAVEHIST, is the number of lines to be kept in the history file.

Setting variable value
Setting variable value

To edit one of those values, all we have to do is to press the corresponding key. For example, to change HISTSIZE we would press (1). We will be prompted to insert the desired value:

Settings are not permanently saved until we return to the main menu (0) and choose (0) Exit, saving the new settings... again.

Configuring the PATH

As we know, the PATH environment variable contains the list of directories in which programs and executables are searched by default, so that they can be launched without having to specify their absolute path. The method we use to add directories to our PATH when using Bash is to list them separated by the : character in the ~/.bash_profile file. For example to add the ~/.local/bin directory to our PATH we would write:

export PATH="$HOME/.local/bin:$PATH"

When using zsh, we define our PATH in a different way. Directories which should be included are specified using an array inside the ~/.zshenv file. To do the same thing we did in the previous example, we would write:

path=("$HOME/.local/bin" $path)

With the above configuration a problem would arise, since at each invocation of the .zshenv file ${HOME}/.local/bin would be added to the array, which would contain many duplicates. To solve this problem we have to use the following line before the array declaration:

typeset -U path

What the typeset -U path line does is simply avoiding duplicates inside the array. The leftmost element is kept in the array if another one already exists. Imagine the array contains the following directories (notice the % symbol in the prompt when we are using zsh):

% echo $path
/home/egdoc/.local/bin /home/egdoc/bin /usr/local/bin

If we add again the /usr/local/bin element to the beginning of the array, the old occurrence is removed:

% typeset -U path
% path=(/usr/local/bin $path)
% echo $path
/usr/local/bin /home/egdoc/.local/bin /home/egdoc/bin

Using zsh as the default shell



To change the default login shell for a user, we use the chsh command. We invoke it with the -s (--shell) option, and pass the path of the shell we want to use as argument. In our case we want to set /bin/zsh as our default shell, therefore we run:

$ chsh -s /usr/bin/zsh

Conclusions

In this tutorial we learned the basics of zsh, a modern shell with many features like advanced tab completion. We saw how to install it on the most used Linux distributions, what are the zsh startup files and in what context they are invoked, how to perform the basic shell configurations, how to setup PATH with zsh, and, finally, how to set zsh as the default login shell.



Comments and Discussions
Linux Forum