zsh and ~/.zshrc: What's Different from bash
- Tell which shell you are running, and know that zsh is another shell whose concepts map onto bash's
- Name zsh's startup files (.zshenv, .zprofile, .zshrc, .zlogin) and when each runs
- Map ~/.zshrc onto ~/.bashrc and ~/.zprofile onto ~/.bash_profile, and know what ~/.zshenv adds
- Put a setting in the right zsh file: environment in .zshenv/.zprofile, interactive niceties in .zshrc
- Apply a change with source, and avoid the trap of editing ~/.bashrc under zsh
Same ideas, different filenames
See that zsh is another shell and that what you know about bash startup transfers.
zsh (the Z shell) is a different shell program from bash, and you will meet it constantly — it has been the default login shell on macOS since Catalina, and many people switch to it on Linux for its completion and prompt features. The good news: almost everything you learned about bash startup files carries straight over. Both are POSIX-style shells; both decide what to run at launch based on whether the shell is a login shell and whether it is interactive; both read per-user startup files from your home directory; both apply a changed file with
source. What changes is the names of the files and one extra category zsh has that bash doesn't.So the goal of this lesson is narrow and useful: not to teach zsh from scratch, but to let you translate. If you know that
~/.bashrcruns for a new interactive bash and~/.bash_profileruns for a login bash, you are two renames away from knowing zsh. First, though, confirm which shell you are actually in — because 'my alias stopped working' is very often 'I'm in a different shell than I edited the config for.'echo "$SHELL"; echo "running now: $0"/bin/bash running now: -bash
$SHELLis your login shell (what the system starts for you), and$0is the shell interpreting this line right now — they can differ if you launched another shell by hand. A leading-on$0(like-bash) marks a login shell. If$SHELLsaid/bin/zsh, your config belongs in zsh's files, not bash's.cat /etc/shells/bin/sh /bin/bash /usr/bin/bash /bin/dash
The shells this system recognises as valid login shells. Whether zshappears here tells you if it is even installed — on a machine where it is, you would see/usr/bin/zsh, andchsh -s /usr/bin/zshwould switch your login shell to it. (This training box ships bash, not zsh, so zsh does not appear — the zsh commands in this lesson are shown for reference, not run here.)zsh's startup files, in order
Learn the five zsh startup files and when each runs.
zsh reads up to five startup files, each with a system-wide version in
/etcand a per-user version in your home directory. In the order they run:1. `~/.zshenv` — read for every zsh, always: interactive, login, and even non-interactive script shells. Because it runs for scripts too, keep it minimal and fast.
2. `~/.zprofile` — read for login shells (a fresh console/SSH login), after
.zshenv.3. `~/.zshrc` — read for interactive shells (a new terminal window/tab). This is the one you will edit most.
4. `~/.zlogin` — read for login shells, after
.zshrc. A less-used spot for things that should run at the very end of login.5. `~/.zlogout` — read when a login shell exits (cleanup).
The two you will touch in practice are `~/.zshrc` (interactive) and `~/.zprofile` (login). The one with no bash equivalent is `~/.zshenv`: bash has no single file guaranteed to run for every invocation including scripts, so
.zshenvis genuinely new — powerful for setting things likePATHonce, but easy to abuse (put slow or interactive-only code there and every script pays for it).Each of these also has a system-wide counterpart under
/etcthat runs before the per-user file —/etc/zshenv,/etc/zprofile,/etc/zshrc,/etc/zlogin— the same way/etc/profileand/etc/bash.bashrcsit above your bash files. Those are the administrator's defaults for every user on the machine; your home-directory files layer your personal settings on top. And note the two axes are independent, just as in bash: a shell can be login and interactive (a console login), login and non-interactive (somessh host commandinvocations), or interactive and non-login (a new terminal tab, which reads only.zshenvthen.zshrc). Matching a setting to the file whose trigger actually fires for your case is the whole game.Example — not runnable on your lab box
print -l $ZSH_VERSION ${(k)options[login]}5.9
A zsh-only command (illustrative — this box runs bash, so it does not execute here). $ZSH_VERSIONis set only inside zsh, which is itself a quick way for a startup file to detect 'am I in zsh?'. The point of showing it is the mental model, not the syntax: each of the five files above runs at a defined moment, and a line like this in~/.zshrcwould run once per interactive shell.Translating from bash
Map the zsh files onto the bash files you already know.
Here is the whole translation, which is most of what you need:
- `~/.zshrc` ≈ `~/.bashrc` — runs for each new interactive shell. This is where your prompt, aliases, key bindings, completion setup, and shell options go. When someone says 'add it to your zshrc,' this is the bashrc-equivalent they mean.
- `~/.zprofile` ≈ `~/.bash_profile` / `~/.profile` — runs for a login shell. The place for environment setup you want once per login — exporting
PATH,EDITOR, and so on. - `~/.zshenv` ≈ (no clean bash equivalent) — runs for every zsh, scripts included. Use sparingly, for environment that truly must exist everywhere.
The practical rule mirrors bash: environment variables and `PATH` go in a login file (
~/.zprofile, or~/.zshenvif scripts need them too), and interactive niceties go in `~/.zshrc`. Putting anexport PATH=...only in~/.zshrcis the classic slip — it works in your terminal but is missing from anything that starts as a login-but-non-interactive shell, exactly the bash.bashrc-vs-.bash_profilemistake wearing az.Example — not runnable on your lab box
printf '%s\n' 'export PATH="$HOME/bin:$PATH"' >> ~/.zprofile # env: login file printf '%s\n' 'alias ll="ls -alF"' >> ~/.zshrc # interactive: zshrcIllustrative (there is no zsh here to read these files). It shows the split you would actually make: the PATHexport lands in~/.zprofileso it is set once at login and inherited everywhere, while the interactivellalias lands in~/.zshrc. Same discipline as bash — just.zprofile/.zshrcinstead of.bash_profile/.bashrc.Applying a change, and the bash trap
Re-read a changed file with source, and avoid editing the wrong shell's config.
Editing a startup file does not change shells that are already open — the file is only read at startup. Exactly as in bash, you either open a new terminal or re-read the file into the current shell with
source ~/.zshrc(or its shorthand,. ~/.zshrc). There is nothing zsh-specific here;sourceis the same tool you already use.The one genuinely new hazard is mixing the two shells up. If you switch your login shell to zsh but keep editing `~/.bashrc`, nothing happens — zsh never reads `~/.bashrc`, and bash never reads
~/.zshrc. This is the single most common 'my alias/PATH/prompt change did nothing after I switched to zsh' cause. The fix is the check from step 1: confirm which shell you are in withecho $SHELL/$0, then edit that shell's files.Example — not runnable on your lab box
source ~/.zshrcRe-reads ~/.zshrcinto the current interactive zsh so a change takes effect without opening a new terminal (illustrative here — no zsh on this box). Identical in spirit tosource ~/.bashrcunder bash. If a change still seems ignored after sourcing, you are almost certainly editing the other shell's file.Check yourself
You switched your login shell to zsh, added an alias to ~/.bashrc, opened a new terminal, and the alias doesn't work. Why?
The mistake that eats an afternoon
Internalize that bash files and zsh files are not interchangeable.
- zsh is another shell (the macOS default) whose startup model matches bash's: login vs interactive, per-user home-directory files, and source to re-read. Only the filenames — and one extra file — differ. Check which shell you're in with echo $SHELL and $0 before editing config.
- zsh's files in order: ~/.zshenv (every zsh, scripts included), ~/.zprofile (login), ~/.zshrc (interactive), ~/.zlogin (login, last). The two you edit are ~/.zshrc and ~/.zprofile.
- Translate: ~/.zshrc ≈ ~/.bashrc (prompt, aliases, interactive), ~/.zprofile ≈ ~/.bash_profile (PATH and environment at login). ~/.zshenv has no clean bash equivalent — it runs for everything, so keep it minimal.
- The two shells never read each other's files, so editing ~/.bashrc while running zsh does nothing — the #1 'my change did nothing after switching to zsh' cause. Edit the file for the shell you're actually in, and source it to apply.
