fd Footguns¶
Mistakes that cause missed files, unintended deletions, or confusing results.
1. Forgetting fd skips hidden files by default¶
You run fd config looking for .config/ directories or .bashrc. Nothing shows up. fd hides dotfiles and dot-directories by default, unlike find.
Fix: Use fd --hidden when you need to search dotfiles. If you always want hidden files, set FZF_DEFAULT_COMMAND='fd --hidden --type f' or create a shell alias.
Default trap: fd's two key defaults — skip hidden files and respect .gitignore — are usually what you want in a code repo. But they are exactly wrong when searching system directories, home directories, or debugging missing config files. Know the flags:
-H(hidden),-I(no-ignore),-u(unrestricted = both).
2. Forgetting fd respects .gitignore by default¶
You search for a file you know exists in node_modules/ or .terraform/. fd returns nothing because those directories are in .gitignore. You question your sanity.
Fix: Use fd --no-ignore to include gitignored files. Use fd --no-ignore --hidden (or -HI) to search absolutely everything. Remember: this is usually a feature, not a bug.
3. Using --exec rm without testing first¶
You run fd -e tmp --exec rm {} intending to clean temp files. The pattern matches files you did not expect — config files with .tmp extension that are actually important. They are deleted instantly with no confirmation.
Fix: Always run fd without --exec first to see what matches. Then add --exec echo rm {} to preview the commands. Only then use --exec rm {}. For destructive operations, prefer --exec rm -i {} for interactive confirmation.
Remember: The three-step pattern for safe destructive fd operations: (1)
fd -e tmp— see what matches, (2)fd -e tmp --exec echo rm {}— preview the commands, (3)fd -e tmp --exec rm {}— execute. Never skip step 1.
4. Confusing regex and glob modes¶
You run fd *.yaml expecting glob matching. fd uses regex by default, so * means "zero or more of the preceding character" (which is nothing). You get unexpected results or an error.
Fix: Use fd -g '*.yaml' for glob patterns. Use fd '\.yaml$' for regex patterns. Use fd -F 'config.yaml' for literal fixed-string matching. Know which mode you are in.
5. Not using -0 with xargs for filenames with spaces¶
You pipe fd output to xargs: fd -e log | xargs wc -l. A file named error log.txt gets split into two arguments. xargs tries to open error and log.txt separately — both fail.
Fix: Use null-separated output: fd -0 -e log | xargs -0 wc -l. This handles any filename safely, including those with spaces, quotes, or newlines.
6. --exec-batch when you meant --exec¶
You run fd -e py --exec-batch python3 -m py_compile {} expecting parallel per-file execution. Instead, all filenames are passed as arguments to a single invocation. py_compile only takes one file argument, so it fails or only checks the first file.
Fix: Use --exec for commands that take one file at a time (runs in parallel). Use --exec-batch for commands that accept multiple files (like shellcheck, ruff check, wc -l). Check the tool's documentation for which it supports.
7. Using fd in /etc or /var without --no-ignore¶
You run fd config /etc/ and get sparse results. There may be a .gitignore or .ignore file in a parent directory that is hiding results. System directories are not git repos, but fd still respects any ignore files it finds.
Fix: When searching system directories, use fd --no-ignore to ensure nothing is filtered out. Ignore files are useful in code repos but misleading in system paths.
8. Missing the -t f flag when counting or processing files¶
You run fd -e yaml | wc -l to count YAML files. fd returns both files and directories matching the pattern. Your count is inflated by directories like yaml-configs/.
Fix: Use fd -t f -e yaml to match only regular files. Use -t d for directories only. Always specify type when the distinction matters.
9. Assuming fd and find have the same flags¶
You try fd -name '*.conf' or fd . -type f. fd uses a completely different flag syntax than find. -name is not a valid fd option.
Fix: Learn fd's syntax independently. Key differences: no leading dot needed, -e for extension (not -name '*.ext'), -t f (not -type f), -d for depth (not -maxdepth), -E for exclude (not -not -path).
10. Not using fd's placeholder tokens with --exec¶
You run fd --exec mv {} /backup/{} expecting the file to be moved to the same relative path under /backup/. The second {} expands to the full path including directories. You get /backup/./src/file.py or errors.
Fix: Use fd's placeholder tokens: {/} for basename, {//} for parent directory, {.} for path without extension, {/.} for basename without extension. Example: fd --exec mv {} /backup/{/} moves files flat into /backup/.
Remember: fd's placeholder tokens:
{}= full path,{/}= basename (think: after the last/),{//}= directory (everything before the last/),{.}= path without extension,{/.}= basename without extension. These mirror the conventions fromfind's-printfdirectives.