Book Image

Learning Shell Scripting with Zsh

By : Gaston Festari
Book Image

Learning Shell Scripting with Zsh

By: Gaston Festari

Overview of this book

Table of Contents (13 chapters)

First run

Now that zsh is on your system, how about we take it for a spin? Go ahead and open your terminal emulator of choice and call the following command:

$ zsh

Like many other applications these days, zsh has a first-run wizard (bear with me, it almost resembles one). This is one of those magic creatures whose sole purpose is to help us configure our tools on a swift swoop of questions and decision making. We'll skip the new user configuration this time, but feel free to choose whatever method works best for you, taking the question-by-question approach or just pressing Q on your keyboard to abort the operation. Just remember that the newuser module is called from <zshInstallFolder>/Functions/Newuser/zsh-newuser-install or <zshInstallFolder>/functions/zsh-newuser-install in OS X—should you require its services in the future.

In order to avoid having to skip the configuration options on each subsequent run, you can go ahead and create what is known as a startup file:

% touch ~/.zshrc

We just created our main preferences file; the problem is, it stands empty as it is. Let's go ahead and add some preferences, shall we?


There will be plenty of references to zsh's options—the various settings that alter the shell's behavior—thus, now is as good a time as any to establish a couple of conventions. Firstly, the naming scheme is somewhat too forgiving—it is case-insensitive and ignores underscores and ignores underscores. As such, both the following option names mean the same.


Secondly, try to think of options as switches. As the name implies, they can either be turned on or off. Of the many ways that zsh provides to toggle its options, it is arguably easier to remember the setopt/unsetopt combo.

setopt SOME_OPTION # enables any option.
unsetopt SOME_OPTION # use this to disable an option.

Conversely, you can negate the behavior of an option by prepending NO to its name, thus making unsetopt SOME_OPTION mean the same as setopt NO_SOME_OPTION or, keeping in mind that underscores are only there for human readability, the same as setopt NOSOMEOPTION.

Just for sanity's sake and because I do love me some standards, we'll use ALL_CAPS_SNAKE_CASE for the options in this book.

Open ~/.zshrc with your favorite editor; you can use editors such as vim, Emacs, nano, or whatever kids find cool these days, and add the following line:

autoload -U promptinit # initialize the prompt system promptinit

Let's go over what we just typed: the first line of the code is our way to tell the shell to start its promptinit module—a series of functions that deal with handling the shell's various prompts and functionality. What you see right after the hash sign is just a comment to remind you of what the command is doing and why it is there. Finally, the last line is the one that actually calls and initializes the prompt module. It might not seem much, but it will come in handy when dealing with prompts, I promise.

Feel free to omit the comments and make sure you save your changes.


Zsh will ignore each line that starts with a hash (#)—or pound—sign. This is really helpful for debugging preferences and, better yet, documenting your functionality. Consider the next example, with comments in bold:

# This is a comment and will be ignored by the shell.
HISTFILE=~/.zsh_history # sets the location of the history file

Making zsh your login shell

If there's something that shells take seriously, is their role. See, the thing with shells is that they like to hang out in very specific categories—they are either interactive or non-interactive, and then there are login shells.

As you might have guessed from their name, interactive shells allow you to interact with them; that is, they display a prompt, you enter a command, and they get back to you with an answer and a prompt that is ready for new input. On the other hand, Apply interactive shells get called to execute a script and go off their own merry way when the job is done.


Put simply, a prompt usually is the blinking cursor that tells you a shell is ready for you.

What about login shells then? Well, unlike interactive shells, login shells are usually called when the user performs a login—be it either on the local machine or when using tools such as SSH, for example—and takes the trouble to go through your startup files and configuration bits and pieces of the shell. More importantly, your login shell doesn't necessarily need to be interactive.

In the previous section, we used a direct call to the binary zsh to start zsh. As you can imagine, this is but a temporary workaround, as typing the name of the shell every single time we want to use it seems a bit impractical, to say the least. Even worse is the thought of having your previous shell lurking beneath and ready to jump back at you as soon as you're done with zsh. If you don't trust me, go ahead and type exit; I'll wait. See that thing that's on your screen? That's your former command-line companion right there. Say your goodbyes and hop back into zsh by typing zsh and pressing return.

So what comes next is—you guessed it—getting rid of that old shell of yours and saving yourself the trouble of remembering to call zsh each time you want to use it.


You can always trick zsh, and many other shells, into thinking it is a login shell by starting it with either the -l or --login flag. Open your terminal and type either of the following commands:

$ zsh -l


$ zsh --login

Voilà! A shell with a login complex.

Luckily for us, the Unix chsh command seems to be just what the doctor recommended, so go ahead and type the following in your terminal:

$ chsh -s $(which zsh)

In the previous snippet, we're telling the system to change the shell for the current user. The option -s is used here to specify the location of the shell binary. That fancy $() construct you see there is our way of telling the shell to expand the result of the command within the parentheses, which is the result of the command which zsh.

You might recall which from the previous section, when we required its services to figure out the location of our existing zsh installation. The job of which consists of shouting out loud the location of any program file in the user's $PATH environment variable. Thus, we can safely assume that if zsh is not there, something has taken a wrong turn somewhere and, perhaps, it's advisable to retrace our steps.

It's more than likely that changing your login shell will require it to run with elevated privileges, so make sure you are using an account with the appropriate permissions.

From now on, you'll be greeted by zsh by default on your system and every time you start your terminal emulator of choice. And likely so, you have installed and made zsh your login shell. Next up is tweaking it.

Shell options

Besides tricking zsh into thinking it's a login shell with the -l flag, there are many other helpful options you can set when invoking it. Namely, zsh -v will switch on the verbose mode, which will make the shell print out any line before it gets executed. Then, there's zsh -x—for xtrace—which can prove invaluable when debugging your scripts, or zsh -f that will start a clean instance of zsh using the default settings.

Any of these options can also be set after the shell has been started; you simply have to call the desired option flag via the set command. The following example triggers the verbose mode on a running session:

% set -v
% echo 'quite the echo in here'
> echo 'quite the echo in here'
> 'quite the echo in here'


Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at If you purchased this book elsewhere, you can visit and register to have the files e-mailed directly to you.

And, you can disable any option with the same set command and replacing the dash/minus sign with a plus sign as follows:

# disables verbose mode
% set +v

More info regarding the various shell options and their usage can be found in the zshoptions(1) manpage (man zshoptions).

The startup files

Like most login shells, zsh relies on a series of configuration files known as startup files, which contain the commands and preferences to be executed and set during the shell startup routine. We used the .zshrc file in the previous sections to avoid being bothered by the newuser function, but now that we have made zsh our login shell, it's time we take a closer look at what we can do with them.


By default, zsh looks for startup files in the user's home directory, $HOME (or its alias, the more popular tilde, ~. We'll alternate their use in this text as the path to the current user's home folder on the system), environment variable. You can tell zsh to look for your configuration files in another folder by setting the parameter ZDOTDIR to a directory of your choice in your .zshenv file under $HOME:


During startup, zsh looks for, or sources, a very specific system and user set of filenames under /etc/. Right after this, each of these files have a user-editable doppelganger, typically located in $HOME, which gets read. There are some rules, however, that might make zsh skip some of these files altogether. The ordering of these files is really important, as setting an option in the wrong file can result in commands getting executed at the wrong time and some really funky behavior. Thus, try to keep in mind the following order when setting preferences on your files:

  • zshenv

  • zprofile

  • zshrc

  • zlogin

If zsh is not called as an interactive shell, zprofile and zshrc together with their counterparts in $HOME (~/.zprofile and ~/.zshrc) will not be sourced. In addition, if zsh is not called as a login shell, zlogin and $HOME/.zlogin will also be skipped.


Depending on how you installed zsh, another directory besides /etc/ can be used when looking for the global files.

Typically, you'd only like to mess with your own user's preferences, so we'll focus on the startup files that reside under $HOME, those are as follows:

  • ~/.zshenv: This will be called immediately after /etc/zshenv. You should only add things such as the PATH settings and stuff you want to make available to any type of shell, whether it's interactive or not.

  • ~/.zprofile: This is the companion to /etc/zprofile and kind of the boring guy out of the startup files bunch. You should put here any scripts you want executed before ~/.zshrc.

  • ~/.zshrc: This is your workhorse. Most of your user settings and shell preferences end up here. Keep in mind it'll only be taken into account for interactive shells. As we'll see later on, you can declutter and expand its reach by sourcing multiple files.

  • ~/.zlogin: This will be executed right after ~/.zshrc and works pretty much like ~/.zprofile, so you should put the scripts that you want called after your main startup file here.

On the opposite corner of the startup files, there are the shutdown files. As you can imagine, this relatively smaller set of files gets called not only in a specific order but also during the logout sequence of the login shell. The shutdown files can be considered a subset of the startup files, so there's no need to lose sleep over them. The important thing to remember is that when you type logout in the command line, the settings stored in the user configurable ~/.zlogout file are read, followed by the installation file /etc/zlogout.

You can use the options RCS and GLOBAL_RCS to disable the loading mechanism of the startup files. This preference has to be unset on the system file /etc/zshenv as follows:

unset RCS # disables loading of files other than zshenv
unset GLOBAL_RCS # disables loading of files under /etc/

For instance, if the RCS option is unset in zshenv (the first file that is read), ~/.zshenv and all the remaining files will be skipped. Keep in mind though, that both of these options can be turned on again by any subsequent file that you load.

For example, if you have the following in /etc/shenv:

unset RCS
source my_options_file.zsh

And then in my_options_file.zsh add:

# some more options here
set RCS

Then, the shell will proceed and load .zshenv as if nothing happened. So, be careful!

We have taken a look at the startup files and their somewhat strict ordering; now, it's time we get up close and personal with the prompt.