You keep a line like `find . -name '*.py' -not -path './node_modules/*'` in a notes file because you never remember its order, and you still get `node_modules` noise when you forget the `-not -path` part. fd is a small program that finds files by name with a shorter command, skips what your `.gitignore` already ignores, and runs a command on every result. This guide covers the 20% of fd that does 80% of the work: matching names with plain text, regular expressions, and globs; filtering by extension, type, depth, and size; ignore rules and hidden files; and running commands with `-x` and `-X`. A searchable command reference and a set of videos, books, and online resources close the article. ## What You'll Learn * What fd does, and when `find` still fits better. * How to find files by name with plain text, regular expressions, and globs. * How to narrow results by extension, file type, depth, and size. * Why fd skips some files, and how to see them with `-H`, `-I`, and `-u`. * How to run a command on every result with `-x` and `-X`. * Where to go next to learn more about fd. ## When to Use fd or find fd searches the current folder and every folder below it for names that match a pattern. It prints paths relative to where you run it, skips hidden files and anything your ignore files list, and treats the pattern as a regular expression unless you ask for a glob. Those defaults fit the search you run most days: find a file in a project by part of its name. Each `find` command below has a shorter fd equivalent: * `find . -name '*cart*'` is `fd cart`. * `find . -name '*.py'` is `fd -e py`. * `find . -type d` is `fd -t d`. * `find . -maxdepth 1 -type f` is `fd -t f -d 1`. * `find . -name '*.py' -not -path './node_modules/*'` is `fd -e py`, when a `.gitignore` lists `node_modules/`. * `find . -name '*.jpeg' -exec mv {} ... \;` is `fd -e jpeg -x mv {} {.}.jpg`. Keep `find` for the jobs fd leaves out: * Matching by modification time relative to another file (`-newer`) or by exact permission bits (`-perm`). * Scripts that must run on any POSIX system, where fd may not be installed. * Complex conditions that combine `-o`, `!`, and grouping in one expression. * Running a different action per match with several `-exec` clauses. fd works the same in any shell. The examples below run in zsh, from a home folder that holds a few practice files: `notes.txt`, a `logs/` folder, a `photos/` folder, and a small web project in `shop/` that is a git repository with a `.gitignore`. ## Find Files by Name Give fd part of a name. It matches anywhere in the file or folder name, below the current folder: ```zsh fd cart shop ``` ```text shop/src/cart.py shop/src/test_cart.py ``` The second argument is the folder to search. Leave it out and fd searches the current folder. fd matches only the last part of each path, the file or folder name, so the folders above a file never cause a match. fd uses *smart case*. A pattern in lowercase ignores case, and a pattern with any uppercase letter matches case exactly. `fd App shop` prints nothing, because no name contains `App` with a capital A. Add `-i` to ignore case anyway, or `-s` to match case even for a lowercase pattern: ```zsh fd -i readme shop ``` ```text shop/README.md ``` ## Use Regex and Glob Patterns The pattern is a regular expression. `^` anchors the start of the name and `$` the end, so `^test_` finds files whose names start with `test_`: ```zsh fd '^test_' shop ``` ```text shop/src/test_cart.py ``` Quote patterns with special characters, so the shell passes them to fd unchanged. Escape a literal dot with a backslash, or use `-F` to treat the whole pattern as fixed text: ```zsh fd 'cart\.py$' shop ``` ```text shop/src/cart.py shop/src/test_cart.py ``` Use `-g` when a shell-style glob reads better than a regular expression. A glob must match the whole name: ```zsh fd -g '*.py' shop ``` ```text shop/src/__init__.py shop/src/app.py shop/src/cart.py shop/src/test_cart.py ``` To match against the full path instead of the name, add `-p`. Then `fd -p 'src/t' shop` matches the `templates` folder and `test_cart.py`, because both paths contain `src/t`. ## Filter by Extension, Type, Depth, and Size `-e` filters by file extension, without the dot. It ignores case, so `-e jpg` also finds `.JPG` files: ```zsh fd -e jpg ``` ```text photos/IMG_2043.JPG ``` To list every file with an extension, use `.` as the pattern. It matches any name: ```zsh fd -e py . shop ``` ```text shop/src/__init__.py shop/src/app.py shop/src/cart.py shop/src/test_cart.py ``` Repeat `-e` to match several extensions, as in `fd -e html -e css`. `-t` filters by type: `f` for files, `d` for directories, `x` for executables, and `e` for empty files and folders. Combine it with `-d` to limit how deep fd goes. `-d 1` stays in the folder you search: ```zsh fd -t d -d 1 ``` ```text logs/ photos/ shop/ ``` fd marks directories with a trailing `/`. Two more filters round out the set. `-E` excludes names that match a glob, and `-S` filters by size, such as `-S +1M` for files over one megabyte: ```zsh fd -e py -E 'test_*' . shop ``` ```text shop/src/__init__.py shop/src/app.py shop/src/cart.py ``` ## Respect .gitignore and Hidden Files By default fd skips two kinds of entries: hidden files and folders, whose names start with a dot, and anything an ignore file lists. Inside `shop/`, `.gitignore` lists `node_modules/`, `dist/`, and `*.log`, so a plain `fd` in that folder shows only the project's own files: ```zsh cd shop fd ``` ```text README.md run.sh src/ src/__init__.py src/app.py src/cart.py src/templates/ src/templates/index.html src/test_cart.py static/ static/style.css ``` Add `-H` to include hidden entries such as `.env` and `.gitignore`, `-I` to include ignored ones, or `-u` for both. Run from the home folder, `fd -e log` lists the three files in `logs/`, and `-I` adds `shop/debug.log`, which `shop/.gitignore` hides: ```zsh fd -I -e log ``` ```text logs/app-2026-09.log logs/app.log logs/error.log shop/debug.log ``` In `shop/`, the same flag brings back `node_modules/` and `dist/`. fd reads `.gitignore` only inside a git repository, a folder with a `.git` directory at or above it. A `.gitignore` copied into a folder that isn't a repository does nothing until you add `--no-require-git`: ```zsh mkdir ~/scratch cp ~/shop/.gitignore ~/scratch/ echo hi > ~/scratch/debug.log cd ~/scratch fd ``` ```text debug.log ``` Run `fd --no-require-git` in the same folder and it prints nothing, because the copied `.gitignore` now applies. To hide files from fd without changing what git tracks, list them in `.fdignore` (fd only) or `.ignore` (fd, ripgrep, and other tools that read it). Each uses the same syntax as `.gitignore`: ```zsh cd shop echo 'static/' > .fdignore fd ``` ```text README.md run.sh src/ src/__init__.py src/app.py src/cart.py src/templates/ src/templates/index.html src/test_cart.py ``` `static/` and its stylesheet are gone. `fd --no-ignore` shows everything again. For patterns you never want to see in any folder, write them to `~/.config/fd/ignore`. `--no-global-ignore-file` turns that file off for one search, and `--no-ignore-vcs` turns off only `.gitignore`. ## Run Commands on Results with -x and -X `-x` runs a command once for each result, and `-X` runs it once with every result as arguments. Placeholders in braces stand for parts of each path: * `{}` is the whole path, `src/app.py`. * `{.}` is the path without its extension, `src/app`. * `{/}` is the file name, `app.py`. * `{//}` is the parent folder, `src`. * `{/.}` is the file name without its extension, `app`. When you leave out the placeholder, fd adds `{}` at the end. Rename every `.jpeg` file to `.jpg`. The `mv` prints nothing, so check the result with a second search. Sort it, because a renamed file moves to the end of its folder, and fd lists a slow search in the order it finds files: ```zsh fd -e jpeg -x mv {} {.}.jpg fd -e jpg | sort ``` ```text photos/IMG_2041.jpg photos/IMG_2042.jpg photos/IMG_2043.JPG ``` `-X` suits commands that take many files at once, such as `wc -l` to count lines: ```zsh cd shop fd -e py -X wc -l | sort ``` ```text 0 ./src/__init__.py 7 ./src/test_cart.py 11 ./src/app.py 13 ./src/cart.py 31 total ``` Paths that fd passes to a command start with `./`, so a name that begins with `-` can't pass for an option. And the output ends in `| sort` because the order varies. fd sorts its own results only when the search finishes within about 100 milliseconds, and otherwise prints them in the order it finds them. `-x` runs several commands at once and prints their output as each finishes, so its order changes from run to run. Pipe to `sort` whenever the order matters. `-j 1` runs one command at a time. Find every file that mentions `TODO`: ```zsh cd shop fd -t f -X grep -l TODO | sort ``` ```text ./README.md ./src/cart.py ``` ## Format Output A few flags change how fd prints each path: * `-a` prints absolute paths, starting at `/`. * `-l` prints a long listing, like `ls -l`, with permissions, owner, size, and date. * `-0` ends each path with a NUL character instead of a newline, for `xargs -0` and other tools that read NUL-separated names safely. * `--color never` turns colors off; `--color always` keeps them when you pipe to `less -R`. ```zsh fd -a -e css ``` ```text /home/reader/shop/static/style.css ``` Pair `-0` with `xargs -0` when names may contain spaces: ```zsh cd shop fd -0 -e py | xargs -0 wc -l ``` ```text 0 ./src/__init__.py 11 ./src/app.py 13 ./src/cart.py 7 ./src/test_cart.py 31 total ``` ## Practice fd fd sticks once you type it. The practice shell below runs real fd 10.5.0 in zsh, in this page. Nothing downloads until you press Start, nothing you type leaves your browser, and Reset brings back a fresh shell. Click the shell to type. The practice shell has no network and holds only fd, zsh, and BusyBox's basic programs. Its home holds the practice files the examples above use: `notes.txt`, `logs/`, `photos/`, and the `shop/` project with its `.gitignore`. These commands cannot run there, so run them on your own machine: * `git`, `rg`, `fzf`, `zoxide`, `bat`, `curl`, `brew`, and `sudo` are not installed, so zsh answers `command not found`. * `fd -x convert` and `fd -x ffmpeg` fail once per match, because neither program is installed. * `wget`, `ping`, `nc`, and `nslookup` fail, because the practice shell has no network. Run the drill below in the practice shell or in your own terminal with the same files. Type each command exactly as shown and press Enter. It takes about ten minutes: 1. Move into the project: `cd shop`. The prompt changes to `reader@practice ~/shop %`. 2. List the Python files: `fd -e py`. You see `src/__init__.py`, `src/app.py`, `src/cart.py`, and `src/test_cart.py`. Nothing from `node_modules` or `dist` shows up, because the project's `.gitignore` lists them. 3. Add hidden files: `fd -H`. The list now starts with `.env`, `.git/`, `.git/HEAD`, and `.gitignore`, then the 11 files and folders that plain `fd` shows. `node_modules`, `dist`, and `debug.log` stay hidden, because `-H` shows hidden files but still respects `.gitignore`. 4. Go back home with `cd`, then list the log files: `fd -e log`. You see `logs/app-2026-09.log`, `logs/app.log`, and `logs/error.log`. Run `fd -I -e log` to stop respecting ignore files, and `shop/debug.log` joins the list. 5. Rename the `.jpeg` photos: `fd -e jpeg -x mv {} {.}.jpg`. `{}` is each match and `{.}` is the match without its extension. It prints nothing. Check the result with `fd -e jpg | sort`. You see `photos/IMG_2041.jpg`, `photos/IMG_2042.jpg`, and `photos/IMG_2043.JPG`, because `-e` ignores the case of the extension. A renamed file moves to the end of its folder, and fd lists a slow search in the order it finds files, so `sort` keeps the order fixed. 6. Press Reset shell. The renamed photos are back to `.jpeg`, and the shell starts again in your home. Run `fd -e jpeg` to see both `.jpeg` photos again. 7. Type `cd sh` and press Tab. zsh completes the line to `cd shop/`. Press Enter to move into the project. To practice on your own machine, install fd first. On macOS, install fd with Homebrew: ```zsh {practice="machine"} brew install fd ``` On Debian and Ubuntu, the package is `fd-find` and the program is named `fdfind`, because another package already uses `fd`. Install it, then link `fd` to it in a folder on your `PATH`: ```zsh {practice="machine"} sudo apt install fd-find mkdir -p ~/.local/bin ln -s "$(command -v fdfind)" ~/.local/bin/fd ``` With a Rust toolchain on any platform, build it with cargo: ```zsh {practice="machine"} cargo install fd-find ``` Then work through these steps in one of your own project folders that is a git repository: 1. List every file fd sees: `fd -t f`. Compare the count with `fd -t f -u`, which adds hidden and ignored files. The difference is what your ignore files hide. 2. Find a file by part of its name, such as `fd config`. Then try the same word with a capital letter, `fd Config`, and see smart case match case exactly. 3. List one kind of file: `fd -e md`, or any extension your project uses. Add `-d 2` to stay near the top. 4. List only folders, two levels deep: `fd -t d -d 2`. 5. Find the files that mention `TODO`: `fd -t f -X grep -l TODO`. 6. Count lines in every file of one kind: `fd -e md -X wc -l`. Pipe it to `sort -n` to see the longest file last. 7. Hide a folder from fd without touching git: write its name to `.fdignore`, run `fd` again, then delete `.fdignore`. ## Command Reference Verified with fd 10.5.0. Search the reference to find a command fast. Each entry shows the command, then what it does. ### Patterns * *`fd cart`* - Find names that contain `cart`, ignoring case for a lowercase pattern (smart case) * *`fd App`* - Match case exactly, because the pattern has an uppercase letter * *`fd -s app`* - Match case exactly for any pattern * *`fd -i readme`* - Ignore case for any pattern * *`fd '^test_'`* - Find names that start with `test_` (regular expression) * *`fd 'cart\.py$'`* - Find names that end with `cart.py` * *`fd -F '.py'`* - Treat the pattern as fixed text, not a regular expression * *`fd -g '*.py'`* - Match a whole name with a glob instead of a regular expression * *`fd -p 'src/t'`* - Match the pattern against the full path, not only the name * *`fd . src`* - List everything in one folder (`.` matches any name) ### Filters * *`fd -e py`* - Find files with an extension, ignoring case * *`fd -e html -e css`* - Find files with any of several extensions * *`fd -t f`* - Find files only * *`fd -t d`* - Find directories only * *`fd -t x`* - Find executable files * *`fd -t e`* - Find empty files and directories * *`fd -d 1`* - Search one level deep at most * *`fd --min-depth 2`* - Skip results less than two levels deep * *`fd -E 'test_*'`* - Exclude names that match a glob * *`fd -S +1M`* - Find files larger than one megabyte * *`fd -S -1k`* - Find files smaller than one kilobyte ### Hidden and Ignored Files * *`fd -H`* - Include hidden files and folders * *`fd -I`* - Include files that an ignore file lists * *`fd -u`* - Include hidden and ignored files * *`fd --no-ignore-vcs`* - Stop reading `.gitignore`, but keep `.ignore` and `.fdignore` * *`fd --no-require-git`* - Read `.gitignore` outside a git repository * *`fd --no-global-ignore-file`* - Skip `~/.config/fd/ignore` for one search * *`echo 'static/' > .fdignore`* - Hide a folder from fd only * *`echo 'static/' > .ignore`* - Hide a folder from fd and other tools that read `.ignore` * *`echo '*.html' > ~/.config/fd/ignore`* - Hide a pattern from fd in every folder ### Output * *`fd -a`* - Print absolute paths * *`fd -l`* - Print a long listing with permissions, size, and date * *`fd -0`* - End each path with a NUL character, for `xargs -0` * *`fd --color never`* - Turn colors off ### Run Commands * *`fd -e jpeg -x mv {} {.}.jpg`* - Run a command once per result * *`fd -e py -X wc -l`* - Run a command once with every result as arguments * *`fd -x echo {}`* - Use the whole path * *`fd -x echo {.}`* - Use the path without its extension * *`fd -x echo {/}`* - Use the file name * *`fd -x echo {//}`* - Use the parent folder * *`fd -x echo {/.}`* - Use the file name without its extension * *`fd -j 1 -x wc -l`* - Run one command at a time ### Examples Each example runs on the practice files and shows what fd prints. ```zsh fd -e log ``` ```text logs/app-2026-09.log logs/app.log logs/error.log ``` ```zsh fd -H . shop -d 1 ``` ```text shop/.env shop/.git/ shop/.gitignore shop/README.md shop/run.sh shop/src/ shop/static/ ``` ```zsh fd -t x . shop ``` ```text shop/run.sh ``` ```zsh fd -t e . shop ``` ```text shop/src/__init__.py ``` ```zsh fd -e py -x echo {/.} | sort ``` ```text __init__ app cart test_cart ``` ```zsh fd app shop ``` ```text shop/src/app.py ``` ```zsh fd -F '.py' shop ``` ```text shop/src/__init__.py shop/src/app.py shop/src/cart.py shop/src/test_cart.py ``` ```zsh fd -p 'src/t' shop ``` ```text shop/src/templates/ shop/src/templates/index.html shop/src/test_cart.py ``` ```zsh fd -e html -e css . shop ``` ```text shop/src/templates/index.html shop/static/style.css ``` ```zsh fd -t d . shop ``` ```text shop/src/ shop/src/templates/ shop/static/ ``` ```zsh fd -I -t d -d 2 . shop ``` ```text shop/dist/ shop/node_modules/ shop/node_modules/left-pad/ shop/src/ shop/src/templates/ shop/static/ ``` ```zsh fd --min-depth 3 -e py ``` ```text shop/src/__init__.py shop/src/app.py shop/src/cart.py shop/src/test_cart.py ``` ```zsh fd -e py -x echo {//} | sort -u ``` ```text ./shop/src ``` ## Learn fd: Beyond the Basics With names, filters, ignore rules, and `-x` in hand, these resources take you further. Start with a video, then keep fd's manual open while you replace the `find` lines in your notes. ### Video * [The Modern UNIX command "fd" replaces the "find" command with a twist][video-akamai] by Akamai Developers, a walkthrough of fd's defaults next to `find`. * [The FAST Way to Find Files from the Command Line][video-murphy] by Eric Murphy, a short tour of everyday fd searches. * [How to Use fd: Fast File Searching in Linux][video-linuxhowto] by LinuxHowTo, installation and the common flags on Linux. ### Books * [Efficient Linux at the Command Line][book-efficient-linux] by Daniel J. Barrett, on combining small commands such as `find`, `xargs`, and `sort` into fast workflows. * [The Linux Command Line][book-tlcl] by William Shotts, free online, with chapters on searching for files and on `xargs`. ### Online * [fd on GitHub][fd-repo], the README with installation for every platform, a feature tour, and benchmarks. * [fd's manual page][fd-man], the full list of options and placeholders. * [fd's changelog][fd-changelog], useful when an option you read about behaves differently in your version. * [Regular expression syntax][regex-syntax] from the Rust `regex` crate, the engine fd's patterns use. * [modern-unix][modern-unix], a list of newer command-line tools that pair with fd, such as ripgrep, bat, and fzf. ## Related Content * [Learn Zsh][learn-zsh] covers globbing and glob qualifiers, the shell's own way to match files. * [Learn Nushell][learn-nushell] shows a shell that lists files as tables you can filter and sort. * [What Is fzf?][what-is-fzf] explains the fuzzy finder that reads fd's output to pick files interactively. * [What Is Zoxide?][what-is-zoxide] explains a smarter `cd` for jumping to folders you visit often. [video-akamai]: https://www.youtube.com/watch?v=f0N_4SgCoMY [video-murphy]: https://www.youtube.com/watch?v=cIuaJj7A258 [video-linuxhowto]: https://www.youtube.com/watch?v=AgV7EdiLUdk [book-efficient-linux]: https://danieljbarrett.com/books/efficient-linux-at-the-command-line/ [book-tlcl]: https://linuxcommand.org/tlcl.php [fd-repo]: https://github.com/sharkdp/fd [fd-man]: https://github.com/sharkdp/fd/blob/master/doc/fd.1 [fd-changelog]: https://github.com/sharkdp/fd/blob/master/CHANGELOG.md [regex-syntax]: https://docs.rs/regex/latest/regex/#syntax [modern-unix]: https://github.com/ibraheemdev/modern-unix [learn-zsh]: https://jeffbailey.us/blog/2026/10/06/learn-zsh/ [learn-nushell]: https://jeffbailey.us/blog/2026/10/03/learn-nushell/ [what-is-fzf]: https://jeffbailey.us/blog/2026/04/25/what-is-fzf/ [what-is-zoxide]: https://jeffbailey.us/blog/2026/04/21/what-is-zoxide/