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:

bash
vim ~/.ssh/config

You can create an alias:

~/.aliases
alias sshconf='vim ~/.ssh/config'

Now, simply run:

bash
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

bash
echo $SHELL

Step 2: Edit Shell Configuration File

Linux typically uses Bash by default, while macOS defaults to Zsh.

For Bash:

bash
vim ~/.bashrc

For Zsh:

bash
vim ~/.zshrc

Append to the end of shell config file:

For Bash:

~/.bashrc
# 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.
  • &&
    • Logical AND operator. The command after this will only run if the test before it ([ -f ... ]) returns true.
  • . ~/.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.

For Zsh:

~/.zshrc
# Load aliases
[ -f ~/.aliases ] && source ~/.aliases

Step 3: Create Aliases File

Create file:

bash
vim ~/.aliases

Step 4: Create Alias

Basic syntax:

~/.aliases
alias name='<command>'

Example:

~/.aliases
alias update='sudo apt update && sudo apt upgrade -y'

Step 5: Apply Changes

Reload configuration file:

bash
. ~/.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:

bash
vim ~/.functions

Step 2: Create Function

Basic syntax:

~/.functions
command() {
  command
}

Example:

~/.functions
# ** Create and enter a directory **

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

Step 3: Apply Changes

Reload configuration file:

bash
. ~/.bashrc       # Bash
source ~/.zshrc   # Zsh

Or simply restart the terminal.

Useful Commands #

Search for alias:

bash
alias | grep <pattern>

Show what an alias does:

Use type for full, accurate info.

bash
type <alias>   # Shows exactly what the shell will execute: alias, function, or command.

or:

bash
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:

bash
mkdir -p ~/dotfiles

This folder will store all your custom configuration files in one place.

Structure files logically:

filesystem
~/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:

~/.bashrc
# 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:

~/.zshrc
# 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.

~/.bashrc or ~/.zshrc
# 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:

bash
mv ~/.bashrc ~/dotfiles/.bashrc   # Bash
mv ~/.zshrc ~/dotfiles/.zshrc     # Zsh

Create symbolic link in home directory:

bash
ln -s ~/dotfiles/.bashrc ~/.bashrc   # Bash 
ln -s ~/dotfiles/.zshrc ~/.zshrc     # Zsh

Syntax:

bash
ln -s <target> <link>
  • target: The original file or directory to link to.
  • link: The name and location of the symlink.

Verify symbolic link :

bash
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:

bash
type <alias>
  • Use short, unique, and memorable names
  • Follow a consistent naming convention:
    • Use lowercase letters
    • Use underscores to separate words

Example:

~/.aliases
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:

~/.aliases
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:

~/.functions
# ================================
# 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.
~/.aliases
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:

~/.aliases
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.

Examples

Check if command is installed:

~/.functions
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 the htop 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:

~/.functions
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:

~/.functions
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 the apt 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 the brew 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:

~/.functions
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 the ZSH_VERSION variable is set, indicating you are running Zsh.
  • [ -n "$BASH_VERSION" ] checks if the BASH_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:

bash
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:
bash
. ~/.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:

~/.aliases
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:

~/.aliases
# ================================
# 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:

ini
alias php_conf="vim /etc/php/${PHP_VER}/fpm/php.ini"

Incorrect:

ini
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:

~/.functions
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:

bash
grep bash_completion ~/.bashrc

Output:

bash
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:

bash
sudo apt install bash-completion

Add this to ~/.bashrc to enable it:

~/.bashrc
# Load bash-completion if available
[[ -r /usr/share/bash-completion/bash_completion ]] && . /usr/share/bash-completion/bash_completion

Reload shell:

bash
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:
bash
[ -f ~/dotfiles/.aliases ] && source ~/dotfiles/.aliases
  • Reload shell configuration:
bash
. ~/.bashrc       # for Bash
source ~/.zshrc   # for Zsh
  • Use alias to confirm the alias exists:
bash
alias <alias>
  • Make sure the alias is quoted correctly using single quotes:
~/.aliases
alias ll='ls -l'
  • Check for conflicts using:
bash
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

These quick wins will improve your workflow and make your terminal experience more efficient and enjoyable.