- How can I search a file by its name and partial path?
- 5 Answers 5
- command to find files by searching only part of their names?
- 6 Answers 6
- Finding Files with bat Anywhere
- Broadening or Narrowing Your Search, Based on Name
- Matching Only Files
- Getting Detailed Output
- Further Reading
- Why Learn Find
- Syntax and Patterns
- Find by Name
- Find by Path
- Find File / Directory Only
- Regex
- Find by Size
- Execute Command Line
- Conclusion
- Resources
How can I search a file by its name and partial path?
Often I have a file name and it’s partial path, e.g. «content/docs/file.xml». Is there a simple way to search for that file, without manually cutting into parts its name to provide directory name and file name separately? It’d be great if find worked in that way, so I could run find content/docs/file.xml , but unfortunately it doesn’t.
thanks, @Bob, it’s really worked for me. Btw, interesting, that if I’m adding a slash after an asterisk: «find -path */content/docs/file.xml», it doesn’t work. Thanks a lot anyway.
The reason the slash after asterisk didn’t work is probably because the asterisk should have been escaped, see the edit to my answer.
5 Answers 5
Pass in a * wildcard to indicate a match for anything. You also need to escape the * s, e.g.:
find . -path \*content/docs/file.xml
or enclose the pattern in quotes, e.g.:
find . -path "*content/docs/file.xml"
As the man page describes it:
$ find . -name *.c -print
find: paths must precede expression
This happens because *.c has been expanded by the shell resulting in find actually receiving a command line like this:
find . -name bigram.c code.c frcode.c locate.c -print
That command is of course not going to work. Instead of doing things this way, you should enclose the pattern in quotes or escape the wild‐ card:
$ find . -name \*.c -print
command to find files by searching only part of their names?
Do you want to list matching files in only the current directory? in any directory at or below the current directory? anywhere on the system?
6 Answers 6
Finding Files with bat Anywhere
To find all files anywhere inside /path/to/folder whose names contain bat , you can use:
find /path/to/folder -name '*bat*'
I have quoted the search pattern *bat* because, if the quotes were omitted and files match *bat* in the current directory, the shell will expand *bat* into a list of them and pass that to find . Then find wouldn’t work right. ( \*bat\* and «*bat*» also work.)
To search in the folder you’re currently in (e.g., that you’ve cd ed to), use . , as usual:
To search your whole computer, use / . To search your home directory, use ~ , or the full name of your home directory. (The shell expands ~ to your home directory’s fully qualified path.)
Broadening or Narrowing Your Search, Based on Name
If you want to search case-insensitively, so files containing BAT , bAt , and so forth are matched, use the -iname test instead of the -name test:
find /path/to/folder -iname '*bat*'
I’ve noticed all your files end in .c . If you only want to find files like that, use:
find /path/to/folder -name '*bat*.c'
I noticed all your filenames have bat either at the very beginning or the very end of the part preceding the .c suffix. If you want to avoid matching files like embattled.c , you could use:
find /path/to/folder -name '*bat.c' -o -name 'bat*.c'
Matching Only Files
To find only regular files—and not folders, symbolic links, and special device nodes—you can use -type f . This is frequently recommended and sometimes quite appropriate. but often not what you really want, especially if you’re running find for the purpose of examining the output yourself. If you had a symbolic link that matched your search, wouldn’t you want to know about it?
If you want to find both regular files and symbolic links, you can use:
find /path/to/folder -name '*bat*' \( -type f -o -type l \)
That uses the -o operator and also parentheses for grouping (which must be quoted so the shell does not treat them specially; otherwise you’ll get a syntax error).
But suppose you only want to see symbolic links that ultimately point to a regular file (and not symbolic links to directories, device nodes, etc.). That’s actually even simpler: use -xtype instead of -type . Provided you’re not running find with -L flag, -xtype on a symbolic link tests the type of the file the link points to.
find /path/to/folder -name '*bat*' -xtype f
If you have a symlink to another symlink to a file, -xtype f will match it even though its direct target is another symlink rather than a regular file. This is almost always what you want.
Often people think they want -type f , but really they want -xtype f .
Getting Detailed Output
find ‘s default action if you don’t specify one is -print . All the commands given above are equivalent to themselves with -print tacked on at the end.
find is often used to run commands based on the files found—often, commands that make changes. But there are also other actions whose purpose is to display results, besides -print . Of particular interest is -ls :
find /path/to/folder -name '*bat*' -ls
This gives detailed information on each file, in a multi-column format, similar to (though not quite the same as) what you would see by running ls file .
Further Reading
For more information on find and other ways to find files, see:
- The find manual page, accessible online or by running man find in a terminal.
- The GNU findutils reference manual, providing extensive documentation on the find , locate , and xargs utilities.
- FindingFiles in the Ubuntu help wiki, which shows how to use find as well as several other methods.
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
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?»:
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.
To find anything containing the word «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»:
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.
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:
[. ] matches exactly any one character enclosed. In addition, you can give it a range like [a-z] , [A-Z] , or 6 . 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):
Adding ^ as first character inside [. ] negates the match inside [] . This means «give me anything EXCEPT modal that ends with 0-9».
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*" \)
- Files that end with email.rb , or
- Files/directories containing «deserializers» in its path and name containing address .
What if we want to search for everything except «address»? Find accepts -not , we can do:
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.
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:
To search all directories containing the word «model» in its path:
Regex
Find also accepts -regex option:
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):
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:
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!