I spent the first two years of my Linux journey typing the same long commands over and over. Then I discovered bash aliases, and everything changed. After building up around 40 of them across several machines, I learned the hard way which techniques actually survive a reboot, a new terminal window, or a fresh SSH connection.
This guide is the resource I wish I’d had on day one. I’ll show you exactly how to write bash functions and aliases that persist across shell sessions, with real examples from my own setup. By the end, you’ll know where to put your shortcuts, how to pass arguments with bash functions, and how to fix the five most common alias problems.
Table of Contents
What Are Bash Aliases and Functions?
Bash aliases are shortcut commands that expand into longer commands when you type them. A bash function is a reusable block of shell code that can accept arguments and run logic. Both save typing, but they serve slightly different jobs.
When you type ll in a terminal, your shell expands it to ls -la because someone defined an alias. When you run mkcd my-project, that is almost always a function, because it needs to accept your directory name as an argument. Aliases cannot take arguments directly. Functions can.
Here’s the shortest possible definition: aliases replace text, functions run code. Aliases are best for fixed shortcuts. Functions are best when you need arguments, conditionals, or multiple commands chained together.
Creating Temporary Aliases in the Current Shell
Temporary aliases exist only until you close the terminal. They are perfect for testing before committing to a permanent change.
To create a temporary alias, use this syntax:
alias shortcut='longer command'
Here are some real examples I use during my workday:
alias ll='ls -la'— detailed file listing including hidden filesalias gs='git status'— check git status quicklyalias ports='ss -tulnp'— show listening network portsalias update='sudo apt update && sudo apt upgrade -y'— full system update
To verify the alias was created, run alias ll and the shell prints the expansion. To see every alias currently active, run alias with no arguments. The output lists each name and its definition.
These aliases disappear the moment your shell exits. To make them survive a reboot, you need to write them into a configuration file. That is what the next section covers.
Making Aliases Persistent Across Shell Sessions
To make bash aliases and functions persist across shell sessions, add them to your ~/.bashrc file. This file runs every time you open a new interactive non-login shell, which covers most day-to-day terminal use on Ubuntu, Debian, Fedora, and most modern Linux distributions.
Open the file with your favorite editor:
nano ~/.bashrc
Scroll to the bottom of the file and add your aliases. I like to keep them in a clearly labeled block so I can find them later:
# My custom aliases
alias ll='ls -la'
alias gs='git status'
alias ports='ss -tulnp'
Save the file and exit. The new aliases will not load automatically in your current shell. You have to either open a new terminal or reload the file with the source command:
source ~/.bashrc
The source command tells bash to re-read the file in the current shell. I run this dozens of times a day while tweaking my setup.
Login Shells Versus Interactive Shells
This is the part that trips up most beginners. Bash reads different files depending on how the shell starts.
- Login shell: starts when you log in via console, SSH, or
bash --login. It reads~/.bash_profile,~/.bash_login, or~/.profile. - Interactive non-login shell: starts when you open a new terminal window in your desktop environment. It reads
~/.bashrc.
If your aliases only live in ~/.bashrc and you SSH into a server, your aliases are missing because the login shell never reads ~/.bashrc. The fix is to make ~/.bash_profile source your ~/.bashrc:
# In ~/.bash_profile
if [ -f ~/.bashrc ]; then
source ~/.bashrc
fi
This single block covers both cases and is the standard pattern recommended across the Linux community. Ubuntu’s default ~/.bash_profile already does this for you, but other distributions may not.
Using ~/.bash_aliases as a Separate File
On Debian and Ubuntu systems, ~/.bashrc already contains this block near the bottom:
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
That snippet checks whether ~/.bash_aliases exists and, if it does, sources it. This gives you a dedicated file just for aliases, separate from your main shell config. On Fedora, Arch, and most non-Debian systems, this block is missing. You can add it yourself.
To use this approach, create the file and add your aliases there:
nano ~/.bash_aliases
Then add your shortcuts:
alias ll='ls -la'
alias gs='git status'
alias update='sudo apt update && sudo apt upgrade -y'
I prefer this method because it keeps ~/.bashrc clean and makes my aliases easy to back up, sync via Git, or copy to a new machine with one command.
Bash Functions: When Aliases Need Arguments
Aliases cannot accept arguments. If you try alias mkcd='mkdir $1 && cd $1', the shell does not substitute $1 because aliases do simple text replacement at parse time, not runtime. The argument would be empty.
That is exactly what bash functions are for. Functions run inside the shell and have access to $1, $2, and every other parameter.
Here is a simple function that creates a directory and changes into it:
mkcd() {
mkdir -p "$1" && cd "$1"
}
Now mkcd my-project creates the folder and moves you into it. The -p flag prevents errors if the directory already exists. Always quote your variables inside functions, otherwise paths with spaces will break.
Here are more useful functions I rely on every day:
extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.bz2) tar xjf "$1" ;;
*.tar.gz) tar xzf "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.rar) unrar x "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xf "$1" ;;
*.tbz2) tar xjf "$1" ;;
*.tgz) tar xzf "$1" ;;
*.zip) unzip "$1" ;;
*.Z) uncompress "$1" ;;
*) echo "'$1' cannot be extracted" ;;
esac
else
echo "'$1' is not a valid file"
fi
}
Now extract archive.tar.gz just works, no matter the compression type. Another one I keep around:
gitdiff() {
git diff --color "$1" | less -R
}
Functions live in the same files as aliases — ~/.bashrc or ~/.bash_aliases. They persist the same way.
Reloading Configuration Without Restarting
After editing ~/.bashrc or ~/.bash_aliases, you have three options to apply the changes.
Option 1: Source the file
source ~/.bashrc
This is the most common approach. It re-reads the file in your current shell.
Option 2: Use the dot shorthand
. ~/.bashrc
The dot command is identical to source. Some shell scripts use the dot for compactness.
Option 3: Restart the shell
exec bash
This replaces your current shell with a fresh one. Useful when source causes problems, but it kills your shell history buffer unless you save it first with history -a.
I run source ~/.bashrc after every edit. It is fast and predictable.
Alias vs Function vs Script: When to Use Each
Beginners often ask: should I write an alias, a function, or a script? Here is the decision rule I follow after years of trial and error.
Use an alias when:
- The shortcut is a fixed command with no arguments
- It is a simple rename, like
ll='ls -la' - You want it to feel like a single word you type
Use a function when:
- You need to accept arguments like
$1or$@ - The logic needs conditionals, loops, or pipes
- The shortcut is specific to your interactive shell and not useful in scripts
Use a script when:
- Other people need to run it, including non-interactive contexts
- It should work inside other scripts or cron jobs
- You want to version it independently and ship it somewhere
The key technical difference is that aliases are not expanded inside scripts by default. If you put alias rm='rm -i' in your ~/.bashrc, then run a script that calls rm, the script does not see the alias. This is by design — scripts should be predictable and not depend on your interactive shell setup.
If you absolutely need aliases inside a script, you can enable them with:
shopt -s expand_aliases
But I would not recommend it. Convert the alias to a function or script instead, and put it somewhere on your PATH.
Bash vs Zsh Compatibility Notes
Most of this guide assumes bash. If your shell prompt says % instead of $, you are probably running zsh, the default on macOS since Catalina.
For zsh, the equivalent file is ~/.zshrc. Most bash aliases and functions work in zsh without modification, because zsh was designed to be largely compatible. But two differences catch people out:
- Zsh does not expand aliases inside scripts by default either, but the option is
setopt aliasesinstead ofshopt -s expand_aliases. - Some bash-specific extensions like
shoptoptions are missing in zsh.
If you switch between bash and zsh on the same machine, the cleanest approach is to keep your definitions in a shared file and source it from both ~/.bashrc and ~/.zshrc:
# In both ~/.bashrc and ~/.zshrc
source ~/.shell_shared
This works for simple aliases and functions, and I have used it on dual-shell systems for years.
Removing Aliases and Functions
To remove an alias from your current session, use unalias:
unalias ll
To remove every temporary alias:
unalias -a
To remove a function:
unset -f mkcd
These commands affect only the running shell. To remove them permanently, edit the line out of ~/.bashrc or ~/.bash_aliases and reload.
If you want to bypass an alias temporarily and run the original command, prefix it with a backslash:
rm file.txt
The backslash tells bash to skip alias expansion and run /bin/rm directly. This saved me once when I aliased rm to rm -i for safety but needed to wipe a build directory without prompts.
Troubleshooting Common Alias Problems
After reading countless forum threads and answering questions on Ask Ubuntu and Stack Overflow, I see the same five alias problems over and over. Here is how to fix each one.
1. Alias Works Once, Then Disappears
You defined an alias in the terminal, opened a new window, and it was gone. That is because the alias was temporary. Edit ~/.bashrc instead, then run source ~/.bashrc.
2. Permission Denied When Editing ~/.bashrc
This happens when the file is owned by root but your user does not have write permission. Use sudo nano ~/.bashrc if you must, but the cleaner fix is to fix ownership:
sudo chown $USER:$USER ~/.bashrc
3. Alias Works on Local Login but Not Over SSH
SSH sessions start login shells, which read ~/.bash_profile first. Add the source trick from earlier so ~/.bash_profile pulls in ~/.bashrc.
4. Alias Not Expanding Inside a Shell Script
By design, scripts do not inherit interactive aliases. Convert the alias to a function in ~/.bashrc, or better, write the logic directly in the script.
5. Alias Conflicts With an Existing Command
Sometimes an alias name shadows a real command. Run type ll to see what ll actually resolves to. If your alias wins and you need the original, prefix with backslash: ll.
For deeper diagnostics, type -a shows every definition. For example, type -a cd reveals whether you have a function, alias, or built-in named cd.
Quick Reference Table
| Task | Command |
|---|---|
| Create temporary alias | alias ll='ls -la' |
| List all aliases | alias |
| Remove one alias | unalias ll |
| Remove all aliases | unalias -a |
| Reload bashrc | source ~/.bashrc |
| Reload bash_aliases | source ~/.bash_aliases |
| Define function | name() { commands; } |
| Remove function | unset -f name |
| Show command origin | type name |
| Bypass alias once | command |
Frequently Asked Questions
How to make Bash alias permanent?
Add the alias definition to your ~/.bashrc file using a text editor, then run source ~/.bashrc to load it in the current shell. Every new terminal window you open afterward will pick it up automatically because interactive non-login shells read ~/.bashrc on startup. On Debian and Ubuntu, you can also use ~/.bash_aliases, which ~/.bashrc sources automatically if the file exists.
Should I put aliases in bashrc or bash_profile?
Put aliases in ~/.bashrc for most setups, because interactive non-login shells read it on every new terminal window. Add a guard block at the bottom of ~/.bash_profile that sources ~/.bashrc if present, so SSH and login sessions also pick them up. On Debian and Ubuntu the default ~/.bash_profile already does this for you.
Where to store Bash aliases?
Store bash aliases in ~/.bashrc for full compatibility, or in ~/.bash_aliases if you want a dedicated file. On Debian and Ubuntu, ~/.bashrc automatically sources ~/.bash_aliases if it exists. For sharing across bash and zsh, keep definitions in a shared file like ~/.shell_shared and source it from both ~/.bashrc and ~/.zshrc.
How to get all alias in the current session?
Run the alias command with no arguments to list every alias currently defined in your shell. The output shows each name alongside its expansion. For more detail, use type -a commandname to see whether the command is an alias, function, built-in, or external binary.
Why are my bash aliases not working in scripts?
Bash scripts run in non-interactive mode by default and do not expand interactive aliases, even if you defined them in ~/.bashrc. This is intentional so scripts behave predictably. To work around it, convert the alias to a function in ~/.bashrc, or place the logic directly inside the script. Avoid using shopt -s expand_aliases in production scripts because it relies on the parent shell configuration.
Conclusion
You now have everything you need to write bash functions and aliases that persist across shell sessions, on bash and zsh, with or without arguments. Start with a few high-value aliases like ll and update, add a function or two for the workflows you repeat daily, and back the files up in Git so you can clone them onto a new server in seconds.
Pick one task you do every day, write an alias or function for it, save it to ~/.bashrc or ~/.bash_aliases, and run source ~/.bashrc. That single habit, repeated, is how you turn a slow shell into a fast one.