Skip to content
C Codeloom
Linux

Linux find and xargs Tutorial

Master find and xargs to locate files and run bulk operations safely. Includes -exec vs xargs, null-delimited pipelines, parallelism, and how to avoid destroying directories.

·4 min read · By Codeloom
Beginner 9 min read

What you'll learn

  • How find evaluates expressions and prunes trees
  • When to use -exec vs piping to xargs
  • Null-delimited pipelines for filenames with spaces
  • Running operations in parallel with xargs -P
  • How a missing flag can wipe a directory

Prerequisites

  • Basic familiarity with bash and Unix commands

What and Why

find walks a directory tree and tests each file against expressions you supply. xargs reads items from stdin and turns them into command-line arguments for another program. Together they are the duct tape of the shell: cleanup scripts, bulk renames, log pruning, code searches, mass chmod, and ad-hoc migrations all start here.

People reach for them because they are everywhere, scriptable, and predictable. They also bite hard when filenames contain spaces, newlines, or quotes, which is most of the reason this tutorial exists.

Mental Model

find is a tiny language. Each argument after the starting path is either an option (-maxdepth, -xdev), a test (-name, -mtime, -size), or an action (-print, -delete, -exec). Tests return true or false; expressions combine with implicit AND, or explicit -o (OR) and ! (NOT). The default action is -print.

xargs is the bridge from “lines on stdin” to “arguments on a command line”. It batches inputs into as few command invocations as it can, which is much faster than running one command per file.

The single most important habit: never trust whitespace in filenames. Use find -print0 paired with xargs -0, or pass \; vs + to -exec deliberately.

Hands-on Example

Find all .log files modified more than 7 days ago under /var/log and show their size:

find /var/log -type f -name '*.log' -mtime +7 -printf '%s %p\n'

Delete those logs safely. Two equivalent forms:

# Using -exec with + batches calls like xargs
find /var/log -type f -name '*.log' -mtime +7 -exec rm -f {} +

# Using xargs with null delimiters
find /var/log -type f -name '*.log' -mtime +7 -print0 \
  | xargs -0 rm -f

Convert a tree of PNGs to WebP in parallel using all cores:

find ./images -type f -name '*.png' -print0 \
  | xargs -0 -P "$(nproc)" -I {} cwebp -q 80 {} -o {}.webp

Search for a string in source files but skip node_modules and .git:

find . \( -path ./node_modules -o -path ./.git \) -prune \
  -o -type f -name '*.ts' -print0 \
  | xargs -0 grep -nH 'TODO'
find . -type f -name '*.log' -print0
      |
      v   stream of NUL-terminated paths
      |
 xargs -0 -P 4 -n 50 gzip
      |
      v   builds command lines:
 gzip file1 file2 ... file50   <- worker 1
 gzip file51 ... file100        <- worker 2
 gzip file101 ... file150       <- worker 3
 gzip file151 ... file200       <- worker 4
How find and xargs pass data

-exec ... + already batches like xargs. Use xargs when you need parallelism (-P), a custom replacement string (-I), or to feed a pipeline.

Common Pitfalls

  • Spaces and newlines in filenames: find ... | xargs rm will tear apart My Photo.jpg into My and Photo.jpg. Always pair -print0 with -0.
  • -exec without quoting: -exec mv {} /tmp \; is fine; -exec sh -c 'mv $1 /tmp' _ {} \; is required if you need shell features.
  • Forgetting -type f: find . -name 'build' matches directories too, and rm -rf later regrets it.
  • Pruning order: -prune must come before -o -print, and the parenthesized group needs escaping.
  • xargs with empty input: by default it still runs the command once with no args. Pass -r (GNU) or --no-run-if-empty to skip.
  • Cross-mounting: find / happily descends into NFS or /proc. Use -xdev to stay on one filesystem.

Practical Tips

  • Build commands in stages: replace -delete or rm with -print first, eyeball the list, then swap back.
  • -mtime, -mmin, -newer, and -size accept ranges; combine them to scope dangerous operations.
  • Use -I {} only when you must; it forces one invocation per item and is slow on large sets.
  • For complex matches, prefer fd and ripgrep for daily use, but keep find and xargs in your toolbox for portability.
  • Pipe through sort -z | uniq -z when you need deduplication with null delimiters.

Wrap-up

find plus xargs is the ultimate Swiss army knife: a tiny language for selecting files and a tiny language for running commands on them. Treat filenames as untrusted bytes, use null-delimited pipelines, and dry-run anything destructive. Get those habits in and you will reach for these tools without flinching.