Two orthogonal axes
Every shell has two flags:
- Login or not — login shells are the first shell of a session: Terminal.app on open, ssh login,
su -. - Interactive or not — interactive shells have a prompt; non-interactive shells run scripts.
That gives 2x2 = four shell types, each loading a different set of config files.
The matrix (zsh)
| Interactive | Non-interactive | |
|---|---|---|
| Login | .zshenv → .zprofile → .zshrc → .zlogin | .zshenv → .zprofile → .zlogin |
| Non-login | .zshenv → .zshrc | .zshenv only |
Real-world cases
- Open Terminal.app → login + interactive (full reload).
ssh user@host→ login + interactive.ssh user@host 'echo hi'→ login + non-interactive.- Run
./script.shfrom a session → non-login + non-interactive. - Type
zshinside an existing zsh → non-login + interactive. - cron / launchd jobs → non-login + non-interactive (
.zshenvonly).
Why this trips people up
You add export OPENAI_API_KEY=... to ~/.zshrc and it works in your terminal. Cron picks up your script and the variable is empty. Cron's shell is non-login + non-interactive, so .zshrc is skipped. Move the line to .zshenv (or have your script source the file explicitly).
Confirm at runtime
[[ -o login ]] && echo login. [[ -o interactive ]] && echo interactive. The flags are introspectable from inside the shell.