Skip to content

fd — Fast Find Alternative Primer

Why This Matters

find is powerful but its syntax is archaic and it is slow on large directory trees. fd is a modern replacement: faster (parallel traversal), friendlier syntax, respects .gitignore by default, and produces colorized output. For DevOps work — locating configs, cleaning temp files, executing batch operations — fd eliminates the friction of find.

Name origin: fd is written in Rust by David Peter (sharkdp), first released in 2017. The name is short for "find" — a deliberate abbreviation. The same author also created bat (cat replacement) and hyperfine (benchmarking tool).

Under the hood: fd is typically 5-10x faster than find on large directory trees because it uses parallel directory traversal and avoids searching inside .git/ and other ignored directories. On a repo with 100,000 files, the difference is seconds vs. milliseconds.

Core Concepts

# Find files matching a pattern (substring match, case-insensitive by default)
fd nginx

# Find in a specific directory
fd config /etc/

# Find with full path matching
fd --full-path '/etc/.*\.conf$'

fd searches recursively from the current directory by default. Unlike find, you do not need . or -name.

Regex vs Glob

# Default: regex pattern
fd '^test.*\.py$'

# Glob mode
fd -g '*.yaml'

# Fixed string (no regex interpretation)
fd -F 'config.yaml'

Type Filters

# Files only
fd -t f "log"

# Directories only
fd -t d "config"

# Symlinks only
fd -t l

# Executable files only
fd -t x
Short Long Matches
-t f --type file Regular files
-t d --type directory Directories
-t l --type symlink Symbolic links
-t x --type executable Executable files
-t e --type empty Empty files/dirs

Extension Filter

# Find by extension (faster than regex for simple cases)
fd -e yaml
fd -e py
fd -e tf

# Combine extensions
fd -e yaml -e yml

Hidden Files and Ignore Rules

By default, fd skips hidden files (dotfiles) and respects .gitignore:

# Include hidden files
fd --hidden "bashrc"

# Include .gitignore'd files
fd --no-ignore "node_modules"

# Include both hidden and ignored
fd --hidden --no-ignore "secret"

# Only search files tracked by git
fd --no-ignore-vcs

Depth and Excludes

# Maximum depth
fd -d 2 "README"

# Exclude directories or patterns
fd -E node_modules -E .git -E '*.bak' "config"

Executing Commands

fd can run commands on each match, replacing find -exec:

# Run a command on each result
fd -e py --exec python3 -m py_compile {}

# Batch execution (single process, all matches as args)
fd -e sh --exec-batch shellcheck {}

Placeholder tokens: {} (full path), {/} (basename), {//} (parent dir), {.} (no extension), {/.} (basename, no extension).

Remember: fd placeholder mnemonic: {} = full path (curly = complete), {/} = basename (slash strips the path), {.} = no extension (dot removes the dot-extension). The slashes and dots in the placeholders hint at what they strip.

exec vs exec-batch

--exec runs the command once per match (parallel). --exec-batch runs it once with all matches as arguments (like xargs).

Size and Time Filters

# Files larger than 10MB
fd -t f --size +10m

# Modified in the last 24 hours
fd -t f --changed-within 24h

# Modified more than 7 days ago
fd -t f --changed-before 7d

Practical DevOps Patterns

# Find all Dockerfiles
fd -g 'Dockerfile*'

# Clean up old temp files
fd -e tmp -e bak --changed-before 30d --exec rm {}

# Verify all shell scripts pass syntax check
fd -e sh --exec-batch bash -n

# Find large log files consuming disk
fd -e log -t f --size +100m --exec ls -lh {}

# Null-separated output (safe for xargs)
fd -0 -e py | xargs -0 ruff check

Gotcha: fd is case-insensitive by default (unlike find). If you search fd README, it also matches readme and Readme. Use -s (or --case-sensitive) when you need exact case matching. This is the opposite default from find -name, which is case-sensitive.

fd vs find

Task find fd
Find by name find . -name '*.yaml' fd -e yaml
Type filter find . -type f fd -t f
Execute find . -exec cmd {} \; fd --exec cmd {}
Exclude dir find . -not -path '*/node_modules/*' fd -E node_modules
Speed Single-threaded Multi-threaded
.gitignore Not aware Respected by default

Configuration

Create a ~/.fdignore or project .fdignore (same syntax as .gitignore):

node_modules/
.terraform/
__pycache__/

fd also reads .ignore files (shared with rg).

One-liner: fd -e yaml -e yml --exec-batch yamllint — find all YAML files and lint them in a single invocation. The --exec-batch flag passes all matches as arguments to one process, like xargs but without the pipe.

Interview tip: "How would you find all log files over 100MB modified in the last week?" Answer: fd -e log -t f --size +100m --changed-within 7d. This demonstrates modern CLI fluency. The find equivalent is find . -name '*.log' -type f -size +100M -mtime -7 — functional but less readable.


Wiki Navigation