alias gs='git status' defines a shortcut for the current shell only. To keep it, put the same line in ~/.bashrc and reload with source ~/.bashrc. And when you find yourself wanting the alias to accept an argument in the middle, you have outgrown aliases — that is a shell function.
Aliases are the smallest possible productivity change and one of the few that genuinely compounds. Typing gs instead of git status forty times a day is not dramatic on any single occasion.
The two things people get stuck on are making them survive a new terminal, and discovering that arguments do not work the way they expect.
Table of contents
- Defining, listing, removing
- Making them permanent
- Where aliases stop and functions start
- Aliases in scripts, and why they do not work
- A starting set
- How this fits the rest of the stack
- FAQ
Defining, listing, removing
alias gs='git status' # define
alias # list all defined aliases
alias gs # show one
unalias gs # remove
unalias -a # remove all
Use single quotes. Double quotes let the shell expand variables at definition time, which is almost never what you want:
alias here="echo $PWD" # expands NOW -- always prints the directory you defined it in
alias here='echo $PWD' # expands when RUN -- prints the current directory
That difference catches people out because the double-quoted version appears to work until they change directory.
To run the original command when an alias shadows it, prefix with a backslash:
alias ls='ls --color=auto'
\ls # the real ls, no alias
command ls # also the real ls
This matters in scripts, where an alias defined interactively should not change behaviour — although in practice aliases are not expanded in non-interactive shells at all, which is a related gotcha covered below.
Making them permanent
An alias defined at the prompt dies with the shell. To keep it, put it in a file that runs at startup:
echo "alias gs='git status'" >> ~/.bashrc
source ~/.bashrc
Which file depends on the shell and how it started:
~/.bashrc— interactive non-login shells. This is where aliases belong on Linux.~/.bash_profileor~/.profile— login shells. On macOS, Terminal starts a login shell by default, so.bashrcmay never be read unless.bash_profilesources it.~/.bash_aliases— a separate file that Debian and Ubuntu’s default.bashrcsources automatically. Cleaner if you have more than a handful.~/.zshrc— for zsh, which is now the default on macOS.
The macOS case trips people up regularly. If your aliases work after source ~/.bashrc but vanish in a new window, add this to ~/.bash_profile:
[ -f ~/.bashrc ] && source ~/.bashrc
Keep aliases in their own file and source it, rather than growing .bashrc indefinitely. It makes the collection portable — one file to copy to a new machine or keep in a dotfiles repository.
Where aliases stop and functions start
This is the real limit, and it is the reason most alias frustration exists.
An alias performs textual substitution at the start of a command. Arguments you type get appended to the end. There is no way to place an argument in the middle.
alias mkcd='mkdir -p && cd' # broken: nowhere for the argument to go
mkcd newdir # becomes: mkdir -p && cd newdir
A function takes parameters properly:
mkcd() {
mkdir -p "$1" && cd "$1"
}
Functions go in the same config files and are used identically. The rule of thumb is simple: if the shortcut needs an argument anywhere other than the very end, write a function.
A few functions worth having:
# extract any archive by extension
extract() {
case "$1" in
*.tar.gz|*.tgz) tar xzf "$1" ;;
*.tar.bz2) tar xjf "$1" ;;
*.zip) unzip "$1" ;;
*) echo "unknown archive: $1" ;;
esac
}
# git commit with a message, no quoting ceremony
gc() { git commit -m "$*"; }
# find a process by name
psg() { ps aux | grep -v grep | grep -i "$1"; }
Always quote "$1". Unquoted, a path with a space becomes two arguments and the function does something you did not ask for.
Aliases in scripts, and why they do not work
This surprises people the first time: aliases are disabled in non-interactive shells. A script that uses one gets command not found, even though the alias works fine at your prompt.
That is deliberate. A script whose behaviour depends on the invoking user’s personal aliases is a script that behaves differently for everyone who runs it.
You can enable them explicitly, though it is rarely the right call:
#!/bin/bash
shopt -s expand_aliases
source ~/.bashrc
The better approach in a script is to define a function, which works in non-interactive shells without any flag, or simply to write the full command. Scripts should be explicit — that is their purpose.
The same reasoning applies to sudo, which does not see your aliases either because it runs a new process. If you need one under sudo, this small trick makes sudo expand aliases in its argument:
alias sudo='sudo ' # note the trailing space
The trailing space tells bash to check the next word for alias expansion too. It is obscure and genuinely useful.
A starting set
Worth having, and worth typing out rather than copying a 300-line collection you will never remember:
# navigation
alias ..='cd ..'
alias ...='cd ../..'
alias ll='ls -alFh'
# safety
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# git
alias gs='git status -sb'
alias gd='git diff'
alias gl='git log --oneline --graph --decorate -20'
# system
alias ports='ss -tulpn'
alias df='df -h'
alias myip='curl -s ifconfig.me'
One caution on the safety block. Aliasing rm to rm -i builds a habit that depends on the alias existing — and on a server where it does not, or in a script where aliases are disabled, the confirmation you have come to rely on simply is not there. Some people consider this a net negative for exactly that reason. It is a real trade rather than an obvious win.
Keep the set small. An alias you have to look up is slower than typing the command.
How this fits the rest of the stack
Aliases are personal tooling — they make one machine faster for one person and travel only as far as your dotfiles. That is fine for a shell, and it is exactly the wrong model for deployment, where anything that lives only on someone’s laptop is a step nobody else can reproduce.
The deploy equivalent of a good alias is a build that runs the same way for everyone, defined in the repository rather than in someone’s shell history. RunxBuild builds services and static sites from a connected repository with the environment configured per service, so shipping does not depend on which shortcuts happen to be on a given machine. If you are sizing up what the running service, its database and its storage cost, the RunxBuild hosting calculator shows them separately.
Useful related references:
- zsh Aliases: Global, Suffix, and the Ones bash Never Had
- Bash Append to File:
>>,tee -a, and Heredoc - How to Run .sh File in Linux: bash, chmod, and shebang
- Services on RunxBuild
FAQ
Why does my bash alias disappear when I close the terminal?
Aliases defined at the prompt exist only for that shell session. Add the line to ~/.bashrc (or ~/.bash_aliases on Debian and Ubuntu) and run source ~/.bashrc to load it into the current shell. New terminals will then pick it up automatically.
Can a bash alias take arguments?
Only at the end, because an alias is textual substitution at the start of a command — whatever you type is appended. If you need an argument in the middle, write a shell function instead. Functions accept parameters properly and go in the same config files.
Why do aliases not work in my shell script?
Bash disables alias expansion in non-interactive shells by design, so scripts do not silently change behaviour based on the running user’s personal shortcuts. Use a function or the full command in scripts; shopt -s expand_aliases will enable them but is rarely the right choice.
Why do my aliases not load on macOS?
macOS Terminal starts a login shell, which reads ~/.bash_profile rather than ~/.bashrc. Add [ -f ~/.bashrc ] && source ~/.bashrc to ~/.bash_profile. If you are on a recent macOS the default shell is zsh, so the file is ~/.zshrc.
How do I run the original command when an alias overrides it?
Prefix it with a backslash — \ls — or use command ls. Both bypass alias lookup and run the real binary, which is useful when an alias adds flags you do not want for one particular invocation.