Shape the shell

Map Zsh startup files

Choose the correct Zsh startup file by distinguishing login, interactive, and every-shell configuration.

10 minute lesson

~~~

Zsh reads different files for different shell modes, and most configuration problems on macOS trace back to a line living in the wrong one.

The three files that matter:

  • .zshenv — read by every zsh process, including scripts.
  • .zprofile — read by login shells, once per session.
  • .zshrc — read by interactive shells, the ones with a prompt.

They run in that order when all apply. macOS has one quirk worth memorizing: Terminal opens each new window as a login and interactive shell, so both .zprofile and .zshrc run every time. On Linux, login shells are rarer; on a Mac they are the norm.

See which mode you are in

Inspect the current shell modes:

print -r -- "login=$options[login] interactive=$options[interactive]"
# login=on interactive=on

Run the same line inside a script and both flip to off. That script still read .zshenv — and nothing else. This is why “it works in my terminal but not in the script” is almost always a startup-file question.

Put each thing where it belongs

Keep .zshenv minimal. It runs for every zsh invocation, so anything slow here taxes every script on the system, and anything that prints breaks tools that parse command output.

Use .zprofile for login environment setup: PATH additions, eval "$(/opt/homebrew/bin/brew shellenv)", exported variables. Login runs once per session, so the cost is paid once and child shells inherit the result.

Use .zshrc for interactive prompts, aliases, and completion — things that only make sense when a human is typing.

# quick sanity check of what each file currently does
head -5 ~/.zshenv ~/.zprofile ~/.zshrc 2>/dev/null

Putting every command in .zshrc can slow interactive terminals, while putting interactive output in .zshenv can break scripts. The classic symptom of the first: a noticeable pause before every prompt appears. The classic symptom of the second: an echo in .zshenv corrupting the output of some automation that captured it.

If a tool’s installer forces you to break these rules — some insist on editing .zshrc — document any exception with a comment saying which tool demanded it. Future you will want to know whether that line is load-bearing.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →