Find files by name
Searches every subdirectory for filenames matching a case-insensitive pattern, quoted so the shell hands the wildcard to find untouched.
Last updated
find . -iname "*.log"
How it works
-iname matches case-insensitively — plain -name is case-sensitive, so it would miss "error.LOG" or "Error.Log" while -iname catches all of them with one pattern. The quotes around "*.log" are what make this actually recursive: an UNQUOTED *.log gets expanded by the shell itself, against files in the current directory only, before find ever sees it — so find would receive a list of literal filenames (or nothing, if none exist right there) instead of the pattern to search with.
. as the starting path means "search here and every subdirectory beneath it," which is find's default recursive behavior with no depth limit unless you add one.
Watch out for
- →Quoting the pattern isn't a style choice here — it's the difference between a working recursive search and one that silently searches nothing beyond the current directory, because of how the shell's own globbing runs before find gets a chance to see the raw pattern.
- →-iname is a widely supported extension on both GNU find (Linux) and BSD find (macOS), but it's not part of the strict POSIX find specification — a minimal or embedded find (some containers, busybox) may only support the case-sensitive -name.
- →This starts at . and searches every level below it by default; add -maxdepth 1 to restrict the search to just the current directory, or swap . for a different starting path to search elsewhere.
Part by part
- find .
- Start from the current directory and descend. Every path printed is relative to this starting point.
- -iname
- Match the filename case-insensitively. The case-sensitive form is -name, and choosing between them is the most common decision here.
- "*.log"
- Quoted deliberately. Without quotes the shell expands the glob against the current directory before find ever sees it.
Common mistakes
find . -iname *.logUnquoted, the shell expands the pattern first. If exactly one .log file exists in the current directory the command searches for that literal name; if several exist find reports a syntax error. It appears to work in an empty directory, which is how it survives testing.
find . -iname "*.log"find . -name "*.LOG"Case-sensitive matching misses the mixed-case variants that real systems produce. Use -iname unless you specifically need to distinguish them.
find . -iname "*.log" -deleteDeleting in the same pass as the search gives you no chance to check the list. Run the find alone first, read the output, then add -delete — it is not undoable.
Questions
How do I search only a certain depth?
-maxdepth limits how far down find descends, and -mindepth skips shallow results. Put them before other tests, since find warns when they appear after non-option arguments.
How do I exclude a directory like node_modules?
Prune it: find . -name node_modules -prune -o -iname "*.log" -print. The -o and explicit -print are required, because prune's structure changes how the default print applies.
Why is find slower than my editor's search?
Editors keep an index; find walks the filesystem every time. For repeated searches in a project, a tool like ripgrep or fd is dramatically faster because it skips ignored directories by default.
How do I act on each result?
-exec runs a command per match, ending in \; for one at a time or + to batch them. Piping into xargs -0 with -print0 is the safer route when filenames may contain spaces.