Skip to content

Finding Files

find walks a directory tree and selects files by name, type, size, time, owner or permissions, then prints them or runs a command on each. It is the standard tool for cleanups, audits and "where did the disk go" questions; locate answers name lookups faster from a prebuilt index.

Track: Core · Interview weight: High


Must-Know Facts

Fact Value Verify with
Syntax find <paths> <tests> <actions>; default action -print find /etc -name '*.conf'
Quote patterns Otherwise the shell expands them first find . -name '*.log'
Size units -size +100M, -size -10k; + more than, - less than find / -xdev -size +1G
Time tests -mtime +30 older than 30 days; -mmin -60 newer than an hour find /var/log -mtime +30
Permission tests -perm 644 exact, -perm -4000 all bits set, -perm /022 any bit set find / -perm -4000
Owner tests -user, -group, -nouser, -nogroup find / -nouser
Run a command -exec cmd {} \; once per file; -exec cmd {} + batched find . -exec ls {} +
Safe with xargs -print0 and xargs -0 handle spaces and newlines find . -print0
Delete -delete; put it last find /tmp -name '*.tmp' -delete
Stay on one filesystem -xdev find / -xdev
Depth -maxdepth N, -mindepth N find . -maxdepth 1
Name index locate searches the database built by updatedb locate sshd_config
Command location which searches PATH; whereis adds man pages whereis sshd

Test Setup

The examples run against a small tree: a 120 MB log, a 5 MB log dated 40 days ago, two config files (one with a space in its name), and two temporary files, one of them world-writable.

mkdir -p /tmp/fdemo/{logs,conf,cache} && cd /tmp/fdemo
dd if=/dev/zero of=logs/app.log bs=1M count=120 status=none
dd if=/dev/zero of=logs/old.log bs=1M count=5 status=none
touch -d '40 days ago' logs/old.log
echo "port=8080" > conf/app.conf
echo "port=9090" > "conf/web server.conf"
touch -d '3 days ago' conf/app.conf
touch cache/a.tmp cache/b.tmp
chmod 777 cache/a.tmp

By Name, Size and Time

find /tmp/fdemo -name '*.conf'
find /tmp/fdemo -iname 'APP*'
find /tmp/fdemo -type f -size +100M
find /tmp/fdemo -type f -size +1M -size -10M
find /tmp/fdemo -type f -mtime +30
find /tmp/fdemo -type f -mtime -7 -name '*.conf'
find /tmp/fdemo -type f -mmin -60 -name '*.tmp'

Output:

/tmp/fdemo/conf/web server.conf
/tmp/fdemo/conf/app.conf
/tmp/fdemo/conf/app.conf
/tmp/fdemo/logs/app.log
/tmp/fdemo/logs/app.log
/tmp/fdemo/logs/old.log
/tmp/fdemo/logs/old.log
/tmp/fdemo/conf/web server.conf
/tmp/fdemo/conf/app.conf
/tmp/fdemo/cache/b.tmp
/tmp/fdemo/cache/a.tmp

Several tests in a row are combined with AND; -o means OR and ! negates.

Test Meaning
-mtime +30 Modified more than 30 full days ago
-mtime -7 Modified within the last 7 days
-mtime 0 Modified in the last 24 hours
-mmin -60 Modified within the last 60 minutes
-newer ref Modified more recently than ref
-size +100M Larger than 100 MiB (c bytes, k, M, G)
-empty Empty files and directories

-size rounds up to the unit

-size -1M matches only empty files, because every non-empty file rounds up to at least 1 MiB. Use -size -1024k for "smaller than 1 MiB".


By Permission and Owner

find /tmp/fdemo -type f -perm -o=w
find /tmp/fdemo -type f -perm 777
sudo find / -xdev -type f -perm -4000 2>/dev/null | head -5

Output:

/tmp/fdemo/cache/a.tmp
/tmp/fdemo/cache/a.tmp
/usr/sbin/pam_timestamp_check
/usr/sbin/userhelper
/usr/sbin/unix_chkpwd
/usr/bin/chage
/usr/bin/newgrp

-perm -4000 finds SUID binaries, a standard step in a privilege-escalation audit. -nouser finds files whose UID no longer exists, which is what deleting a user without -r leaves behind.


Acting on Results

-exec ... \; runs the command once per file; -exec ... + passes many files to one command, like xargs:

find /tmp/fdemo -type f -name '*.conf' -exec grep -H port {} \;
find /tmp/fdemo -type f -name '*.conf' -exec grep -H port {} +
find /tmp/fdemo -name '*.conf' | xargs grep -H port
find /tmp/fdemo -name '*.conf' -print0 | xargs -0 grep -H port

Output:

/tmp/fdemo/conf/web server.conf:port=9090
/tmp/fdemo/conf/app.conf:port=8080
/tmp/fdemo/conf/web server.conf:port=9090
/tmp/fdemo/conf/app.conf:port=8080
grep: /tmp/fdemo/conf/web: No such file or directory
grep: server.conf: No such file or directory
/tmp/fdemo/conf/app.conf:port=8080
/tmp/fdemo/conf/web server.conf:port=9090
/tmp/fdemo/conf/app.conf:port=8080

Plain xargs split web server.conf into two names. -print0 with xargs -0 separates names with a NUL byte, which cannot appear in a file name.

find /tmp/fdemo -name '*.tmp' -delete
find /tmp/fdemo -name '*.tmp' | wc -l
find /tmp/fdemo -type f -printf '%s\t%TY-%Tm-%Td\t%p\n' | sort -rn
find /tmp/fdemo -type f -name '*.log' -mtime +30 -ls

Output:

0
125829120   2026-09-16  /tmp/fdemo/logs/app.log
5242880 2026-08-07  /tmp/fdemo/logs/old.log
10  2026-09-16  /tmp/fdemo/conf/web server.conf
10  2026-09-13  /tmp/fdemo/conf/app.conf
   128896   5120 -rw-r--r--   1 laborant laborant  5242880 Aug  7 14:00 /tmp/fdemo/logs/old.log

Test before -delete

Run the same find with -print first and read the list. -delete placed before a test (find . -delete -name '*.tmp') deletes everything, because actions run in order.


find /tmp/fdemo -maxdepth 1 -type d
find /tmp/fdemo -path '*/logs' -prune -o -type f -print
find / -xdev -name sshd_config 2>/dev/null
find /root -name x

Output:

/tmp/fdemo
/tmp/fdemo/conf
/tmp/fdemo/cache
/tmp/fdemo/logs
/tmp/fdemo/conf/web server.conf
/tmp/fdemo/conf/app.conf
/etc/ssh/sshd_config
find: ‘/root’: Permission denied

-prune skips the logs directory; the explicit -print is required so the pruned directory itself is not printed. -xdev keeps find / off /proc, /sys and network mounts, and 2>/dev/null hides "Permission denied" noise when not running as root.


locate, which and whereis

sudo updatedb
locate sshd_config
which sshd
whereis sshd

Output:

/etc/ssh/sshd_config
/etc/ssh/sshd_config.d
/usr/sbin/sshd
sshd: /usr/sbin/sshd

locate (from plocate on RHEL 10 and Ubuntu 24.04) reads an index refreshed daily by a timer, so files created since the last updatedb are missing and deleted files still appear. It also hides files the caller cannot read.


Common Errors

find: ‘/root’: Permission denied

Cause: the user cannot read that directory; find reports it and continues.

Fix: use sudo, or add 2>/dev/null when the directory is irrelevant.

find: paths must precede expression: `old.log'

Cause: an unquoted pattern such as -name *.log was expanded by the shell into several file names. Recent find adds possible unquoted pattern after predicate '-name'?.

Fix: quote the pattern: -name '*.log'.

find: missing argument to `-exec'

Cause: the command after -exec is not terminated with \; or +.

Fix: end it with {} \; or {} +.


Interview Checkpoints

L1: What is the difference between -exec {} \; and -exec {} +?

Say first: \; runs the command once per file; + appends as many files as fit and runs the command a few times, which is much faster.

Proof: find /etc -name '*.conf' -exec ls {} +

Follow-up: When must you use \;? (When the command accepts only one file, or {} is not last.)

L1: What is the difference between find and locate?

Say first: find walks the filesystem live and can test any attribute; locate searches a name index built by updatedb, which is fast but can be stale.

Proof: touch /tmp/new-file; locate new-file finds nothing until sudo updatedb.

Follow-up: Why can locate hide files that exist?

L2: Find files larger than 1 GiB on the root filesystem, largest first.

Say first: size test, stay on one filesystem, print sizes and sort.

Proof:

sudo find / -xdev -type f -size +1G -printf '%s %p\n' 2>/dev/null | sort -rn | head

Follow-up: Why -xdev?

L2: Delete log files older than 30 days under /var/log/app.

Say first: preview, then delete.

Proof:

find /var/log/app -type f -name '*.log' -mtime +30 -print
find /var/log/app -type f -name '*.log' -mtime +30 -delete

Follow-up: Which tool normally handles this instead? (logrotate with maxage or rotate.)

L2: List every SUID and SGID file on the system.

Say first: permission test with the / (any bit) form.

Proof: sudo find / -xdev -type f -perm /6000 -ls 2>/dev/null

Follow-up: Why is an unexpected SUID binary a security finding?

L2: Change the group of every file owned by a deleted user's old UID.

Say first: search by numeric UID and act in batches.

Proof: sudo find / -xdev -uid 1002 -exec chown amor: {} +

Follow-up: How do you find all files with no valid owner at all? (-nouser.)

L3: A cleanup script using find and xargs fails on some files and deletes the wrong ones.

Say first: check for names with spaces or newlines being split.

Proof: find ... | xargs rm splits web server.conf into web and server.conf; -print0 | xargs -0 or -delete fixes it.

Follow-up: What else can go wrong with xargs rm? (Names starting with -; use xargs -0 rm --.)

L3: The disk filled up in the last hour. Find what grew.

Say first: look for recently modified large files on the affected filesystem.

Proof: sudo find / -xdev -type f -mmin -60 -size +100M -ls

Follow-up: What if find shows nothing but df keeps growing? (A deleted file held open: lsof +L1.)


Captured on Rocky Linux 10.2 and Ubuntu 24.04.4 LTS (iximiuz Labs microVMs, kernel 6.1.167), 2026-09.