TechEarl

find -regex vs -name: When to Use Regex in find

find -name takes a shell glob and matches the basename; find -regex takes a full regular expression and matches the whole path. That whole-path detail is the number one surprise: -regex '.*\.txt' works but -regex '.txt' matches nothing. The flag reference, -regextype flavors, the GNU vs BSD default-flavor drift, and when -name is the better tool.

Ishan KarunaratneIshan Karunaratne⏱️ 12 min readUpdated
find -name uses shell globs on the basename; find -regex matches a full regular expression against the whole path. The -regextype flavors, the GNU emacs vs BSD basic default drift, and when each one is the right tool.

find . -name '*.txt' and find . -regex '.*\.txt' look like they do the same thing, and most of the time they return the same files. They are not the same tool. -name takes a shell glob and matches the basename. -regex takes a full regular expression and matches the whole path. That whole-path detail is the single biggest surprise: find . -regex '.txt' matches nothing, because the pattern has to match the entire path string, not just the part you care about.

This page is the reference I keep open when a -name expression is turning into a pile of -o clauses and it is time to switch to -regex. It covers the glob-versus-regex split, the -regextype flavors, the GNU-versus-BSD default-flavor drift that quietly breaks scripts moved between machines, and the cases where -name is still the better choice.

Set your values

Try it with your own values

Set your OS and search path. Every find example below updates with your values.

-name uses shell globs

-name matches each file's basename against a shell glob pattern. Glob is the small wildcard language the shell itself uses for filename expansion:

GlobMatches
*Any run of characters (including none)
?Exactly one character
[abc]One character from the set
[a-z]One character from the range
[!abc]One character not in the set

So -name '*.txt' reads as "basename ends with .txt". A few things follow from "glob, basename":

  • The pattern is matched against the basename only. find . -name 'src/*.ts' never matches, because a basename never contains a slash.
  • * in a glob is "any characters". It is not the regex * (which means "zero or more of the previous token"). Different language, same symbol.
  • Always quote the pattern. Unquoted -name *.txt lets the shell expand the glob before find ever sees it.
bash· Linux (GNU)
find :search_path -name '*.txt'

-iname is the case-insensitive variant, matching .txt, .TXT, and .Txt alike.

-regex uses a full regular expression

-regex matches each file against a regular expression, and here is the part that trips everyone up: the regex must match the entire path string that find is currently considering, not the basename, and not a substring.

That means a pattern like .txt fails. find is testing paths such as ./notes/todo.txt, and the regex .txt only matches a three-character string. The pattern has to describe the whole thing:

bash
# WRONG: matches nothing, the regex must match the whole path
find . -regex '.txt'

# RIGHT: .* soaks up the leading directory components
find . -regex '.*\.txt'

.* at the front is doing real work. It matches "any leading path", so .*\.txt reads as "any path that ends in a literal .txt". The \. is an escaped dot (a literal period); a bare . in a regex means "any character".

bash· Linux (GNU)
find :search_path -regextype posix-extended -regex '.*\.txt'

-iregex is the case-insensitive variant of -regex, exactly as -iname is to -name.

Note the PowerShell column. Where-Object { $_.FullName -match '...' } is the closest Windows analogue: PowerShell's -match operator runs a .NET regular expression, and unlike find -regex, it matches a substring by default, so '\.txt$' (anchored at the end) is the natural way to express "ends with .txt".

-regextype: choosing the regex flavor (GNU only)

There is no single "regular expression" syntax. There are flavors, and they disagree about things like whether ( is a literal paren or a group, and how + behaves. GNU find lets you pick the flavor with -regextype, which must appear before the -regex test on the command line:

-regextype valueFlavorNotes
emacsEmacs regexThe GNU find default if you never pass -regextype
posix-basicPOSIX BRE( and + are literals; groups need \( \)
posix-extendedPOSIX ERE( groups, + and ? work unescaped; what most people want
posix-awkawk regexERE-like, with awk's escape rules
posix-egrepegrep regexERE-like, matches old egrep behavior

In practice I pass -regextype posix-extended almost every time I use -regex on Linux. ERE is the flavor that behaves the way a regex written in 2026 expects: (jpe?g|png) works without backslash-escaping every grouping construct.

bash
# emacs default: groups still work, but escape rules differ from ERE
find . -regex '.*\.\(jpe?g\|png\)'

# posix-extended: unescaped groups and alternation, the modern default
find . -regextype posix-extended -regex '.*\.(jpe?g|png)'

BSD find has no -regextype

This is the cross-platform footgun. macOS ships BSD find, and BSD find has no -regextype flag at all. It supports -regex and -iregex, but the flavor is fixed.

Worse, the default flavor differs between the two implementations:

BehaviorGNU find (Linux)BSD find (macOS)
-regex testSupportedSupported
-iregex (case-insensitive)SupportedSupported
-regextype flagSupportedNot supported, errors out
Default regex flavorEmacs regexBasic POSIX (BRE)
Match targetWhole pathWhole path

So a script written and tested on Linux with find . -regextype posix-extended -regex '.*\.(jpg|png)' fails outright on a Mac: BSD find does not recognize -regextype and exits with an error. Strip the -regextype and the alternation (jpg|png) then needs \(jpg\|png\) because BSD's default is basic POSIX, where the parens are literals unless escaped.

The portable approaches, in order of how much I trust them:

  • Install GNU findutils on macOS (brew install findutils) and call gfind explicitly. Now both machines run the same implementation.
  • Avoid -regex in cross-platform scripts. Use -name with a few -o clauses, which behaves identically everywhere.
  • Branch on uname and keep two versions of the expression. Workable, but the version drift is exactly the bug you were trying to avoid.

Worked example: match three extensions

Here is the case that actually pushes me from -name to -regex. I want every JPEG, JPG-spelled-jpeg, and PNG image under a tree. With -name it is three clauses joined by -o (logical OR), and the whole group needs parentheses so the OR does not swallow later tests:

bash· Linux (GNU)
find :search_path -type f \( -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.png' \)

With -regex it is one alternation. The i in -iregex makes it case-insensitive so .JPG matches too:

bash· Linux (GNU)
find :search_path -type f -regextype posix-extended -iregex '.*\.(jpe?g|png)'

The regex form is shorter and reads as a single intent: "files ending in jpg, jpeg, or png". jpe?g collapses jpg and jpeg into one token (the ? makes the e optional). Note the macOS variant drops -regextype posix-extended: BSD find rejects that flag. Whether BSD's default flavor accepts the unescaped (jpe?g|png) depends on the macOS version, which is exactly the kind of uncertainty that makes me reach for the -name form in any script that has to run on both.

For the dedicated extension-matching reference, including the glob-only patterns, see the find files by extension deep dive.

Common find -regex mistakes

1. Forgetting that -regex matches the whole path. find . -regex 'todo.txt' matches nothing. find evaluates paths like ./notes/todo.txt, and the pattern has to describe that entire string. Prefix with .* or anchor against the real path shape.

2. Forgetting the leading .*. Even find . -regex 'todo\.txt' fails for the same reason. Use .*todo\.txt ("any path ending in todo.txt") or .*/todo\.txt if you specifically want it as a basename.

3. Using a regex * where you meant a glob *. In a regex, * means "zero or more of the previous token". A bare -regex '*.txt' is a malformed regex (nothing precedes the *). The "any characters" wildcard in regex is .*.

4. Forgetting to escape the dot. A bare . matches any character, so .*.txt also matches mytxt, aXtxt, and 9_txt. Write .*\.txt so the dot before the extension is literal.

5. Passing -regextype to BSD find. On macOS this is a hard error, not a warning. There is no -regextype on BSD. Either install GNU findutils or drop the flag and accept BSD's fixed flavor.

6. Assuming the default flavor is the same everywhere. GNU defaults to Emacs regex, BSD defaults to basic POSIX. A pattern with unescaped (...) alternation can work on one and fail on the other. Always pass -regextype posix-extended on GNU, and test on the real target platform.

7. Putting -regextype after -regex. GNU find reads the command line left to right; -regextype only affects -regex tests that come after it. Put it before the -regex it should govern.

When NOT to use -regex

-regex is the right tool for alternation, character classes, and full-path matching. For everything else, -name is faster to type, easier to read, and behaves identically on every platform. Reach for -name (or -iname) when:

  • You are matching a single extension or prefix. -name '*.log' is clearer than -regex '.*\.log' and there is no whole-path gotcha to remember.
  • The script has to run on both Linux and macOS. -name has no flavor and no -regextype dependency. -regex carries the GNU-versus-BSD drift with it.
  • You only care about the basename. -name matches the basename for free. With -regex you have to model the leading directories with .* or .*/ yourself.
  • The pattern is simple enough for one or two -o clauses. Two extensions joined by -o is fine. It is when the list hits three or more, or you need a character class, that -regex starts to pay for itself.

-regex earns its place when the matching logic genuinely needs a regular expression: alternation across several patterns, a character class like [0-9], anchoring against directory structure, or "this set of extensions but only under that subtree". Below that bar, -name is the better default.

See also

FAQ

TagsfindregexCLILinuxmacOSBSDShell Scripting
Share
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years across software, Linux systems, DevOps, and infrastructure — and a more recent focus on AI. Currently Chief Technology Officer at a tech startup in the healthcare space.

Keep reading

Related posts

grep -E vs grep -P explained: basic regex (BRE) treats + ? | ( ) { } as literal text, extended regex (ERE) makes them metacharacters, and PCRE adds lookaround and \d. Plus why macOS BSD grep has no -P.

grep Regex: BRE vs ERE vs PCRE Explained

grep has three regex engines and the default one surprises everyone: in basic regex (BRE) the characters + ? | ( ) { } are literal text until you backslash-escape them. -E switches to extended regex (ERE) where they work bare, and -P unlocks Perl-compatible regex with lookaround and \d. The full BRE vs ERE vs PCRE comparison, the same pattern in all three, and why -P does not exist on macOS.

find -exec ... {} + batches arguments into one command (fast). find ... -exec ... {} \; forks per file (slow). xargs adds shell flexibility but needs -0 for safety. The decision matrix and performance comparison.

find -exec vs xargs: Which to Use (and the {} + Trick That Beats Both)

find -exec ... {} + and find -print0 | xargs -0 are roughly equivalent for batch operations on matched files. find -exec ... {} \; forks once per match and is much slower. The decision matrix: when -exec is enough, when xargs adds value, and the safety rules for filenames with spaces, newlines, and quotes.

Use find -size +100M to list files larger than 100 megabytes. Unit suffixes (c/k/M/G), +/- sign convention, combine with sort -rn to surface the biggest files on disk, and BSD vs GNU rendering differences.

How to Find Files Larger Than a Size with find -size

find . -size +100M lists every file larger than 100 megabytes. The unit suffixes (c, k, M, G), the +/- sign convention, how to combine with sort to find the biggest files on disk, the BSD vs GNU divergence for printing sizes, and the wc -c trick for byte-exact thresholds.