Name zsh's user startup files in the order they are read, and say which of them are read by a login shell, by an interactive non-login shell, and by a shell that is only running a single command.
answer
- two flags: login and interactive
- env, profile, rc, login
- only one file runs for every shell
- rc means interactive
- zlogin comes after zshrc
basics
~10 szsh always reads ~/.zshenv; ~/.zprofile and ~/.zlogin are read only by login shells, and ~/.zshrc only by interactive ones. The order is .zshenv, .zprofile, .zshrc, .zlogin, with .zlogout on logout of a login shell.
solid answer
~40 szsh reads up to five user files, and two independent properties of the shell decide which ones apply. `~/.zshenv` is read by *every* zsh, interactive or not, login or not. `~/.zprofile` and `~/.zlogin` are read only by login shells; `~/.zshrc` only by interactive ones. The order is `.zshenv` → `.zprofile` → `.zshrc` → `.zlogin`, so `.zlogin` deliberately runs *after* your interactive configuration, and `.zlogout` runs when a login shell exits. In practice: an interactive login shell reads four files, a new terminal tab inside an existing session reads two (`.zshenv` and `.zshrc`), and `ssh host 'somecmd'` reads only `.zshenv`. Each file has an `/etc` counterpart that is read first, and if `ZDOTDIR` is set the user files are looked for there instead of `$HOME`.
code
bash · 8 linesfor f in ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do
echo "echo loaded: $f >&2" >> "$f"
done
zsh -c 'true' # non-interactive, non-login: .zshenv only
zsh -i -c exit # interactive: .zshenv .zshrc
zsh -l -i -c exit # login + interactive: all four
zsh -f -i -c exit # skips the user startup files entirelygo deeper
Be able to name ~/.zshenv, ~/.zprofile, ~/.zshrc and ~/.zlogin, and say plainly that aliases and prompt settings belong in ~/.zshrc.
Explain the two independent flags — login and interactive — and walk the read order out loud, including the fact that ~/.zlogin runs after ~/.zshrc rather than before it.
Show that you use the split deliberately: environment every process needs in ~/.zshenv, interactive-only work in ~/.zshrc, and be ready to prove on a broken host which files actually ran.
Own the convention for a team: what belongs in the machine-wide /etc files, whether dotfiles relocate via ZDOTDIR, and how the always-read file is kept cheap and safe for everything that is not a terminal.
## The five files zsh has an unusually rich set of startup files, and each one exists because it answers a different question about *when* configuration should apply. - **`~/.zshenv`** — read by every single zsh invocation. Interactive or not, login or not, script or prompt. This is the only unconditional file. - **`~/.zprofile`** — read by login shells only, before `~/.zshrc`. - **`~/.zshrc`** — read by interactive shells only. - **`~/.zlogin`** — read by login shells only, after `~/.zshrc`. - **`~/.zlogout`** — run when a *login* shell exits. ## The two flags that decide everything "Login" and "interactive" are independent properties, not points on a scale. A shell is **interactive** when it reads commands from a terminal — you type at it. `zsh -i` forces it. A shell is a **login** shell when it is the first shell of a session: the shell `sshd` gives you on an interactive connection, a console login, or anything started with `zsh -l` or with a leading `-` in `argv[0]`. Terminal emulators differ: many open a plain interactive shell, while some are configured to open a login shell for each tab. The four combinations: | Shell | Files read | |---|---| | interactive login (`ssh host`, console login) | `.zshenv`, `.zprofile`, `.zshrc`, `.zlogin` | | interactive non-login (a new tab or pane) | `.zshenv`, `.zshrc` | | non-interactive login (rare; `zsh -l -c cmd`) | `.zshenv`, `.zprofile`, `.zlogin` | | non-interactive non-login (`ssh host 'cmd'`, a script) | `.zshenv` | ## Why `.zlogin` exists when `.zprofile` already runs The two login files bracket `.zshrc` on purpose. `.zprofile` is there for people who like the ksh ordering — environment set up before the interactive configuration. `.zlogin` is the csh-style hook and runs last, which makes it the right place for anything that must see the finished interactive shell: a session banner, a message of the day, starting something once per login. ## The `/etc` counterparts and `ZDOTDIR` Every user file has a system-wide sibling that is read first: `/etc/zshenv`, `/etc/zprofile`, `/etc/zshrc`, `/etc/zlogin`, `/etc/zlogout` (some distributions place them under `/etc/zsh/`). This is how a machine imposes defaults on everyone, and it is worth checking when a shell behaves oddly on one host only. If the `ZDOTDIR` parameter is set, zsh looks for the user files in `$ZDOTDIR` rather than `$HOME`. Because `ZDOTDIR` has to be set before the files can be found, it is normally set in `/etc/zshenv` or inherited from the environment — or `$HOME/.zshenv` sets it and the remaining files are then read from the new location. It is the clean way to keep dotfiles out of `$HOME` or to test an alternative configuration without touching your own. ## Deciding what goes where The split is not decoration; it is what makes a shell configuration survive being run by something that is not you. - **`~/.zshenv`**: environment that *any* zsh process needs — `PATH` entries that scripts and remote commands depend on, `EDITOR`. Keep it small, silent and fast, because it runs for everything. - **`~/.zshrc`**: everything that only makes sense with a human at the keyboard — aliases, key bindings, prompt, completion setup, plugin managers, options that change line editing. - **`~/.zprofile`** / **`~/.zlogin`**: once-per-session work — starting an agent, printing a summary, anything you do not want repeated for every subshell. ## Skipping the files `zsh -f` (also `--no-rcs`) starts a shell that ignores the startup files, which is the fastest way to prove a problem comes from your own configuration. `zsh -d` skips only the global `/etc` files. `/etc/zshenv` is always read regardless, so a machine-wide policy there cannot be opted out of. ## Proving it rather than remembering it When a host behaves unexpectedly, do not reason about which file ran — instrument them. Append a line to each file that prints its own name to stderr, then start shells in each mode and read the output. Five minutes of that ends most arguments about why an alias or a `PATH` entry is missing. ```zsh # in ~/.zshrc print -u2 "loaded: ~/.zshrc" ``` Remove the markers afterwards — output from a startup file has its own failure modes.
- What does setting ZDOTDIR change?If `ZDOTDIR` is set, zsh looks for `.zshenv`, `.zprofile`, `.zshrc`, `.zlogin` and `.zlogout` in that directory instead of `$HOME`. It is usually set in `/etc/zshenv` or inherited from the environment, or `$HOME/.zshenv` sets it and the remaining files are read from the new location. Useful for keeping dotfiles out of `$HOME` and for running an isolated configuration.
- Why does .zlogin exist when .zprofile already runs only for login shells?They bracket `.zshrc` deliberately. `.zprofile` is the ksh-style hook and runs before the interactive configuration; `.zlogin` is the csh-style hook and runs after it. Put things that must see the finished interactive shell — a banner, a once-per-login summary — in `.zlogin`, and environment setup in `.zprofile`.
- How do you start a zsh that ignores this configuration entirely?`zsh -f` (`--no-rcs`) skips the startup files, which is the standard baseline when you suspect your own configuration. `zsh -d` skips only the global `/etc` files. `/etc/zshenv` is always read, so a site-wide policy placed there still applies.
- A terminal emulator opens tabs that read ~/.zprofile — is that a zsh behaviour?No. zsh reads `.zprofile` because the shell was started *as a login shell*; the emulator chose that, usually via a "run command as a login shell" setting or by invoking `zsh -l`. It explains why two people on the same machine see different files run for a new tab.
Think of them as three concentric jackets: .zshenv is worn by every process that ever becomes a zsh, .zprofile/.zlogin only when you arrive for the session, and .zshrc only when a human is at the keyboard.
saying these in an interview costs you the question
- Assumes ~/.zshrc runs for every zsh invocation
- Thinks ~/.zprofile is read by ordinary interactive tabs
- Believes .zlogin runs before .zshrc
- Cannot say which file a remote one-shot command reads
- Puts prompt and alias setup in ~/.zshenv