$ du -sh .
143M . $ find . -type f | wc -l
10900 $ find . -type d | wc -l
1506
$ du -sh .
143M . $ find . -type f | wc -l
10900 $ find . -type d | wc -l
1506
$ du -sh .
143M . $ find . -type f | wc -l
10900 $ find . -type d | wc -l
1506
# GNU find
find . -name '*.txt' # fd (extension filter)
fd -e txt # ripgrep listing all files, piped to grep
rg --files | grep '\.txt$'
# GNU find
find . -name '*.txt' # fd (extension filter)
fd -e txt # ripgrep listing all files, piped to grep
rg --files | grep '\.txt$'
# GNU find
find . -name '*.txt' # fd (extension filter)
fd -e txt # ripgrep listing all files, piped to grep
rg --files | grep '\.txt$'
Benchmark 1: find . -name '*.txt' Time (mean ± σ): 94.5 ms ± 17.6 ms [User: 21.1 ms, System: 26.1 ms] Range (min … max): 74.8 ms … 139.9 ms 10 runs Benchmark 1: fd -e txt Time (mean ± σ): 39.9 ms ± 8.2 ms [User: 70.7 ms, System: 134.3 ms] Range (min … max): 30.8 ms … 55.3 ms 10 runs Benchmark 1: rg --files | grep '\.txt$' Time (mean ± σ): 117.6 ms ± 12.8 ms [User: 51.5 ms, System: 106.0 ms] Range (min … max): 104.8 ms … 144.0 ms 10 runs
Benchmark 1: find . -name '*.txt' Time (mean ± σ): 94.5 ms ± 17.6 ms [User: 21.1 ms, System: 26.1 ms] Range (min … max): 74.8 ms … 139.9 ms 10 runs Benchmark 1: fd -e txt Time (mean ± σ): 39.9 ms ± 8.2 ms [User: 70.7 ms, System: 134.3 ms] Range (min … max): 30.8 ms … 55.3 ms 10 runs Benchmark 1: rg --files | grep '\.txt$' Time (mean ± σ): 117.6 ms ± 12.8 ms [User: 51.5 ms, System: 106.0 ms] Range (min … max): 104.8 ms … 144.0 ms 10 runs
Benchmark 1: find . -name '*.txt' Time (mean ± σ): 94.5 ms ± 17.6 ms [User: 21.1 ms, System: 26.1 ms] Range (min … max): 74.8 ms … 139.9 ms 10 runs Benchmark 1: fd -e txt Time (mean ± σ): 39.9 ms ± 8.2 ms [User: 70.7 ms, System: 134.3 ms] Range (min … max): 30.8 ms … 55.3 ms 10 runs Benchmark 1: rg --files | grep '\.txt$' Time (mean ± σ): 117.6 ms ± 12.8 ms [User: 51.5 ms, System: 106.0 ms] Range (min … max): 104.8 ms … 144.0 ms 10 runs
$ find . -name '*.txt' | wc -l
3500 $ fd -e txt | wc -l
3000
$ find . -name '*.txt' | wc -l
3500 $ fd -e txt | wc -l
3000
$ find . -name '*.txt' | wc -l
3500 $ fd -e txt | wc -l
3000
$ fd -H -e txt | wc -l
3500
$ fd -H -e txt | wc -l
3500
$ fd -H -e txt | wc -l
3500
# GNU grep (excluding .-weight: 500;">git manually, suppressing permission errors)
grep -r 'test' . --exclude-dir=.-weight: 500;">git 2>/dev/null # ripgrep (auto-excludes .-weight: 500;">git, hidden dirs, binaries)
rg 'test'
# GNU grep (excluding .-weight: 500;">git manually, suppressing permission errors)
grep -r 'test' . --exclude-dir=.-weight: 500;">git 2>/dev/null # ripgrep (auto-excludes .-weight: 500;">git, hidden dirs, binaries)
rg 'test'
# GNU grep (excluding .-weight: 500;">git manually, suppressing permission errors)
grep -r 'test' . --exclude-dir=.-weight: 500;">git 2>/dev/null # ripgrep (auto-excludes .-weight: 500;">git, hidden dirs, binaries)
rg 'test'
Benchmark 1: grep -r 'test' . --exclude-dir=.-weight: 500;">git 2>/dev/null Time (mean ± σ): 302.7 ms ± 50.8 ms [User: 92.8 ms, System: 100.2 ms] Range (min … max): 228.6 ms … 370.8 ms 10 runs Benchmark 1: rg 'test' Time (mean ± σ): 103.2 ms ± 6.8 ms [User: 135.3 ms, System: 177.5 ms] Range (min … max): 95.3 ms … 116.0 ms 10 runs
Benchmark 1: grep -r 'test' . --exclude-dir=.-weight: 500;">git 2>/dev/null Time (mean ± σ): 302.7 ms ± 50.8 ms [User: 92.8 ms, System: 100.2 ms] Range (min … max): 228.6 ms … 370.8 ms 10 runs Benchmark 1: rg 'test' Time (mean ± σ): 103.2 ms ± 6.8 ms [User: 135.3 ms, System: 177.5 ms] Range (min … max): 95.3 ms … 116.0 ms 10 runs
Benchmark 1: grep -r 'test' . --exclude-dir=.-weight: 500;">git 2>/dev/null Time (mean ± σ): 302.7 ms ± 50.8 ms [User: 92.8 ms, System: 100.2 ms] Range (min … max): 228.6 ms … 370.8 ms 10 runs Benchmark 1: rg 'test' Time (mean ± σ): 103.2 ms ± 6.8 ms [User: 135.3 ms, System: 177.5 ms] Range (min … max): 95.3 ms … 116.0 ms 10 runs
$ grep -r 'test' . --exclude-dir=.-weight: 500;">git 2>/dev/null | wc -l
3500 $ rg 'test' | wc -l
3500
$ grep -r 'test' . --exclude-dir=.-weight: 500;">git 2>/dev/null | wc -l
3500 $ rg 'test' | wc -l
3500
$ grep -r 'test' . --exclude-dir=.-weight: 500;">git 2>/dev/null | wc -l
3500 $ rg 'test' | wc -l
3500
# GNU find — no special flags needed, it sees everything
find . -name '.*' -type f # fd — needs -H to include hidden stuff, regex anchor for dot-prefix
fd -H -t f '^\.' # ripgrep — needs --hidden to see dotfiles
rg --hidden --files | grep '^\.'
# GNU find — no special flags needed, it sees everything
find . -name '.*' -type f # fd — needs -H to include hidden stuff, regex anchor for dot-prefix
fd -H -t f '^\.' # ripgrep — needs --hidden to see dotfiles
rg --hidden --files | grep '^\.'
# GNU find — no special flags needed, it sees everything
find . -name '.*' -type f # fd — needs -H to include hidden stuff, regex anchor for dot-prefix
fd -H -t f '^\.' # ripgrep — needs --hidden to see dotfiles
rg --hidden --files | grep '^\.'
Benchmark 1: find . -name '.*' -type f Time (mean ± σ): 104.3 ms ± 20.5 ms [User: 16.0 ms, System: 27.6 ms] Range (min … max): 79.0 ms … 144.5 ms 10 runs Benchmark 1: fd -H -t f '^\.' Time (mean ± σ): 40.5 ms ± 9.2 ms [User: 56.9 ms, System: 106.0 ms] Range (min … max): 26.6 ms … 57.2 ms 10 runs Benchmark 1: rg --hidden --files | grep '^\.' Time (mean ± σ): 118.8 ms ± 9.4 ms [User: 64.4 ms, System: 93.3 ms] Range (min … max): 106.9 ms … 135.0 ms 10 runs
Benchmark 1: find . -name '.*' -type f Time (mean ± σ): 104.3 ms ± 20.5 ms [User: 16.0 ms, System: 27.6 ms] Range (min … max): 79.0 ms … 144.5 ms 10 runs Benchmark 1: fd -H -t f '^\.' Time (mean ± σ): 40.5 ms ± 9.2 ms [User: 56.9 ms, System: 106.0 ms] Range (min … max): 26.6 ms … 57.2 ms 10 runs Benchmark 1: rg --hidden --files | grep '^\.' Time (mean ± σ): 118.8 ms ± 9.4 ms [User: 64.4 ms, System: 93.3 ms] Range (min … max): 106.9 ms … 135.0 ms 10 runs
Benchmark 1: find . -name '.*' -type f Time (mean ± σ): 104.3 ms ± 20.5 ms [User: 16.0 ms, System: 27.6 ms] Range (min … max): 79.0 ms … 144.5 ms 10 runs Benchmark 1: fd -H -t f '^\.' Time (mean ± σ): 40.5 ms ± 9.2 ms [User: 56.9 ms, System: 106.0 ms] Range (min … max): 26.6 ms … 57.2 ms 10 runs Benchmark 1: rg --hidden --files | grep '^\.' Time (mean ± σ): 118.8 ms ± 9.4 ms [User: 64.4 ms, System: 93.3 ms] Range (min … max): 106.9 ms … 135.0 ms 10 runs
$ find . -name '.*' -type f -not -path './.-weight: 500;">git/*' | wc -l
1500 $ fd -H -t f '^\.' | wc -l
1500
$ find . -name '.*' -type f -not -path './.-weight: 500;">git/*' | wc -l
1500 $ fd -H -t f '^\.' | wc -l
1500
$ find . -name '.*' -type f -not -path './.-weight: 500;">git/*' | wc -l
1500 $ fd -H -t f '^\.' | wc -l
1500
# GNU find — manual .-weight: 500;">git exclusion
find . -not -path './.-weight: 500;">git/*' -type f | wc -l # fd — auto-ignores .-weight: 500;">git AND hidden dirs
fd -t f | wc -l # ripgrep — auto-ignores .-weight: 500;">git AND hidden dirs
rg --files | wc -l
# GNU find — manual .-weight: 500;">git exclusion
find . -not -path './.-weight: 500;">git/*' -type f | wc -l # fd — auto-ignores .-weight: 500;">git AND hidden dirs
fd -t f | wc -l # ripgrep — auto-ignores .-weight: 500;">git AND hidden dirs
rg --files | wc -l
# GNU find — manual .-weight: 500;">git exclusion
find . -not -path './.-weight: 500;">git/*' -type f | wc -l # fd — auto-ignores .-weight: 500;">git AND hidden dirs
fd -t f | wc -l # ripgrep — auto-ignores .-weight: 500;">git AND hidden dirs
rg --files | wc -l
Benchmark 1: find . -not -path './.-weight: 500;">git/*' -type f | wc -l Time (mean ± σ): 139.2 ms ± 16.5 ms [User: 27.9 ms, System: 37.6 ms] Range (min … max): 119.4 ms … 173.4 ms 10 runs Benchmark 1: fd -t f | wc -l Time (mean ± σ): 85.4 ms ± 10.2 ms [User: 82.9 ms, System: 118.2 ms] Range (min … max): 69.2 ms … 103.9 ms 10 runs Benchmark 1: rg --files | wc -l Time (mean ± σ): 129.0 ms ± 12.2 ms [User: 60.7 ms, System: 106.3 ms] Range (min … max): 111.4 ms … 151.3 ms 10 runs
Benchmark 1: find . -not -path './.-weight: 500;">git/*' -type f | wc -l Time (mean ± σ): 139.2 ms ± 16.5 ms [User: 27.9 ms, System: 37.6 ms] Range (min … max): 119.4 ms … 173.4 ms 10 runs Benchmark 1: fd -t f | wc -l Time (mean ± σ): 85.4 ms ± 10.2 ms [User: 82.9 ms, System: 118.2 ms] Range (min … max): 69.2 ms … 103.9 ms 10 runs Benchmark 1: rg --files | wc -l Time (mean ± σ): 129.0 ms ± 12.2 ms [User: 60.7 ms, System: 106.3 ms] Range (min … max): 111.4 ms … 151.3 ms 10 runs
Benchmark 1: find . -not -path './.-weight: 500;">git/*' -type f | wc -l Time (mean ± σ): 139.2 ms ± 16.5 ms [User: 27.9 ms, System: 37.6 ms] Range (min … max): 119.4 ms … 173.4 ms 10 runs Benchmark 1: fd -t f | wc -l Time (mean ± σ): 85.4 ms ± 10.2 ms [User: 82.9 ms, System: 118.2 ms] Range (min … max): 69.2 ms … 103.9 ms 10 runs Benchmark 1: rg --files | wc -l Time (mean ± σ): 129.0 ms ± 12.2 ms [User: 60.7 ms, System: 106.3 ms] Range (min … max): 111.4 ms … 151.3 ms 10 runs
$ find . -not -path './.-weight: 500;">git/*' -type f | wc -l
10400 $ fd -t f | wc -l
8400
$ find . -not -path './.-weight: 500;">git/*' -type f | wc -l
10400 $ fd -t f | wc -l
8400
$ find . -not -path './.-weight: 500;">git/*' -type f | wc -l
10400 $ fd -t f | wc -l
8400
$ fd -H -t f | wc -l # with hidden files
10900
$ fd -H -t f | wc -l # with hidden files
10900
$ fd -H -t f | wc -l # with hidden files
10900
$ time find . -path '*deep*' -type f | wc -l
1000
real 0m0.088s $ time fd 'deep' -t f | wc -l
500
real 0m0.094s
$ time find . -path '*deep*' -type f | wc -l
1000
real 0m0.088s $ time fd 'deep' -t f | wc -l
500
real 0m0.094s
$ time find . -path '*deep*' -type f | wc -l
1000
real 0m0.088s $ time fd 'deep' -t f | wc -l
500
real 0m0.094s
$ fd -H 'deep' -t f | wc -l
1000
$ fd -H 'deep' -t f | wc -l
1000
$ fd -H 'deep' -t f | wc -l
1000
$ time find . -type f | wc -l
10900
real 0m0.110s $ time fd -t f | wc -l
8400
real 0m0.116s
$ time find . -type f | wc -l
10900
real 0m0.110s $ time fd -t f | wc -l
8400
real 0m0.116s
$ time find . -type f | wc -l
10900
real 0m0.110s $ time fd -t f | wc -l
8400
real 0m0.116s
rg --files | grep '\.ts$' | xargs rg 'useState'
rg --files | grep '\.ts$' | xargs rg 'useState'
rg --files | grep '\.ts$' | xargs rg 'useState'
# Instead of the pipe combo:
fd -e ts -x rg 'useState' # Or even better:
rg --type ts 'useState'
# Instead of the pipe combo:
fd -e ts -x rg 'useState' # Or even better:
rg --type ts 'useState'
# Instead of the pipe combo:
fd -e ts -x rg 'useState' # Or even better:
rg --type ts 'useState'
# Install the tools
cargo -weight: 500;">install fd-find ripgrep hyperfine
# or: -weight: 500;">apt -weight: 500;">install fd-find ripgrep hyperfine # Clone my setup (approximate recreation):
mkdir -p /tmp/fd-benchmark && cd /tmp/fd-benchmark
for i in $(seq 1 2000); do echo "test content line $i" > "file_${i}.txt"; done
for i in $(seq 1 1000); do echo "hidden content $i" > ".hidden_${i}"; done
mkdir -p .hidden_dir/subdir
for i in $(seq 1 500); do echo "deep hidden $i" > ".hidden_dir/subdir/deep_hidden_${i}.txt"; done
mkdir -p .-weight: 500;">git/objects
for i in $(seq 1 500); do echo "-weight: 500;">git object $i" > ".-weight: 500;">git/objects/obj_${i}"; done
# ... etc. Full generation script in the article source. # Run the benchmarks
hyperfine -w 3 -r 10 "find . -name '*.txt'" "fd -e txt"
hyperfine -w 3 -r 10 "grep -r 'test' . --exclude-dir=.-weight: 500;">git" "rg 'test'"
# Install the tools
cargo -weight: 500;">install fd-find ripgrep hyperfine
# or: -weight: 500;">apt -weight: 500;">install fd-find ripgrep hyperfine # Clone my setup (approximate recreation):
mkdir -p /tmp/fd-benchmark && cd /tmp/fd-benchmark
for i in $(seq 1 2000); do echo "test content line $i" > "file_${i}.txt"; done
for i in $(seq 1 1000); do echo "hidden content $i" > ".hidden_${i}"; done
mkdir -p .hidden_dir/subdir
for i in $(seq 1 500); do echo "deep hidden $i" > ".hidden_dir/subdir/deep_hidden_${i}.txt"; done
mkdir -p .-weight: 500;">git/objects
for i in $(seq 1 500); do echo "-weight: 500;">git object $i" > ".-weight: 500;">git/objects/obj_${i}"; done
# ... etc. Full generation script in the article source. # Run the benchmarks
hyperfine -w 3 -r 10 "find . -name '*.txt'" "fd -e txt"
hyperfine -w 3 -r 10 "grep -r 'test' . --exclude-dir=.-weight: 500;">git" "rg 'test'"
# Install the tools
cargo -weight: 500;">install fd-find ripgrep hyperfine
# or: -weight: 500;">apt -weight: 500;">install fd-find ripgrep hyperfine # Clone my setup (approximate recreation):
mkdir -p /tmp/fd-benchmark && cd /tmp/fd-benchmark
for i in $(seq 1 2000); do echo "test content line $i" > "file_${i}.txt"; done
for i in $(seq 1 1000); do echo "hidden content $i" > ".hidden_${i}"; done
mkdir -p .hidden_dir/subdir
for i in $(seq 1 500); do echo "deep hidden $i" > ".hidden_dir/subdir/deep_hidden_${i}.txt"; done
mkdir -p .-weight: 500;">git/objects
for i in $(seq 1 500); do echo "-weight: 500;">git object $i" > ".-weight: 500;">git/objects/obj_${i}"; done
# ... etc. Full generation script in the article source. # Run the benchmarks
hyperfine -w 3 -r 10 "find . -name '*.txt'" "fd -e txt"
hyperfine -w 3 -r 10 "grep -r 'test' . --exclude-dir=.-weight: 500;">git" "rg 'test'" - 10,900 total files
- Minus 500 .-weight: 500;">git/ objects = 10,400 (what find reports with -not -path)
- Minus 2,000 hidden files/dirs = 8,400 (what fd reports by default) - You're doing any filename-based search (replace find . -name)
- You want smart defaults that skip .-weight: 500;">git, node_modules, hidden dirs
- You want colored, readable output out of the box
- You're typing into an interactive shell and want the shorter syntax - You're writing a portable script that needs to run on any Linux/Unix
- You need the insane flexibility of -exec with complex predicates
- You're on a machine where you can't -weight: 500;">install Rust tooling (embedded, minimal containers)
- You need to search hidden files without remembering a flag - You're searching file contents (this is what it was built for)
- You want .gitignore-aware searching (no more grepping through node_modules)
- You need speed — it's SIMD-accelerated and uses memory mapping - You need POSIX compatibility in scripts
- You're piping from stdin with complex flags (grep -oP with Perl regex is still great)
- rg isn't installed and you can't -weight: 500;">install it - fd is genuinely faster for filename searches — not by 10x like some blog posts claim, but a solid 2-2.5x in real-world use. That's worth the -weight: 500;">install.
- rg vs grep isn't even a contest — 3x faster with better defaults. If you take one thing from this article: alias grep=rg.
- Hidden files will bite you. Both fd and rg skip them by default. This is documented, but the first time you spend 30 minutes wondering why your .env file isn't showing up, you'll remember to add -H / --hidden.
- The ergonomics matter more than the speed. Even if fd were the same speed as find, I'd still use it. fd whatever is just nicer than find . -name '*whatever*'. The speed is gravy.
- hyperfine is a beautiful benchmarking tool. If you're ever debating tool performance, just run the damn benchmark instead of arguing on Hacker News.