You know it’s there. Somewhere. That one config file you edited last week. That tiny log entry with the critical error message. That single line of code in a project with 10,000 files.
In Linux, not being able to find something is a temporary problem. The system gives you a toolkit of near-supernatural power. But this toolkit isn’t one tool; it’s three.
locate: The Librarian. It has a pre-built index (a card catalog) of every file on your system. It’s blindingly fast for finding files by name, but its index can be outdated.find: The Bloodhound. It doesn’t use an index. It crawls through every directory you tell it to, right now, sniffing for files based on a complex set of rules: name, size, modification time, owner, permissions… anything. It’s slower, but it’s 100% accurate and infinitely powerful.grep: The X-Ray.findandlocatefind files.greplooks inside them. It scans file contents line-by-line for a specific text pattern, from a simple word to a complex Regular Expression.
Using them individually is useful. Using them together is the mark of a true Linux master.
This is not a quick overview. This is the definitive, reference guide. By the end of this post, you will be able to find anything, anywhere, any time.
locate — The Blazingly Fast Librarian
We’ll start with the simplest and fastest tool. locate doesn’t actually search your disk. Instead, it searches a database called mlocate.db (or slocate.db on older systems).
This database is a snapshot of your entire filesystem, built by a background process.
The Basic Syntax:
locate [options] [pattern]
The “Hello, World” of locate:
Let’s say you’re looking for the sshd_config file, but you don’t remember where it is.
locate sshd_config
Output:
/etc/ssh/sshd_config
/usr/share/man/man5/sshd_config.5.gz
In less than a second, it found the config file and its manual page. This is the primary strength of locate: speed.

The “Gotcha”: When locate Fails
This speed comes at a cost. locate is not a real-time search.
Scenario:
- You create a new file:
touch my_brand_new_file.txt - You immediately search for it:
locate my_brand_new_file.txt - No output.
locatecan’t find it.
Why? Because the mlocate.db database hasn’t been updated yet. This database is typically updated by a cron job once every 24 hours.
How to Manually Update the locate Database
You can force an update at any time using the updatedb command. This command does crawl the whole filesystem, so it can take a minute, and it requires sudo privileges.
sudo updatedb
After this command completes:
locate my_brand_new_file.txt
Output:
/home/user/my_brand_new_file.txt
Now it works.
Pro-Tip: If you’re a developer and just installed a new library or tool, run sudo updatedb so you can instantly locate its files without guessing the path.
Key locate Options
While simple, locate has a few essential flags.
-i(case-insensitive): The most useful flag. It ignores the difference between uppercase and lowercase.# This will find MyFile.txt, myfile.txt, and MYFILE.TXT locate -i myfile.txt-c(count): Don’t show the results, just show how many files matched.locate -c ".txt" # Output: 1428-r(regex): Use a Basic Regular Expression. This lets you search for patterns. For example, to find all.confor.configfiles:locate -r "(\.conf|\.config)$"(The$“anchors” the search to the end of the filename.)-b(basename): Only match against the “basename” (the filename itself), not the entire path.# This will find /usr/bin/config, but... locate "config" # Output: # /etc/ssh/sshd_config # /usr/bin/config # /home/user/my-project/config/settings.py # This will *only* find files/dirs named exactly "config" locate -b "\config" # Output: # /usr/bin/config(Note: The\is needed to search for the exact name and not files that contain “config”.)
locate vs. find: When to Use Which?
| Feature | locate (The Librarian) | find (The Bloodhound) |
|---|---|---|
| Speed | Insanely fast. Milliseconds. | Slow. Proportional to disk size/files. |
| Data | Searches a database (index). | Searches the live filesystem. |
| Accuracy | As accurate as the last updatedb run. | 100% accurate, right now. |
| Criteria | Name/Path only. | Name, Size, Time, Owner, Perms… |
| Use Case | “Where is the nginx config file?” | “Find all .log files in /var modified in the last 24 hours that are larger than 10MB and contain the word ‘Error’.” |
Rule of Thumb: Always try locate first. If it fails or you need to search by criteria other than name, use find.
find — The All-Powerful Bloodhound
Welcome to the deep end. find is one of the most powerful and complex commands in all of Linux. It doesn’t use an index. It walks your directory tree in real-time and runs a series of tests on every single file and directory it encounters.
The Basic Syntax:
find [path...] [options] [tests] [actions]
[path...]: Where to start searching. Common examples:.(search the current directory and everything below it)/(search the entire system — requiressudo!)~(search your home directory)/var/log(search just the log directory)
[options]: Controls howfindbehaves (e.g., following symlinks).[tests]: The rules to match (e.g.,-name "*.txt",-size +10M).[actions]: What to do with the files that match (e.g.,-print,-delete,-exec).
If no action is specified, the default action is -print (print the full path to the screen).
Section 2.1: The Core find Tests (Filtering)
Let’s build up our knowledge, one test at a time.
Test 1: -name and -iname (Search by Name)
This is the most common test. It searches by filename.
-name [pattern]: Case-sensitive search.-iname [pattern]: Case-insensitive search.
The pattern needs to be in quotes to prevent the shell from expanding “wildcards” like * before find can see them.
# Find all files ending in .conf in the /etc directory
find /etc -name "*.conf"
# Find all files named "README.md", case-insensitive, in our home dir
find ~ -iname "readme.md"
Gotcha: “Permission Denied” When you search a large tree, you’ll see a lot of find: ‘/var/spool/cron’: Permission denied. This is just find telling you it doesn’t have permission to look in that directory. It’s not an error.
You can hide these messages by redirecting “stderr” (error output) to /dev/null (the “void”):
# This is the "pro" way to run a find command
find / -name "sshd_config" 2>/dev/null
Or, you can run the search as root:
sudo find / -name "sshd_config"
Test 2: -type (Search by File Type)
This is essential. It lets you find only files, or only directories.
-type f: Find regular files.-type d: Find directories.-type l: Find symbolic links.
# Find all directories named "src" in the current directory
find . -type d -name "src"
# Find all regular files named "config.ini"
find / -type f -name "config.ini" 2>/dev/null
You can combine tests. find assumes an “AND” between them. The command above finds things that are (a directory) AND (named “src”).
Test 3: -path and -regex (Search by Full Path)
-nameonly looks at the filename at the end of the path.-pathruns the pattern against the entire path.
This is perfect for excluding directories.
# Find all ".py" files, but ignore anything in a ".git" directory
find . -type f -name "*.py" -not -path "./.git/*"
Here we introduced -not, which inverts the next test. This command says:
- Start at
. - Find things of type
f(file) - AND with a name matching
*.py - AND
NOTwith a path matching./.git/*
-regex is even more powerful. It lets you use a full regular expression on the path.
# Find all config files that end in .conf or .ini
find /etc -regex ".*\.
(conf|ini)$"
(This is complex; we’ll cover regex more in the grep section.)
Test 4: -size (Search by Size)
This is a sysadmin’s best friend for finding “what’s eating all my disk space?”
The size flags have special suffixes:
c: bytesk: KilobytesM: MegabytesG: Gigabytes
And special prefixes:
+: More than (e.g.,+100M= > 100MB)-: Less than (e.g.,-50k= < 50KB)- (no prefix): Exactly (e.g.,
1024c= exactly 1024 bytes)
# Find all files in /var/log larger than 100 Megabytes
find /var/log -type f -size +100M
# Find all files in your home dir smaller than 1 Kilobyte
find ~ -type f -size -1k
# Find all empty files in the current directory
find . -type f -empty
(-empty is a handy shortcut for -size 0c.)
Test 5: -mtime, -atime, -ctime (Search by Time)
This is the other “killer app” for find. You can find files based on when they were last modified, accessed, or had their metadata (like permissions) changed.
-mtime: Modified Time (when the file’s content last changed).-atime: Accessed Time (when the file was last read).-ctime: Changed Time (when the file’s metadata or content last changed).
90% of the time, you want -mtime.
The time flags use the same + / - / (none) logic as -size, but the unit is days.
-mtime -7: Modified less than 7 days ago (i.e., within the last week).-mtime +30: Modified more than 30 days ago.-mtime 1: Modified exactly 1 day ago (between 24 and 48 hours ago).
# Find all files in /etc modified in the last 2 days
find /etc -type f -mtime -2
# Find all files in my home dir I haven't touched in over a year (365 days)
find ~ -type f -mtime +365
Minutes: Sometimes days are too broad. You can use minutes instead:
-mmin: Modified Minutes ago.-amin: Accessed Minutes ago.-cmin: Changed Minutes ago.
# Find all config files I've edited in the last 60 minutes
find /etc -type f -name "*.conf" -mmin -60
Test 6: -user and -group (Search by Owner)
This is perfect for finding all the files owned by a specific user or group.
# Find all files on the system owned by the user 'www-data'
sudo find / -user www-data 2>/dev/null
# Find all directories in /home owned by the 'students' group
find /home -type d -group students
Test 7: -perm (Search by Permissions)
This links directly to our previous day’s topic. You can find files with specific permissions. This is a critical security-auditing tool.
find‘s permission search is uniquely powerful.
-perm 644: Find files with permissions exactly644(rw-r--r--).-perm -644: Find files where at least these permissions are set. This is a “mask.” A file with755would match, because it has at leastrw-r--r--.-perm /222: Find files where any of these bits are set (the “OR” mode). This finds files that have write permission for User OR Group OR Others.
This is advanced, but here are the key use cases:
# Find all "world-writable" files on the system. A HUGE security risk!
# This searches for the "other" write bit (002)
sudo find / -perm /o=w 2>/dev/null
# Find all "world-writable" directories
sudo find / -type d -perm /o=w 2>/dev/null
# Find all executable files in /usr/bin
find /usr/bin -perm /a=x
# Find all files with the "setuid" bit set (another security risk)
# The '4' in '4755' is the setuid bit.
sudo find / -perm /4000 2>/dev/null

Section 2.2: Combining Tests with Operators
What if you want to find files that match (Test A) OR (Test B)? By default, find uses “AND”. You can override this.
-or: The OR operator.!or-not: The NOT operator.\( ... \): Grouping parentheses. (You must escape them with\so the shell doesn’t interpret them).
Example: Find all .jpg or .png files.
find . -type f \( -name "*.jpg" -or -name "*.png" \)
This is a very common pattern. The command says:
- Start at
. - Find things of type
f(file) - AND (open a group)
- Find things with name
*.jpg - OR
- Find things with name
*.png - (close the group)
Example: Find all .log files that are not owned by root.
find /var/log -type f -name "*.log" -not -user root
Section 2.3: find Actions (Doing Things with Results)
This is where find goes from a search tool to an automation engine.
Action 1: -delete (The “Danger” Action)
This does exactly what it says. It deletes the files that match.
Warning: This is DANGEROUS. There is no “undo.” There is no “trash.” They are gone.
ALWAYS run your
findcommand without-deletefirst. See what files it matches. Only when you are 100% sure, press the “up” arrow and add-deleteto the end.
Example: Clean up all .tmp files in your home directory.
# Step 1: The "Dry Run"
find ~ -type f -name "*.tmp"
# Output:
# /home/user/docs/report.tmp
# /home/user/projects/old_stuff.tmp
# (Looks safe, those are the files I want to delete)
# Step 2: The "Real" Command
find ~ -type f -name "*.tmp" -delete
Action 2: -exec (The “God Mode” Action)
This is the single most powerful action. It lets you run any command on every file that matches.
Syntax:
find ... -exec [command] {} \;
-exec: Tellsfindto execute a command.[command]: The command to run (e.g.,chmod,ls,grep).{}: This is the magic part. It’s a placeholder thatfindreplaces with the full path of the matched file.\;: This is also magic. It tellsfindwhere the command ends. It must be\;(a backslash and a semicolon).
Example: Let’s use yesterday’s lesson. Find all files in the current directory and chmod them to 644.
find . -type f -exec chmod 644 {} \;
What this does:
findfinds./src/main.py- It runs
chmod 644 ./src/main.py findfinds./README.md- It runs
chmod 644 ./README.md…and so on, for every file.
Example: Find all directories and chmod them to 755.
find . -type d -exec chmod 755 {} \;
Example: Find all .jpg files and move them to a ~/Pictures/jpegs folder.
find . -type f -name "*.jpg" -exec mv {} ~/Pictures/jpegs \;
This runs mv ./photo.jpg ~/Pictures/jpegs, then mv ./vacation/img.jpg ~/Pictures/jpegs, etc.
The Problem with \; This method is slow on a large number of files. If you match 10,000 files, it runs the chmod command 10,000 times.
The Solution: -exec ... + There is a more efficient, modern syntax. If you replace \; with +, find will “batch” the files together.
find . -type f -exec chmod 644 {} +
What this does:
findfinds 10,000 files.- It runs
chmod 644 ./file1.txt ./file2.jpg ... ./file10000.pyThis runs thechmodcommand once (or as few times as possible), which is dramatically faster.
Rule of Thumb: Always use + instead of \; if the command can accept multiple files at once (like chmod, chown, mv, cp, rm, ls).
Action 3: -ok (The “Safe” Exec)
This works exactly like -exec ... \;, but it prompts you for confirmation (“y” or “n”) before running the command on every single file.
It’s a great way to be careful, but it’s not practical for 10,000 files.
# This will ask you "y/n" for every .tmp file before deleting
find . -type f -name "*.tmp" -ok rm {} \;
Section 2.4: find Cookbook (Practical Recipes)
Let’s put all this together.
- Recipe 1: Find and delete all empty files.
find . -type f -empty -delete - Recipe 2: Find and delete all empty directories.
find . -type d -empty -delete - Recipe 3: Fix permissions on a web root. (Find all dirs and set to 755, find all files and set to 644).
find /var/www/html -type d -exec chmod 755 {} + find /var/www/html -type f -exec chmod 644 {} + - Recipe 4: Find all
.logfiles modified in the last 7 days.find /var/log -type f -name "*.log" -mtime -7 - Recipe 5: Find all files over 500MB on the entire system.
sudo find / -type f -size +500M 2>/dev/null - Recipe 6: Find all
.mp3or.flacfiles in your home directory.find ~ -type f \( -name "*.mp3" -or -name "*.flac" \) - Recipe 7: Find all files owned by ‘bob’ and change ownership to ‘alice’.
sudo find /home/bob -user bob -exec chown alice {} +
grep — The All-Powerful X-Ray
find and locate find files. grep finds content. It is, without exaggeration, one of the most important commands in Linux. grep stands for “Global Regular Expression Print.”
The Basic Syntax:
grep [options] [pattern] [file(s)...]
[options]: Controls howgrepsearches (e.g.,-i,-r,-v).[pattern]: What you are looking for. This can be a simple string or a complex “Regular Expression.”[file(s)...]: Where to look. This can be a single file, multiple files (file1.txt file2.log), or a wildcard (*.log).
The “Hello, World” of grep
Let’s search for the word “root” in the /etc/passwd file.
grep "root" /etc/passwd
Output:
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
grep printed every line that contained the string “root”.
The #1 Use Case: grep and Pipes (|)
grep is powerful on its own, but its most common use is filtering the output of other commands. The “pipe” | symbol sends the output of the command on the left to the input of the command on the right.

Example: See if the “nginx” process is running. ps aux lists all running processes. This is a ton of output.
ps aux | grep "nginx"
Output:
root 1234 0.0 0.1 123456 7890 ? Ss Nov13 0:00 nginx: master process /usr/sbin/nginx
www-data 1235 0.0 0.2 123456 8901 ? S Nov13 0:00 nginx: worker process
user 5678 0.0 0.0 1234 567 pts/0 S+ 09:30 0:00 grep --color=auto nginx
We instantly filtered hundreds of lines down to the three we care about. (The last line is our own grep command!)
Section 3.1: Essential grep Options
These flags change how grep behaves.
-i(case-insensitive): The most common option.# Will find "Error", "error", and "ERROR" grep -i "error" /var/log/syslog-ror-R(recursive): The second most common option. This makesgrepsearch recursively through a directory.# Search for "API_KEY" in the current directory and all subdirectories grep -r "API_KEY" .This is your “find my code” command.grep -r "function_name" .is a daily-driver for developers.-v(invert-match): Show all lines that do not match the pattern. This is for filtering out noise.# Show all running processes, but hide the ones run by 'root' ps aux | grep -v "root"-c(count): Don’t print the matching lines, just print how many lines matched.# How many user accounts have a real shell? grep "/bin/bash" /etc/passwd # Output: 2-l(list-files): Don’t print the matching lines, just print the filenames of files that contain a match.# Which files in my project mention "TODO"? grep -r -l "TODO" . # Output: # ./src/main.py # ./docs/notes.txt-L(list-files-without-match): The opposite. Print the filenames of files that do not contain a match.-n(line-number): Show the line number for each match. Incredibly useful for debugging.grep -n "error" app.log # Output: # 87:2025-11-14 [ERROR] Failed to connect to database. # 132:2025-11-14 [INFO] User login failed (error code 403)-w(whole-word): Only match whole words.# This will match "error" but not "errors" or "terrorist" grep -w "error" app.log-o(only-matching): Only print the exact part of the line that matched.# Extract all email addresses from a file grep -o -E "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" contacts.txt
Section 3.2: Context Control (The Debugger’s Dream)
When you’re searching a log file, finding the “error” line is good. Seeing the 5 lines before and after it is great.
-A [n](After): Show N lines of context after the match.-B [n](Before): Show N lines of context before the match.-C [n](Context): Show N lines of context before AND after the match.
# Find "Failed login" and show the 2 lines before and 2 lines after
grep -C 2 "Failed login" /var/log/auth.log
Output:
Nov 14 09:30:15 server sshd[1234]: Connection from 1.2.3.4 port 5678
Nov 14 09:30:15 server sshd[1234]: pam_unix(sshd:auth): authentication failure;
--
Nov 14 09:30:17 server sshd[1234]: Failed login for invalid user 'admin' from 1.2.3.4
--
Nov 14 09:30:17 server sshd[1234]: Connection closed by 1.2.3.4
Nov 14 09:30:17 server sshd[1234]: pam_service_fail_now(sshd:auth): TTY=ssh
See? Now you have the IP address and the full context, not just the error line.
Section 3.3: grep and Regex (The Real Power)
The “pattern” in grep is a Regular Expression (Regex). This is a mini-language for describing text patterns.
By default, grep uses Basic Regular Expressions (BRE). If you use the -E flag, it uses Extended Regular Expressions (ERE), which are much more powerful.
Pro-Tip:
egrepis an old command that is just an alias forgrep -E. Always usegrep -Efor modern regex.
Let’s learn the most important ERE tokens (using grep -E).
.(Dot): Matches any single character.grep -E "gr.p"matches “grep”, “grip”, “grXp”, etc.
^(Caret): Matches the start of a line.grep -E "^root"only matches lines beginning with “root”.
$(Dollar): Matches the end of a line.grep -E "nologin$"only matches lines ending with “nologin”.
|(Pipe): The “OR” operator. This is the #1 reason to use-E.grep -E "error|warning"matches lines containing “error” OR “warning”.
*(Star): Match zero or more of the previous character.grep -E "go*gle"matches “ggle”, “gogle”, “google”, “goooooogle”.
+(Plus): Match one or more of the previous character.grep -E "go+gle"matches “gogle”, “google”,… but not “ggle”.
?(Question): Match zero or one of the previous character.grep -E "colou?r"matches “color” and “colour”.
[](Brackets): A “character class.” Matches any one character inside the brackets.grep -E "gr[ae]y"matches “gray” and “grey”.grep -E "[0-9]"matches any single digit.
()(Parentheses): Groups an expression.grep -E "(http|ftp)s?"matches “http”, “https” “ftp”, “ftps”.
Regex Cookbook (Practical Recipes):
- Recipe 1: Find all IPs in a log file. (An IP is 1-3 digits, four times, separated by dots)
grep -E -o "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" access.log - Recipe 2: Find all lines that are not comments (don’t start with
#).grep -v "^#" /etc/nginx/nginx.conf - Recipe 3: Find all empty lines. (A line that starts and ends with nothing in between).
grep -E "^$" /path/to/file.txt - Recipe 4: Find all
TODOorFIXMEcomments in code.grep -r -E "(TODO|FIXME)" .
Section 3.4: grep‘s Relatives
egrep: Same asgrep -E. (Extended Regex)fgrep: Same asgrep -F. (Fixed string, no regex). It’s very fast if you’re just searching for a literal string.zgrep:grepfor gzipped files. It’s the same aszcat file.gz | grep "pattern", but easier to type.
The Holy Trinity: find + xargs + grep
We’ve found files with find. We’ve searched inside files with grep. Now, let’s combine them.
Goal: Search for the string “API_KEY” only inside .py (Python) files in the current project.
How do we “pipe” find‘s results into grep?
Method 1: find -exec grep ... This is the “obvious” way.
find . -type f -name "*.py" -exec grep -n "API_KEY" {} \;
- Pro: It works.
- Con: It’s slow. It runs
greponce for every single file. 1000 files = 1000grepcommands.
Method 2: find ... | xargs grep (The “Pro” Way) This is the “Unix Philosophy” way. We use a middle-man command: xargs.
xargs (eXtended ARGuments) is a simple utility. It reads a list of items from standard input (a pipe) and turns them into arguments for another command.
find . -type f -name "*.py" | xargs grep -n "API_KEY"
What this does:
findruns and prints a list of files, one per line:./src/main.py./src/db.py./tests/test_api.py- The
|(pipe) sends this list toxargs. xargs“bundles” this list and runsgreponce with all of them:grep -n "API_KEY" ./src/main.py ./src/db.py ./tests/test_api.py
This is thousands of times faster than Method 1.
The xargs “Spaces in Filenames” Gotcha: This command will break if you have a file named my test file.py. xargs sees spaces as delimiters. It will try to find files named my and test and file.py.
The find and xargs “Bulletproof” Method: This is the true professional, 100% safe method. It uses “null” characters (which can’t be in filenames) to separate files.
find ... -print0: Tellsfindto separate filenames with a\0(null) character instead of a newline.xargs -0: Tellsxargsto read\0(null) separated input.
The “Holy Trinity” Command:
find . -type f -name "*.py" -print0 | xargs -0 grep -n "API_KEY"

This command will never fail, even with weird filenames. This is the pattern you should memorize.
You Are Now a Linux Search Master
You have just read a guide on three commands. But you now have a skill that separates Linux beginners from Linux professionals.
Let’s recap your new toolkit:
locate(The Librarian): Your first choice. Blindingly fast, uses an index. Perfect for finding files by name when you know they’re not brand new. Remembersudo updatedb.find(The Bloodhound): Your power tool. Slower, but 100% accurate. Can search by any criteria:name,type,size,time,user,perm. Its power is amplified by actions like-deleteand-exec ... +.grep(The X-Ray): Your content scanner. Searches inside files for text patterns. Its power is amplified by pipes (|), recursive search (-r), and Regular Expressions (-E).find | xargs | grep(The Holy Trinity): The ultimate combination.findselects the exact files you care about, andgrep(withxargsas the glue) searches only inside those files.
You’re no longer fumbling in the dark. You have a flashlight, a map, and an x-ray machine. Go find what you’re looking for.








