I keep forgetting the exact flags. Every time I need to find something on disk, I end up googling the same three patterns. So here’s the cheatsheet I wish I had pinned to my terminal. Heavy on tables and code blocks, light on prose. Organized by what you’re trying to do, not which tool you happen to know.
Platform bias: macOS daily driver, Emacs editor, but cross-platform notes where they matter.
Quick decision tree
| I want to… | Use this | Why |
|---|---|---|
| Find a file by name | fd (or find) |
Fast, respects .gitignore |
| Find a file by name, fuzzy | fzf |
Interactive, forgiving |
| Find a file by Spotlight metadata | mdfind |
Instant, macOS only |
| Search file contents | rg (ripgrep) |
Fastest grep replacement |
| Search contents, interactively | fzf + rg |
Live preview |
| Search contents in Emacs | consult-ripgrep / counsel-rg |
Never leave the editor |
| Find large files | find + -size |
No modern replacement needed |
| Find recently modified files | fd --changed-within or find -mtime |
Both work fine |
| Search compressed files | zgrep |
Handles .gz transparently |
File search (by name/path)
The classics
| Command | What it does | Platform |
|---|---|---|
find . -name "*.py" |
Recursive file search by name | All (BSD on mac, GNU on linux) |
find . -iname "*.py" |
Case-insensitive | All |
find . -type f -name "*.go" |
Files only (no dirs) | All |
find . -type d -name "vendor" |
Directories only | All |
locate filename |
Search pre-built index (fast) | Linux; macOS needs setup |
mdfind -name "report" |
Spotlight CLI search by filename | macOS only |
mdfind "kMDItemFSName == '*.pdf'" |
Spotlight with metadata query | macOS only |
Modern replacements
| Command | What it does | Notes |
|---|---|---|
fd pattern |
Find files matching pattern | Respects .gitignore, colored output, smart case |
fd -e py |
Find by extension | Cleaner than -name "*.py" |
fd -H pattern |
Include hidden files | .gitignore still respected |
fd -I pattern |
Don’t respect .gitignore |
Search everything |
fd -t f pattern |
Files only | -t d for dirs, -t l for symlinks |
fd -t f -e go . src/ |
All .go files under src/ |
Dot pattern means “match all” |
fzf |
Interactive fuzzy finder | Pipe anything into it |
fd -t f | fzf |
Fuzzy-find a file | Best of both worlds |
Install: brew install fd fzf
mdfind (Spotlight CLI, macOS only)
Spotlight indexes constantly in the background. mdfind queries that index, which makes it nearly instant for the kinds of searches Spotlight supports.
# Search by filename
mdfind -name "config.yaml"
# Search by content (Spotlight indexed content)
mdfind "database connection"
# Restrict to a directory
mdfind -onlyin ~/projects "TODO"
# Search by file type
mdfind "kMDItemContentType == 'public.python-script'"
# Files modified today
mdfind "kMDItemFSContentChangeDate > \$time.today"
# List all metadata attributes for a file
mdls somefile.py
Gotcha: mdfind doesn’t search inside .gitignore’d dirs or anything Spotlight excludes (check System Settings > Spotlight > Privacy). It also won’t index files in directories like node_modules or .git.
locate (index-based search)
# Linux: usually pre-installed, index updated by cron
locate "*.conf"
# macOS: must build the index first
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist
# Wait for index build, then:
locate "*.conf"
locate is fast but stale. The index updates periodically (daily on most Linux distros). If you need fresh results, use find or fd.
Content search (by what’s inside files)
grep family
| Command | What it does | Notes |
|---|---|---|
grep "pattern" file |
Search one file | Basic regex |
grep -r "pattern" . |
Recursive search | Slow on large trees |
grep -rn "pattern" . |
With line numbers | Almost always want -n |
grep -ri "pattern" . |
Case-insensitive | |
grep -rl "pattern" . |
List matching files only | No content, just paths |
grep -rw "pattern" . |
Whole word match | Won’t match “patterned” |
grep -A3 -B3 "pattern" file |
3 lines context | -C3 for both before/after |
grep -E "foo|bar" |
Extended regex (OR) | Same as egrep |
grep -P "\d{3}-\d{4}" |
Perl regex | GNU only, not on macOS BSD grep |
grep --include="*.py" -r "import" |
Filter by file extension | Works with BSD and GNU |
egrep "pattern1|pattern2" |
Multiple patterns | Shorthand for grep -E |
zgrep "pattern" file.gz |
Search compressed files | Also: zcat, zless |
zgrep -r "ERROR" /var/log/*.gz |
Search compressed logs | Lifesaver for log analysis |
Modern replacements
| Command | What it does | Notes |
|---|---|---|
rg "pattern" |
Recursive content search | Fastest. Respects .gitignore |
rg -i "pattern" |
Case-insensitive | |
rg -w "pattern" |
Whole word | |
rg -l "pattern" |
List files only | |
rg -t py "pattern" |
Search only Python files | Built-in type filters |
rg -T js "pattern" |
Exclude JavaScript files | |
rg -g "*.go" "pattern" |
Glob filter | More flexible than -t |
rg -g "!vendor" "pattern" |
Exclude by glob | Negate with ! |
rg --hidden "pattern" |
Include hidden files | |
rg -c "pattern" |
Count matches per file | |
rg --json "pattern" |
JSON output | Pipe to jq |
rg -U "multi\nline" |
Multiline search | |
rg -o "\d{3}-\d{4}" |
Print only the match | Great for extraction |
rg --stats "TODO" |
Show match statistics | |
ag "pattern" |
The Silver Searcher | Slightly slower than rg, same idea |
Install: brew install ripgrep (for rg), brew install the_silver_searcher (for ag)
When to use which
| Tool | Speed | .gitignore aware |
Regex flavor | Best for |
|---|---|---|---|---|
grep |
Slow | No | BRE/ERE | Single file, quick checks |
rg |
Fastest | Yes | Rust regex (PCRE2 optional) | Daily driver, codebase search |
ag |
Fast | Yes | PCRE | If you already have it |
grep -r |
Slowest | No | BRE/ERE | When nothing else is installed |
zgrep |
N/A | No | BRE | Compressed files |
Practical recipes
Find files
# All Python files modified in the last 24 hours
fd -e py --changed-within 1d
# find equivalent:
find . -name "*.py" -mtime -1
# All Python files modified today (calendar day)
fd -e py --changed-within 1d
# mdfind equivalent (macOS):
mdfind -onlyin . "kMDItemFSName == '*.py' && kMDItemFSContentChangeDate > \$time.today"
# Files larger than 100MB
find . -type f -size +100M
# With human-readable sizes (macOS/BSD):
find . -type f -size +100M -exec ls -lh {} \;
# GNU coreutils version:
gfind . -type f -size +100M -printf '%s %p\n' | sort -rn
# Find and delete all .DS_Store files
fd -H -t f '.DS_Store' -x rm
# find equivalent:
find . -name '.DS_Store' -type f -delete
# Find empty files
find . -type f -empty
# Find empty directories
find . -type d -empty
# Find broken symlinks
find . -type l ! -exec test -e {} \; -print
# Find files NOT matching a pattern
fd -e py --exclude "test_*"
# Find duplicate filenames (different directories)
fd -t f | xargs -I{} basename {} | sort | uniq -d
# Executable files
find . -type f -perm +111
# GNU:
find . -type f -executable
Search content
# Search for "TODO" in all Go files
rg -t go "TODO"
# Search for a string, replace it (preview)
rg "old_name" -l | xargs sed -n 's/old_name/new_name/gp'
# Actually replace:
rg "old_name" -l | xargs sed -i '' 's/old_name/new_name/g' # macOS BSD sed
rg "old_name" -l | xargs sed -i 's/old_name/new_name/g' # GNU sed
# Find all imports of a module
rg "^(import|from)\s+requests" -t py
# Find function definitions
rg "def (test_\w+)" -t py -o --no-filename | sort -u
# Count TODOs per file
rg -c "TODO" --sort path
# Search with context, piped to less
rg -C5 "database" --color=always | less -R
# Search for IP addresses
rg -o "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
# Search only in filenames matching a pattern
rg "error" -g "*.log"
# Files containing BOTH "import" and "class" (two greps)
rg -l "import" | xargs rg -l "class"
Combo patterns
# Fuzzy find a file, open in $EDITOR
fd -t f | fzf --preview 'head -50 {}' | xargs $EDITOR
# Fuzzy search file contents
rg --no-heading --line-number "" | fzf --delimiter : --preview 'bat --color=always {1} --highlight-line {2}'
# Find recently modified files, search their contents
fd --changed-within 2h -e py | xargs rg "def "
# Find large files, sorted
find . -type f -size +10M -exec ls -lhS {} + 2>/dev/null | head -20
# Git-tracked files only, search contents
git ls-files '*.py' | xargs rg "pattern"
Emacs integration
Built-in
| Command | What it does | Notes |
|---|---|---|
M-x grep |
Run grep, results in buffer | Basic, always available |
M-x rgrep |
Recursive grep with prompts | Asks for pattern, file glob, directory |
M-x find-dired |
Run find, results in dired |
Good for file operations after search |
M-x find-name-dired |
Find by name, results in dired | Simpler than find-dired |
M-x find-grep-dired |
Find + grep combo in dired | Files containing a pattern |
M-x occur |
Search current buffer, list matches | M-x multi-occur for multiple buffers |
Ivy/Counsel ecosystem
;; Requirements: counsel, ivy
(use-package counsel
:bind
(("C-c g" . counsel-rg) ; ripgrep from project root
("C-c f" . counsel-fzf) ; fzf integration
("C-c l" . counsel-locate))) ; locate integration
;; counsel-rg is the daily driver:
;; M-x counsel-rg → type pattern → results stream in → C-n/C-p to navigate
;; Prefix with C-u to change starting directory
| Command | What it does |
|---|---|
counsel-rg |
Ripgrep with live results |
counsel-fzf |
Fuzzy file finder |
counsel-locate |
System locate integration |
counsel-git-grep |
Grep only git-tracked files |
Vertico/Consult ecosystem
;; Requirements: consult, vertico, orderless (recommended)
(use-package consult
:bind
(("C-c g" . consult-ripgrep) ; ripgrep with live preview
("C-c f" . consult-find) ; find with live preview
("C-c l" . consult-locate))) ; locate integration
;; consult-ripgrep: type #pattern#filter to narrow results
;; e.g., #TODO#.py → searches "TODO" in files matching ".py"
| Command | What it does |
|---|---|
consult-ripgrep |
Ripgrep with live preview |
consult-grep |
Regular grep with live preview |
consult-find |
Find files with preview |
consult-locate |
Locate with preview |
consult-line |
Search lines in current buffer (like swiper) |
Projectile / project.el
;; Projectile (classic)
(projectile-ripgrep "pattern") ; rg in project
(projectile-grep "pattern") ; grep in project
(projectile-find-file) ; find file in project (C-c p f)
;; project.el (built-in, Emacs 28+)
(project-find-regexp "pattern") ; grep in project (C-x p g)
(project-find-file) ; find file in project (C-x p f)
Helm ecosystem
| Command | What it does |
|---|---|
helm-do-ag |
Silver Searcher with helm |
helm-ag |
ag from project root |
helm-do-grep-ag |
ag with file type selection |
helm-find |
find integration |
helm-locate |
locate integration |
wgrep: edit grep results in-place
This is the killer feature. Search, edit matches in the grep buffer, save back to files.
(use-package wgrep
:config
(setq wgrep-auto-save-buffer t)) ; auto-save after applying changes
;; Workflow:
;; 1. M-x counsel-rg (or consult-ripgrep) → search for pattern
;; 2. C-c C-o → export results to occur/grep buffer (ivy: C-c C-o, consult: embark-export)
;; 3. C-c C-p → enter wgrep mode (editable)
;; 4. Edit the matches directly in the buffer
;; 5. C-c C-c → apply changes to all files
;; 6. C-c C-k → discard changes
This replaces sed for most interactive refactoring. Search for the old name, edit in the buffer, apply. No regex escaping headaches.
My Emacs search setup
The Emacs integration section above covers generic options. Here’s my actual config. Vertico + Orderless + Marginalia + Consult stack, no Ivy/Counsel/Helm.
Completion framework
The foundation. Vertico handles the minibuffer UI, Orderless makes every prompt a fuzzy/regex match, Marginalia annotates candidates with metadata.
(use-package vertico
:init (vertico-mode)
:config (setq vertico-cycle t))
(use-package orderless
:config
(setq completion-styles '(orderless basic)
completion-category-defaults nil
completion-category-overrides '((file (styles partial-completion)))))
(use-package marginalia
:after vertico
:init (marginalia-mode))
orderless is the secret sauce. Type space-separated tokens in any order, and it matches all of them. py init matches __init__.py, init_config.py, anything with both substrings. partial-completion override for files means ~/p/s/f TAB still expands paths.
Consult: the search engine
Every search command gets live preview. The :preview-key 'any setting means candidates preview as you move through them, no extra keypress needed.
(use-package consult
:bind (("C-x b" . consult-buffer) ; buffer switching with preview
("C-s" . consult-line) ; replaces isearch for in-buffer search
("M-g g" . consult-goto-line)
("M-s r" . consult-ripgrep)) ; project-wide ripgrep
:config
(consult-customize
consult-ripgrep consult-git-grep consult-grep
consult-line consult-find
consult-projectile-find-file consult-projectile-recentf
:preview-key 'any)
(setq consult-find-args "find . -not ( -path */.git* -prune )"))
Key rebinds worth noting:
C-s→consult-lineinstead of isearch. Fuzzy, orderless matching on every line in the buffer. I never use isearch directly anymore.M-s r→consult-ripgrepfor project-wide content search. Type#pattern#filterto narrow:#TODO#.pysearches “TODO” only in.pyfiles.C-x b→consult-buffershows buffers, recent files, and bookmarks in one list with live preview.
Consult + Projectile
consult-projectile bridges the two. File finding and recent files get consult’s preview and orderless matching.
(use-package consult-projectile
:after (consult projectile)
:config
(setq consult-projectile-use-projectile-switch-project t))
Projectile config
Projectile handles project detection. When rg is available, it uses rg --files for indexing, which respects .gitignore and is fast.
(use-package projectile
:init
(setq projectile-project-search-path
'(("~/zcode/" . 1) ("~/mcode/" . 1) ("~/ocode/" . 1) ("~/zorg" . 1))
projectile-completion-system 'default
projectile-enable-caching t
projectile-indexing-method 'alien
projectile-sort-order 'recentf)
:config
(projectile-mode 1)
(when (executable-find "rg")
(setq projectile-generic-command "rg --files --color=never --hidden"))
(projectile-discover-projects-in-search-path)
:bind-keymap ("C-c p" . projectile-command-map))
projectile-project-search-path auto-discovers projects under ~/zcode/, ~/mcode/, ~/ocode/, and ~/zorg/. The (. 1) means one level deep.
Search hydra
All search commands behind a single C-c s prefix. Organized by what I’m looking for.
(use-package hydra)
(defhydra hydra-search (:color blue :hint nil)
"
╭─────────────────────────────────────────────────╮
│ Project Search │
├─────────────────────────────────────────────────┤
│ Content: _r_ ripgrep _g_ git-grep _l_ line │
│ Files: _f_ find _F_ recent _d_ dir │
│ Buffers: _b_ all _B_ project │
│ Projects: _p_ switch _P_ find-file │
╰─────────────────────────────────────────────────╯
"
("r" consult-ripgrep "ripgrep in project")
("g" consult-git-grep "git grep (faster)")
("l" consult-line "search current buffer")
("f" consult-projectile-find-file "find file in project")
("F" consult-projectile-recentf "recent files")
("d" consult-find "find in directory")
("b" consult-buffer "switch buffer")
("B" consult-project-buffer "project buffers")
("p" consult-projectile-switch-project "switch project")
("P" (lambda () (interactive)
(consult-projectile-switch-project)
(consult-projectile-find-file)) "project → file")
("q" nil "quit"))
(global-set-key (kbd "C-c s") #'hydra-search/body)
C-c s r for ripgrep, C-c s f for find file, C-c s g for git-grep. Blue hydra means each key exits after action, no sticky menu.
Navigation helpers
Not strictly search, but they close the loop between “found it” and “I’m there.”
(use-package avy
:custom
(avy-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
(avy-timeout-seconds 0.5)
:bind
(("C-'" . avy-goto-char-timer) ; jump to visible text
("C-:" . avy-goto-char-2)
("M-g w" . avy-goto-word-1))
:config
(with-eval-after-load 'isearch
(define-key isearch-mode-map (kbd "C-'") 'avy-isearch))) ; jump during isearch
(use-package expand-region
:bind ("C-=" . er/expand-region)) ; grow selection around search results
C-'(avy-goto-char-timer): type a character or two, then jump labels appear on all visible matches. Home row keys only (asdfghjkl).C-'during isearch: switches from scrolling through matches to avy jumping. Useful when isearch found too many hits and you can see the one you want.C-=(expand-region): after landing on a match, grow the selection semantically (word → expression → statement → block).
Dired as search output
(global-set-key (kbd "C-x C-j") 'dired-jump)
C-x C-j opens dired at the current file’s directory. From there, find-dired and find-name-dired dump find results into a dired buffer for bulk file operations (rename, delete, chmod).
Missing piece: wgrep
I don’t have wgrep in my config yet, but I should. It lets you edit grep/ripgrep results in-place and write changes back to files. The config from the section above works:
(use-package wgrep
:config
(setq wgrep-auto-save-buffer t))
The workflow: consult-ripgrep → embark-export to a grep buffer → C-c C-p to enter wgrep → edit matches → C-c C-c to apply. Replaces sed for most interactive refactoring.
Common usage reference
Quick-reference table
Keybindings for my Vertico/Consult/Projectile setup. Not generic Emacs, these are the bindings from the config above.
| I want to… | Keys | What happens |
|---|---|---|
| Search current buffer | C-s |
consult-line with orderless fuzzy matching |
| Search project contents | M-s r or C-c s r |
consult-ripgrep with live preview |
| Find file in project | C-c p f or C-c s f |
consult-projectile-find-file |
| Find recent file | C-c s F |
consult-projectile-recentf |
| Git grep (faster in git repos) | C-c s g |
consult-git-grep |
| Search and replace across files | M-s r → export → wgrep → edit → apply |
See workflow below |
| Find file by name anywhere | C-c s d |
consult-find in directory |
| Switch buffer | C-x b |
consult-buffer with preview |
| Jump to line | M-g g |
consult-goto-line |
| Open search hydra | C-c s |
hydra-search menu |
| Jump to visible text | C-' |
avy-goto-char-timer |
| Open file manager at current file | C-x C-j |
dired-jump |
| Switch project | C-c s p or C-c p p |
consult-projectile-switch-project |
| Project buffers only | C-c s B |
consult-project-buffer |
Workflow recipes
Rename a variable across a project
M-s r(orC-c s r) → type the old variable name- Results stream in with live preview. Verify the matches are correct.
embark-export(usuallyC-c C-oorEin embark) → exports matches to a grep bufferC-c C-p→ enter wgrep mode. The buffer becomes editable.M-%(query-replace) or manual edit → change the variable name in each matchC-c C-c→ apply all changes to the source filesC-c C-kif you want to discard instead
Find a file I half-remember the name of
C-c s f→ type fragments of the filename, in any order- Orderless matches fuzzy:
conf yamlfindsyaml_config.py,config.yaml, etc. - Live preview shows the file content as you move through candidates
RETopens it
Search then jump to a specific match
C-c s r→ type the search pattern- Results appear, live preview shows surrounding context as you navigate
- Arrow keys or
C-n/C-pto find the right match RETopens the file at the exact line
Switch project and immediately search
C-c s P→ select a project from the list- Immediately opens
consult-projectile-find-filein the new project - Or:
C-c s pto switch project, thenC-c s rto ripgrep in it
GNU coreutils on macOS
macOS ships BSD versions of find, grep, sed, xargs, etc. They differ from GNU versions in small but annoying ways.
Key differences
| Feature | BSD (macOS default) | GNU (Linux / Homebrew) |
|---|---|---|
sed -i |
Requires '' arg: sed -i '' 's/a/b/' |
No arg needed: sed -i 's/a/b/' |
find -printf |
Not supported | Supported |
find -executable |
Not supported | Supported |
find -delete |
Supported (both) | Supported (both) |
grep -P (Perl regex) |
Not supported | Supported |
xargs -d (delimiter) |
Not supported | Supported |
readlink -f |
Not supported | Supported |
date formatting |
Different flags | Different flags |
stat output |
Different format strings | Different format strings |
Install GNU coreutils
brew install coreutils findutils grep gnu-sed gawk
# GNU tools are prefixed with 'g':
# gfind, ggrep, gsed, gawk, gxargs, greadlink, gstat, gdate
#
# If you want them as default (unprefixed), add to PATH:
export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH"
export PATH="/opt/homebrew/opt/findutils/libexec/gnubin:$PATH"
export PATH="/opt/homebrew/opt/grep/libexec/gnubin:$PATH"
export PATH="/opt/homebrew/opt/gnu-sed/libexec/gnubin:$PATH"
My preference: Keep the g prefix. Muscle memory is already on fd and rg for daily work. I only reach for gfind or gsed when I need GNU-specific features like -printf or -P.
Performance comparison
Tested on a ~1M-file monorepo. Numbers are approximate, your mileage will vary.
| Task | Tool | Time | Notes |
|---|---|---|---|
| Find file by name | fd |
~0.3s | Respects .gitignore |
find |
~2.5s | Searches everything | |
mdfind |
~0.05s | Pre-indexed, macOS only | |
locate |
~0.1s | Pre-indexed, may be stale | |
| Content search | rg |
~0.8s | Parallelized, .gitignore aware |
ag |
~1.2s | Good, slightly slower | |
grep -r |
~8s | No parallelism, searches everything | |
git grep |
~0.6s | Fast, git-tracked only |
Why rg and fd are fast
- Parallelism. They use all CPU cores by default.
.gitignoreawareness. Skipnode_modules,__pycache__,.git,vendor, etc. automatically.- Smart defaults. Skip binary files, use memory-mapped I/O.
- Regex engine. Rust’s regex crate compiles to DFA, avoids catastrophic backtracking.
When to NOT use rg/fd
- You need to search inside
.gitignore’d directories: userg -u(or-uufor full unrestricted) - You need Spotlight metadata (file kind, creation date, tags): use
mdfind - You need PCRE2 lookaheads/lookbehinds: use
rg -P(enables PCRE2 engine) - The repo has no
.gitignore:rgstill wins on parallelism, but the gap shrinks
Cross-platform reference
Linux
Most of the above works identically on Linux. Key differences:
| Topic | macOS | Linux |
|---|---|---|
Default grep |
BSD (no -P) |
GNU (has -P) |
Default find |
BSD (no -printf) |
GNU (has -printf) |
Default sed |
BSD (-i '' required) |
GNU (-i without arg) |
| Package manager | brew install |
apt install / dnf install / pacman -S |
locate |
Needs manual setup | Usually pre-installed + cron |
mdfind |
Spotlight CLI | N/A (use locate or recoll) |
Windows
| Unix command | PowerShell equivalent | Notes |
|---|---|---|
find . -name "*.py" |
Get-ChildItem -Recurse -Filter "*.py" |
Alias: gci -R or ls -R |
find . -type f -size +100M |
gci -R | ? { $_.Length -gt 100MB } |
|
grep -r "pattern" . |
Select-String -Recurse -Pattern "pattern" |
Alias: sls |
grep -rl "pattern" . |
sls -Recurse -Pattern "pattern" | select Path -Unique |
|
| N/A | findstr /s /i "pattern" *.* |
CMD legacy, still works |
WSL is the escape hatch. If you’re on Windows and want the Unix tools, wsl gives you a real Linux environment. All the rg, fd, fzf commands work as-is inside WSL.
# Install fd and rg on Windows (without WSL)
winget install sharkdp.fd
winget install BurntSushi.ripgrep.MSVC
scoop install fzf
# They work identically:
fd -e py
rg "pattern" -t py
Install everything
# macOS (Homebrew)
brew install fd ripgrep fzf the_silver_searcher \
coreutils findutils grep gnu-sed
# Ubuntu/Debian
sudo apt install fd-find ripgrep fzf silversearcher-ag
# Note: on Debian/Ubuntu, fd is installed as 'fdfind'
# alias fd=fdfind
# Arch
sudo pacman -S fd ripgrep fzf the_silver_searcher
# Windows (winget)
winget install sharkdp.fd
winget install BurntSushi.ripgrep.MSVC
winget install junegunn.fzf
Quick reference card
The 10 commands I use most, in order:
| # | Command | What | When |
|---|---|---|---|
| 1 | rg "pattern" |
Search file contents | Multiple times daily |
| 2 | fd pattern |
Find files by name | Multiple times daily |
| 3 | fd -e ext |
Find files by extension | Daily |
| 4 | rg -t py "pattern" |
Search in specific file type | Daily |
| 5 | fd | fzf |
Fuzzy find a file | When I half-remember the name |
| 6 | consult-ripgrep |
Search from Emacs | When I’m already in Emacs |
| 7 | rg -l "pattern" |
List matching files | Before bulk operations |
| 8 | find . -size +100M |
Find large files | Disk cleanup |
| 9 | fd -H '.DS_Store' -x rm |
Delete junk files | Occasional cleanup |
| 10 | mdfind -name "file" |
Instant filename search | When I have no idea where it is |
The best search tool is the one you remember the flags for. Pin this somewhere.