Skip to content

Regex vs Glob Patterns: When to Use Each

Regex vs glob patterns — why .gitignore and build tools use glob, why regex is more powerful, and when to reach for each one.

Try it now: Glob Pattern Tester Test glob and .gitignore patterns against a list of paths and see exactly which rule matched each one — including which negation won.

Regex and Glob Solve Different Problems

The regex vs glob question comes up constantly and gets answered badly just as often, usually with “regex is more powerful, so use that.” It's true that regex is more powerful in the general sense — but power isn't the axis that matters here. A glob pattern isn't a weaker regex; it's a different, much smaller language, built for exactly one job: describing which file paths match and which don't. Regex is a general-purpose text-matching language with no built-in idea of what a path even is. Reach for the wrong one and you either fight a tool that can't express what you mean, or you write something ten times longer than it needed to be to do a job a simpler tool already does well.

What a Glob Pattern Can Do

Glob syntax is deliberately small, and that's the whole point of it. Four tokens cover almost every real-world case:

  • * — matches anything except a path separator. *.ts matches every .ts file in one directory, not in its subdirectories.
  • **— the globstar, matches across directory boundaries in implementations that support it (Bash 4's globstar option, Node's fast-glob, .gitignore, most build tool configs). src/**/*.ts reaches every .ts file at any depth under src/.
  • ? — matches exactly one character. file?.txt matches file1.txt but not file10.txt.
  • [abc] — a character class, matching any single character listed (or a range, like [a-z]).

That's the entire language. There's no alternation, no capture groups, no lookaheads or lookbehinds, and no quantifiers like {2,4}— a glob can't say “two to four digits” because it was never meant to describe the shape of arbitrary text, only which paths a rule selects. Some implementations bolt on brace expansion ({js,ts}) or negation (a leading !, as in .gitignore), but the core stays small on purpose — small enough that most developers can hold the entire syntax in their head, which is exactly why file-matching tools standardized on it instead of regex.

What Regex Can Do That Glob Can't

Regular expressions are a genuinely general-purpose pattern language for text, and the feature list is long for a reason: capture groups to pull substrings out of a match, alternation (|) to match one of several alternatives, quantifiers ({2,4}, +, *, ?) to describe repetition precisely, lookaheads and lookbehinds to assert context without consuming it, and anchors (^, $, \b) to pin a match to a position. All of that machinery exists because regex's job is validating and extracting structure from arbitrary strings — not files specifically, any string.

What regex conspicuously lacks is any built-in concept of a path separator or a directory boundary. To a regex engine, /is just another character; it has no special meaning unless you write it in. That's exactly the concept glob was built around — * stopping at a /is glob's entire reason for existing as a separate language rather than a regex shorthand. If you want a regex to behave the way *does in a glob, you have to spell out “anything except a slash” yourself, every time.

The Same Intent, Two Ways

Take a concrete, common goal: match every .ts file under src/, at any depth, while excluding anything inside node_modules. As glob patterns — the kind you'd put in a .gitignore, a bundler's include/exclude list, or an ESLint ignorePatternsarray — it's two short lines:

glob — the file-selection rule
src/**/*.ts
!**/node_modules/**

The equivalent, expressed as a single regex matched against a full path string, looks like this:

regex — the same intent
^(?!.*\/node_modules\/)src\/(?:[^\/]+\/)*[^\/]+\.ts$

Both do roughly the same job on roughly the same inputs, but the regex needs a negative lookahead just to rule out node_modules, a repeated non-capturing group to stand in for “any number of directories,” and an explicit [^\/]+ everywhere glob gets for free with *. It's also more fragile: it assumes forward slashes, breaks silently on a path that starts differently than expected, and reads as a small puzzle rather than a rule anyone skimming a config file can verify at a glance. That fragility and verbosity — not a lack of imagination from tool authors — is exactly why .gitignore, package.json's files field, ESLint's ignorePatterns, and shell brace/glob expansion all standardized on glob instead of regex for this exact job.

Regex vs Glob at a Glance

AxisGlobRegex
Designed forSelecting file and directory pathsMatching, validating and extracting arbitrary text
ExpressivenessSmall, fixed set of tokensFull grammar — groups, alternation, quantifiers, lookaround
Path-separator awarenessBuilt in — * stops at /, ** crosses itNone — / is just another character unless you say otherwise
Common tools that use it.gitignore, package.json files field, ESLint ignorePatterns, shell globbingValidation, string search-and-replace, log parsing, form fields
Learning curveMinutes — four tokens to rememberHours to days for full fluency, including flavor differences
Partial / substring matchingNo — a glob matches the whole pathYes — a regex can match, and capture, a substring anywhere in the text

Where Each Is Actually Used in Practice

Glob shows up almost exclusively where the question is “which files does this rule apply to.” A .gitignore file is glob patterns from top to bottom. A bundler's include/exclude config, a tsconfig.json's include array, the arguments a CLI accepts for a batch of files (rm -rf dist/**/*.log), and a linter's ignore list are all the same job in different clothing: pick a set of paths out of a filesystem tree, without writing code.

Regex takes over the moment the question stops being about paths. Validating that a string looks like an email address or a phone number, extracting a version number out of a changelog line, rewriting every occurrence of one identifier with another across a file, splitting a log line into its timestamp and message — none of those are about a directory tree, and glob has no tools for any of them because it was never meant to. GenKitLab's regex guide covers that side of the line in full, including flag differences and named capture groups.

Which Should You Use? A Decision Framework

The two questions collapse into one test, and it's reliable enough to apply without thinking twice:

  • “Does this path match this file-selection pattern?” — reach for glob. That covers .gitignore rules, build-tool includes and excludes, and any CLI argument that names a group of files.
  • “Does this text follow a general structural pattern, or do I need to extract or transform part of it?” — reach for regex. That covers validation, parsing, and search-and-replace across arbitrary text, files included but not limited to it.

The two aren't competitors so much as neighbors that occasionally get confused for each other because both use * and both look like “a pattern.” Test glob and .gitignore patterns directly against a list of paths — including which negation rule actually won, which is the part people get wrong by hand — with GenKitLab's Glob Pattern Tester. If the pattern you're staring at needs a capture group or a quantifier instead, it was never a glob problem to begin with — the regex cheat sheet and GenKitLab's Regex Tester are the right tools for that job instead.

Frequently asked questions

What is a glob pattern, exactly?

A glob pattern is a small, fixed set of wildcard tokens — * for anything except a path separator, ** for anything across directories, ? for a single character, and [abc] for a character class — used to select file and directory paths. It has no alternation, capture groups, lookaheads or quantifiers; it was designed to be small enough to read at a glance.

Is glob just a simpler version of regex?

No — glob is a different, purpose-built language rather than a weaker regex. Its defining feature, a path separator that * doesn't cross, has no equivalent concept in regex at all. Regex can be made to imitate a glob pattern, but only by spelling out rules glob gets for free.

Can regex do everything glob can do?

In terms of raw matching power, yes — anything a glob pattern selects, an equivalent regex can also select. But the regex version is longer, more fragile, and harder to read at a glance, which is exactly why file-matching tools like .gitignore standardized on glob instead of asking every contributor to write regex.

What is gitignore pattern syntax based on?

It's glob syntax, extended with a leading ! for negation (un-ignoring a path an earlier rule ignored) and directory-only matching when a pattern ends in a trailing slash. Everything else — *, **, ?, and character classes — is standard glob.

Does glob support case-insensitive or substring matching?

No to both, by default. A glob pattern matches an entire path, not a substring within it, and case sensitivity depends on the underlying filesystem rather than the glob syntax itself. Regex supports both natively, with an i flag for case-insensitivity and no requirement to match the whole string.

When should I use regex for file matching instead of glob?

Almost never for simple inclusion/exclusion rules — but if you need to extract part of a filename (like a version number embedded in it) or apply a rule based on content rather than path shape, that's a text-processing problem regex is built for and glob isn't.

Why doesn't glob support quantifiers like {2,4}?

Because glob was never meant to describe the shape of arbitrary text, only which paths match a selection rule. Brace expansion in glob (like {js,ts}) exists in many implementations, but it means "one of these literal alternatives," not a regex-style repeat count — that distinction is a common source of confusion for people coming from regex.

Last updated