Skip to content

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 from find's -printf directives.