Glob Patterns: The Invisible Abstraction Everyone Uses But Nobody Learns
You use glob patterns every day in .gitignore, package.json, and shell commands - but when did you actually learn glob syntax? Most developers never do.
- tags
- #Shell #Unix #Patterns #Glob #Regex #Cli #Developer-Tools #Terminal #Bash #Zsh #Gitignore #File-Matching #Pattern-Matching
- categories
- Developer-Tools Tutorials
- published
- reading time
- 7 minutes
📚 Series: Glob Patterns
- Glob Patterns: The Invisible Abstraction Everyone Uses But Nobody Learns (current)
- Glob Patterns: Complete Syntax Reference with Examples
You write .gitignore patterns. You use *.js in shell commands. You configure "files": ["dist/**/*"] in package.json.
But when did you actually learn glob syntax?
Most developers never do. They learn regex (for text matching), then encounter glob patterns in the wild and assume “it’s probably like regex.” They copy patterns from Stack Overflow, adjust until it works, and move on.
Glob is the invisible abstraction. It’s everywhere, but nobody teaches it explicitly.
The History: Glob Came First
Glob patterns appeared in Unix v1 (1971) for filename matching in the shell. Simple wildcards: *, ?, [...].
Regex came later - theoretically in 1968, but practically in the ed editor (1973) and grep (1974). More powerful, more complex, designed for text processing not filenames.
For decades, developers learned in this order:
- Shell glob patterns (
ls *.txt) - Later: Regex for text processing (
grep '^[A-Z]')
They were distinct tools for distinct jobs.
The Shift: Regex Became Default
Somewhere around the 2000s-2010s, this reversed:
Why developers learn regex first now:
- Web forms - Email validation, password rules (regex)
- Text editors - VSCode search, find-and-replace (regex)
- Programming languages - Every language has regex built-in
- Online tutorials - Regex has more visibility, more teaching resources
Why glob faded into the background:
- IDEs replaced shell workflows - File trees instead of
ls, fuzzy search instead of glob patterns - Tools abstract it away -
cargo testfinds files, no glob needed - Language-specific tooling - npm, pytest, go handles file discovery
- Glob became invisible - Present in configs, but not explicitly taught
The Problem: Glob By Copy-Paste
Here’s how most developers encounter glob today:
Scenario 1: .gitignore
# What does this actually mean?
*.log
build/
**/node_modules
dist/**/*.map
You copy this from a template. It works. You never learn the rules.
Then it breaks:
- Why doesn’t
*.logignorelogs/debug.log? (because*doesn’t cross directory boundaries) - Why does
node_modules/ignoresrc/vendor/node_modules/? (because trailing/means “directory anywhere”) - What’s the difference between
**/fooandfoo/**? (you have no idea)
Scenario 2: Shell wildcards
| |
You’ve seen * before. You guess ** means “recursive.” It works. You move on.
Until it doesn’t:
| |
Scenario 3: Build configs
| |
You copy this from documentation. Adjust the paths. Ship it. Never learn what ! actually does.
Then you need to modify it:
- How do I exclude multiple patterns?
- Can I use
{js,ts}here? - Why isn’t
[!.]*.jsworking?
You’re stuck copy-pasting variations until something works.
What You’re Actually Using
Glob patterns have specific rules, distinct from regex:
| Pattern | Meaning | Example |
|---|---|---|
* | Match any characters (except /) | *.js matches file.js, not src/file.js |
** | Match directories recursively | src/**/*.js matches src/a/b/c/file.js |
? | Match exactly one character | file?.js matches file1.js, fileA.js |
[abc] | Match one character from set | file[0-9].js matches file5.js |
[!abc] | Match one character NOT in set | [!.]*.js matches file.js, not .hidden.js |
{a,b} | Match alternatives (brace expansion) | *.{js,ts} matches both .js and .ts files |
Not regex:
- No
^or$anchors - No
+or*quantifiers (glob*is different) - No
\dor\wcharacter classes - No capture groups or backreferences
Where Glob Lives Today
You use glob syntax in:
.gitignoreand.dockerignore- Shell wildcards (
ls,rm,cp) rsync --excludepatterns- Build tool configs (webpack, vite, rollup)
- Test frameworks (pytest, jest)
package.json“files” field.npmignore,.eslintignore- Makefile targets
fdandrgfile filtering- GitHub Actions
pathsfilters
Glob is invisible infrastructure. You interact with it daily without realizing it.
Common Mistakes (Because Nobody Teaches This)
Mistake 1: Using regex syntax in glob
| |
Mistake 2: Expecting * to be recursive
# Only ignores *.log in root directory
*.log
# Ignores *.log in all subdirectories
**/*.log
# Ignores *.log everywhere (root + subdirs)
*.log
**/*.log
Mistake 3: Misunderstanding trailing /
# Matches file OR directory named "build"
build
# Only matches directory named "build"
build/
Mistake 4: Forgetting shell-specific behavior
| |
Mistake 5: Not knowing brace expansion
| |
Why This Matters
1. You waste time debugging patterns
Copying .gitignore patterns without understanding leads to:
- “Why isn’t
*.logignoringlogs/debug.log?” (because*doesn’t match/) - “Why does
build/ignoredist/build/too?” (trailing/has meaning)
2. You miss powerful features
Not knowing glob syntax means missing:
**for recursive matching{a,b}brace expansion for multiple extensions[!...]negation for “everything except”
3. You can’t transfer knowledge
Glob appears in so many tools. Learn it once, use it everywhere:
- Same syntax in
.gitignore,rsync, shell commands, build tools - Transferable knowledge across the entire Unix ecosystem
The Case for Explicit Teaching
Glob deserves explicit instruction because:
- It’s fundamental - Older than regex, foundational to Unix
- It’s everywhere - More daily usage than regex for most developers
- It’s distinct - Not a subset of regex, has its own rules
- It’s practical - Immediate utility in shell, git, configs
- It’s invisible - Currently learned by accident, poorly
The current state: Developers learn regex explicitly (courses, tutorials, practice sites), then encounter glob by accident and guess the rules.
The better state: Teach glob as a first-class pattern system with clear rules, examples, and practice.
What’s Next
This is the start of a series on glob patterns:
- Part 1: (this post) Why glob is invisible and why it matters
- Part 2: Complete glob syntax reference with examples
- Part 3: Common glob patterns for
.gitignore, shell, and build tools - Part 4: Glob vs regex - when to use each
Start Learning Glob Today
Here’s a practical path to learn glob explicitly:
1. Learn the core patterns (5 minutes)
Practice in your shell right now:
| |
2. Fix your .gitignore (10 minutes)
Open your .gitignore and understand every line:
# What these actually mean:
*.log # All .log files in root only
**/*.log # All .log files in any subdirectory
logs/ # Directory named "logs" anywhere
/logs/ # Only logs/ in root directory
*.log # Ignore pattern
!important.log # But keep this one (negation)
3. Practice with real scenarios
Try these exercises:
| |
4. Build intuition through experimentation
Create a test directory and try patterns:
| |
Try This
Next time you write a .gitignore pattern or use * in the shell, pause and ask:
- Do I understand why this pattern works?
- Could I write this from scratch without copying?
- What would happen if I changed
*to**or added[...]?
If you can’t answer confidently, you’re using glob by copy-paste. And you’re not alone - but now you know how to change that.
What’s Next
This is Part 1 of a series on glob patterns. Coming soon:
- Part 2: Complete glob syntax reference with edge cases
- Part 3: Mastering .gitignore patterns
- Part 4: Glob vs regex: when to use each
- Part 5: Advanced glob: negation, ranges, and tool-specific extensions
📚 Series: Glob Patterns
- Glob Patterns: The Invisible Abstraction Everyone Uses But Nobody Learns (current)
- Glob Patterns: Complete Syntax Reference with Examples