A snippet that works grows a life of its own: first it is an alias, then it needs an argument, then a loop, then it belongs in three shells. Knowing which container — alias, function, or script — fits each stage saves the rewrite you were about to regret.
The three containers #
# alias: text substitution, no logic
alias gs='git status -sb'
# function: logic, arguments, lives in the shell
gco() { git checkout "$1" && git pull; }
# script: a program anyone can run
#!/usr/bin/env bash
# ~/bin/deploy <env>
set -euo pipefail
env="${1:?usage: deploy <env>}"
rsync -a dist/ "server:/srv/$env/"
An alias is string replacement the shell performs before anything else — perfect for retyping, useless for logic. A function is real code inside your interactive shell session. A script is a file: versioned, shareable, callable from cron and CI, working in every shell.
The decision table #
| Need | Use |
|---|---|
| Shorter typing, no logic | Alias |
| Logic, needs your aliases/env, interactive | Function |
| Logic, runs anywhere, unattended | Script |
| Shared with a team | Script (in a repo) |
| Needs to survive a different shell | Script (POSIX or explicit shebang) |
| Needs arguments and flags | Function or script — never an alias |
Aliases cannot take positional arguments — alias gco='git checkout' works only because the rest of the line trails on; the day you need $1, it is a function.
Where each lives #
Aliases and functions go in shell startup files — ~/.bashrc for interactive bash, or a ~/.*-aliases file you source from it. Scripts go in a PATH directory, ideally ~/.local/bin or a repo's bin/:
# ~/.bashrc
if [ -f "$HOME/.bash_aliases" ]; then
. "$HOME/.bash_aliases"
fi
export PATH="$HOME/.local/bin:$PATH"
That split matters for one subtle reason: functions are not inherited by scripts or non-interactive shells (with few exceptions like exported functions), so anything a cron job, CI pipeline, or teammate touches must be a script.
The migration path #
The honest lifecycle most utilities follow:
- Typed twice — leave it, or alias it.
- Typed with variations — function, with a usage guard.
- Needed outside the shell — script, with a shebang,
set -euo pipefail, and shellcheck. - Needed by others — repo, README, tests.
Each step is small; skipping two at once is where unmaintainable one-liner-scripts come from.
Functions make the middle stage pleasant — see bash functions; scripts start with the first-script note. The scripting series continues.
