How to Create a Bash Alias That Accepts Parameters
TLDR
Bash aliases don't support parameters directly, but you can achieve the same result using functions. Define a function in your .bashrc or .zshrc file that accepts arguments through $1, $2, etc., and it works like an alias with parameters. Functions are more flexible and can handle complex logic while remaining easy to invoke.
Why Standard Aliases Don't Work with Parameters
A standard Bash alias is just text substitution. When you create an alias like this:
alias ll='ls -lah'
Bash replaces ll with ls -lah before executing. If you try to pass parameters:
alias search='grep -r $1 .'
This doesn't work as expected. The $1 gets expanded when you define the alias, not when you use it. When you run search "pattern", Bash doesn't pass "pattern" to $1.
Using Functions Instead of Aliases
The solution is to use a function, which can accept parameters properly:
search() {
grep -r "$1" .
}
Now when you run search "pattern", the parameter is correctly passed to the grep command.
Add this to your ~/.bashrc (on Linux) or ~/.zshrc (if you use zsh) to make it permanent:
# Add to ~/.bashrc or ~/.zshrc
search() {
grep -r "$1" .
}
After adding it, reload your configuration:
source ~/.bashrc
Working with Multiple Parameters
Functions can accept any number of parameters through $1, $2, $3, etc., or $@ for all parameters:
# Function that takes source and destination
cpb() {
cp "$1" "$1.backup"
echo "Created backup: $1.backup"
}
# Usage:
cpb important.conf
Here's a more practical example with multiple parameters:
# Create a directory and navigate into it
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Usage:
mkcd ~/projects/new-app
The mkdir -p creates parent directories if needed, and && only runs cd if mkdir succeeds.
Functions with Optional Parameters
You can provide default values for parameters that might be omitted:
# Git commit with optional message
gcm() {
local message="${1:-Update files}"
git add -A
git commit -m "$message"
}
# Usage:
gcm "Add new feature" # Uses provided message
gcm # Uses default: "Update files"
The ${1:-Update files} syntax means "use $1 if provided, otherwise use 'Update files'".
Practical Examples
Here are some useful function-based aliases that accept parameters:
Extract any archive format:
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" ;;
*.7z) 7z x "$1" ;;
*) echo "'$1' cannot be extracted" ;;
esac
else
echo "'$1' is not a valid file"
fi
}
# Usage:
extract archive.tar.gz
extract files.zip
Find and replace text in files:
replace() {
if [ $# -ne 2 ]; then
echo "Usage: replace <search> <replace>"
return 1
fi
find . -type f -exec sed -i "s/$1/$2/g" {} +
}
# Usage:
replace "oldtext" "newtext"
Quick note taking:
note() {
local note_file="$HOME/notes.txt"
if [ $# -eq 0 ]; then
cat "$note_file"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - $*" >> "$note_file"
fi
}
# Usage:
note Remember to review the PR
note Check production logs tomorrow
note # Display all notes
This function uses $* to capture all arguments as the note text.
Complex Function with Multiple Options
For more sophisticated needs, you can parse options within your function:
deploy() {
local env="staging"
local branch="main"
local verbose=false
while [[ $# -gt 0 ]]; do
case $1 in
-e|--env)
env="$2"
shift 2
;;
-b|--branch)
branch="$2"
shift 2
;;
-v|--verbose)
verbose=true
shift
;;
*)
echo "Unknown option: $1"
return 1
;;
esac
done
[ "$verbose" = true ] && echo "Deploying $branch to $env"
git fetch origin
git checkout "$branch"
git pull origin "$branch"
case $env in
production)
./scripts/deploy-prod.sh
;;
staging)
./scripts/deploy-staging.sh
;;
dev)
./scripts/deploy-dev.sh
;;
esac
}
# Usage:
deploy -e production -b release/v2.0 -v
deploy --env staging
This approach mirrors how standard command-line tools work, making your functions feel like native commands.
Organizing Your Functions
As you accumulate functions, organize them in separate files:
# Create a directory for your functions
mkdir -p ~/.bash_functions
# Create individual function files
cat > ~/.bash_functions/git.sh << 'EOF'
# Git-related functions
gcm() {
git add -A
git commit -m "$1"
}
gps() {
git push origin "$(git branch --show-current)"
}
gpl() {
git pull origin "$(git branch --show-current)"
}
EOF
cat > ~/.bash_functions/docker.sh << 'EOF'
# Docker-related functions
dex() {
docker exec -it "$1" /bin/bash
}
dlogs() {
docker logs -f "$1"
}
EOF
Load them in your .bashrc:
# Add to ~/.bashrc
if [ -d ~/.bash_functions ]; then
for file in ~/.bash_functions/*.sh; do
source "$file"
done
fi
This keeps your configuration clean and makes functions easy to manage. The structure looks like:
~/.bashrc (loads function files)
|
v
~/.bash_functions/
|
+-- git.sh (Git shortcuts)
+-- docker.sh (Docker helpers)
+-- system.sh (System utilities)
+-- work.sh (Project-specific functions)
Performance Considerations
Functions run in the current shell process, so they're fast. However, if you define too many functions in .bashrc, shell startup can slow down. For rarely-used functions, consider:
# Lazy load functions only when needed
if [ -f ~/.bash_functions/heavy.sh ]; then
load_heavy_functions() {
source ~/.bash_functions/heavy.sh
unset -f load_heavy_functions
}
fi
Debugging Your Functions
When a function isn't working as expected, add set -x at the beginning to see what's executing:
deploy() {
set -x # Enable debug mode
git fetch origin
git checkout "$1"
set +x # Disable debug mode
}
This prints each command before executing it, showing variable expansions.
You can also use type to verify how Bash interprets your function:
type gcm
This shows whether gcm is a function, alias, or command, and displays its definition.
Converting Existing Aliases
If you have existing aliases that need parameters, converting them is straightforward:
Before (doesn't work with parameters):
alias ports='netstat -tulanp | grep $1'
After (works correctly):
ports() {
netstat -tulanp | grep "$1"
}
The conversion pattern is simple: wrap the command in a function and reference parameters with $1, $2, etc.
Functions give you the flexibility of full Bash scripting within simple one-liners. You can validate inputs, handle errors, use conditionals, and even call other functions. While they're technically not aliases, they serve the same purpose of creating memorable shortcuts for common tasks, with the added benefit of accepting parameters and handling complexity.
Found an issue?