Finding Files with find and xargs

By the end of this lesson you'll be able to hunt down files by name, type, size, time, and permissions with find , run a command on every match with -exec , and pipe results into xargs for fast, safe bulk operations — including the all-important -print0 / -0 pairing that survives filenames with spaces.

Learn Finding Files with find and xargs in our free Command Line course — an interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Cli course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

0. Setup: a predictable directory tree

Run this once. It creates a tiny project/ folder with source files, logs, and text files — plus one file deliberately named with a space ( my report.txt ) so we can later prove why -print0 matters. Every block after this assumes this exact layout.

1. Searching by Name and Type

find takes a starting path and walks every directory beneath it, keeping entries that pass your tests . -name "*.txt" matches by filename (case-sensitive); -iname is the case- insensitive version. -type f limits results to regular files and -type d to directories. Tests combine — listing them together means "all of these must be true".

2. Filtering by Size, Time, and Permissions

Three more tests do most of the heavy lifting. -size takes a number with a suffix ( c bytes, k KB, M MB) — +1M means larger than 1 MB, -100k smaller than 100 KB. -mtime counts in days: -7 is "within the last 7 days" and +30 is "older than 30 days". -perm filters by permission bits — -u+x matches anything the owner can execute.

3. Acting on Matches with -exec

Finding files is half the job — usually you want to do something with them. -exec runs a command on each match, with {' '} standing in for the filename. The action must be terminated, and how you terminate it matters: \; runs the command once per file , while + packs many filenames into fewer invocations (just like xargs ), which is dramatically faster for large result sets.

4. Piping to xargs (and Why -print0 Matters)

xargs reads items from standard input and appends them as arguments to a command — the classic way to feed find 's output into another tool in a single fast call. But plain find | xargs splits on whitespace, so a filename containing a space becomes two broken arguments. The fix is the safe pairing : find … -print0 | xargs -0 , which separates names with a NUL byte that can never appear inside a filename.

5. Placeholders and Parallelism: -I and -P

By default xargs appends arguments at the end of the command. When you need the filename elsewhere — say to build cp file file.bak — use -I {' '} to define a placeholder that gets substituted in place (this also makes xargs run one item per invocation). And when the work is slow, -P N runs up to N jobs in parallel , putting your CPU cores to use.

Fill in the one blank marked ___ using the # 👉 hint so the search catches every capitalization of .txt , then run it and check the Output panel matches.

Wire up the safe NUL-separated pipeline. Fill in both blanks so a filename with a space could never break the command, then run it.

No blanks this time — just a brief and an outline. Combine name/type tests, -mtime , the safe -print0 | xargs -0 pairing, and -exec … + into a few commands over the project/ tree, and check your results against the hints.

Practice quiz

What is the core job of the find command?

  • It searches a directory tree for files and directories matching tests like name, type, size, or time
  • It searches the contents of files for text
  • It downloads files from the web
  • It renames every file in a folder

Answer: It searches a directory tree for files and directories matching tests like name, type, size, or time. find walks a directory tree and selects entries by tests (-name, -type, -size, -mtime, ...). grep is the tool that searches text INSIDE files.

Which test matches only regular files (not directories or symlinks)?

  • -file
  • -type d
  • -type f
  • -type l

Answer: -type f. -type f selects regular files; -type d selects directories; -type l selects symbolic links.

How do you match files named with any capitalization of .TXT, like a.txt, B.Txt, and c.TXT?

  • find . -type txt
  • find . -iname "*.txt"
  • find . -name "*.txt"
  • find . -match "*.txt"

Answer: find . -iname "*.txt". -iname is the case-insensitive version of -name, so it matches *.txt regardless of letter case. -name is case-sensitive.

What does find . -mtime -7 select?

  • The 7 most recently changed files
  • Files modified more than 7 days ago
  • Files exactly 7 bytes in size
  • Files modified within the last 7 days

Answer: Files modified within the last 7 days. -mtime -7 means modified less than 7 days ago (within the last week). A plain 7 means exactly 7 days, and +7 means older than 7 days.

What is the difference between -exec cmd {} (semicolon) and -exec cmd {} + in find?

  • The semicolon form runs cmd once per file, while the plus form passes many files to fewer cmd invocations
  • The plus form runs once per file, while the semicolon form batches
  • The plus form deletes files and the semicolon form copies them
  • They are identical

Answer: The semicolon form runs cmd once per file, while the plus form passes many files to fewer cmd invocations. -exec cmd {} (semicolon) runs the command once for each matched file. -exec cmd {} + appends as many filenames as possible per invocation, like xargs, which is far faster for many files.

Why is find . -print0 | xargs -0 safer than find . | xargs?

  • It sorts the filenames first
  • It separates filenames with a NUL character so names containing spaces or newlines are handled correctly
  • It runs commands in parallel
  • It compresses the output

Answer: It separates filenames with a NUL character so names containing spaces or newlines are handled correctly. -print0 ends each name with a NUL byte and xargs -0 splits on NUL, so spaces, tabs, and newlines inside filenames no longer break the command.

What does the -I {} option do in xargs?

  • It runs jobs in parallel
  • It ignores blank input lines
  • It reads input separated by NUL
  • It defines a placeholder so each input argument is substituted at that spot, not just appended at the end

Answer: It defines a placeholder so each input argument is substituted at that spot, not just appended at the end. xargs -I {} sets {} as a replacement string, letting you place the argument anywhere in the command (for example, in the middle) instead of only at the end.

What does xargs -P 4 do?

  • It limits output to 4 lines
  • It passes at most 4 arguments per command
  • It runs up to 4 command invocations in parallel
  • It prints the command before running it

Answer: It runs up to 4 command invocations in parallel. xargs -P 4 runs as many as 4 jobs at the same time, which can dramatically speed up CPU-bound bulk work on multi-core machines.

Which command counts how many .log files exist under the current directory?

  • find . -name "*.log" -size
  • find . -type f -name "*.log" | wc -l
  • grep -c log .
  • ls *.log

Answer: find . -type f -name "*.log" | wc -l. find emits one path per line for each match, so piping the .log matches to wc -l counts them. ls *.log would miss files in subdirectories.

You want to delete every .tmp file safely, even ones with spaces in their names. Which is best?

  • find . -name "*.tmp" -print0 | xargs -0 rm
  • rm *.tmp
  • find . -name "*.tmp" | xargs rm
  • find . -name "*.tmp" -delete -now

Answer: find . -name "*.tmp" -print0 | xargs -0 rm. Pairing -print0 with xargs -0 NUL-separates the names so spaces and newlines are safe. find . -name "*.tmp" -delete also works, but the plain pipe to xargs breaks on spaces.