4 min read
On this page

Aliases & Scripts

The Compound Effect of Small Savings

You type git status maybe 30 times a day. That's about 3 seconds each time, counting the keystrokes and the mental overhead of typing a full command. Alias it to gs and you save maybe 1.5 seconds per invocation. That's 45 seconds per day.

Sounds trivial. It's not.

45 seconds per day is 3.75 minutes per week, 3 hours per year — for a single alias. Now consider that you probably have 20-30 commands you type repeatedly throughout the day. The compound savings from a good alias set easily reach 30-60 hours per year.

But the real benefit isn't the time. It's the reduced friction. Every keystroke between you and the result you want is a tiny barrier. Remove enough tiny barriers and your terminal starts feeling like an extension of your thoughts rather than an obstacle between you and your work.

Aliases

An alias is a shortcut for a command. Define them in your shell's RC file (~/.zshrc or ~/.bashrc).

Git aliases (the highest-ROI set)

# Core workflow
alias gs="git status"
alias ga="git add"
alias gc="git commit"
alias gp="git push"
alias gl="git pull"
alias gd="git diff"
alias gds="git diff --staged"
alias gco="git checkout"
alias gsw="git switch"
alias gb="git branch"
alias glog="git log --oneline --graph --decorate -20"

# Common operations
alias gca="git commit --amend"
alias gcan="git commit --amend --no-edit"
alias grb="git rebase"
alias grbi="git rebase -i"
alias gcp="git cherry-pick"
alias gst="git stash"
alias gstp="git stash pop"
alias ..="cd .."
alias ...="cd ../.."
alias ....="cd ../../.."

# Project shortcuts (customize these)
alias proj="cd ~/projects"
alias work="cd ~/work"
alias dots="cd ~/dotfiles"

Listing aliases

alias ll="ls -la"
alias la="ls -A"
alias lt="ls -lt"     # Sort by modification time

# If you use eza (modern ls replacement)
alias ls="eza"
alias ll="eza -la"
alias lt="eza -la --sort modified"
alias tree="eza --tree"

Safety aliases

# Prompt before overwriting files
alias cp="cp -i"
alias mv="mv -i"

# Show what's being removed
alias rm="rm -v"

Utility aliases

# Quick editing
alias zshrc="$EDITOR ~/.zshrc"
alias reload="source ~/.zshrc"

# Network
alias myip="curl -s ifconfig.me"
alias ports="lsof -i -P -n | grep LISTEN"

# Disk usage
alias duh="du -h -d 1 | sort -hr"

# Process searching
alias psg="ps aux | grep -v grep | grep"

Alias tips

Keep alias names short but memorable. Single-letter aliases are tempting but become confusing fast — two-letter aliases hit the sweet spot.

Don't alias commands that change behavior in surprising ways. Aliasing rm to rm -rf will eventually destroy something important.

Review your shell history to find alias candidates:

history | awk '{print $2}' | sort | uniq -c | sort -rn | head -20

This shows your 20 most frequently used commands. Anything in the top 10 deserves an alias.

Shell Functions

When an alias isn't enough — when you need arguments in the middle of a command, or you need conditional logic — use a shell function.

Functions that should be in everyone's RC file

# Create a directory and cd into it
mkcd() {
    mkdir -p "$1" && cd "$1"
}

# Find a file by name
ff() {
    find . -type f -name "*$1*" 2>/dev/null
}

# Find a directory by name
fd() {
    find . -type d -name "*$1*" 2>/dev/null
}

# Quick grep with context
gre() {
    grep -rn --color=auto "$1" "${2:-.}"
}

# Open the current git repo in the browser
ghopen() {
    local url
    url=$(git remote get-url origin 2>/dev/null)
    if [ -z "$url" ]; then
        echo "Not a git repository or no remote set"
        return 1
    fi
    url=$(echo "$url" | sed 's/git@github.com:/https:\/\/github.com\//' | sed 's/\.git$//')
    open "$url" 2>/dev/null || xdg-open "$url" 2>/dev/null
}

Git-specific functions

# Switch to a branch with fuzzy finding (requires fzf)
gswf() {
    local branch
    branch=$(git branch --all | sed 's/remotes\/origin\///' | sort -u | fzf --height 40%)
    if [ -n "$branch" ]; then
        git switch "$branch" 2>/dev/null || git switch -c "$branch"
    fi
}

# Show a compact, useful git log
gll() {
    git log --oneline --graph --decorate "${1:--20}"
}

# Delete merged branches
gclean() {
    git branch --merged main | grep -v "main\|master\|\*" | xargs -r git branch -d
    echo "Cleaned up merged branches."
}

Project-specific functions

# Start your dev environment
devup() {
    docker compose up -d
    echo "Waiting for services..."
    sleep 3
    echo "Services ready."
}

# Run tests with common options
t() {
    if [ -f "package.json" ]; then
        npm test -- "$@"
    elif [ -f "go.mod" ]; then
        go test ./... "$@"
    elif [ -f "Cargo.toml" ]; then
        cargo test "$@"
    elif [ -f "pytest.ini" ] || [ -f "setup.py" ]; then
        pytest "$@"
    else
        echo "Unknown project type"
    fi
}

# Quick serve current directory
serve() {
    local port="${1:-8000}"
    echo "Serving on http://localhost:$port"
    python3 -m http.server "$port"
}

Small Scripts

When a function grows beyond 10-15 lines, move it to a standalone script. Keep personal scripts in a directory like ~/.local/bin or ~/bin and make sure it's in your PATH.

Script template

Every script should start with these basics:

#!/usr/bin/env bash
set -euo pipefail

# Description: What this script does
# Usage: script-name [options] <argument>

# set -e: Exit on error
# set -u: Error on undefined variables
# set -o pipefail: Pipe fails if any command in the pipe fails

set -euo pipefail is non-negotiable. Without it, your script will silently continue past errors, giving you wrong results or, worse, corrupting data.

Example: daily log helper

#!/usr/bin/env bash
set -euo pipefail

LOG_DIR="$HOME/worklogs"
TODAY=$(date +%Y-%m-%d)
LOG_FILE="$LOG_DIR/$TODAY.md"

mkdir -p "$LOG_DIR"

if [ ! -f "$LOG_FILE" ]; then
    cat > "$LOG_FILE" << EOF
# Work Log - $TODAY

## Plan
-

## Done
-

## Notes
-
EOF
fi

${EDITOR:-vim} "$LOG_FILE"

Example: port finder

#!/usr/bin/env bash
set -euo pipefail

PORT="${1:?Usage: whichport <port>}"
echo "Processes using port $PORT:"
lsof -i ":$PORT" -P -n 2>/dev/null || echo "Nothing found on port $PORT"

Building Your Personal Toolkit

The key principle: build tools that solve your specific problems. Generic productivity tools exist by the thousands. What makes your toolkit powerful is that it's tailored to your workflow, your projects, and your pain points.

Start with friction

Every time you catch yourself doing something repetitive or annoying, write it down. At the end of the week, look at the list and automate the top items.

Keep it simple

The best personal scripts are under 30 lines. If it's getting complex, you're probably solving the wrong problem or should reach for a real programming language instead of bash.

Version control your dotfiles

Your aliases, functions, and scripts are valuable. They represent years of accumulated workflow optimization. Put them in a git repository:

~/dotfiles/
  .zshrc
  .gitconfig
  bin/
    new-project
    worklog
    whichport
  install.sh     # Symlinks everything into place

When you get a new machine, clone the repo, run install.sh, and you're productive in minutes instead of days.

Common Pitfalls

Over-aliasing. If you can't remember what your aliases do without looking them up, you have too many. Start with 10-15 high-frequency commands and grow slowly.

Not using set -euo pipefail in scripts. This will burn you eventually. A script that silently ignores errors is a script that will delete the wrong directory at 2am.

Making scripts too clever. A script that handles every edge case, accepts 15 flags, and works on every operating system is not a personal productivity script — it's a software project. Keep personal tools simple and fix them when they break.

Forgetting to make scripts executable. After creating a new script, run chmod +x script-name. If you keep forgetting, add it as a step in your new-script workflow.

Not adding the script directory to PATH. Scripts in ~/bin are useless if ~/bin isn't in your PATH. Add export PATH="$HOME/bin:$PATH" to your RC file once and forget about it.

Key Takeaways

Aliases for your most-used commands save time and reduce friction. Check your shell history to find the best candidates.

Shell functions handle anything that needs arguments or logic. Keep a small library of them in your RC file.

Standalone scripts belong in a dedicated directory on your PATH. Always use set -euo pipefail.

Version control your dotfiles. Your personal toolkit is a valuable asset that should follow you across machines.

The best personal tools are small, specific, and built to solve problems you actually have. Don't build tooling for imaginary problems.