Commandfind

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

Commandfind
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 *.log

    Unquoted, 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" -delete

    Deleting 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.

Related in Commands