Knowing how to use command line find
will help you search for files and directories quickly. Below are the things that will be covered:
- Why Learn Find
- Syntax and Patterns
- Find by Name
- Find by Path
- Or
- Not
- Find File / Directory Only
- Regex
- Find by Size
- Execute Command Line
- Conclusion
- Resources
Why Learn Find
Why do we need to learn find? Can't we just use tools like ripgrep, ag, or browse through GUI manually?
Here are some advantages find has:
- It is found in most terminal.
- Searching from GUI can be tedious and hard to filter.
- Although ripgrep and ag may have better features than find, they are not universally available. If you work with remote servers, you may find
find
more convenient (pun intended).
You don't have to learn every single feature to be productive, just learn the important ones. I believe Pareto's Principle (80/20 law) applies here: learning 20% of find
's feature should cover 80% of use cases.
Syntax and Patterns
Here is the basic syntax:
find location options
For 95-99% of my usage, I use .
for location (current directory). find
will use current directory as starting path and search recursively. You can use absolute or relative path location, like /Users/iggy/projects/
or ./projects
.
Let's talk about options. There are two important options: -name
and -path
. I think you can get a lot of milage with these two. When describing name and path, you can use basic patterns: [
, ]
, *
, and ?
(there are more patterns). Note that they are not regex. I will explain them later, but just want to keep in mind.
For more information about patterns, check this out.
Find by Name
The -name
option means the last component of pathname. If I want to search all files containing the name "model.rb?":
find . -name "model.rb"
This returns everything that matches "model.rb" exactly. But what if we want to search for "user_model.rb", or "address_model.rb"? To find everything that contains "model.rb", we can use wildcard (*
) pattern to match zero or more characters.
find . -name "*model.rb"
To find anything containing the word "address":
find . -name "*address*"
Note: -name
does not mean file name. It could return a directory name. If I have an address directory (./src/components/address/
), find
it will return this directory too.
Find by Path
Recall that -name
searches for the last component of pathname. To find by anything in the path, we use -path
.
To find all files/directories inside "controllers":
find . -path "*controllers*"
By the way, if you do
find . -path "*controllers"
It returns just the "controllers" directories.
Find treats forward slash (/
) as normal characters. If you are looking specifically for "client/components":
find . -path "*client/components*"
It returns all path containing client followed by components
If you need to search for either "model", "modal", or "modes", you can use ?
to match any one character.
find . -path "*mod??"
This will match "mod", followed by any two characters (model/modal/modes). Remember, ?
is not a regex pattern (in regex ?
means 0 or more). Here it means any one character.
If you just want to search for either model or modal, you can do:
find . -path "*mod[ae]l"
[...]
matches exactly any one character enclosed. In addition, you can give it a range like [a-z]
, [A-Z]
, or [0-9]
. You can also mix the ranges [a-zA-Z0-9]
.
If you have "modalA", "modalB", ... "modalZ" and "modal0", "modal1", ... "modal9" and you want to return only the paths with a-z suffix (you do not want to search for numbers):
find . -path "*modal[^0-9]"
Adding ^
as first character inside [...]
negates the match inside []
. This means "give me anything EXCEPT modal that ends with 0-9".
Or
To search for either A or B, find
has an -or
option:
find . -name "*email*" -or -name "*address*"
This searches for either "email" or "address". You can mix and match any options, like name or path. You can also use parentheses to group the conditions:
find . -name "*email.rb" -or \( -path "*deserializers*" -name "*address*" \)
This searches either:
- Files that end with
email.rb
, or - Files/directories containing "deserializers" in its path and name containing
address
.
Not
What if we want to search for everything except "address"? Find accepts -not
, we can do:
find . -not -name "*address*"
Keep in mind that order matters. -not
must be followed by an option. find . -name -not "*address*"
won't work because -not
is placed after -name
.
We can also use !
instead of -not
.
Find File / Directory Only
So far when we search we have been getting results for either files of directories. What if we we want to search for only files or only directories?
We can filter it with -type
option.
- For directory, w use
-type d
- For file, we use
-type f
There are more file types ("block special", "character special", "symbolic link", "FIFO", "socket"), but I think files and directories are the two that are used most.
To find all files containing "model" in path, we do:
find . -type f -path "*model*"
To search all directories containing the word "model" in its path:
find . -type d -path *model*
Regex
Find also accepts -regex
option:
find . -regex ".*address.rb"
Note that when we're using regex, find
matches against the entire relative path. What this means is, find returns matches with relative path (./
), for example:
./src/client/whatever.js
./src/client/frontend/
./server.js
Keep this pattern in mind when you are describing your regex.
Find by Size
You can find files based on their file size (rounded up 512b):
find . -size +1M
Finds files 1 Megabyte or greater.
Here are different file size options that find
accepts:
k kilobytes (1024 bytes)
M megabytes (1024 kilobytes)
G gigabytes (1024 megabytes)
T terabytes (1024 gigabytes)
P petabytes (1024 terabytes)
Execute Command Line
Sometimes it is not enough to just find files. Sometimes we need to execute command line commands into our find results. Find has -exec
command for that. Let's look at some examples:
find . -name "*model.rb" -exec cat {} ";"
find . -type d -path "*model*" -exec ls -l {} ";"
You can also chain multiple execs together
find . -name "*model.rb" -exec grep -q to_hash {} ";" -exec cat {} ";"
This searches for all results containing "model.rb" in their path's last component, run grep quietly (-q
) to look for lines containing "to_hash" string, then cat
these files.
-exec
is a useful feature to learn. To learn more about -exec
, here are some resources to get started:
- Linux shell, how to use the exec option in find with examples
- find -exec with multiple commands
- Understanding the -exec option of
find
Conclusion
I think this is a good place to stop. This knowledge should be sufficient to get you started and be productive. I cannot recommend man find
enough. There are many more useful options that I didn't mention here. Experiment with different command lines, use it everyday, and more importantly, have fun!
Thanks for reading. Happy coding!