Bash one-liners that save your afternoon: pipes, xargs and friends

Level up your terminal workflow with 10 practical Bash one-liners combining pipes, xargs, awk, and grep to automate tedious development tasks.

By Asahluma Tyika

Why Bash One-Liners Save Your Afternoon

We have all had afternoons derailed by repetitive grunt work: cleaning up stale Git branches one by one, hunting down whatever rogue process is hogging port 3000, or digging through a 500 MB access log to find out why a server spiked at 2:00 AM.

You don’t need a full Python script or a third-party GUI tool for these tasks. The standard Unix toolkit—tools like grep, awk, sort, uniq, and xargs linked together by the pipe operator (|)—can solve them in seconds.

This guide is for developers who know basic commands like cd, ls, and mkdir, but want to link them together to solve real daily workflow problems. Here are 10 battle-tested, composable one-liners broken down in plain English.


Part 1: Finding and Inspecting Disk & Text Bottlenecks

Pipes pass the standard output (stdout) of one program directly into the standard input (stdin) of the next. Think of each command as an assembly line worker performing one specific transform.

1. Spot the Top 10 Disk Hogs in Your Current Directory

du -ah . | sort -rh | head -n 10
  • What it does: Lists every file and folder in the current directory with human-readable sizes, sorts them from largest to smallest, and prints the top 10.
  • Component breakdown:
Tool Flag Purpose
du -ah . Estimates file space usage for all files (-a) in human-readable units (-h) starting at .
sort -rh Sorts lines using human-readable numeric values (-h) in reverse/descending order (-r)
head -n 10 Cuts off the stream, outputting only the first 10 entries

2. Extract Top 5 Most Frequent IP Addresses from an Access Log

awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -n 5
  • What it does: Grabs the first column of an Apache or Nginx log file (usually the client IP), groups duplicates, counts their occurrences, and ranks the most frequent visitors.
  • Plain-English breakdown:
    1. awk '{print $1}' access.log reads the file and outputs only the first whitespace-separated column.
    2. sort alphabetizes the IPs. (This step is required because uniq only detects adjacent duplicate lines).
    3. uniq -c collapses duplicates and prefixes each line with its frequency count.
    4. sort -nr sorts numerically (-n) in descending order (-r).
    5. head -n 5 gives you the top 5 IPs hitting your server.

3. Tally HTTP Response Codes in Real Time

grep -oE "HTTP/[0-9.]+ [0-9]{3}" access.log | sort | uniq -c | sort -nr
  • What it does: Filters out log clutter to isolate just the HTTP version and status codes (like HTTP/1.1 200 or HTTP/1.1 404), aggregating their total counts.
  • Key trick: The -o flag tells grep to output only the matching text instead of the entire line, while -E enables extended regular expressions.

Part 2: Taming Stubborn Processes and Ports

When local dev servers crash uncleanly, they often leave phantom processes behind. Instead of reaching for a system restart, clear them instantly.

4. Kill Whatever Process is Hogging a Specific Port

lsof -ti :3000 | xargs kill -9
  • What it does: Instantly frees up port 3000 by finding the Process ID (PID) listening on it and terminating it.
  • Component breakdown:
    • lsof -ti :3000: lsof lists open files and ports. -t outputs purely terse PIDs (no headers, no user info), and -i :3000 limits the check to port 3000.
    • xargs kill -9: xargs takes standard input lines and turns them into arguments for another command. Here, it transforms raw PID numbers into kill -9 <PID>.

5. Kill Multiple Runaway Scripts by Name

pgrep -f "worker.py" | xargs kill -15
  • What it does: Safely sends a termination signal (SIGTERM or -15) to all processes running a Python script named worker.py.
  • Pro-tip: Start with -15 to let the program clean up database connections and temp files. Only escalate to kill -9 if the process ignores the signal.

6. Show the Top 5 Memory-Consuming Processes

ps aux --sort=-%mem | awk 'NR<=6 {printf "%-8s %-6s %-6s %s\n", $1, $2, $4, $11}'
  • What it does: Displays user, PID, memory percentage, and command path for the top 5 memory hogs without needing to launch heavy interactive utilities like top or htop.
  • Component breakdown:
    • ps aux --sort=-%mem: Gathers all system processes and sorts them descending by memory usage.
    • awk 'NR<=6 ...': Limits output to the header plus the first 5 records (NR <= 6), printing neatly formatted columns.

Part 3: Batch Operations with xargs and find

While standard piping feeds data into a program’s standard input, many Linux utilities (like rm, mv, or git) require arguments passed to them directly. That is where xargs shines.

7. Search and Replace a String Across Project Files

grep -rl "http://localhost:8080" ./src | xargs sed -i 's|http://localhost:8080|https://api.example.com|g'
  • What it does: Scans the ./src directory, finds every file containing the local URL, and updates it in-place to the production URL.
  • Component breakdown:
    • grep -rl: Recursively (-r) looks through files and outputs only their file paths (-l), skipping match details.
    • sed -i 's|old|new|g': Stream editor performing in-place (-i) global substitution. We use pipe delimiters (|) instead of slashes (/) so we don’t have to escape slashes in URLs.

8. Download Multiple Assets in Parallel

cat urls.txt | xargs -n 1 -P 4 curl -s -O
  • What it does: Downloads a list of URLs saved in urls.txt 4 at a time instead of waiting for them sequentially.
  • Component breakdown:
Flag Meaning Function
-n 1 Max arguments Passes exactly 1 URL per curl command execution
-P 4 Max processes Runs up to 4 concurrent worker processes at once
curl -s -O Silent / Remote name Downloads silently and saves the file using its remote filename

9. Delete Merged Git Branches in One Go

git branch --merged | grep -vE "^\*|main|master|develop" | xargs -r git branch -d
  • What it does: Cleans up local Git branches that have already been merged into your current branch, while protecting critical branches like main or active work.
  • Component breakdown:
    • git branch --merged: Lists local branches merged into the current HEAD.
    • grep -vE "^\*|main|master|develop": Inverts matching (-v), filtering out the currently checked out branch (prefixed with *) and protected trunk branches.
    • xargs -r: The -r flag prevents Git from running if the input is empty, avoiding error warnings when your branches are already clean.

10. Safely Purge Empty Files from a Directory Tree

find . -type f -empty -print0 | xargs -0 -r rm -v
  • What it does: Recursively locates zero-byte files and safely removes them, properly handling files that contain spaces or special characters in their names.
  • The magic trick: -print0 and -0 ensure that filenames are separated by null bytes (\0) rather than spaces or newlines.

Hands-on Example

Let’s test these concepts in an isolated sandbox. Open your terminal, paste this block, and watch the tools work together:

# 1. Create a safe sandbox directory
mkdir -p /tmp/bash_sandbox && cd /tmp/bash_sandbox

# 2. Generate dummy access log
cat << 'EOF' > access.log
192.168.1.10 - - [10/May/2024:14:00:01] "GET /api/v1/users HTTP/1.1" 200
10.0.0.5 - - [10/May/2024:14:00:02] "POST /api/v1/auth HTTP/1.1" 401
192.168.1.10 - - [10/May/2024:14:00:03] "GET /favicon.ico HTTP/1.1" 404
172.16.0.4 - - [10/May/2024:14:00:04] "GET /api/v1/users HTTP/1.1" 200
192.168.1.10 - - [10/May/2024:14:00:05] "GET /dashboard HTTP/1.1" 200
EOF

# 3. Create dummy project files
mkdir -p src
echo "const API = 'http://localhost:8080';" > src/app.js
echo "const MOCK = 'http://localhost:8080';" > src/mock.js
touch src/empty_cache.tmp

# 4. Run our pipeline: Count requests per IP
echo "=== Requests per IP ==="
awk '{print $1}' access.log | sort | uniq -c | sort -nr

# 5. Run our search & replace pipeline
echo "=== Updating API URL ==="
grep -rl "http://localhost:8080" ./src | xargs sed -i 's|http://localhost:8080|https://api.example.com|g'
cat src/app.js

# 6. Delete empty file safely
echo "=== Removing empty files ==="
find . -type f -empty -print0 | xargs -0 -r rm -v

# 7. Clean up sandbox
cd ~ && rm -rf /tmp/bash_sandbox

Common Mistakes / Troubleshooting

  • File paths with spaces breaking xargs: By default, xargs splits on whitespace. If you have a file named User Report.pdf, it will try to run your command on User and Report.pdf separately. Fix: Always pair find ... -print0 with xargs -0.
  • sed -i crashing on macOS: macOS uses BSD sed, which requires an empty extension string for in-place edits (sed -i '' 's/.../...'), whereas GNU sed on Linux does not accept empty quotes. Fix: If writing scripts meant to run across both platforms, use temporary files or install gnu-sed on Mac (gsed).
  • Accidentally running destructive commands without checking: Running xargs rm or xargs kill directly on untracked input can wipe files or crash servers. Fix: Pipe to cat or echo first (e.g., ... | xargs echo rm) to preview what arguments will be executed before running the real command.
  • Forgetting that uniq requires sorted input: Running uniq -c on unsorted data only counts consecutive identical lines, returning incorrect tallies. Fix: Always place sort directly before uniq.

Try It Yourself

Navigate to any active coding repository on your machine and run a pipeline that counts lines of code across all JavaScript or Python files, sorted from smallest to largest file:

find . -name "*.js" -not -path "*/node_modules/*" | xargs wc -l | sort -n

(If you are in a Python project, swap *.js for *.py and remove the node_modules exclusion).


What’s Next

  • Explore standard file descriptors: mastering 2>&1, stderr redirection, and process substitution (<()).
  • Write defensive Bash scripts using set -euo pipefail so your pipelines fail cleanly when errors occur.
  • Look out for our upcoming post: “Mastering awk and jq for Devs Who Hate Manual Data Entry.”

Bookmark this cheat sheet so you never have to Google port-killing syntax again.

Share: X (Twitter) Facebook LinkedIn