Signal9Signal9 — home
online
all lessons

zsh and ~/.zshrc: What's Different from bash

Intermediate25 min5 steps
What you'll learn
  • 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
  1. 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 ~/.bashrc runs for a new interactive bash and ~/.bash_profile runs 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
    $SHELL is your login shell (what the system starts for you), and $0 is 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 $SHELL said /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 zsh appears here tells you if it is even installed — on a machine where it is, you would see /usr/bin/zsh, and chsh -s /usr/bin/zsh would 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.)
  2. 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 /etc and 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 .zshenv is genuinely new — powerful for setting things like PATH once, 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 /etc that runs before the per-user file — /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin — the same way /etc/profile and /etc/bash.bashrc sit 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 (some ssh host command invocations), or interactive and non-login (a new terminal tab, which reads only .zshenv then .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_VERSION is 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 ~/.zshrc would run once per interactive shell.
  3. 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 ~/.zshenv if scripts need them too), and interactive niceties go in `~/.zshrc`. Putting an export PATH=... only in ~/.zshrc is 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_profile mistake wearing a z.

    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: zshrc
    Illustrative (there is no zsh here to read these files). It shows the split you would actually make: the PATH export lands in ~/.zprofile so it is set once at login and inherited everywhere, while the interactive ll alias lands in ~/.zshrc. Same discipline as bash — just .zprofile/.zshrc instead of .bash_profile/.bashrc.
  4. 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; source is 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 with echo $SHELL / $0, then edit that shell's files.

    Example — not runnable on your lab box

    source ~/.zshrc
    Re-reads ~/.zshrc into the current interactive zsh so a change takes effect without opening a new terminal (illustrative here — no zsh on this box). Identical in spirit to source ~/.bashrc under 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?

  5. The mistake that eats an afternoon

    Internalize that bash files and zsh files are not interchangeable.

Recap
  • 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.