
Linux gives you several tools for finding files, and each one solves a different problem. find walks the filesystem in real time, giving you precise control over what to match and what to do with the results. locate queries a pre-built index, making it the faster option when you need a quick path lookup and do not need results to reflect the last few seconds of filesystem activity. whereis and which are narrower tools focused on command binaries and man page locations, useful when you need to confirm which version of a program your shell will run.
Knowing which tool to reach for saves time. Running find / to locate a config file you just installed is slower than running locate against an up-to-date index. Conversely, using locate in a Docker container where updatedb has never run will return nothing at all. The right choice depends on whether you need real-time accuracy, how large the search scope is, and whether you intend to act on the results.
This tutorial covers each tool from basic usage through practical administration patterns: filtering by name, type, size, ownership, permissions, and modification time with find; managing the locate index with updatedb; running bulk actions with -exec and xargs; searching file contents with grep; and excluding directories from traversal with -prune.
find for real-time filesystem searches with filters such as name, ownership, permissions, size, timestamps, and depth.locate for fast path lookups from an index, then run updatedb when recent files do not appear.whereis and which to resolve binaries and command paths, not general file discovery.-exec and xargs to act on results in bulk, and prefer batched execution for better performance.Use find when accuracy and real-time results matter, and use locate when speed matters more than immediate freshness. whereis and which solve a narrower problem, command lookup, while fd is a modern alternative to find that is not installed by default on Debian and Ubuntu.
| Tool | Speed | Real-Time Results | Searches By | Install Required |
|---|---|---|---|---|
find |
Slower on large trees | Yes | Name, type, owner, permissions, size, time, depth, and more | No |
locate |
Very fast | No, depends on index age | Indexed path substrings, basename (-b) |
Usually (mlocate or plocate) |
fd |
Fast | Yes | Name and pattern matching with simpler defaults | Yes, apt install fd-find on Debian and Ubuntu |
whereis |
Fast | Yes | Binary, source, and man page locations | No |
which |
Fast | Yes | First executable match in $PATH |
No |
For day-to-day administration, start with find for precise filtering, use locate for quick broad lookup, and use whereis or which when validating command binaries. You can also review related Linux tooling in DigitalOcean’s Linux commands guide.
To follow along with this guide, you need access to a Linux-based system, either a local machine or a remote server you log in to with SSH. The examples were validated on a Linux server running Ubuntu, and they also apply to Ubuntu and later versions as well as many other Linux distributions.
If you plan to use a remote server, complete the Initial Server Setup for Ubuntu first. This prepares a non-root user with sudo privileges and a firewall so you can run administrative commands safely.
Note: To illustrate how the find and locate commands work, the example commands in this guide search for files stored under /, or the root directory. Because of this, if you’re logged into the terminal as a non-root user, some of the example commands may include Permission denied in their output.
This is to be expected, since you’re searching for files within directories that regular users typically don’t have access to. However, these example commands should still work and be useful for understanding how these programs work.
Use locate when you need fast path lookups across the system and can accept results from an index that may be slightly out of date.
Install locate on Debian and Ubuntu by updating package metadata and installing plocate, which is the default on current Ubuntu releases:
- sudo apt update
- sudo apt install plocate
On older systems that use mlocate, install it with:
- sudo apt install mlocate
On Rocky Linux, CentOS, and other Red Hat-derived distributions, install with dnf:
- sudo dnf install mlocate
locate is faster than find because it reads from a database instead of walking the filesystem in real time. The basic form is:
- locate query
This returns paths containing query anywhere in the full path. To match only basenames, add -b:
- locate -b query
To return only existing paths and skip stale index entries, use -e:
- locate -e query
To inspect index statistics:
- locate -S
Database /var/lib/mlocate/mlocate.db:
21015 directories
136787 files
7727763 bytes in file names
3264413 bytes used to store database
updatedb builds the locate index by traversing mounted filesystems and recording paths in the database file. Most systems run it daily, often through /etc/cron.daily/mlocate, while newer systems may use a systemd timer managed by the plocate or mlocate package.
Run a manual database refresh when needed:
- sudo updatedb
updatedb behavior is controlled by /etc/updatedb.conf, including pruned paths and filesystems:
PRUNE_BIND_MOUNTS="yes"
PRUNEPATHS="/tmp /var/spool /media /var/lib/os-prober /var/lib/docker"
PRUNEFS="NFS nfs nfs4 rpc_pipefs afs binfmt_misc proc smbfs autofs iso9660 devpts cgroup2"
Note: The values shown above are representative. Your system may
include additional paths in PRUNEPATHS and additional filesystem
types in PRUNEFS. Edit only the values relevant to your environment.
To exclude a directory from indexing, add it to PRUNEPATHS and run sudo updatedb again.
To verify the directory was excluded, run locate for a path inside it and confirm no results are returned:
- locate excluded_directory_path
If results still appear, confirm you saved /etc/updatedb.conf and that the updatedb command completed without errors.
Note: locate only reflects the last successful updatedb run. Files created, removed, or moved after that run will not appear correctly until the index updates again.
Warning: In Docker containers and other ephemeral filesystems, locate databases may be missing, stale, or reset when the container is recreated. Use find for guaranteed real-time checks in short-lived environments.
If you want to schedule your own refresh cadence, review this guide on scheduling routine tasks with cron and anacron.
Use find when you need precise, real-time filtering across directories.
Search by exact, case-sensitive filename with:
- find -name "query"
When no starting directory is specified, find searches from the current working directory.
For case-insensitive matching:
- find -iname "query"
To invert the search:
- find -not -name "query_to_avoid"
You can also invert with !, escaped so the shell does not interpret it first:
- find \! -name "query_to_avoid"
Use -type when you need to restrict results to a specific kind of filesystem object, such as only regular files or only directories, regardless of their name.
Filter by file type with:
- find -type type_descriptor -name query
Common descriptors:
f: regular filed: directoryl: symbolic linkc: character deviceb: block deviceFind character devices in /dev:
- find /dev -type c
/dev/vcsa5
/dev/vcsu5
/dev/vcs5
/dev/vcsa4
/dev/vcsu4
/dev/vcs4
/dev/vcsa3
/dev/vcsu3
/dev/vcs3
/dev/vcsa2
/dev/vcsu2
/dev/vcs2
. . .
Search for .conf files under /usr:
- find /usr -type f -name "*.conf"
/usr/src/linux-headers-5.4.0-88-generic/include/config/auto.conf
/usr/src/linux-headers-5.4.0-88-generic/include/config/tristate.conf
/usr/src/linux-headers-5.4.0-90-generic/include/config/auto.conf
/usr/src/linux-headers-5.4.0-90-generic/include/config/tristate.conf
/usr/share/adduser/adduser.conf
/usr/share/ufw/ufw.conf
/usr/share/popularity-contest/default.conf
/usr/share/byobu/keybindings/tmux-screen-keys.conf
/usr/share/libc-bin/nsswitch.conf
/usr/share/rsyslog/50-default.conf
. . .
Note: The previous example combines two find query expressions; namely, -type f and -name "*.conf". For any file to be returned, it must satisfy both of these expressions.
You can combine expressions like this by separating them with the -and option, but as this example shows the -and is implied any time you include two expressions. You can also return results that satisfy either expression by separating them with the -or option:
- find -name query_1 -or -name query_2
This example will find any files whose names match either query_1 or query_2.
Use -size, -mtime, -atime, -ctime, and related flags to narrow results quickly.
Filter by size with unit suffixes:
c: bytesk: kilobytesM: megabytesG: gigabytesb: 512-byte blocksFind files exactly 50 bytes:
- find /usr -size 50c
Find files less than 50 bytes:
- find /usr -size -50c
Find files larger than 700 MB:
- find /usr -size +700M
Use -mtime, -atime, and -ctime to filter files by when they were last modified, accessed, or had their metadata changed. Pass a positive integer for exactly N days ago, a negative integer (-N) for within the last N days, and a positive integer with + (+N) for more than N days ago.
-atime): the last time a file was read or written to.-mtime): the last time file contents changed.-ctime): the last time inode metadata changed.To find files modified within the last day, use -1 to mean less than one day ago:
- find /usr -mtime -1
Using 1 without a sign matches files modified exactly one day ago, which is rarely what you want. Using -1 matches anything within the last 24 hours.
To find files accessed within the last day, run:
- find /usr -atime -1
To find files whose metadata changed more than three days ago, run:
- find /usr -ctime +3
These options also have companion parameters you can use to specify minutes instead of days:
- find /usr -mmin -1
This will give the files that have been modified in the last minute.
Use -newer to compare against a reference file:
- find / -newer reference_file
This syntax will return every file on the system that was created or changed more recently than the reference file.
Search files by owner with -user and by group with -group:
- find /var -user syslog
Similarly, you can specify files in the /etc directory owned by the shadow group by typing:
- find /etc -group shadow
You can also search by permissions, which is useful when auditing access controls. For a deeper permissions refresher, read An Introduction to Linux Permissions.
Note: Searching from / traverses the entire filesystem and may
produce significant Permission denied output for non-root users.
Scope your search to a specific directory such as /home or /var
when possible.
Match an exact mode:
- find / -perm 644
This will match files with exactly the permissions specified.
Match files with at least these permissions:
- find / -perm -644
This will match any files that have additional permissions. A file with permissions of 744 would be matched in this instance.
Use -empty to find files or directories that contain no data or no entries. This is useful for cleaning up placeholder files and empty directories left by installers or build tools.
Find empty files:
- find /tmp -type f -empty
Find empty directories:
- find /tmp -type d -empty
Use -exec for direct per-result actions inside find, and use xargs when you want batched or parallel command execution.
Run one command per matched file:
- find find_parameters -exec command_and_options {} \;
The {} token is replaced by each matched path. The \; terminates the command.
For example, from /tmp/test, update matching file modes:
- find . -type f -perm 644 -exec chmod 664 {} \;
Change matching directory modes:
- find . -type d -perm 755 -exec chmod 700 {} \;
Delete temporary .bak files:
- find /tmp/test -type f -name "*.bak" -exec rm {} \;
If no matching files exist, the command runs silently and returns to the prompt.
When you use -exec ... {} \;, find starts one subprocess per match, which adds overhead on large result sets. Batched mode with -exec ... {} + passes many paths at once, which reduces process creation and usually runs faster:
- find /tmp/test -type f -name "*.log" -exec rm {} +
The same silent behavior applies when batching with {} + and no matches are found.
Warning: Deletion commands are destructive. Test first with the same find query without -exec rm, or replace rm with ls -l for a dry run.
xargs reads path lists from standard input and builds command lines efficiently. This is useful when you want parallel execution, explicit batching, or commands not directly attached to find.
Count matching files, then gzip them in parallel with 4 workers:
- find /var/log -type f -name "*.log" | wc -l
42
- find /var/log -type f -name "*.log" -print0 | xargs -0 -n 10 -P 4 gzip
If the files compress successfully, the command runs silently and returns to the prompt.
In practice, use -exec ... {} + when you want simple, safe batching directly in find. Use xargs when you need options like parallel workers (-P), fixed batch sizes (-n), or more complex command chaining.
Use find to select files by name or type, then pipe or pass those results to grep to search inside them.
Search all .conf files under /etc for a specific string, with the filename printed alongside each match:
- find /etc -type f -name "*.conf" -exec grep -H "PermitRootLogin" {} \;
/etc/ssh/sshd_config:PermitRootLogin no
The -H flag tells grep to include the filename in the output. Without it, you see only the matching line, which makes it impossible to tell which file it came from when multiple files match.
To return only the filenames that contain a match, without printing the matching lines themselves, use -l:
- find /etc -type f -name "*.conf" -exec grep -l "PermitRootLogin" {} \;
/etc/ssh/sshd_config
For file names that contain spaces or special characters, use -print0 with xargs -0 to handle them safely:
- find /etc -type f -name "*.conf" -print0 | xargs -0 grep -H "127.0.0.1"
On large directory trees, the xargs form is faster than -exec grep {} \; because it passes multiple files to a single grep invocation instead of spawning one process per file.
Use whereis and which to locate command-related paths, not arbitrary files.
whereis returns binary, source, and man page locations when present:
- whereis ls
ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz
You can query multiple commands at once:
- whereis find grep ssh
find: /usr/bin/find /usr/share/man/man1/find.1.gz
grep: /usr/bin/grep /usr/share/man/man1/grep.1.gz
ssh: /usr/bin/ssh /usr/share/man/man1/ssh.1.gz
which returns the first executable match in the current $PATH:
- which python3
/usr/bin/python3
Use which when you need to verify which binary a shell session will invoke, particularly when multiple versions of the same tool are installed. Use whereis when you also need man page locations. For general filesystem discovery, use find, because whereis and which do not apply ownership, permission, size, time, or depth filters.
To narrow whereis output to a specific location type, use the -b, -m, or -s flags:
- whereis -b find
find: /usr/bin/find
-b returns the binary path only. -m returns the man page path only. -s returns the source path only, if present.
Use which to confirm a command is available and locate its binary:
- which htop
/usr/bin/htop
If the command is installed but which returns nothing, check whether its directory is included in $PATH:
- echo $PATH
On systems with multiple Python versions, which python3 and which python may return different paths. Use which to confirm which binary your shell will invoke before running scripts that depend on a specific version.
Use advanced predicates when you need tighter control over traversal depth or want to exclude specific directories from a search.
Use -maxdepth and -mindepth to control how many directory levels find descends. This is useful when you know files exist at a specific depth and want to avoid traversing an entire tree.
To follow the examples below, create a test directory structure in /tmp/. Files and directories under /tmp/ are removed on the next reboot, so this will not clutter your system.
After running the commands in this section, your /tmp/ directory will contain three levels of directories, with 10 directories at the first level. Each directory, including the temporary directory, will contain 10 files and 10 subdirectories.
Create the example directory structure within the /tmp/ directory with the following command:
- mkdir -p /tmp/test/level1dir{1..10}/level2dir{1..10}/level3dir{1..10}
Following that, populate these directories with some sample files using the touch command:
- touch /tmp/test/{file{1..10},level1dir{1..10}/{file{1..10},level2dir{1..10}/{file{1..10},level3dir{1..10}/file{1..10}}}}
With these files and directories in place, navigate into the test/ directory you just created:
- cd /tmp/test
To get a baseline understanding of how find will retrieve files from this structure, begin with a regular name search that matches any files named file1:
- find -name "file1"
./level1dir7/level2dir8/level3dir9/file1
./level1dir7/level2dir8/level3dir3/file1
./level1dir7/level2dir8/level3dir4/file1
./level1dir7/level2dir8/level3dir1/file1
./level1dir7/level2dir8/level3dir8/file1
./level1dir7/level2dir8/level3dir7/file1
./level1dir7/level2dir8/level3dir2/file1
./level1dir7/level2dir8/level3dir6/file1
./level1dir7/level2dir8/level3dir5/file1
./level1dir7/level2dir8/file1
. . .
Note: Always quote name arguments passed to -name and -iname.
Without quotes, the shell may expand glob characters such as * or
? before find sees them, producing unexpected results.
This will return a lot of results. If you pipe the output into a counter, you’ll find that there are 1111 total results:
- find -name "file1" | wc -l
1111
This is probably too many results to be useful to you in most circumstances. To narrow it down, you can specify the maximum depth of the search under the top-level search directory:
- find -maxdepth num -name query
To find file1 only in the level1 directories and above, you can specify a max depth of 2 (1 for the top-level directory, and 1 for the level1 directories):
- find -maxdepth 2 -name "file1"
./level1dir7/file1
./level1dir1/file1
./level1dir3/file1
./level1dir8/file1
./level1dir6/file1
./file1
./level1dir2/file1
./level1dir9/file1
./level1dir4/file1
./level1dir5/file1
./level1dir10/file1
That is a much more manageable list.
You can also set a minimum depth if you know the files you need are nested beyond a certain level:
- find -mindepth num -name query
You can use this to find only the files at the end of the directory branches:
- find -mindepth 4 -name "file1"
./level1dir7/level2dir8/level3dir9/file1
./level1dir7/level2dir8/level3dir3/file1
./level1dir7/level2dir8/level3dir4/file1
./level1dir7/level2dir8/level3dir1/file1
./level1dir7/level2dir8/level3dir8/file1
./level1dir7/level2dir8/level3dir7/file1
./level1dir7/level2dir8/level3dir2/file1
. . .
Again, because of the branching directory structure, this will return a large number of results (1000).
You can combine the min and max depth parameters to focus in on a narrow range:
- find -mindepth 2 -maxdepth 3 -name "file1"
./level1dir7/level2dir8/file1
./level1dir7/level2dir5/file1
./level1dir7/level2dir7/file1
./level1dir7/level2dir2/file1
./level1dir7/level2dir10/file1
./level1dir7/level2dir6/file1
./level1dir7/level2dir3/file1
./level1dir7/level2dir4/file1
./level1dir7/file1
. . .
Combining these options like this narrows down the results significantly, with only 110 lines returned instead of the previous 1000.
Use -prune to skip specific directories during a search. This prevents find from descending into directories like .git, node_modules, or vendor paths that would otherwise produce noise or slow the search significantly.
Search for .js files while skipping the node_modules directory:
- find . -type d -name "node_modules" -prune -o -type f -name "*.js" -print
The -o operator means OR. find prunes the node_modules directory and then, for everything else, prints .js files. Without -print at the end, find would also print the pruned directory name itself.
Note: -prune does not delete directories. It only tells find
not to descend into them during the current search.
A: find walks the filesystem in real time, so its results reflect the current state immediately. locate queries an indexed database, which is much faster on large systems. locate can miss very recent changes until updatedb runs.
A: Run sudo updatedb to rebuild the locate index on demand. This command refreshes the file path database used by locate. If permissions block some paths, run it with appropriate privileges.
A: Use find -name "query" for case-sensitive matching by filename pattern. Use find -iname "query" to ignore case. Add a start directory such as find /etc -name "*.conf" to scope the search.
A: Combine find with grep, for example find /etc -type f -name "*.conf" -exec grep -H "pattern" {} \;. This filters by filename first, then searches content. For safer handling of special characters in filenames, use -print0 with xargs -0.
A: -exec runs another command on each match returned by find. The {} placeholder is replaced by the matched path. Use \; for one command per file, or + for batched execution.
A: Use xargs when you need explicit batching with -n, parallelism with -P, or command pipelines fed from standard input. It is often faster on large result sets because it groups many files per invocation. Use -print0 and xargs -0 to handle spaces and special characters safely.
A: which returns the first executable found in your $PATH, which helps confirm what runs in your shell. whereis can return binary, source, and man page locations. Neither command replaces find for general file search.
A: It depends on whether an index exists and is kept current in that runtime. Many short-lived containers do not maintain a useful locate database between restarts. In those environments, prefer find for accurate results.
Use find when you need immediate, filter-rich results, and use locate when you need fast indexed lookups. Add whereis and which when validating executable paths and man page locations in shell and package troubleshooting workflows.
For continued practice, read man find, man locate, and info find. You can also expand your command line toolkit with DigitalOcean’s Linux commands tutorial.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Former Senior Technical Writer at DigitalOcean, specializing in DevOps topics across multiple Linux distributions, including Ubuntu 18.04, 20.04, 22.04, as well as Debian 10 and 11.
Building future-ready infrastructure with Linux, Cloud, and DevOps. Full Stack Developer & System Administrator. Technical Writer @ DigitalOcean | GitHub Contributor | Passionate about Docker, PostgreSQL, and Open Source | Exploring NLP & AI-TensorFlow | Nailed over 50+ deployments across production environments.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Is there a fairly simple way to delete all of those files and directories we created in the “Filtering by Depth” section of this tutorial?
I’m still learning the basics of Linux, and a simple simple rm combined with rmdir will take quite a bit of time… Perhaps some sort of mv ‘target’ >> null approach?
@wizardware: You can:
<ul><li>delete ~/test completely by running <code>rm -r ~/test</code>; or</li> <li>delete all of the files/directories in ~/test by running <code>rm -r ~/test/</code>; or</li> <li>delete all of the files/directories in ~/test that start with <strong>level</strong> by running <code>rm -r ~/test/level</code>;</li></ul>
I think there is a typo in this section: http://d.pr/i/15yf3
It should have -type f and -type d respectively.
You can use KrojamSoft FilesSearch Tool, it helped me out on many cases. Hope this helped you out!
find -mindepth 4 -name file
find -mindepth 2 -maxdepth 3 -name file
The above need “file” to be changed to “file1” otherwise they get no hits
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Full documentation for every DigitalOcean product.
The Wave has everything you need to know about building a business, from raising funding to marketing your product.
Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.
New accounts only. By submitting your email you agree to our Privacy Policy
Scale up as you grow — whether you're running one virtual machine or ten thousand.
Sign up and get $200 in credit for your first 60 days with DigitalOcean.*
*This promotional offer applies to new accounts only.