How can I grep recursively, but only in files with certain extensions?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Use grep -r --include="*.ext" "pattern" /path to search recursively but only in files matching a specific extension. The --include flag tells grep to examine only files whose names match the given glob pattern, skipping everything else. This is the simplest and most efficient approach for extension-filtered recursive searches.
The --include Flag: The Direct Solution
The -r flag (or --recursive) tells grep to descend into subdirectories. The --include flag filters which files to search. Files that do not match the pattern are never opened, making this faster than grepping everything and filtering afterward.
Combining --include with --exclude-dir
In real projects, you almost always want to skip certain directories like node_modules, .git, or build output:
Without --exclude-dir, grep will descend into node_modules (which can contain hundreds of thousands of files) and produce both slow performance and irrelevant results.
Alternative: Using find with grep
The find + grep combination gives you more control over file selection, especially when you need criteria beyond just the extension (file size, modification time, permissions):
The -exec ... {} + syntax passes multiple filenames to grep at once (like xargs), which is faster than -exec ... {} \; that runs grep once per file.
Handling Filenames with Spaces
If your project contains filenames with spaces or special characters, use null-delimited output:
The -print0 and -0 flags use null bytes as delimiters instead of newlines, correctly handling filenames like my file (copy).md.
Modern Alternatives: ripgrep, ag, and fd
For large codebases, specialized search tools are significantly faster than grep because they respect .gitignore, skip binary files, and use parallel processing:
ripgrep (rg)
ripgrep is typically 2-10x faster than grep on large repositories.
The Silver Searcher (ag)
fd + grep (modern find replacement)
Comparison of Approaches
| Approach | Syntax | Respects .gitignore | Speed on Large Repos | Handles Spaces |
grep -r --include | grep -r --include="*.py" "pat" . | No | Moderate | Yes |
find + grep | find . -name "*.py" -exec grep "pat" {} + | No | Moderate | With -print0 |
ripgrep (rg) | rg -t py "pat" | Yes | Fast | Yes |
ag | ag --python "pat" | Yes | Fast | Yes |
fd + grep | fd -e py | xargs grep "pat" | Yes | Fast | With -print0 |
Useful grep Flags for Code Search
These flags combine well with --include for practical code searching:
Searching for Multiple Patterns
Creating a Shell Alias for Common Searches
If you search code frequently with the same extensions, create aliases:
Common Pitfalls
Forgetting to quote the glob pattern. Without quotes, the shell expands *.py against files in the current directory before grep sees it. Always write --include="*.py" with quotes.
Using -r vs -R. On GNU grep, -r does not follow symlinks while -R does. On macOS/BSD grep, -r follows symlinks. If your project has symlinked directories (common in monorepos), this distinction matters. Use -R explicitly if you want to follow symlinks.
Mixing up --include and --exclude order. grep processes --include and --exclude in the order given. If you write --exclude="*.test.ts" --include="*.ts", the include re-adds what the exclude removed. Put --include first, then --exclude.
Not excluding build directories. Searching without --exclude-dir in a JavaScript project scans node_modules, which can contain 50,000+ files. Always exclude node_modules, .git, build, dist, and other generated directories.
Expecting recursive search by default. Plain grep "pattern" . does not recurse. You must pass -r or -R. Omitting the flag searches only the specified path (and fails on directories with "Is a directory" error).
macOS grep limitations. macOS ships BSD grep, which lacks -P (Perl regex). For advanced regex patterns on macOS, install GNU grep via brew install grep (available as ggrep) or use ripgrep.
Summary
grep -r --include="*.ext" "pattern" . is the standard way to search recursively within specific file types. Combine it with --exclude-dir to skip generated directories, and add -n, -l, or -c flags depending on whether you need line numbers, filenames only, or match counts. For large codebases, consider ripgrep (rg) as a faster drop-in replacement that automatically respects .gitignore and skips binary files. Create shell aliases for your most common search patterns to save time on repeated lookups.
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.