Shell Productivity
Pipes, redirection, shell scripting, aliases, and workflow tips that make you faster in the terminal - tailored for web developers.
Search across all documentation pages
Pipes, redirection, shell scripting, aliases, and workflow tips that make you faster in the terminal - tailored for web developers.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Quick-reference recipe card - copy-paste ready.
# Pipes - chain commands
cat package.json | jq '.dependencies | keys[]'
git log --oneline | head -20
npm outdated | grep -v "Package"
# Redirection
npm run build > build.log 2>&1 # stdout + stderr to file
npm test 2>/dev/null # suppress errors
# Command substitution
echo "Node $(node -v) on $(uname -s)"
# History
history | grep "docker" # search past commands
Ctrl+R # reverse search (interactive)
!! # repeat last command
sudo !! # repeat last command with sudoWhen to reach for this: When you need to combine commands, automate repetitive tasks, or speed up your terminal workflow.
#!/bin/bash
# save as: scripts/health-check.sh
# run with: bash scripts/health-check.sh
echo "=== Project Health Check ==="
echo ""
echo "Node: $(node -v) | npm: $(npm -v)"
echo "Branch: $(git branch --show-current)"
echo "Uncommitted changes: $(git status --porcelain | wc -l | tr -d ' ')"
echo ""
echo "--- Dependencies ---"
echo "Total packages: $(ls node_modules | wc -l | tr -d ' ')"
echo "Outdated:"
npm outdated 2>/dev/null || echo " All up to date"
echo ""
echo "--- Code Stats ---"
echo "TypeScript files: $(find src -name '*.ts' -o -name '*.tsx' 2>/dev/null | wc -l | tr -d ' ')"
echo "Test files: $(find src -name '*.test.*' -o -name '*.spec.*' 2>/dev/null | wc -l | tr -d ' ')"
echo "TODO count: $(grep -r 'TODO\|FIXME' src/ --include='*.ts' --include='*.tsx' 2>/dev/null | wc -l | tr -d ' ')"
echo ""
echo "--- Type Check ---"
npx tsc --noEmit 2>&1 | tail -1What this demonstrates:
$(...) for inline command outputfind, grep, wc to count things2>/dev/null to suppress noisePipes (|) pass the output of one command as input to the next.
# Count lines of code by file type
find src -name "*.tsx" | xargs wc -l | sort -n | tail -20
# Find the largest 10 dependencies
du -sh node_modules/* | sort -rh | head -10
# List all unique import sources
grep -rh "from ['\"]" src/ | sed "s/.*from ['\"]//;s/['\"].*//" | sort -u
# Find components that use useState but not useEffect
grep -rl "useState" src/components/ | xargs grep -L "useEffect"
# Count commits per author
git shortlog -sn --all | head -10
# Find files changed in the last commit that contain TODO
git diff --name-only HEAD~1 | xargs grep -l "TODO" 2>/dev/null
# JSON processing with jq
cat package.json | jq '.scripts'
cat package.json | jq '.dependencies | keys | length'
curl -s https://api.example.com/data | jq '.items[] | {id, name}'# Redirect stdout to file
npm run build > build.log
# Redirect stderr to file
npm run build 2> errors.log
# Redirect both to same file
npm run build > build.log 2>&1
# Modern syntax (bash 4+):
npm run build &> build.log
# Append instead of overwrite
echo "new entry" >> log.txt
# Discard output
npm install > /dev/null 2>&1
# Redirect stdin from file
node script.js < input.txt
# Here document (multiline input)
cat << 'EOF' > .env.local
DATABASE_URL=postgresql://localhost:5432/mydb
NEXT_PUBLIC_API_URL=http://localhost:3000/api
EOFAdd to ~/.bashrc or ~/.zshrc:
# Navigation
alias ..="cd .."
alias ...="cd ../.."
alias proj="cd ~/projects"
# Git shortcuts
alias gs="git status -sb"
alias gl="git log --oneline --graph -20"
alias gd="git diff"
alias gc="git commit -m"
alias gp="git push"
alias gpu="git pull"
alias gco="git checkout"
alias gcb="git checkout -b"
# npm shortcuts
alias ni="npm install"
alias nd="npm run dev"
alias nb="npm run build"
alias nt="npm test"
alias nrd="npm run dev"
# Quick servers
alias serve="npx serve ."
alias py-serve="python3 -m http.server 8080"
# Utility
alias ll="ls -la"
alias cls="clear"
alias ports="lsof -i -P | grep LISTEN"
alias myip="curl -s ifconfig.me"
# Functions
mkcd() { mkdir -p "$1" && cd "$1"; }
# Kill process on a port
killport() { lsof -ti :"$1" | xargs kill -9 2>/dev/null && echo "Killed port $1" || echo "Nothing on port $1"; }
# Create a new component file
newcomp() {
local name="$1"
local dir="src/components"
mkdir -p "$dir"
cat > "$dir/$name.tsx" << EOF
interface ${name}Props {
// props
}
export function ${name}({}: ${name}Props) {
return (
<div>
<h2>${name}</h2>
</div>
);
}
EOF
echo "Created $dir/$name.tsx"
}# Reload after editing
source ~/.zshrc # or ~/.bashrc#!/bin/bash
# Variables
NAME="my-app"
PORT=${PORT:-3000} # default value if not set
TIMESTAMP=$(date +%Y%m%d)
# Conditionals
if [ -f "package.json" ]; then
echo "Node project found"
elif [ -f "Cargo.toml" ]; then
echo "Rust project found"
else
echo "Unknown project type"
fi
# Check if command exists
if command -v pnpm &> /dev/null; then
pnpm install
else
npm install
fi
# Loops
for file in src/components/*.tsx; do
echo "Component: $(basename "$file" .tsx)"
done
# Loop over lines
git diff --name-only | while read -r file; do
echo "Changed: $file"
done
# Exit on error (always use in scripts)
set -euo pipefail
# -e: exit on error
# -u: error on undefined variables
# -o pipefail: catch errors in pipes
# Trap for cleanup
cleanup() { rm -f /tmp/myapp-*.log; }
trap cleanup EXIT# Quick deploy check: type-check, lint, test, build
#!/bin/bash
set -euo pipefail
echo "1/4 Type checking..."
npx tsc --noEmit
echo "2/4 Linting..."
npx next lint
echo "3/4 Testing..."
npx vitest run
echo "4/4 Building..."
npm run build
echo "All checks passed!"# Watch a file and auto-run a command
# Install: brew install entr (macOS) or apt install entr (Linux)
find src -name "*.test.ts" | entr -c npx vitest run
# Or watch with a simple loop
while true; do
inotifywait -r -e modify src/ 2>/dev/null || fswatch -1 src/
npx tsc --noEmit
doneCtrl+R Reverse search history
Ctrl+A Move cursor to beginning of line
Ctrl+E Move cursor to end of line
Ctrl+W Delete word before cursor
Ctrl+K Delete from cursor to end of line
Ctrl+U Delete entire line (bash) / to beginning (zsh)
Ctrl+L Clear screen (same as `clear`)
Ctrl+C Cancel current command
Ctrl+Z Suspend current process (resume with `fg`)
Alt+B Move back one word
Alt+F Move forward one word
!! Repeat last command
!$ Last argument of previous command
# Delete all .log files found
find . -name "*.log" | xargs rm
# Run prettier on all changed TypeScript files
git diff --name-only --diff-filter=M | grep '\.tsx\?$' | xargs npx prettier --write
# Open all files containing a pattern in VS Code
rg -l "deprecated" src/ | xargs code
# Parallel execution
find tests -name "*.test.ts" | xargs -P 4 -I {} npx vitest run {}
# -P 4 = 4 parallel processes
# -I {} = placeholder for the inputThings that will bite you. Each gotcha includes what goes wrong, why it happens, and the fix.
Aliases not loading - Aliases defined in ~/.bashrc don't appear in new terminals. Fix: macOS uses ~/.zshrc by default (zsh). Put aliases there, or add source ~/.bashrc to ~/.zshrc.
Script fails silently - A command in the middle fails but the script continues. Fix: Add set -euo pipefail at the top of every script.
Spaces in filenames break xargs - xargs splits on whitespace by default. Fix: Use find -print0 | xargs -0 or quote variables: "$file".
Pipe hides exit codes - cmd1 | cmd2 returns the exit code of cmd2, hiding failures in cmd1. Fix: set -o pipefail catches errors in any pipe stage.
History not saving - Commands from one terminal don't appear in another. Fix: Add to ~/.zshrc: setopt SHARE_HISTORY and setopt INC_APPEND_HISTORY.
Other ways to solve the same problem - and when each is the better choice.
| Alternative | Use When | Don't Use When |
|---|---|---|
zsh + Oh My Zsh | Rich plugin ecosystem, themes, auto-suggestions | You need maximum POSIX compatibility |
fish shell | Sane defaults, amazing auto-complete out of the box | Scripts need to be portable to bash |
just (justfile) | Task runner replacing Makefile, simpler syntax | npm scripts are sufficient |
Makefile | Multi-language projects, CI automation | Simple Node.js projects (npm scripts suffice) |
| Turbo/Nx | Monorepo task orchestration with caching | Single-package projects |
git log --oneline | head -20 shows only the first 20 log entries> overwrites the file with the new output>> appends to the file without erasing existing content>> for log files; use > when you want a fresh filenpm run build > build.log 2>&1
# or (bash 4+):
npm run build &> build.log2>&1 redirects stderr (file descriptor 2) to wherever stdout (1) is going-e exits the script immediately if any command fails-u treats undefined variables as errors-o pipefail catches failures in any stage of a pipe, not just the lastAdd to ~/.zshrc (or ~/.bashrc):
alias gp="git push"Then reload: source ~/.zshrc
zsh, which reads ~/.zshrc, not ~/.bashrc~/.zshrc or add source ~/.bashrc inside ~/.zshrcCtrl+R for interactive reverse searchhistory | grep "docker" to search non-interactively!! repeats the last command; sudo !! reruns it with sudoxargs takes stdin lines and passes them as arguments to another commandfind . -name "*.log" | xargs rm deletes all found .log files-P 4 for parallel execution and -0 with find -print0 for filenames with spacesset -o pipefail so any failure in the pipe causes the script to failnewcomp() {
local name="$1"
cat > "src/components/$name.tsx" << EOF
interface ${name}Props {}
export function ${name}({}: ${name}Props) {
return <div>${name}</div>;
}
EOF
}newcomp Button to create src/components/Button.tsx$(command) and `command` both capture a command's output$() is preferred because it nests cleanly: $(echo $(date))#!/bin/bash
set -euo pipefail
echo "Type check..." && npx tsc --noEmit
echo "Lint..." && npx next lint
echo "Test..." && npx vitest run
echo "Build..." && npm run build
echo "All checks passed!"Reviewed by Chris St. John·Last updated Jul 10, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥