Linux find Command: 30 Practical Examples

Most people learn find twice. Once badly, by copying a command off a forum that mostly works, and then properly a few years later after it does something they didn’t intend. The second lesson usually involves deleted files.

The command isn’t hard, it just reads differently to everything else in the shell. It’s a series of tests evaluated left to right, and the order genuinely matters. Get that idea and the rest is detail.

Thirty examples below, grouped by what you’re trying to do. The last section covers the ways this command bites people, and it’s worth reading before you run anything with -delete in it.

The shape of a find command

find [where] [what to match] [what to do]

Leave off the path and it searches from the current directory. Leave off the action and it prints what it found. So the shortest useful version is just find ., which lists everything below you.

One habit to build immediately: when a search hits directories you can’t read, the errors clutter the output. Send them away.

find /etc -name "*.conf" 2>/dev/null

Finding files by name

1. By exact name

$ find /etc -name "nginx.conf"
/etc/nginx/nginx.conf

2. Ignoring case

find /home -iname "readme*"

That matches README, readme, ReadMe and whatever else. Use it by default when you’re searching for something a human named.

3. By extension

find /var/log -name "*.log"

Those quotes are not decoration. Without them your shell expands *.log against the current directory before find ever runs, and you get either the wrong results or an error about too many arguments. Quote every pattern, every time.

4. Directories only, or files only

find /srv -type d -name "cache"      # directories
find /srv -type f -name "*.tmp"      # regular files

5. By path rather than filename

find . -path "*/config/*.yml"

-name looks at the filename alone. -path matches against the whole path, which is how you target files that only matter in a particular location.

6. Matching several extensions at once

find . -name "*.jpg" -o -name "*.png" -o -name "*.gif"

-o is or. Tests sit side by side with an implied and, so you only need -o when you want alternatives.

Finding files by size

7. Anything over 100MB

$ find /var -type f -size +100M 2>/dev/null
/var/log/journal/system@abc123.journal
/var/lib/mysql/ibdata1

The suffix matters more than it looks. M is mebibytes, k kibibytes, G gibibytes, c plain bytes. Leave the suffix off entirely and find counts 512-byte blocks, so -size +100 means 50KB and not 100 of anything you meant.

8. Within a size range

find . -type f -size +10M -size -50M

Two tests next to each other, both must pass. Plus is bigger than, minus is smaller than, and a bare number is exactly.

9. Empty files and directories

find . -type f -empty
find . -type d -empty

10. The ten largest files under a directory

$ find /var -type f -printf "%s\t%p\n" 2>/dev/null | sort -rn | head -3
4294967296      /var/lib/mysql/ibdata1
1073741824      /var/log/journal/system.journal
524288000       /var/cache/apt/archives/some.deb

This is the one to keep. When a disk fills up, df tells you which filesystem and this tells you what to blame. -printf with %s gives raw bytes, which sorts correctly, unlike human-readable sizes.

Finding files by time

11. Changed in the last 24 hours

find /etc -type f -mtime 0

12. Older than 30 days

find /backup -type f -mtime +30

Here’s the part that trips everyone. -mtime counts 24-hour periods and throws away the fraction. A file that’s 30.9 days old counts as 30, so -mtime +30 doesn’t match it. In practice +30 means 31 days and up. Not a problem for log cleanup, definitely a problem if you’re reconciling exact dates.

13. Changed in the last 15 minutes

find /var/www -type f -mmin -15

When you’re chasing something that just happened, minutes beat days. This is the fastest way to answer “what did that deploy actually touch”.

14. Newer than a reference file

touch -d "2025-12-04" /tmp/marker
find /srv -newer /tmp/marker

15. Between two dates

find /var/log -newermt "2025-12-04" ! -newermt "2025-12-08"

! negates the test that follows, so this reads as newer than the 1st and not newer than the 8th. Much clearer than working out day counts for -mtime, and it accepts most sensible date formats.

16. Access time and change time

find . -atime -7      # read in the last week
find . -ctime -1      # metadata changed in the last day

ctime catches permission and ownership changes as well as content. It is not creation time, which is a reasonable guess and wrong. Linux traditionally doesn’t track creation time at all.

Finding files by owner and permissions

17. Owned by a particular user

find /var/www -user www-data
find /home -group developers

18. Files nobody owns

find /home -nouser -o -nogroup

These turn up after you delete a user account without cleaning up their files. Worth checking occasionally, because a new user created later can inherit that UID and quietly acquire someone else’s old files.

19. World-writable files

$ find /var/www -type f -perm /o+w
/var/www/html/uploads/config.php

A file anyone on the system can edit. On a web server that’s how a small vulnerability becomes a large one, so this is a good thing to run after somebody has been fixing permission errors with chmod 777.

20. Setuid binaries

sudo find / -type f -perm /u+s 2>/dev/null

Setuid programs run as their owner rather than as you, which is how passwd lets a normal user change a root-owned file. Legitimate ones exist. An unexpected setuid binary in a home directory is worth investigating.

21. An exact permission mode

find /etc -type f -perm 600      # exactly 600
find /etc -type f -perm -600     # at least 600
find /etc -type f -perm /600     # either of those bits

Three different meanings from one punctuation mark. No prefix is exact, minus means all of these bits are set, slash means any of them. If permissions are a regular part of your work, Linux ACLs with setfacl and getfacl covers the layer beyond these.

Controlling where find looks

22. Limiting the depth

find /etc -maxdepth 2 -name "*.conf"

Put -maxdepth first, before other tests. Find will still work if you don’t, but it warns you, and the reason is that it’s an option rather than a test, so its position changes when it takes effect.

23. Skipping a directory entirely

find . -path "./node_modules" -prune -o -name "*.js" -print

-prune is the ugliest thing in this article and it’s worth the five minutes. Read it as: if the path is node_modules, prune it and stop descending, otherwise test for .js and print. The explicit -print at the end is required here, because once you use -o the automatic printing no longer applies to what you expect.

The payoff is speed. Searching a project tree without pruning node_modules or .git can take ten times longer for no useful results.

24. Staying on one filesystem

sudo find / -xdev -type f -size +500M

Without -xdev, a search from / wanders into every mounted filesystem, including network mounts that may be slow or hung. This keeps it on the root filesystem, which is nearly always what you wanted when hunting for space.

25. Broken symlinks

find /usr/local/bin -xtype l

Links pointing at things that no longer exist. Common after an upgrade removes a versioned binary.

Doing something with what you find

26. Running a command per file

find /var/log -name "*.log" -exec gzip {} \;

{} is the filename, and \; ends the command. The backslash is there to stop the shell eating the semicolon.

27. Running it once for everything

find /var/log -name "*.log" -exec gzip {} +

Same result, very different behaviour. With \; find launches gzip once per file. With + it batches as many filenames as fit into a single invocation. On a few files you won’t notice. On fifty thousand it’s the difference between a coffee and an afternoon. Use + unless the command genuinely needs one file at a time.

28. Handling filenames with spaces

find . -name "*.mp3" -print0 | xargs -0 -I{} mv {} /music/

Piping find into xargs normally splits on whitespace, so My Song.mp3 arrives as two broken arguments. -print0 separates with null bytes instead and -0 tells xargs to expect that. Null is the one character a filename cannot contain, which is why this pairing is the correct answer rather than a workaround.

29. Copying or moving matches

find /srv/app -name "*.conf" -exec cp {} /backup/configs/ \;

For anything larger than a handful of files, rsync is the better tool, since it only transfers what changed and resumes if interrupted.

30. Fixing permissions in bulk

find /var/www -type d -exec chmod 755 {} +
find /var/www -type f -exec chmod 644 {} +

Two commands rather than one, because directories need the execute bit to be traversable and files almost never should have it. Running a single recursive chmod 755 across a web root is how you end up with every PHP file marked executable.

The four ways find bites people

Putting -delete in the wrong place. This is the serious one.

find . -name "*.log" -delete      # correct
find . -delete -name "*.log"      # deletes everything

Find evaluates left to right. In the second version -delete is reached first for every single file, so it deletes them all and then checks the name of something that no longer exists. There’s no confirmation and no undo.

The habit that prevents this costs you three seconds. Run it without the action first, look at the list, then add -delete to the end of the same command.

find /backup -type f -mtime +90            # look
find /backup -type f -mtime +90 -delete    # then act

Forgetting to quote the pattern. An unquoted *.log gets expanded by your shell first, so find receives filenames from your current directory rather than a pattern. Sometimes it appears to work, which is worse than failing.

Assuming -size counts bytes. A bare number is 512-byte blocks. Always give the suffix.

Expecting -mtime to be exact. Fractions are discarded, so +7 means eight days and up. Use -newermt when the actual date matters.

When not to use find

Find walks the filesystem every time you run it, which is thorough and slow. If you just want to locate a file by name and you don’t need it to be current to the second, locate reads a prebuilt index and returns instantly.

locate nginx.conf

The tradeoff is that the index updates on a schedule, so anything created in the last few hours may be missing. For searching file contents rather than names, grep -r is the right tool, and find only earns its place when you need to filter by size, age, ownership or permissions.

A search across a large filesystem can run for a long time. If you’re on a remote box, start it inside screen so a dropped connection doesn’t take the job with it.

Putting it to work

Cleaning up old files on a schedule is the most common real use, and it’s two lines:

# delete backups older than 90 days, every night at 2am
0 2 * * * /usr/bin/find /backup -type f -mtime +90 -delete

# compress logs older than a week
0 3 * * * /usr/bin/find /var/log/app -name "*.log" -mtime +7 -exec gzip {} +

Use the full path to find in a cron job, since cron runs with a minimal PATH and a bare find may not resolve. More on scheduling in setting up a cron job in Linux.

Test that exact command by hand first, without -delete, and read every line it prints. A find command in cron runs unattended forever, so a mistake in the path doesn’t fail once. It fails quietly every night until somebody needs a backup.

If a file refuses to delete even as root, the filesystem may have it flagged immutable, which the chattr command covers. And if the wider shell is still new to you, Linux commands and directory structure is the place to start.

Thirty examples is more than anyone needs on day one. Learn the shape of the command, learn to look before you act, and the rest is a matter of checking which flag you want.

Avatar photo

Asif Khan

I have spent over 10 years working across IT systems, open source software, DevOps, Linux administration and cloud operations. Three things drive most of what I do: automation, security and resilience. Much of that work involves planning and building the platforms that sit behind services people rely on daily, which means designing for failure just as carefully as for load. Cloud computing held my attention early on, largely for its flexibility. Being able to scale up and then back down again means far less guessing about how much capacity you will need. Across projects I work with the full DevOps toolchain, from provisioning, orchestration and configuration management through to release management and microservices architecture.