Terminal Emulator Ghostty · iTerm2 · Terminal.app · Alacritty keystrokes ↓ pixels ↑ Pseudoterminal (PTY) — OS bridge Shell zsh (default) · bash · fish exec() system calls Unix Programs & Kernel ls · git · node · python · your scripts

You type in the emulator. The shell interprets. Unix executes.

The core confusion: "Terminal" is the window. "Shell" is the program running inside it. You can swap either independently — run zsh in Alacritty, or fish in Terminal.app. They are not the same thing.
The Window
Terminal emulators — what they actually do
The emulator is purely a rendering surface — fonts, colors, themes, scroll buffer. It starts a shell process when it opens. Swapping emulators does not change your shell or config.
Ghostty

GPU-accelerated, native macOS app. Supports Kitty keyboard protocol. Fast, modern, opinionated defaults.

iTerm2

Most popular among Mac devs. Split panes, tmux integration, rich profiles and triggers. Feature-heavy.

Terminal.app

Ships with macOS, zero setup required. Fine to start with, but hits limitations as your workflow grows.

Alacritty

Fastest renderer. No tabs, no UI chrome — for minimalists who use tmux. Config via TOML file.

They all do the same fundamental job. Your ~/.zshrc works identically in every one of them. Pick one and stop thinking about it.
The Interpreter
Shells — the program that reads your commands
The shell is a REPL — it reads a line, executes it, prints output, and repeats. When you type git status, the shell finds the git binary and runs it.
Before macOS Catalina (2019)
Default shell: bash 3.2
Why 3.2? Apple froze it in 2007 — GPLv3 licensing issue prevented updates.
Still works, just very old.
macOS Catalina 2019+
Default shell: zsh
Better completions, themes via Oh My Zsh, Starship prompts.
Same syntax as bash for everyday use.
zsh
The new default
  • Config: ~/.zshrc
  • Themes: Oh My Zsh, Starship
  • Great tab completions
bash
The old default
  • Config: ~/.bashrc
  • Most scripts target bash
  • Frozen at v3.2 on macOS
fish
The friendly shell
  • Config: ~/.config/fish/
  • Autosuggestions built-in
  • Not POSIX — scripts differ
Which shell am I using? Run: echo $SHELL
To change it: chsh -s /bin/zsh
Putting It Together
The full stack, top to bottom
Terminal Emulator Ghostty, iTerm2
Shell zsh, bash, fish
Shell Config .zshrc, .zprofile
Unix Environment $PATH, processes, files
macOS Kernel & Hardware Darwin, CPU, disk
Two Types of Shell
Why there are multiple config files
Login Shell
Started by: ssh, new Terminal window
Reads: .zprofile (zsh) / .bash_profile (bash)
Sets: $PATH, environment variables that must exist early
Think of it as: the session initializer
Interactive Shell
Started by: every new Terminal tab (on macOS, also a login shell)
Reads: .zshrc (zsh) / .bashrc (bash)
Sets: aliases, functions, prompt, completions
Think of it as: your shell personality
macOS-specific: Every Terminal window is both a login shell and an interactive shell. That's why .zprofile and .zshrc both run. On Linux this is often not the case.
Config Files
Which file to edit
zsh
always
.zshenv
Runs for every zsh invocation
login
.zprofile
PATH, Homebrew, version managers
interactive
.zshrc
Aliases, prompt, completions
bash
login
.bash_profile
PATH, env (sources .bashrc)
interactive
.bashrc
Aliases, prompt, functions
fish
always
config.fish
Everything — one file, no split
~/.config/fish/config.fish

Highlighted boxes = the file you'll edit 99% of the time for each shell.

What Goes Where
The rules for each file
~/.zshrc

Your everyday config — edit this 99% of the time.

# Aliases alias ll='ls -la' alias gs='git status' # Prompt (via Starship or Oh My Zsh) eval "$(starship init zsh)" # PATH additions export PATH="$HOME/.local/bin:$PATH"
~/.zprofile

For things that must run once at login — before .zshrc.

# Homebrew (must be in PATH first) eval "$(/opt/homebrew/bin/brew shellenv)" # Language version managers eval "$(rbenv init -)" # Java / Android SDK export JAVA_HOME=$(/usr/libexec/java_home)
~/.zshenv

Runs for every zsh invocation — including scripts. Keep it minimal.

# Only variables every invocation needs export EDITOR="nvim" export ZDOTDIR="$HOME/.config/zsh"
Do not put aliases or heavy setup here. Non-interactive scripts will inherit them, causing unexpected behavior.
fish config

Fish breaks the pattern — everything lives in one file.

# ~/.config/fish/config.fish set -x EDITOR nvim alias ll='ls -la' fish_add_path ~/.local/bin
Fish does not read .zshrc or .bashrc at all. If you switch to fish, port your aliases manually.
Loading Order
What runs when you open a new Terminal tab (zsh)
1
/etc/zshenv
System-wide env. Ships with macOS. You rarely touch this.
2
~/.zshenv
Your universal env vars. Runs for every zsh — interactive, scripts, everything.
3
/etc/zprofile
System-wide login config. macOS uses this to run path_helper.
4
~/.zprofile
Your login setup — Homebrew, version managers, env that must exist early.
5
/etc/zshrc
System-wide interactive config. You don't edit this either.
6
~/.zshrc
Your aliases, prompt, completions, plugins. The file you actually care about.
On macOS, Terminal.app opens a login shell by default, so all six steps run every time you open a tab. On many Linux distros, new tabs only get steps 1, 2, 5, 6 (interactive, not login).
Essential Commands
Shell management cheatsheet
Identify
echo $SHELL Your default login shell
echo $0 Shell running in this session
cat /etc/shells All shells installed on the system
which zsh Path to a specific shell binary
Change
chsh -s /bin/zsh Set default shell to zsh
chsh -s /bin/bash Set default shell to bash
chsh -s /opt/homebrew/bin/fish Set to Homebrew-installed fish
exec zsh Restart shell without new window
Reload Config
source ~/.zshrc Reload zsh config in current session
. ~/.zshrc Same — dot is POSIX alias for source
exec $SHELL Start fresh shell (cleaner reload)
source ~/.bash_profile Reload bash login config
Inspect
echo $PATH Full PATH (colon-separated)
which git Which binary runs when you type 'git'
type git Like which but also shows aliases
printenv HOME Print a specific env variable
Quick Reference
Config file comparison
File Shell When it runs Use for Edit?
~/.zshrc zsh interactive aliases, functions, prompt YES — main config
~/.zprofile zsh login PATH, Homebrew, version mgrs YES — for PATH setup
~/.zshenv zsh always $EDITOR, $ZDOTDIR only rarely
~/.bash_profile bash login same as .zprofile if using bash
~/.bashrc bash interactive same as .zshrc if using bash
~/.config/fish/config.fish fish always everything if using fish
Watch Out
Common beginner mistakes
Changes don't apply instantly. After editing .zshrc, run source ~/.zshrc or open a new tab.
Homebrew goes in .zprofile. It modifies PATH — putting it in .zshrc means it runs after PATH-dependent tools need it.
Don't add PATH entries twice. Check before appending or you'll get duplicates that slow shell startup.
Fish ignores .zshrc. If you switch to fish, all your zsh aliases are invisible. Port them to config.fish.
chsh changes future shells only. Your current window stays on the old shell until you open a new one.
Oh My Zsh replaces .zshrc. It creates its own template on install. Back up your existing one first.

Related Topics