I will be giving a half-day ‘Moving to zsh’ class at our offices in Amsterdam on September 6! Visit the website for details!
In this series, I will document my experiences moving
bash settings, configurations, and scripts over to
- Part 1: Moving to zsh
- Part 2: Configuration Files (this article)
- Part 3: Shell Options
- Part 4: Aliases and Functions
- Part 5: Completions
- Part 6: Customizing the
- Part 7: Miscellanea
- Part 8: Scripting
In part one I talked about Apple’s motivation to switch the default shell and urge existing users to change to
Since I am new to
zsh as well, I am planning to document my process of transferring my personal
bash setup and learning the odds and ends of
While these projects are very impressive and certainly show off the flexibility and power of
zsh customization, I feel this will actually prevent an understanding of how
zsh works and how it differs from
bash. So, I am planning to build my own configuration ‘by hand’ first.
At first, I actually took a look at my current
bash_profile and cleaned it up. There were many aliases and functions which I do not use or broke in some macOS update. I the end, this is what I want to re-create in
- mostly shortcuts to
openfiles with a specific application
- mostly shortcuts to
- shell settings
- case-insensitive globbing
- case-insensitive path-completion (for
bashthis is set in
- command history, shared across windows and sessions
- use BBEdit as the editor
- show current working dir
- show a colored symbol showing the last command’s exit code
- update the Terminal window title bar to show the cwd
Most of these should be fairly easy to transfer. Some might be… interesting.
But first, where do we put our custom
zsh Configuration Files
bash has a list of possible files that it tries in predefined order. I have the description in my post on the
zsh also has a list of files it will execute at shell startup. The list of possible files is even longer, but somewhat more ordered.
|all users||user||login shell||interactive shell||scripts||Terminal.app|
The files in
/etc/ will be launched (when present) for all users. The
.z* files only for the individual user.
zsh will look in the root of the home directory for the user
.z* files, but this behavior can be changed by setting the
ZDOTDIR environment variable to another directory (e.g.
~/.zsh/) where you can then group all user
zsh configuration in one place.
On macOS you could set the
~/Documents/zsh/ and then use iCloud syncing (or a different file sync service) to have the same files on all your Macs. (I prefer to use
bash will either use
.bash_profile for login shells, or
.bashrc for interactive shells. That means, when you want to centralize configuration for all use cases, you need to
.bash_profile or vice versa.
zsh behaves differently.
zsh will run all of these files in the appropriate context (login shell, interactive shell) when they exist.
zsh will start with
/etc/zshenv, then the user’s
zshenv files are always used when they exist, even for scripts with the
#!/bin/zsh shebang. Since changes applied in the
zshenv will affect
zsh behavior in all contexts, you should you should be very cautious about changes applied here.
Next, when the shell is a login shell,
zsh will run
.zprofile. Then for interactive shells (and login shells)
.zshrc. Then, again, for login shells
.zlogin. Why are there two files for login shells? The
zprofile exists as an analog for
sh’s profile files, and
zlogin as an analog for
ksh login files.
Finally, there are
zlogout files that can be used for cleanup, when a login shell exits. In this case, the user level
.zlogout is read first, then the central
/etc/zlogout. If the shell is terminated by an external process, these files might not be run.
Apple Provided Configuration Files
macOS Mojave (and earlier versions) includes
/etc/zshrc files. Both are very basic.
/usr/libexec/path_helper to set the default
/etc/zshrc enables UTF–8 with
/etc/bashrc there is a line in
/etc/zshrc that would load
/etc/zshrc_Apple_Terminal if it existed. This is interesting as
/etc/bashrc_Apple_Terminal contains quite a lot of code to help
bash to communicate with the Terminal application. In particular
bash will send a signal to the Terminal on every new prompt to update the path and icon displayed in the Terminal window title bar, and provides other code relevant for saving and restoring Terminal sessions between application restarts.
However, there is no
/etc/zshrc_Apple_Terminal and we will have to provide some of this functionality ourselves.
Note: As of this writing,
/etc/zshrcin the macOS Catalina beta is different from the Mojave
/etc/zshrcand provides more configuration. However, since Catalina is still beta, I will focus these articles on Mojave and earlier. Once Catalina is released, I may update these articles or write a new one for Catalina, if necessary.
Which File to use?
When you want to use the
ZDOTDIR variable to change the location of the other
zsh configuration files, setting that variable in
~/.zshenv seems like a good choice. Other than that, you probably want to avoid using the
zshenv files, since it will change settings for all invocations of
zsh, including scripts.
macOS Terminal considers every new shell to be a login shell and an interactive shell. So, in Terminal a new
zsh will potentially run all configuration files.
For simplicity’s sake, you should use just one file. The common choice is
Most tools you can download to configure
zsh, such as ‘prezto’ or ‘oh-my-zsh’, will override or re-configure your
.zshrc. You could consider moving your code to
.zlogin instead. Since
.zlogin is sourced after
.zshrc it can override settings from
.zlogin is only called for login shells.
The most common situation where you do not get a login shell with macOS Terminal, is when you switch to
zsh from another shell by typing the
I would recommend to put your configuration in your
.zshrc file and if you want to use any of the theme projects, read and follow their instructions closely as to how you can preserve your configurations together with theirs.
Managing the shell for Administrators
MacAdmins may have the need to manage certain shell settings for their users, usually environment variables to configure certain command line tool’s behaviors.
The most common need is to expand the
PATH environment variable for third party tools. Often the third party tools in question will have elaborate postinstall scripts that attempt to modify the current user’s
.bashrc. Sometimes, these tools even consider that a user might have changed the default shell to something other than
On macOS, system wide changes to the
PATH should be done by adding files to
As an administrator you should be on the lookout for scripts and installers that attempt to modify configuration files on the user level, disable the scripts during deployment, and manage the required changes centrally. This will allow you to keep control of the settings even as tools change, are added or removed from the system, while preserving the user’s custom configurations.
To manage environment variables other than
PATH centrally, administrators should consider
/etc/zshenv or adding to the existing
/etc/zshrc. In these cases you should always monitor whether updates to macOS overwrite or change these files with new, modified files of their own.
There are many possible files where the
zsh can load user configuration. You should use
~/zshrc for your personal configurations.
There are many tools and projects out there that will configure
zsh for you. This is fine, but might keep you from really understanding how things work.
MacAdmins who need to manage these settings centrally, should use
/etc/paths.d and similar technologies or consider
Apple’s built-in support for
zsh in Terminal is not as detailed as it is for
Next: Part 3 – Shell Options