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.