How to Use fd Instead of find
Learn to use fd, the modern find alternative: friendlier syntax, smart defaults, .gitignore support, and parallel command execution with --exec.
Before you start
- ▸A terminal and a user account with sudo privileges
- ▸Basic familiarity with navigating the filesystem from the command line
- ▸Internet or repository access to install packages
fd is a fast, user-friendly alternative to find. It offers sensible defaults — case-insensitive matching, automatic .gitignore respect, colored output, and a cleaner syntax — without sacrificing power. If you regularly reach for find and mentally rehearse its argument order each time, fd is worth fifteen minutes of your life.
Installing fd
The binary is called fd on most systems. On Debian and Ubuntu it was historically packaged as fd-find (with the binary named fdfind) to avoid a conflict with an older package; that conflict is largely resolved on modern releases but the package name persists.
Debian / Ubuntu
sudo apt install fd-find
# Symlink so you can type 'fd'
ln -s $(which fdfind) ~/.local/bin/fd
Fedora / RHEL 9+ / Rocky
sudo dnf install fd-find
The binary is fd directly on this family.
Arch Linux
sudo pacman -S fd
Verify the install
fd --version
You should see something like fd 9.x.x. Output will vary.
Basic Pattern Matching
fd takes a search pattern as a regular expression and an optional directory. Without a directory it searches the current working tree.
# Find any file whose name contains 'config'
fd config
# Search only inside /etc
fd config /etc
By default the match is case-insensitive. Use --case-sensitive (-s) when you need exact casing, or --ignore-case (-i) to be explicit. Contrast this with find, which is always case-sensitive unless you use -iname.
# Case-sensitive: matches 'Config' but not 'config'
fd -s Config /etc
Filtering by Type
The -t flag restricts results to a specific entry type. The most useful values are f (file), d (directory), l (symlink), and x (executable).
# Only directories whose name contains 'log'
fd -t d log
# Only executable files named 'python*'
fd -t x python /usr
Filtering by Extension
Use -e instead of crafting a regex like \.rs$. You can stack multiple -e flags.
# All Markdown files
fd -e md
# All C source and header files
fd -e c -e h ~/projects
Hidden Files and .gitignore
fd automatically skips hidden files (dot-files) and anything matched by .gitignore, .fdignore, or .ignore files. This is the right default for most development workflows. Override it when you need to dig deeper.
# Include hidden files
fd -H .bashrc ~
# Include files ignored by .gitignore
fd -I target ~/projects
# Include both hidden AND ignored files
fd -HI node_modules ~/projects
If you maintain a project and want fd to always skip a certain directory, add it to a .fdignore file in that directory — same syntax as .gitignore.
Limiting Search Depth
Deep trees can produce overwhelming output. --max-depth (-d) caps recursion.
# Only look one level deep
fd -d 1 -t d
# Two levels max, looking for YAML files
fd -d 2 -e yaml ~/infrastructure
Executing Commands with --exec and --exec-batch
This is where fd pulls ahead of a simple grep wrapper. --exec (-x) runs a command for each match in parallel; --exec-batch (-X) passes all matches as arguments to a single command invocation.
The placeholder {} expands to the full path. Additional placeholders:
{/}— filename only (basename){.}— path without extension{//}— parent directory{/.}— basename without extension
Per-file parallel execution
# Convert every PNG to WebP in parallel
fd -e png -x cwebp {} -o {.}.webp
# Show file sizes for all log files
fd -e log -x du -sh {}
Batch execution (single command, all matches)
# Open all TODO files in vim at once
fd TODO -X vim
# Count total lines across all Python files
fd -e py -X wc -l
Note: -x is parallel and non-deterministic in order; -X collects all results first. Use -X when order or a single process matters, -x when you want speed.
Comparing fd to find: Common Translations
| find command | fd equivalent |
|---|---|
find . -name "*.rs" | fd -e rs |
find . -type d -name "build" | fd -t d build |
find . -maxdepth 2 -name "*.yml" | fd -d 2 -e yml |
find . -name "*.log" -exec rm {} \; | fd -e log -x rm {} |
find /etc -iname "*nginx*" | fd nginx /etc |
Useful Real-World Patterns
Delete all compiled Python bytecode
fd -e pyc -x rm {}
Find files modified in the last 2 days
fd --changed-within 2d
Find large files (requires filtering with xargs or awk)
fd -t f -x du -sh {} | sort -rh | head -20
Search excluding a specific directory
fd -e js --exclude node_modules
Verifying Your Results
When you're unsure whether fd is skipping something it shouldn't, add --show-errors and temporarily override ignore rules with -HI to see the full picture, then narrow back down.
fd -HI --show-errors config /etc 2>&1 | head -40
If you want to confirm fd and find agree on a result set:
diff <(fd -HI -e conf /etc | sort) <(find /etc -name '*.conf' | sort)
Troubleshooting
Command not found after install on Debian/Ubuntu
The package installs the binary as fdfind. Either use that name, or create the symlink shown in the install section. Confirm with which fdfind.
Expected files are missing from results
Run with -HI to disable all filtering. If the file appears now, it's being hidden either because it's a dotfile (-H fixes that) or it matches an ignore file (-I fixes that). Check for a .gitignore or .fdignore in a parent directory.
Regex pattern not matching what you expect
fd uses Rust regex syntax. Character classes and anchors work as expected, but it matches against the filename only (not the full path) unless you pass --full-path (-p).
# Match against the full path, not just the filename
fd -p 'projects/web.*\.js$'
Performance seems slower than expected
fd is multithreaded by default. On spinning disks or network filesystems it can sometimes be slower than on NVMe due to seek overhead from parallel access. Try --threads 1 in those environments.
fd --threads 1 -e log /mnt/nfsFrequently asked questions
- Does fd replace find entirely?
- For most day-to-day searches, yes. However, find has capabilities fd lacks, such as -perm permission filtering, -newer comparisons, and complex boolean logic with -and/-or/-not. For scripting in environments where fd may not be installed, find remains the portable choice.
- Why does fd skip files I know exist?
- fd respects .gitignore, .fdignore, and .ignore files, and hides dotfiles by default. Run with -HI to disable all filtering and confirm the file is reachable, then identify which rule is excluding it.
- Is -x (--exec) safe to use with destructive commands like rm?
- Be careful: -x runs commands in parallel without confirmation. Do a dry run first by replacing rm with echo to preview what would be deleted, then re-run with the real command.
- Can fd search file contents like grep?
- No. fd matches file and directory names only. For content searching, pipe fd results into ripgrep: fd -e py -x rg 'TODO' {} is one pattern, though rg alone with its own file filtering is usually simpler.
- How does fd handle symbolic links?
- By default fd does not follow symbolic links during traversal. Pass --follow (-L) to make fd follow symlinks, but be aware this can produce duplicate results or infinite loops in circular link structures.
Related guides
Bash Arrays and Associative Arrays
Master bash indexed and associative arrays: declaration, element access, looping, mapfile, namerefs, and practical patterns for real scripting work.
Bash Functions and Variable Scoping
Master Bash function scoping with local variables, source-based libraries, correct use of return codes, and array passing techniques including namerefs.
Bash Loops: for, while and until
Learn all three Bash loop types — for, while, and until — with practical, copy-paste examples covering file iteration, counting, polling, and safe line reading.
Bash Scripting for Beginners
Learn Bash scripting from scratch: shebang lines, variables, conditionals, loops, and arguments, plus a real backup script to tie it all together.