Skip to content
daniel@MacBookPro:~
Shell & TerminalHow-To September 22, 2026 3 min read

How to Set Up Shell Aliases and Functions Properly

A complete walkthrough of the actual difference between an alias and a function, when each one is the right tool, and how to avoid the specific mistakes that make aliases behave unpredictably.

Aliases and shell functions both let you create shortcuts for longer commands, but they work through genuinely different mechanisms — knowing which one to reach for avoids a specific category of confusing, hard-to-debug behavior.

Step 1: create a basic alias

# in ~/.bashrc or ~/.zshrc
alias ll='ls -lah'
alias gs='git status'

An alias is a simple text substitution — typing ll is, as far as the shell is concerned, exactly equivalent to having typed ls -lah directly.

Step 2: understand exactly where aliases fall short

alias backup='cp $1 $1.bak'
# does NOT work as expected — aliases don't accept
# positional arguments in any meaningful way

Because an alias is pure text substitution happening before argument parsing, it cannot reference $1 or any other positional parameter meaningfully — anything needing actual logic, conditionals, or arguments needs a function instead.

Step 3: write a shell function for anything needing real logic

backup() {
    cp "$1" "$1.bak"
}

Functions are genuine, callable code blocks that accept arguments properly, can include conditionals and loops, and can return values — the right tool the moment your “shortcut” needs to do more than substitute fixed text.

Step 4: use functions for anything involving conditionals

mkcd() {
    mkdir -p "$1" && cd "$1"
}

This function creates a directory and immediately changes into it — genuinely impossible to express as a plain alias, since it requires sequencing two commands conditionally based on the first one’s success.

Step 5: check whether a name you’re about to use is already an alias, function, or command

type ll

type reports whether a given name resolves to an alias, a function, a builtin, or an external command — genuinely useful before defining a new alias or function with a name that might already be doing something else.

Step 6: organize aliases and functions in a separate, sourced file

# in ~/.bashrc:
[ -f ~/.bash_aliases ] && source ~/.bash_aliases

Keeping aliases and functions in a dedicated file, sourced from your main shell config, keeps a large personal collection organized and makes it trivial to share or version-control separately from the rest of your shell configuration.

Step 7: avoid aliasing over standard command names without a safety net

alias rm='rm -i'

Aliasing rm to always prompt for confirmation is common and reasonable — just be aware that if you rely on this safety net, running commands as a different user or through sudo (which doesn’t inherit your aliases by default) bypasses it, so the underlying habit of double-checking destructive commands still matters.

Step 8: check alias definitions when something behaves unexpectedly

alias
type suspicious-command-name

If a familiar command suddenly behaves differently than expected, checking whether it’s actually been aliased (possibly by a plugin, framework, or something you forgot you configured) is a quick, often-overlooked diagnostic step.

Why understanding this distinction prevents a specific class of confusing bugs

Attempting to pass arguments meaningfully to an alias, or expecting conditional logic inside one, produces behavior that looks like it should work but silently doesn’t — recognizing upfront that aliases are text substitution and functions are real code is what prevents time spent debugging a fundamentally mismatched tool choice rather than an actual logic error.