Bash Scripts Every Developer Should Have
Most developers spend a surprising amount of time on repetitive terminal tasks — switching between projects, running the same sequence of commands, tailing logs, deploying. A few well-placed Bash scripts can cut that friction significantly.
This isn't a Bash tutorial — it's a collection of scripts I actually use, with enough explanation that you can adapt them for your own workflow.
1. Project Switcher
If you're jumping between multiple projects throughout the day, this saves you from repeatedly navigating to directories and starting services.
#!/usr/bin/env bash
# ~/bin/project
PROJECTS_DIR="$HOME/Websites"
projects=(
"matthew-blog:$PROJECTS_DIR/matthew-blog"
"client-app:$PROJECTS_DIR/client-app"
"api:$PROJECTS_DIR/api"
)
if [ -z "$1" ]; then
echo "Available projects:"
for project in "${projects[@]}"; do
echo " ${project%%:*}"
done
exit 0
fi
for project in "${projects[@]}"; do
name="${project%%:*}"
path="${project##*:}"
if [ "$name" = "$1" ]; then
cd "$path" || exit 1
echo "Switched to $name ($path)"
# Start any services you need — comment out what you don't
# docker compose up -d 2>/dev/null
# code .
exec $SHELL
fi
done
echo "Project '$1' not found."
exit 1
Make it executable and put it on your PATH:
chmod +x ~/bin/project
# Ensure ~/bin is in your PATH in ~/.bashrc or ~/.zshrc:
# export PATH="$HOME/bin:$PATH"
Usage:
project # list all
project matthew-blog # switch to it
2. Git Branch Cleanup
After a few weeks of active development, you end up with dozens of stale local branches. This script deletes all local branches that have already been merged into main (or whatever your default branch is).
#!/usr/bin/env bash
# ~/bin/git-clean
DEFAULT_BRANCH="${1:-main}"
echo "Fetching latest from remote..."
git fetch --prune
merged=$(git branch --merged "$DEFAULT_BRANCH" | grep -v "^\*" | grep -v "$DEFAULT_BRANCH" | tr -d ' ')
if [ -z "$merged" ]; then
echo "No merged branches to clean up."
exit 0
fi
echo "Branches to delete:"
echo "$merged"
echo ""
read -rp "Delete these branches? [y/N] " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
echo "$merged" | xargs git branch -d
echo "Done."
else
echo "Aborted."
fi
git-clean # cleans branches merged into main
git-clean dev # cleans branches merged into dev
The git fetch --prune at the top removes references to remote branches that no longer exist, so your git branch -r output doesn't get cluttered either.
3. New Laravel Project Bootstrap
Starting a new Laravel project always involves the same steps. This script handles them all:
#!/usr/bin/env bash
# ~/bin/new-laravel
if [ -z "$1" ]; then
echo "Usage: new-laravel <project-name>"
exit 1
fi
PROJECT_NAME="$1"
PROJECTS_DIR="$HOME/Websites"
PROJECT_PATH="$PROJECTS_DIR/$PROJECT_NAME"
echo "Creating Laravel project: $PROJECT_NAME"
composer create-project laravel/laravel "$PROJECT_PATH"
cd "$PROJECT_PATH" || exit 1
# Copy .env
cp .env.example .env
# Set DB name to project name (replace hyphens with underscores)
DB_NAME="${PROJECT_NAME//-/_}"
sed -i "s/DB_DATABASE=laravel/DB_DATABASE=$DB_NAME/" .env
php artisan key:generate
# Init git
git init
git add -A
git commit -m "Initial Laravel install"
echo ""
echo "Done! Project created at $PROJECT_PATH"
echo "Next: cd $PROJECT_PATH && php artisan serve"
4. Log Watcher
Tailing logs is fine, but this adds colour-coding so errors jump out at you:
#!/usr/bin/env bash
# ~/bin/watch-log
LOG_FILE="${1:-storage/logs/laravel.log}"
if [ ! -f "$LOG_FILE" ]; then
echo "Log file not found: $LOG_FILE"
exit 1
fi
tail -f "$LOG_FILE" | while read -r line; do
if echo "$line" | grep -q "ERROR\|CRITICAL\|EMERGENCY"; then
echo -e "\033[31m$line\033[0m" # Red
elif echo "$line" | grep -q "WARNING"; then
echo -e "\033[33m$line\033[0m" # Yellow
elif echo "$line" | grep -q "INFO"; then
echo -e "\033[32m$line\033[0m" # Green
else
echo "$line"
fi
done
Run it from your project root:
watch-log # default Laravel log
watch-log /var/log/nginx/error.log # any log file
5. Quick Git Stats
A summary of what's happened in a repo lately — useful when coming back to a project after time away:
#!/usr/bin/env bash
# ~/bin/git-stats
DAYS="${1:-7}"
echo "=== Git activity for the last $DAYS days ==="
echo ""
echo "--- Commits ---"
git log --oneline --since="$DAYS days ago"
echo ""
echo "--- Files changed ---"
git diff --stat "HEAD@{$DAYS days ago}" HEAD 2>/dev/null | tail -1
echo ""
echo "--- Contributors ---"
git shortlog -sn --since="$DAYS days ago"
git-stats # last 7 days
git-stats 30 # last 30 days
6. Simple Deploy Script
This is a minimal deploy script for projects hosted on a VPS where you SSH in and pull. Adapt it for your own setup:
#!/usr/bin/env bash
# deploy.sh (in project root)
set -e # exit immediately on error
SERVER_USER="ubuntu"
SERVER_HOST="your.server.com"
PROJECT_PATH="/var/www/your-app"
echo "Deploying to $SERVER_HOST..."
ssh "$SERVER_USER@$SERVER_HOST" << 'REMOTE'
set -e
cd /var/www/your-app
echo "Pulling latest..."
git pull origin main
echo "Installing dependencies..."
composer install --no-dev --optimize-autoloader
echo "Running migrations..."
php artisan migrate --force
echo "Clearing caches..."
php artisan config:cache
php artisan route:cache
php artisan view:cache
echo "Restarting queue workers..."
php artisan queue:restart
echo "Done."
REMOTE
echo "Deployment complete."
The set -e at the top is important — if any command fails (migration error, composer fail), the script stops immediately rather than blundering forward.
Making Scripts Available Everywhere
Put your scripts in ~/bin/ and add it to your PATH in ~/.bashrc or ~/.zshrc:
export PATH="$HOME/bin:$PATH"
Then source ~/.bashrc (or restart your terminal) and they're available from anywhere.
A few other tips:
- Always add
set -eto scripts where a failure partway through would leave things in a broken state. - Use
set -uto catch references to undefined variables — catches a lot of typo bugs. set -o pipefailmakes pipeline errors propagate correctly (without it,failing_command | grep somethingexits 0).- Add usage messages at the top of every script — future-you will appreciate it.
Final Thoughts
The best Bash scripts are the ones that automate something you'd otherwise do manually three times a week. Start by looking at your own command history — history | sort | uniq -c | sort -rn | head -20 — and see what comes up repeatedly. That's your automation backlog.