I spend 60% of my day in the terminal. Over the years, I’ve accumulated a toolkit of commands and patterns that save me genuinely hours every week. Not obscure tricks I use once a year β€” these are commands I use multiple times daily.

Whether you’re on macOS (zsh), Linux (bash), or WSL, these work everywhere. Let me share the 20 commands and patterns that transformed my terminal workflow.

1. fzf: Fuzzy Find Everything

If you install one tool from this list, make it fzf. It adds fuzzy search to everything:

# Install
brew install fzf  # macOS
sudo apt install fzf  # Ubuntu

# Fuzzy find files
fzf

# Fuzzy search command history (Ctrl+R replacement)
# Type Ctrl+R β†’ fuzzy search through history

# Find and open a file in your editor
vim $(fzf)

# Kill a process with fuzzy search
kill -9 $(ps aux | fzf | awk '{print $2}')

My fzf Configuration

# ~/.zshrc
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'
export FZF_DEFAULT_OPTS='--height 40% --layout=reverse --border'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"

# Preview files with bat
export FZF_CTRL_T_OPTS="--preview 'bat --color=always {}'"

Pro Tip: Combine fzf with git for powerful workflows: git checkout $(git branch | fzf) β€” fuzzy search your branches and check out the selected one. I use this 10+ times a day.

2. ripgrep (rg): grep on Steroids

ripgrep is faster than grep, respects .gitignore, and has better defaults:

# Install
brew install ripgrep  # macOS

# Search for a pattern (respects .gitignore automatically)
rg "TODO" 

# Search specific file types
rg "useState" --type ts

# Search with context (3 lines before/after)
rg "error" -C 3

# Count matches per file
rg "import" --count

# Search and replace (preview)
rg "oldFunction" --files-with-matches | xargs sed -i 's/oldFunction/newFunction/g'

For developers working with large codebases and Git, ripgrep is indispensable for code archaeology.

3. fd: find Files Fast

fd is a faster, simpler alternative to find:

# Install
brew install fd

# Find files by name
fd "config"  # Finds anything with "config" in the name

# Find specific extensions
fd -e ts  # All .ts files
fd -e md -e mdx  # All markdown files

# Find and execute
fd -e test.ts -x rm  # Delete all test files (careful!)
fd -e ts -x wc -l  # Count lines in all TypeScript files

# Exclude directories
fd -e ts --exclude node_modules --exclude dist

4. bat: cat with Syntax Highlighting

# Install
brew install bat

# View file with syntax highlighting and line numbers
bat src/server.ts

# Show only specific lines
bat src/server.ts --line-range 10:30

# Use as a pager for other commands
git diff | bat

# Compare to cat:
cat server.ts    # Plain text, no highlighting
bat server.ts    # Syntax highlighted, line numbers, git changes shown

5. The Power of xargs

xargs takes input lines and runs a command for each one:

# Delete all .log files
find . -name "*.log" | xargs rm

# Run prettier on all changed files
git diff --name-only | grep -E '\.(ts|tsx)$' | xargs npx prettier --write

# Parallel execution (run 4 processes at a time)
find . -name "*.png" | xargs -P 4 -I {} convert {} -quality 80 {}.webp

# Delete all node_modules in subdirectories
fd -t d node_modules | xargs rm -rf

Pro Tip: Use xargs -I {} when you need the argument in a specific position: ls *.txt | xargs -I {} cp {} backup/{}

6. Process Management Tricks

# Find what's using a port
lsof -i :3000
# or
ss -tlnp | grep 3000

# Kill everything on a port
kill -9 $(lsof -t -i :3000)

# Run something in background and detach
nohup npm run build > build.log 2>&1 &

# Monitor a process's resource usage
watch -n 1 'ps aux | grep node'

# See top processes by memory
ps aux --sort=-%mem | head -20

# See top processes by CPU
ps aux --sort=-%cpu | head -20

7. jq: JSON Processing in the Terminal

# Install
brew install jq

# Pretty print JSON
curl -s https://api.github.com/users/octocat | jq .

# Extract specific fields
cat package.json | jq '.dependencies | keys'

# Filter arrays
cat data.json | jq '.users[] | select(.age > 30) | .name'

# Transform structure
cat response.json | jq '{name: .user.name, email: .user.email}'

# Count items
cat data.json | jq '.items | length'

Real-World jq Examples

# Get all dependency versions from package.json
jq -r '.dependencies | to_entries[] | "\(.key): \(.value)"' package.json

# Parse API response and extract specific data
curl -s https://api.github.com/repos/denoland/deno/releases/latest | \
  jq '{version: .tag_name, date: .published_at, assets: [.assets[].name]}'

# Merge two JSON files
jq -s '.[0] * .[1]' base.json override.json

8. History Tricks

# Search history
history | grep "docker"

# Run last command with sudo
sudo !!

# Run last command that started with 'git'
!git

# Replace text in last command and run
^typo^fixed    # If you ran: git comit β†’ runs: git commit

# Show the 20 most used commands
history | awk '{print $2}' | sort | uniq -c | sort -rn | head -20

9. Disk and File Management

# Check disk usage of current directory (human readable, sorted)
du -sh * | sort -rh | head -20

# Find large files (>100MB)
find . -size +100M -type f

# Show total directory size
du -sh node_modules/  # "847M  node_modules/"

# Interactive disk usage (ncdu is amazing)
brew install ncdu
ncdu .  # Visual, interactive disk usage explorer

# Check available disk space
df -h

10. Network Debugging

# Test if a port is open
nc -zv localhost 3000

# DNS lookup
dig example.com
nslookup example.com

# Trace network route
traceroute api.example.com

# Download with timing info
curl -w "\nDNS: %{time_namelookup}s\nConnect: %{time_connect}s\nTTFB: %{time_starttransfer}s\nTotal: %{time_total}s\n" \
  -o /dev/null -s https://example.com

# Watch network connections
watch -n 2 'netstat -an | grep ESTABLISHED | wc -l'

Pro Tip: Use curl -w with timing variables to debug API performance issues. The TTFB (time_starttransfer) tells you how long the server took to respond, separate from DNS and connection time.

11. tmux: Terminal Multiplexer

# Start a named session
tmux new -s project

# Split panes
Ctrl+b %    # Split vertically
Ctrl+b "    # Split horizontally

# Navigate panes
Ctrl+b ←→↑↓

# Detach and reattach
Ctrl+b d          # Detach
tmux attach -t project  # Reattach later

# My typical layout:
# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
# β”‚                  β”‚   npm run    β”‚
# β”‚   editor/code   β”‚     dev      β”‚
# β”‚                  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
# β”‚                  β”‚   git / misc β”‚
# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

12. watch: Repeat Commands

# Watch a log file update in real-time
tail -f /var/log/app.log

# Repeat a command every 2 seconds
watch -n 2 'kubectl get pods'

# Highlight differences between runs
watch -d 'df -h'

# Run until a condition changes
watch -g 'curl -s http://localhost:3000/health'

13. Command Chaining and Control

# Run sequentially (stop on failure)
npm run lint && npm run test && npm run build

# Run sequentially (continue on failure)
npm run lint; npm run test; npm run build

# Run if previous failed
npm run build || echo "Build failed!"

# Pipe and redirect
npm run build 2>&1 | tee build.log  # Show AND save output
command > /dev/null 2>&1  # Suppress all output

14. SSH Tricks

# SSH config for quick connections (~/.ssh/config)
Host prod
  HostName 192.168.1.100
  User deploy
  IdentityFile ~/.ssh/prod_key
  
Host staging
  HostName staging.example.com
  User deploy
  ForwardAgent yes

# Now just:
ssh prod

# Port forwarding (access remote DB locally)
ssh -L 5432:localhost:5432 prod
# Now connect to localhost:5432 to reach prod's PostgreSQL

# Copy files over SSH
scp local-file.txt prod:/home/deploy/
rsync -avz ./dist/ prod:/var/www/app/ --delete

15. Text Processing One-Liners

# Sort and deduplicate
sort file.txt | uniq

# Count unique values
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn

# Extract column from CSV
cut -d',' -f2 data.csv

# Replace text in files (all .ts files)
find . -name "*.ts" -exec sed -i '' 's/oldImport/newImport/g' {} +

# Count lines of code (excluding node_modules)
find . -name "*.ts" -not -path "*/node_modules/*" | xargs wc -l | tail -1

16. Docker Shortcuts

# Remove ALL stopped containers, unused images, volumes
docker system prune -a --volumes

# Logs with follow and timestamps
docker logs -f --tail 100 --timestamps container_name

# Execute command in running container
docker exec -it container_name sh

# Quick container stats
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

# Build with no cache
docker build --no-cache -t myapp .

For more Docker best practices, see our dedicated guide.

17. Git Aliases and Shortcuts

# ~/.gitconfig aliases
[alias]
  s = status -sb
  co = checkout
  br = branch
  ci = commit
  undo = reset --soft HEAD~1
  last = log -1 HEAD --format='%H %s'
  today = log --since='6am' --oneline --author='Michael'
  graph = log --graph --oneline --all --decorate

See our full guide on advanced Git commands for more.

18. Environment Variable Management

# Load .env file into current shell
export $(cat .env | xargs)

# Or use a function
loadenv() {
  set -a
  source "${1:-.env}"
  set +a
}
loadenv .env.local

# Show all env vars matching a pattern
env | grep -i database

# Temporarily set env for one command
DATABASE_URL=postgres://localhost/test npm run migrate

19. Alias and Function Power-Ups

# ~/.zshrc
# Quick directory navigation
alias ..="cd .."
alias ...="cd ../.."
alias dev="cd ~/Projects"

# Development shortcuts
alias nr="npm run"
alias nrd="npm run dev"
alias nrt="npm run test"
alias nrb="npm run build"

# Commonly used sequences as functions
mkcd() { mkdir -p "$1" && cd "$1"; }
port() { lsof -i ":${1:-3000}"; }
killport() { kill -9 $(lsof -t -i ":${1:-3000}") 2>/dev/null && echo "Killed" || echo "Nothing on port $1"; }

# Git shortcuts
alias gs="git status -sb"
alias gd="git diff"
alias gc="git commit"
alias gp="git push"
alias gl="git log --oneline -20"

# Docker shortcuts
alias dps="docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'"
alias dc="docker compose"
alias dcup="docker compose up -d"
alias dcdown="docker compose down"

20. Automation Scripts

Deploy Script

#!/bin/bash
# deploy.sh - One command to deploy
set -e  # Exit on any error

echo "πŸ” Running checks..."
npm run lint
npm run test
npm run build

echo "πŸ“¦ Building Docker image..."
docker build -t myapp:$(git rev-parse --short HEAD) .

echo "πŸš€ Deploying..."
docker push myapp:$(git rev-parse --short HEAD)
kubectl set image deployment/myapp myapp=myapp:$(git rev-parse --short HEAD)

echo "βœ… Deployed $(git rev-parse --short HEAD)"

Database Backup Script

#!/bin/bash
# backup-db.sh
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="$HOME/backups"
DB_NAME="production_db"

mkdir -p "$BACKUP_DIR"

pg_dump "$DATABASE_URL" | gzip > "$BACKUP_DIR/${DB_NAME}_${TIMESTAMP}.sql.gz"

# Keep only last 7 days
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete

echo "βœ… Backup complete: ${DB_NAME}_${TIMESTAMP}.sql.gz"

Common Mistakes

1. Not Using Shell Aliases

If you type the same command more than twice a day, alias it. The two minutes spent creating an alias pays back thousands of keystrokes.

2. Ignoring Tab Completion

Modern shells (zsh with oh-my-zsh) have incredible tab completion. git ch<tab> shows checkout, cherry-pick, etc. Install completions for tools you use frequently.

3. Not Using a Proper Terminal Emulator

Default Terminal.app is fine, but alternatives like Warp, iTerm2, or Alacritty offer split panes, better search, and GPU-accelerated rendering. I use Warp for its AI command suggestions and block-based output.

4. Running Dangerous Commands Without Confirmation

# ❌ Dangerous: removes everything
rm -rf *

# βœ… Safe: preview first
ls *  # See what would be affected
rm -ri *  # Interactive confirmation

5. Not Versioning Your Dotfiles

Keep .zshrc, .gitconfig, .tmux.conf, and other config files in a Git repo:

# Simple dotfiles setup
mkdir ~/dotfiles
cp ~/.zshrc ~/dotfiles/
cp ~/.gitconfig ~/dotfiles/
cd ~/dotfiles && git init

My Terminal Setup

Tool Purpose
Warp Terminal emulator
zsh + oh-my-zsh Shell
Starship Prompt
fzf Fuzzy finding
ripgrep Searching
fd File finding
bat File viewing
jq JSON processing
tmux Session management
ncdu Disk usage

FAQ

What’s the best terminal for macOS in 2025?

Warp if you want AI-assisted terminal with modern UX. iTerm2 if you want battle-tested stability and customization. Alacritty if you want raw speed (GPU-rendered). I switched to Warp for its block-based output and built-in command suggestions, but iTerm2 is still excellent.

Should I use bash or zsh?

Zsh (the default on macOS since Catalina). It’s backwards-compatible with bash but adds better tab completion, spelling correction, glob patterns, and plugin support (oh-my-zsh/zinit). Most scripts are written for bash, but zsh runs them fine with #!/bin/bash shebang.

How do I make my terminal look good?

Install a Nerd Font (JetBrains Mono Nerd Font is my pick), use Starship prompt (cross-shell, fast, beautiful), and pick a color scheme (Catppuccin, Dracula, or Tokyo Night). That’s it β€” function over form, but these three make a huge difference.

What’s the best way to learn terminal commands?

Use tldr instead of man pages β€” it shows practical examples. Install with brew install tldr, then tldr tar gives you the 5 most common tar usages instead of a 2000-line manual. Practice one new command per day in your real workflow.

How do I share terminal sessions with my team?

For live collaboration: use VS Code’s Live Share terminal, tmux shared sessions, or Warp’s Drive feature. For documentation: use script to record sessions or asciinema to record and share terminal sessions as embeddable videos.