Work Faster in the Terminal: Shell Aliases and Functions (with Useful Examples)
Tired of typing the same long commands over and over? Wish you could speed up your terminal workflow with just a few keystrokes? Aliases are a simple but powerful way to customise your shell, save time, and reduce errors.
Whether you're on macOS or Linux, mastering aliases can dramatically improve your productivity. In this guide, you'll learn best practices for defining aliases and functions, keeping them organised, making them portable, and avoiding common mistakes.
Get ready to take control of your terminal and work smarter.
What Is an Alias #
An alias is a user-defined shortcut in the shell that replaces a long or complex command with a shorter, easier-to-remember name.
Example
Instead of typing:
vim ~/.ssh/config
You can create an alias:
alias sshconf='vim ~/.ssh/config'
Now, simply run:
sshconf
This opens your SSH config file instantly, saving time and effort.
Why Use Aliases #
Typing long commands over and over is slow and can lead to errors. Aliases help by:
- Saving you time
- Minimising mistakes
- Making commands easier to read
- Turning complex commands into simple, memorable shortcuts
How It Works #
Creating an Alias #
Step 1: Confirm Active Shell
echo $SHELL
Step 2: Edit Shell Configuration File
Linux typically uses Bash by default, while macOS defaults to Zsh.
For Bash:
vim ~/.bashrc
For Zsh:
vim ~/.zshrc
Append to the end of shell config file:
For Bash:
# Load aliases
[ -f ~/.aliases ] && . ~/.aliases
The .
is a shorthand for source
in Bash, preferred for its brevity and speed. Both work identically.
Explanation:
[ -f ~/.aliases ]
- This checks if the file
~/.aliases
exists and is a regular file. - Returns true if the file exists.
- This checks if the file
&&
- Logical AND operator. The command after this will only run if the test before it (
[ -f ... ]
) returns true.
- Logical AND operator. The command after this will only run if the test before it (
. ~/.aliases
- This sources the
~/.aliases
file. - It executes the commands (aliases and functions) in that file in the current shell context, so the aliases become available immediately.
- This sources the
For Zsh:
# Load aliases
[ -f ~/.aliases ] && source ~/.aliases
Step 3: Create Aliases File
Create file:
vim ~/.aliases
Step 4: Create Alias
Basic syntax:
alias name='<command>'
Example:
alias update='sudo apt update && sudo apt upgrade -y'
Step 5: Apply Changes
Reload configuration file:
. ~/.bashrc # Bash
source ~/.zshrc # Zsh
Or simply restart the terminal.
Creating a Shell Function #
Aliases simplify single commands, but functions unlock greater power by grouping commands, accepting arguments, and enabling dynamic behaviour.
Use functions when:
- You need parameters or user input
- You want to run multiple commands together
- Aliases become too complex or hard to read
In short, functions are dynamic, flexible aliases.
Step 1: Create Functions File
Create file:
vim ~/.functions
Step 2: Create Function
Basic syntax:
command() {
command
}
Example:
# ** Create and enter a directory **
mkcd() {
mkdir -p "$1" && cd "$1"
}
Step 3: Apply Changes
Reload configuration file:
. ~/.bashrc # Bash
source ~/.zshrc # Zsh
Or simply restart the terminal.
Useful Commands #
Search for alias:
alias | grep <pattern>
Show what an alias does:
Use type
for full, accurate info.
type <alias> # Shows exactly what the shell will execute: alias, function, or command.
or:
which <alias> # Shows path to an external command.
Best Practices #
1. Organise in a Dedicated Dotfiles Directory
Storing shell customisations in ~/dotfiles
is a well-established best practice. It offers:
- Portability: Version-control your setup with Git across systems.
- Clean structure: Keeps your home directory uncluttered.
- Maintainability: Easier to manage and update.
- Cross-platform support: Separate and load OS-specific configs as needed.
Create a dotfiles directory:
mkdir -p ~/dotfiles
This folder will store all your custom configuration files in one place.
Structure files logically:
~/dotfiles/
├── .aliases
├── .aliases-linux
├── .aliases-macos
├── .functions
├── .functions-linux
└── .functions-macos
Split general and platform-specific customisations for clarity and conditional loading.
Load dotfiles from shell configuration
For Bash:
# Load shared files
[ -f ~/dotfiles/.functions ] && . ~/dotfiles/.functions
[ -f ~/dotfiles/.aliases ] && . ~/dotfiles/.aliases
# Load Linux-specific files
if command -v apt >/dev/null 2>&1 && apt --version >/dev/null 2>&1; then
[ -f ~/dotfiles/.functions-linux ] && . ~/dotfiles/.functions-linux
[ -f ~/dotfiles/.aliases-linux ] && . ~/dotfiles/.aliases-linux
fi
# Load macOS-specific files
if command -v brew >/dev/null 2>&1 && brew --version >/dev/null 2>&1; then
[ -f ~/dotfiles/.functions-macos ] && . ~/dotfiles/.functions-macos
[ -f ~/dotfiles/.aliases-macos ] && . ~/dotfiles/.aliases-macos
fi
These checks ensure the package manager exists and works properly:
command -v apt
/brew
verifies the command is installed.apt --version
/brew --version
confirms it runs without errors.
Both are needed because commands like apt
can exist on macOS as non-functional stubs. This prevents loading incorrect platform-specific files.
For Zsh:
# Load shared files
[ -f ~/dotfiles/.functions ] && source ~/dotfiles/.functions
[ -f ~/dotfiles/.aliases ] && source ~/dotfiles/.aliases
# Load Linux-specific files
if command -v apt >/dev/null 2>&1 && apt --version >/dev/null 2>&1; then
[ -f ~/dotfiles/.functions-linux ] && source ~/dotfiles/.functions-linux
[ -f ~/dotfiles/.aliases-linux ] && source ~/dotfiles/.aliases-linux
fi
# Load macOS-specific files
if command -v brew >/dev/null 2>&1 && brew --version >/dev/null 2>&1; then
[ -f ~/dotfiles/.functions-macos ] && source ~/dotfiles/.functions-macos
[ -f ~/dotfiles/.aliases-macos ] && source ~/dotfiles/.aliases-macos
fi
Or use the following shell-aware loader, compatible with both Bash and Zsh:
Use this snippet for better readability and maintainability.
# Dotfiles directory
DOTFILES=~/dotfiles
# Detect shell to use correct source command
if [ -n "$ZSH_VERSION" ]; then
load() { source "$1"; }
else
load() { . "$1"; }
fi
# Source file if it exists
load_if_exists() {
[ -f "$1" ] && load "$1"
}
# Load shared files
load_if_exists "$DOTFILES/.functions"
load_if_exists "$DOTFILES/.aliases"
# Load Linux-specific files
if command -v apt >/dev/null 2>&1 && apt --version >/dev/null 2>&1; then
load_if_exists "$DOTFILES/.functions-linux"
load_if_exists "$DOTFILES/.aliases-linux"
fi
# Load macOS-specific files
if command -v brew >/dev/null 2>&1 && brew --version >/dev/null 2>&1; then
load_if_exists "$DOTFILES/.functions-macos"
load_if_exists "$DOTFILES/.aliases-macos"
fi
2. Symlink Primary Shell Config from ~/dotfiles
Place your main shell config file (.bashrc
or .zshrc
) into ~/dotfiles
and create a symlink in your home directory. This keeps your configuration centralised, version-controlled, and easy to sync or back up.
Move files into ~/dotfiles
:
mv ~/.bashrc ~/dotfiles/.bashrc # Bash
mv ~/.zshrc ~/dotfiles/.zshrc # Zsh
Create symbolic link in home directory:
ln -s ~/dotfiles/.bashrc ~/.bashrc # Bash
ln -s ~/dotfiles/.zshrc ~/.zshrc # Zsh
Syntax:
ln -s <target> <link>
target
: The original file or directory to link to.link
: The name and location of the symlink.
Verify symbolic link :
ls -l ~/.bashrc # Bash
ls -l ~/.zshrc # Zsh
This will confirm that the link points to the corresponding file in ~/dotfiles
.
3. Choose Safe and Memorable Alias Names
Always check if a name is already in use:
type <alias>
- Use short, unique, and memorable names
- Follow a consistent naming convention:
- Use lowercase letters
- Use underscores to separate words
Example:
alias blog_admin='open https://example.com/login' # Open admin login page
alias update='sudo apt update && sudo apt upgrade -y' # Update packages
4. Quote Carefully
Always use single quotes around the alias command.
Example:
alias poweroff='sudo shutdown -h now' # Shutdown system
5. Group Logically and Comment Clearly
- Group related aliases and functions together.
- Add short comments to explain each function.
Example:
# ================================
# Networking & Connectivity
# ================================
# ** Fetch HTTP headers from a given URL **
check_headers() {
...
}
# ** Check SSL certificate details for a given host and port **
check_ssl() {
...
}
6. Connect Better Tools to Existing Aliases
- Replace commands with improved alternatives by creating aliases that point to the better tool.
- This lets you enhance familiar commands without changing your workflow.
alias bat='cat'
7. Avoid Risky Overrides
- Never override critical system commands like
rm
,cd
,mv
, unless you have a compelling reason. - If you must override, make the alias safe.
Example:
alias cp='cp -i' # Copy files interactively (prompt before overwrite)
alias mv='mv -i' # Move files interactively (prompt before overwrite)
alias rm='rm -i' # Remove files interactively (prompt before delete)
8. Function Design Guidelines
- Add usage information in the function body to clarify expected inputs.
- Make functions robust:
- Check if required commands are installed.
- Handle errors gracefully and validate inputs.
- Check for missing arguments.
- Confirm the existence of files or directories before operating on them.
- Use meaningful parameter names and handle arguments carefully to prevent unintended behaviour.
- Make functions OS-aware when needed:
- Detect whether the system uses Linux (
apt
) or macOS (brew
) and adjust logic accordingly. - Avoid hardcoding system-specific commands when portability is required.
- Detect whether the system uses Linux (
Examples
Check if command is installed:
command() {
if command -v htop >/dev/null 2>&1; then
# Command exists
...
else
# Command not found
...
fi
}
This function checks if the command htop
is installed:
command -v htop >/dev/null 2>&1
tries to locate thehtop
command without outputting anything.- If
htop
is found, the function executes the code inside the first block (# Command exists
). - If not found, it runs the code inside the
else
block (# Command not found
).
To test the "not found" case, temporarily rename the command inside the function.
Usage information in function body:
command() {
if [ -z "$1" ]; then
echo "Usage: ..."
return 1
fi
...
}
This function checks if an argument is provided when called:
if [ -z "$1" ]
: Tests if the first argument ($1
) is empty.- If empty, it prints a usage message (
echo "Usage: ..."
). return 1
exits the function with an error status, indicating incorrect usage.- If an argument is given, the function continues with its main tasks (
...
).
Detect operating system:
command() {
if command -v apt >/dev/null 2>&1 && apt --version >/dev/null 2>&1; then
# Linux
...
elif command -v brew >/dev/null 2>&1 && brew --version >/dev/null 2>&1; then
# macOS
...
else
echo "Unsupported or unknown environment."
return 1
fi
}
This function detects the package manager based on the environment:
command -v apt >/dev/null 2>&1 && apt --version >/dev/null 2>&1
Checks if theapt
command exists and runs without error, indicating a Linux system using APT.command -v brew >/dev/null 2>&1 && brew --version >/dev/null 2>&1
Checks if thebrew
command exists and runs without error, indicating macOS with Homebrew.- If neither condition matches, it prints an error message and exits with status 1, signalling an unsupported environment.
Detect shell environment:
command() {
if [ -n "$ZSH_VERSION" ]; then
# ZSH
...
elif [ -n "$BASH_VERSION" ]; then
# Bash
...
else
echo "Unsupported or unknown shell."
return 1
fi
}
This function detects the current shell environment:
[ -n "$ZSH_VERSION" ]
checks if theZSH_VERSION
variable is set, indicating you are running Zsh.[ -n "$BASH_VERSION" ]
checks if theBASH_VERSION
variable is set, indicating you are running Bash.- If neither variable is set, the function outputs an error message and returns
1
, signalling an unsupported or unknown shell.
9. Version Control Your Dotfiles
Initialise a Git repository in your ~/dotfiles
directory:
cd ~/dotfiles
git init
git add .aliases .functions
git commit -m "Initial alias and function setup"
Push to a remote repo (e.g. GitHub) to sync across systems.
10. General Tips
- Define each alias or function on its own line.
- Use clear comments and group related entries for readability.
- Add as many as needed, but keep them organised.
- Review and clean up periodically to maintain efficiency.
- Reload your shell config after changes:
. ~/.bashrc # Bash
source ~/.zshrc # Zsh
Tips and Tricks #
1. Chain Multiple Commands
You can combine multiple commands into a single alias using &&
, ;
, or ||
depending on your needs.
Example:
alias h='clear && cd'
This clears the terminal screen and then, if successful, changes the current directory to the home directory.
2. Use Environment Variables in Aliases
Use environment variables to keep your alias config clean and easy to maintain. Define values like versions or paths once, then reuse them in aliases.
Example:
# ================================
# Environment Variables
# ================================
# PHP version
PHP_VER="8.3"
# ================================
# Development & Utilities
# ================================
# PHP
alias php_conf="sudo vim /etc/php/${PHP_VER}/fpm/php.ini"
alias phpfpm_restart="sudo systemctl restart php${PHP_VER}-fpm"
Quoting rule for variables
When using variables in aliases, you must use double quotes ("
), so the shell expands the variable when the alias is created.
Double quotes allow variables like
$PHP_VER
to expand to their actual value.Single quotes treat the variable name as plain text, so the alias won’t work as expected.
Correct:
alias php_conf="vim /etc/php/${PHP_VER}/fpm/php.ini"
Incorrect:
alias php_conf='vim /etc/php/${PHP_VER}/fpm/php.ini' # This sets the literal string, not the value of PHP_VER
3. Use a Function When an Alias Needs sudo
By default, sudo
ignores aliases because it runs commands in a new shell without alias definitions. Use a function instead, as functions are expanded in the current shell and work reliably with sudo
.
Example:
update() {
sudo apt update && sudo apt upgrade -y
}
4. Enable Autocompletion for Aliases
Zsh: Autocompletion works automatically with aliases.
Bash: Not always enabled by default. To enable:
Check if bash-completion is configured:
grep bash_completion ~/.bashrc
Output:
if [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi
This confirms that .bashrc
is set to load bash-completion
automatically if available. No additional setup needed.
If bash-completion is not installed, install it by running:
sudo apt install bash-completion
Add this to ~/.bashrc
to enable it:
# Load bash-completion if available
[[ -r /usr/share/bash-completion/bash_completion ]] && . /usr/share/bash-completion/bash_completion
Reload shell:
source ~/.bashrc
Frequently Asked Questions #
Why doesn’t my alias work in scripts?
Aliases are not expanded in non-interactive shell sessions (such as scripts) by default. Use shell functions instead of aliases for scripts.
Are aliases case-sensitive?
Yes. update
and Update
is treated as separate names.
Troubleshooting #
Alias Not Recognised
Symptoms:
- Running the alias name results in "command not found".
- The alias has no effect.
Cause:
- The alias file is not sourced in the shell configuration file (
~/.bashrc
,~/.zshrc
). - The alias is defined with incorrect syntax (e.g. missing quotes).
- Conflicting alias or command name exists.
- File containing aliases (
.aliases
) has not been loaded or contains errors.
Solution:
- Verify the alias file is sourced in shell config:
[ -f ~/dotfiles/.aliases ] && source ~/dotfiles/.aliases
- Reload shell configuration:
. ~/.bashrc # for Bash
source ~/.zshrc # for Zsh
- Use
alias
to confirm the alias exists:
alias <alias>
- Make sure the alias is quoted correctly using single quotes:
alias ll='ls -l'
- Check for conflicts using:
type <alias>
File Sourcing Errors
Symptoms:
- Startup errors related to
.bashrc
,.zshrc
, or.aliases
.
Solutions:
- Check file syntax: missing quotes, mismatched brackets, etc.
- Test alias definitions manually in the terminal before adding them to config files.
Next Steps #
Aliases are one of the simplest and most effective ways to boost your command-line productivity. With just a few lines in your config file, you can:
- Save time by reducing repetitive typing
- Prevent mistakes by wrapping risky commands
- Maintain consistency across systems
- Customise your terminal to fit your workflow
Get Started
- Create three aliases today for commands you use often
- Explore example aliases and functions for inspiration
These quick wins will improve your workflow and make your terminal experience more efficient and enjoyable.