Chapter 05Lesson 03~60 minutes

Finding Files with find, locate, and xargs

Finding the right filesystem objects is more than matching a name. DevOps investigations combine roots, type, ownership, size, age, and actions—and they must remain correct when filenames contain spaces, newlines, or leading dashes.

IntermediatefindutilsSafe automation

Learning objectives

By the end of this lesson

  • Explain the different data sources used by find and locate.
  • Build find expressions from roots, tests, operators, and actions.
  • Select files by name, type, size, time, and path while controlling traversal.
  • Use -exec and NUL-delimited xargs safely.
  • Perform a dry-run-first cleanup analysis without deleting data.

1. Three tools solve different parts of the problem

Live traversal, indexed lookup, and argument construction
flowchart TB
  N["Need filesystem objects"] --> F["find scans a live directory hierarchy"]
  N --> L["locate queries a previously built filename database"]
  F --> X["xargs groups input into command arguments"]
  L --> X
  F --> E["-exec runs an action directly"]

find walks directory entries at query time, so its result reflects the current tree and can test metadata. locate searches an index created by updatedb, so it is fast but may be stale or omit paths according to database policy. xargs reads items from standard input and constructs one or more command invocations.

Filename safety

Newline-delimited output is not a universal filename transport. Linux filenames may contain spaces, tabs, quotes, and newlines. Use NUL-delimited pairs such as find -print0 | xargs -0 when passing arbitrary pathnames.

2. Read a find command as an expression

The general model is find ROOT... EXPRESSION. Roots define where traversal begins. Tests return true or false. Operators combine tests. Actions print, execute, or otherwise process selected entries.

root="$HOME/devops-academy/linux/chapter05/lesson03/tree"

# Regular log files beneath the root, matched case-sensitively.
find "$root" -type f -name '*.log' -print

# Configuration files or YAML files; parentheses must be protected from the shell.
find "$root" -type f \( -name '*.conf' -o -name '*.yaml' \) -print

# Negate an archive subtree by path.
find "$root" -type f ! -path '*/archive/*' -name '*.log' -print

Adjacent tests imply logical AND. Use \( and \) to group expressions because unquoted parentheses are shell syntax. Quote wildcard patterns so find, not the shell, matches them against each visited pathname.

3. Select by type, size, and time

TestSelectsExample
-type fRegular files-type f
-name PATTERNBasename pattern-name '*.log'
-path PATTERNWhole pathname pattern-path '*/cache/*'
-size +100MFiles larger than 100 MiB units-size +100M
-mtime +7Modification age in rounded 24-hour periods-mtime +7
-newermt DATEGNU date-based modification comparison-newermt '2026-08-01'
-emptyEmpty regular files or directories-empty
# Large regular files with human-readable metadata.
find "$root" -type f -size +1M -printf '%s bytes\t%TY-%Tm-%Td %TH:%TM\t%p\n'

# Files modified during a bounded window (GNU find).
find "$root" -type f \
  -newermt '2026-08-04 09:00:00' \
  ! -newermt '2026-08-04 11:00:00' \
  -print

Time tests have precise rounding rules. When an incident depends on exact boundaries, prefer explicit timestamps, inspect with stat, and record the timezone used by the investigation.

4. Bound traversal and handle errors

Start from the narrowest practical root. -maxdepth and -mindepth constrain levels in GNU find. -xdev avoids descending onto other filesystems. -prune prevents traversal into selected directories.

# Search a deployment tree but skip .git, cache, and vendor directories.
find "$root" \
  \( -type d \( -name .git -o -name cache -o -name vendor \) -prune \) \
  -o \( -type f -name '*.yaml' -print \)

# Stay on the same filesystem and limit depth.
find "$root" -xdev -maxdepth 3 -type f -print

Permission-denied diagnostics are evidence. Do not automatically hide standard error with 2>/dev/null; doing so can make an incomplete result look complete. If noise must be separated, capture it in a file and report that the traversal encountered inaccessible paths.

5. Execute actions without corrupting filenames

-exec COMMAND {} \; runs one command per selected entry. -exec COMMAND {} + groups multiple pathnames into fewer invocations. The grouped form is usually more efficient and retains direct pathname handling.

# One stat process per group of selected files.
find "$root" -type f -name '*.log' -exec stat --format='%s %n' -- {} +

# NUL-delimited transport to xargs for arbitrary names.
find "$root" -type f -name '*.log' -print0 \
  | xargs -0 -r grep -Hn -F 'level=ERROR' --

# Preview grouped arguments rather than changing files.
find "$root" -type f -name '*.tmp' -print0 \
  | xargs -0 -r printf 'candidate: %s\n'

GNU xargs -r avoids running the command when input is empty; it is not universally portable. For destructive work, prefer a preview, an explicit root, NUL delimiters, and an action that cannot escape the intended tree.

6. Use locate for discovery, then verify live state

locate PATTERN searches a filename database, often updated periodically. It is excellent for quickly discovering likely locations, but the result may include deleted files or miss newly created ones.

# Find likely configuration filenames quickly.
locate '/nginx.conf'
locate -i '*prometheus*.yml'

# Verify each candidate against the live filesystem.
locate -0 '*deployment*.yaml' \
  | while IFS= read -r -d '' path; do
      if [[ -f $path ]]; then
        printf 'live file: %s\n' "$path"
      fi
    done

Database coverage depends on how updatedb is configured and which filesystems or paths it excludes. Treat locate as an index-assisted lead, not authoritative inventory.

7. Hands-on lab: build a cleanup candidate report

lab="$HOME/devops-academy/linux/chapter05/lesson03"
root="$lab/tree"
rm -rf -- "$root"
mkdir -p -- "$root"/{active,archive,cache,"odd names"}

printf 'level=INFO\n' > "$root/active/app.log"
printf 'level=ERROR\n' > "$root/active/error log.log"
dd if=/dev/zero of="$root/cache/blob.tmp" bs=1024 count=8 status=none
: > "$root/cache/empty.tmp"
printf 'old archive\n' > "$root/archive/old.log"
touch -d '10 days ago' -- "$root/archive/old.log" 2>/dev/null || true

report="$lab/candidates.txt"
find "$root" -xdev -type f \
  \( -name '*.tmp' -o -path '*/archive/*' \) \
  -printf '%TY-%Tm-%Td %TH:%TM\t%s\t%p\n' \
  | sort > "$report"

cat -- "$report"
printf '%s\n' '=== ERROR records in every log ==='
find "$root" -type f -name '*.log' -print0 \
  | xargs -0 -r grep -Hn -F 'level=ERROR' --

Verification checklist

8. Safe deletion is a separate operation

Do not append -delete while designing a query. First save and inspect the exact candidates. Then rerun the same bounded predicate with a controlled action, ideally after a snapshot or backup. Remember that actions can affect expression truth and traversal order.

Never test deletion against a valuable tree

Use a disposable lab, print candidates, count them, inspect unusual names, and keep the root in a variable you verify before any removal command.

9. Knowledge check

Question 1. Why can locate return a pathname that no longer exists?

Question 2. Why should '*.log' be quoted in a find expression?

Question 3. What problem does -print0 | xargs -0 solve?

10. Summary

find performs current metadata-aware traversal, locate provides fast indexed discovery, and xargs groups streamed items into command arguments. Safe workflows bound the root, quote patterns, preserve errors, use NUL delimiters, preview candidates, and separate selection from destructive action.

Next lesson

Transforming Text with cut, sort, uniq, tr, and wc

The next lesson turns selected files and records into compact summaries using the GNU text toolbox.

11. Further reading

  • GNU Findutils manual — find, locate, updatedb, and xargs.
  • GNU Coreutils manual — stat, sort, and NUL-aware tools.
  • GNU Bash Reference Manual — quoting, pipelines, and read -d.
  • Linux manual pages for find, xargs, locate, and updatedb.
  • Filesystem Hierarchy Standard — common search roots and directory purposes.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.