alias gs='git status' in ~/.zshrc, then source ~/.zshrc. That is the whole basic answer. What makes zsh worth the article is the two alias types bash does not have: global aliases (alias -g G='| grep', expandable anywhere in a line) and suffix aliases (alias -s md=nvim, so typing a filename opens it in the right program).
Aliases are the highest-return five minutes you can spend on a terminal, and they are also where people hit the limits of what an alias can do without noticing they have.
Table of contents
- Defining and persisting
- Inspecting and removing
- Global aliases, which bash cannot do
- Suffix aliases
- When an alias should be a function
- A starting set worth stealing
- Aliases stop at the edge of your machine
- How this fits the rest of the stack
- FAQ
Defining and persisting
# Try it in the current session
alias gs='git status -sb'
# Make it permanent
echo "alias gs='git status -sb'" >> ~/.zshrc
source ~/.zshrc
No spaces around the =. alias gs = 'git status' is a syntax error, and it is the single most common typo here.
Single quotes matter when the alias contains variables. With double quotes, $PWD is expanded once at definition time and frozen; with single quotes it is expanded each time the alias runs, which is almost always what you meant.
alias where="echo $PWD" # frozen at the directory you were in
alias where='echo $PWD' # evaluated when you run it
Inspecting and removing
alias # list everything defined
alias gs # show one definition
unalias gs # remove it for this session
unalias -m 'g*' # remove everything matching a pattern
# Run the real command, bypassing an alias, without unaliasing
\ls
command ls
# What is this name, really?
which ls
type -a ls
The backslash prefix is the one to remember. If ls is aliased to something with colour and grouping flags and you want the raw output for a script or a pipe, \ls gets it without touching the alias.
Global aliases, which bash cannot do
A normal alias only expands as the first word of a command. alias -g expands anywhere on the line, which lets you alias operators and fragments.
alias -g G='| grep -i'
alias -g L='| less'
alias -g NUL='> /dev/null 2>&1'
alias -g C='| wc -l'
# Then
ps aux G nginx
journalctl -u api L
make build NUL
ls C
This is genuinely useful and genuinely dangerous. Because expansion happens anywhere, a short global alias will fire inside contexts you did not intend — a two-letter global alias is asking for it. The convention that has emerged is uppercase names, since command arguments are rarely uppercase.
Do not add a global alias for something you might legitimately type as a literal argument. alias -g A='| awk' will bite you the first time a filename or a git branch is called A.
Suffix aliases
A suffix alias binds a file extension to a program, so typing a filename alone opens it.
alias -s md=nvim
alias -s {yml,yaml}=nvim
alias -s {jpg,png}=open
alias -s log='tail -f'
# Now this just works
README.md
config.yaml
app.log
Almost nobody knows this exists, and it is a small daily pleasure. docker-compose.yml opening the editor because you typed the filename is exactly the kind of thing a shell should do and mostly does not.
When an alias should be a function
The hard limit: an alias cannot take arguments in the middle. It only prepends text. The moment you need $1 somewhere other than the end, you need a function.
# This does not do what it looks like -- the argument lands after the whole string
alias mkcd='mkdir -p $1 && cd $1'
# Correct: a function
mkcd() {
mkdir -p "$1" && cd "$1"
}
# A more useful one
gcm() {
git commit -m "$*"
}
# Jump to a project and show status
proj() {
cd ~/code/"$1" || return
git status -sb
}
Functions go in ~/.zshrc alongside the aliases and are used identically. The rule of thumb: if you find yourself writing $1 in an alias, stop and write a function. If the alias only ever prepends, an alias is fine and marginally faster.
Quote the arguments inside functions. cd $1 breaks on a directory with a space in it; cd "$1" does not.
A starting set worth stealing
# Git
alias gs='git status -sb'
alias gd='git diff'
alias gl='git log --oneline --graph --decorate -20'
alias gp='git push'
alias gco='git checkout'
# Navigation
alias ..='cd ..'
alias ...='cd ../..'
alias ll='ls -lah'
# Safety -- prompt before clobbering
alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'
# Global
alias -g G='| grep -i'
alias -g L='| less'
# Suffix
alias -s {md,txt,yml,yaml,json}=nvim
The -i safety aliases are worth a caveat: they build a habit that fails you on any machine where they are not defined, and scripts do not see them at all. Some people prefer to keep destructive commands feeling destructive.
Keep these in a separate ~/.zsh_aliases sourced from .zshrc if you sync dotfiles across machines — it keeps machine-specific config out of the shared file.
Aliases stop at the edge of your machine
Every alias here lives in your local ~/.zshrc. Nothing in CI has them, no container has them, and a colleague’s terminal has a different set. That is fine — they are personal ergonomics, not project configuration.
The problem is when an alias quietly becomes load-bearing. A deploy that works because you have alias deploy='ssh prod && ...' is a deploy that only you can perform, and only from that laptop. The knowledge lives in a dotfile nobody else reads.
The fix is to move anything a second person needs into the repository — a script, a Makefile target, or a build the platform runs from the repo. Deploying from a connected GitHub repository with build logs and a live route, as RunxBuild does, means the deploy is reproducible by whoever pushes rather than by whoever owns the right shell config.
How this fits the rest of the stack
alias name='command' in ~/.zshrc, no spaces around the equals, single quotes when variables are involved. Use \name to bypass one. Learn alias -g for pipeline fragments and alias -s for opening files by name. Switch to a function the moment you need an argument anywhere but the end.
Keep them personal. Anything a teammate or a pipeline depends on belongs in the repo instead. If you are working out what running that project properly costs, the RunxBuild hosting calculator breaks it into service, database, storage, and bandwidth.
Useful related references:
FAQ
How do I create a permanent alias in zsh?
Add alias name='command' to ~/.zshrc, then run source ~/.zshrc or open a new terminal. Do not put spaces around the equals sign — that is a syntax error rather than an alias. Keep the definition in single quotes if it references variables you want evaluated at run time.
What is a global alias in zsh?
A global alias, defined with alias -g, expands anywhere on the command line rather than only as the first word. alias -g G='| grep -i' lets you write ps aux G nginx. Use uppercase names, because a short lowercase global alias will expand inside arguments where you did not want it.
What is a suffix alias in zsh?
alias -s md=nvim binds a file extension to a program, so typing README.md on its own opens it in that editor. You can bind several at once with brace expansion, as in alias -s {yml,yaml,json}=nvim. bash has no equivalent.
Why can’t my zsh alias take arguments?
Aliases only prepend text to the command line, so an argument always lands at the end. If you need the argument in the middle — like mkdir -p "$1" && cd "$1" — write a shell function instead. Functions live in ~/.zshrc alongside aliases and are called the same way.
How do I temporarily bypass an alias?
Prefix the command with a backslash, as in \ls, or use command ls. Both run the real binary while leaving the alias defined. This matters when an alias adds formatting flags you do not want in a pipe or a script.