Showing someone zsh for the first time can be a fun experience because it's a shell with many tricks. Usually, all it takes is a short demonstration of zsh's tab-completion powers to captivate your audience. Being able to type gcc -[TAB] to see a list of its command line options is something that most users could never imagine, but zsh is full of surprises when it comes to making interactive shells as functional as possible.
Showing someone zsh for the first time can be a fun experience because it’s a shell with many tricks. Usually, all it takes is a short demonstration of zsh‘s tab-completion powers to captivate your audience. Being able to type gcc -[TAB] to see a list of its command line options is something that most users could never imagine, but zsh is full of surprises when it comes to making interactive shells as functional as possible.
If you don’t have zsh on your system, you can download it from http://www.zsh.org/, or with your distribution’s automated installation tool.
However, despite how good zsh is, it’s not widely used. The main reason for this is that it’s rarely the default shell for Linux distributions. That distinction goes to GNU/bash, so it’s not surprising that the majority of Linux users are bash users.
The main problem with zsh is figuring out where to begin. Its man page is located at http://zsh.sunsite.dk/Doc/ and there’s a User’s Guide at http://zsh.sunsite.dk/Guide/, but they are a bit overwhelming, and it’s not entirely obvious how to enable some of zsh‘s more interesting features. Fortunately, both bash and zsh implement a superset of the Bourne shell (sh), so porting your bash configuration to zsh is not too hard. With that in mind, let’s take a look at making the transition from bash to zsh 4.0.0 or later. Along the way, we’ll point out the parts of zsh that set it apart from other shells.
Enabling the Tab Completion System
Zsh has amazing tab-completion abilities, but people wrongly assume that it takes a long time to configure and is probably not worth the effort. Believe it or not, it only takes two lines in your .zshrc to enable all of zsh‘s standard library of completions:
autoload -U compinit
The compinit function initializes the entire tab-completion system by defining a shell function for every Linux utility that zsh is able to tab-complete. But since it’s unlikely that we’d use the tab-completion feature of every possible command in a single session, we’d like to improve the startup time of zsh and reduce its memory usage.
To do this, compinit declares every tab-completion function to be “autoloaded” (by using the word autoload). autoload tells zsh that a particular function exists, but that it can defer reading in the definition of the function until it’s actually used. (The function that expands the tab key for a specific Linux command can be found in a file whose name contains the name of the command in a directory specified by the FPATH environment variable.)
The reason compinit is itself the target of an autoload command has to do with the -U flag. It specifies that shell aliases are not to be used when the specified function (in this case compinit) is run. There could be problems if a shell function relies upon an alias. In fact, the compinit function always uses autoload -U when autoloading the tab-completion functions.
The end result of all this is that your [TAB] key now has an answer for everything. This has to be seen to be fully appreciated, so once you’ve added those two lines to your .zshrc, run zsh and try some of the following:
|Figure One: Zsh’s Tab-Completion in Action|
- ls -[TAB] will give you a list of GNU/ls options (see Figure One).
- man str[TAB] will give you a list of all man pages beginning with “str”.
- mutt -f=[TAB] will look in your ~/Mail directory and give you a list of mail folders to choose from.
- w3m [TAB] will look in your ~/.w3m/history file and give you a list of recently visited URLs
To further customize the completion system, you can also run the compinstall function from the command-line. It provides a menu-driven interface that gives a good overview of some of the more exotic capabilities of the completion system. You can enable options like spell-checking, case-insensitivity, and can even add color to completion lists (similar to the way ls can color its output). Just pick the functionalities you want and compinstall will turn your choices into zsh commands and append them to your .zshrc file. It can even make a backup of the original .zshrc file.
But there’s more to zsh than its completion system. There’s over 150 options that can be enabled via the setopt command and are documented in the zshoptions man page.
For example, the following enables a set of options that makes directory navigation easier.
setopt autocd autopushd \ pushdignoredups
The autocd option lets you type the name of a directory to change into it: there’s no need to type cd first anymore. Then, every time you change directories, the autopushd option puts that directory on the directory stack. Finally, the pushdignoredups prevents duplicate entries in the directory stack.
As a result, after moving around directories for a while, you can view your directory stack by typing dirs -v, which will yield a list of all the directories you’ve been in recently. See Figure Two.
Figure Two: Examining Your Directory Stack
$ dirs -v
Then, if you wanted to go back to /usr/bin, all you’d need to type is ~4, and you’re there.
There are other options in zsh that cater to experienced users of other shells. Some options for csh have funny names like cshjunkieloops which lets you write csh-style loops in zsh. There’s also a handful of options for sh and ksh features (although only csh users are considered to be “junkies”).
When zsh starts up, there are a number of configuration files it reads from. This set of files will depend on what type of shell is being started up. A smart zsh user will make note of this and place configuration commands in the appropriate files.
There are two main types of shells, non-interactive and interactive. A non-interactive shell reads its commands from a file, either by a call to zsh file or because of a hash-bang (#!) on the first line of a shell script.
An interactive shell reads its commands from a terminal (i.e., a user typing commands at a keyboard). A login shell is a special type of interactive shell: it’s been called from the login program, and is the type of shell a user is most likely to encounter. A non-login interactive shell would occur if a user simply typed zsh on the command line and is seldom encountered.
Each type of shell (login, interactive, and non-interactive) uses only a specific set of configuration files. Each configuration change should thus be examined and placed in the correct file. Table One shows the configuration files that each type of shell uses and is listed in the order they are read and executed.
Table One: How Zsh Starts Up
Notice that a non-interactive shell only reads in the /etc/zshenv and $ZDOTDIR/.zshenv files (if the ZDOTDIR environment variable is not set, then $HOME is used instead). Thus, any configuration change needed by a shell script must be placed in one of these two files, as no other configuration files will be read. These kinds of changes tend to only be changes to environment variables such as PATH.
The fact that there are both ksh-style startup files (zprofile) and csh-style (zlogin) is for historical reasons. The order in which they are read relative to the zshrc file corresponds to the respective shells’ behavior.
Remember, the zshenv files are loaded every time zshis started up, so it’s a good idea to keep them as small as possible. Like the name implies, it should mostly be used to set up the environment.
The prompt is another popular area of customization. Unfortunately, the $PS1 variable from bash will not work “as is” in zsh, because zsh uses “%” as an escape character rather than “\“. If you have a complex prompt in bash, you’ll need to redo it in zsh. The man page to look in is zshmisc, and the section titled “Prompt Expansion” will have all the info you need about the “%” escapes.
Alternatively, if you don’t want to create your own prompt, you can use a prepackaged prompt by adding the following to your .zshrc file:
autoload -U promptinit
You can replace “bart” with the name of any prompt that you like. To see a list of choices, type prompt -l, or prompt [TAB] to use tab-completion to get the list of prompts at your disposal. Even if you decide to make your own custom prompt, the prepackaged prompts are a good resource for learning how prompts in zsh work.
To examine and modify the $PS1 variable (or any other variable, for that matter), you can use vared:
At this point, vared will let you use your arrow keys to move around and edit this variable. When you’re done, hit [ENTER] and your changes will take effect.
So far we’ve covered areas where bash and zsh are different, highlighting some of the unique features of zsh. However, bash and zsh have a lot in common. In fact, much of your bash configuration can be copied straight into your zsh configuration with few or no changes.
Aliases should be completely compatible, and functions should be fine if you limited yourself to basic Bourne Shell semantics. If you’ve used any advanced features of bash though, you may have some work to do to get them to work in zsh. And finally, environment variables can be defined and exported exactly the same way. This makes the transition from bash to zsh relatively easy.
If you’ve liked what you’ve seen so far, there’s just one thing left to do. Run chsh and set zsh as your login shell (making sure to define the full path to zsh). The next time you log in, you should be greeted by a zsh prompt.
With zsh you should have all the functionality that you had in bash and more. However, we’ve only scratched the surface as far as what zsh is capable of, so when you get a chance, Read The Fine Manual to uncover more of what zsh has to offer.
John Beppu email@example.com encourages you to use your computer skillfully and creatively.