fd: A Nifty Way to Search And Find Files in Your Filesystem
fd
is a program to find entries in your filesystem. It is a simple, fast and user-friendly alternative to find
. While it does not aim to support all of find
's powerful functionality, it provides sensible (opinionated) defaults for a majority of use cases.
Features
- Intuitive syntax:
fd PATTERN
instead offind -iname '*PATTERN*'
. - Regular expression (default) and glob-based patterns.
- Very fast due to parallelized directory traversal.
- Uses colors to highlight different file types (same as
ls
). - Supports parallel command execution
- Smart case: the search is case-insensitive by default. It switches to case-sensitive if the pattern contains an uppercase character*.
- Ignores hidden directories and files, by default.
- Ignores patterns from your
.gitignore
, by default. - The command name is 50% shorter* than
find
:-).
How to use?
First, to get an overview of all available command line options, you can either run fd -h
for a concise help message or fd --help
for a more detailed version.
Simple search
fd is designed to find entries in your filesystem. The most basic search you can perform is to run fd with a single argument: the search pattern. For example, assume that you want to find an old script of yours (the name included netflix
):
> fd netfl Software/python/imdb-ratings/netflix-details.py
If called with just a single argument like this, fd searches the current directory recursively for any entries that contain the pattern netfl
.
Regular expression search
The search pattern is treated as a regular expression. Here, we search for entries that start with x
and end with rc
:
> cd /etc > fd '^x.*rc$'X11/xinit/xinitrc X11/xinit/xserverrc
The regular expression syntax used by fd
is documented here.
Specifying the root directory
If we want to search a specific directory, it can be given as a second argument to fd:
> fd passwd /etc /etc/default/passwd /etc/pam.d/passwd /etc/passwd
List all files, recursively
fd can be called with no arguments. This is very useful to get a quick overview of all entries in the current directory, recursively (similar to ls -R
):
> cd fd/tests > fd testenv testenv/mod.rs tests.rs
If you want to use this functionality to list all files in a given directory, you have to use a catch-all pattern such as .
or ^
:
> fd . fd/tests/ testenv testenv/mod.rs tests.rs
Searching for a particular file extension
Often, we are interested in all files of a particular type. This can be done with the -e
(or --extension
) option. Here, we search for all Markdown files in the fd repository:
> cd fd > fd -e md CONTRIBUTING.md README.md
The -e
option can be used in combination with a search pattern:
> fd -e rs mod src/fshelper/mod.rs src/lscolors/mod.rs tests/testenv/mod.rs
Searching for a particular file name
To find files with exactly the provided search pattern, use the -g
(or --glob
) option:
> fd -g libc.so /usr /usr/lib32/libc.so /usr/lib/libc.so
Hidden and ignored files
By default, fd does not search hidden directories and does not show hidden files in the search results. To disable this behavior, we can use the -H
(or --hidden
) option:
> fd pre-commit > fd -H pre-commit .git/hooks/pre-commit.sample
If we work in a directory that is a Git repository (or includes Git repositories), fd does not search folders (and does not show files) that match one of the .gitignore
patterns. To disable this behavior, we can use the -I
(or --no-ignore
) option:
> fd num_cpu > fd -I num_cpu target/debug/deps/libnum_cpus-f5ce7ef99006aa05.rlib
To really search all files and directories, simply combine the hidden and ignore features to show everything (-HI
).
Matching the full path
By default, fd only matches the filename of each file. However, using the --full-path
or -p
option, you can match against the full path.
> fd -p -g '**/.git/config'> fd -p '.*/lesson-\d+/[a-z]+.(jpg|png)'
Command execution
Instead of just showing the search results, you often want to do something with them. fd
provides two ways to execute external commands for each of your search results:
- The
-x
/--exec
option runs an external command for each of the search results (in parallel). - The
-X
/--exec-batch
option launches the external command once, with all search results as arguments.
Examples
Recursively find all zip archives and unpack them:
fd -e zip -x unzip
If there are two such files, file1.zip
and backup/file2.zip
, this would execute unzip file1.zip
and unzip backup/file2.zip
. The two unzip
processes run in parallel (if the files are found fast enough).
Find all *.h
and *.cpp
files and auto-format them inplace with clang-format -i
:
fd -e h -e cpp -x clang-format -i
Note how the -i
option to clang-format
can be passed as a separate argument. This is why we put the -x
option last.
Find all test_*.py
files and open them in your favorite editor:
fd -g 'test_*.py' -X vim
Note that we use capital -X
here to open a single vim
instance. If there are two such files, test_basic.py
and lib/test_advanced.py
, this will run vim test_basic.py lib/test_advanced.py
.
To see details like file permissions, owners, file sizes etc., you can tell fd
to show them by running ls
for each result:
fd … -X ls -lhd --color=always
This pattern is so useful that fd
provides a shortcut. You can use the -l
/--list-details
option to execute ls
in this way: fd … -l
.
The -X
option is also useful when combining fd
with ripgrep (rg
) in order to search within a certain class of files, like all C++ source files:
fd -e cpp -e cxx -e h -e hpp -X rg 'std::cout'
Convert all *.jpg
files to *.png
files:
fd -e jpg -x convert {} {.}.png
Here, {}
is a placeholder for the search result. {.}
is the same, without the file extension. See below for more details on the placeholder syntax.
The terminal output of commands run from parallel threads using -x
will not be interlaced or garbled, so fd -x
can be used to rudimentarily parallelize a task run over many files. An example of this is calculating the checksum of each individual file within a directory.
fd -tf -x md5sum > file_checksums.txt
Placeholder syntax
The -x
and -X
options take a command template as a series of arguments (instead of a single string). If you want to add additional options to fd
after the command template, you can terminate it with a \;
.
The syntax for generating commands is similar to that of GNU Parallel:
{}
: A placeholder token that will be replaced with the path of the search result (documents/images/party.jpg
).{.}
: Like{}
, but without the file extension (documents/images/party
).{/}
: A placeholder that will be replaced by the basename of the search result (party.jpg
).{//}
: The parent of the discovered path (documents/images
).{/.}
: The basename, with the extension removed (party
).
If you do not include a placeholder, fd automatically adds a {}
at the end.
Parallel vs. serial execution
For -x
/--exec
, you can control the number of parallel jobs by using the -j
/--threads
option. Use --threads=1
for serial execution.
Excluding specific files or directories
Sometimes we want to ignore search results from a specific subdirectory. For example, we might want to search all hidden files and directories (-H
) but exclude all matches from .git
directories. We can use the -E
(or --exclude
) option for this. It takes an arbitrary glob pattern as an argument:
> fd -H -E .git …
We can also use this to skip mounted directories:
> fd -E /mnt/external-drive …
.. or to skip certain file types:
> fd -E '*.bak' …
To make exclude-patterns like these permanent, you can create a .fdignore
file. They work like .gitignore
files, but are specific to fd
. For example:
> cat ~/.fdignore /mnt/external-drive *.bak
Note: fd
also supports .ignore
files that are used by other programs such as rg
or ag
.
If you want fd
to ignore these patterns globally, you can put them in fd
's global ignore file. This is usually located in ~/.config/fd/ignore
in macOS or Linux, and %APPDATA%\fd\ignore
in Windows.
Deleting files
You can use fd
to remove all files and directories that are matched by your search pattern. If you only want to remove files, you can use the --exec-batch
/-X
option to call rm
. For example, to recursively remove all .DS_Store
files, run:
> fd -H '^\.DS_Store$' -tf -X rm
If you are unsure, always call fd
without -X rm
first. Alternatively, use rm
s "interactive" option:
> fd -H '^\.DS_Store$' -tf -X rm -i
If you also want to remove a certain class of directories, you can use the same technique. You will have to use rm
s --recursive
/-r
flag to remove directories.
Note: there are scenarios where using fd … -X rm -r
can cause race conditions: if you have a path like …/foo/bar/foo/…
and want to remove all directories named foo
, you can end up in a situation where the outer foo
directory is removed first, leading to (harmless) "'foo/bar/foo': No such file or directory" errors in the rm
call.
Command-line options
This is the output of fd -h
. To see the full set of command-line options, use fd --help
which also includes a much more detailed help text.
Usage: fd [OPTIONS] [pattern] [path]...
Arguments:
[pattern] the search pattern (a regular expression, unless '--glob' is used; optional)
[path]... the root directories for the filesystem search (optional)
Options:
-H, --hidden Search hidden files and directories
-I, --no-ignore Do not respect .(git|fd)ignore files
-s, --case-sensitive Case-sensitive search (default: smart case)
-i, --ignore-case Case-insensitive search (default: smart case)
-g, --glob Glob-based search (default: regular expression)
-a, --absolute-path Show absolute instead of relative paths
-l, --list-details Use a long listing format with file metadata
-L, --follow Follow symbolic links
-p, --full-path Search full abs. path (default: filename only)
-d, --max-depth <depth> Set maximum search depth (default: none)
-E, --exclude <pattern> Exclude entries that match the given glob pattern
-t, --type <filetype> Filter by type: file (f), directory (d), symlink (l),
executable (x), empty (e), socket (s), pipe (p)
-e, --extension <ext> Filter by file extension
-S, --size <size> Limit results based on the size of files
--changed-within <date|dur> Filter by file modification time (newer than)
--changed-before <date|dur> Filter by file modification time (older than)
-o, --owner <user:group> Filter by owning user and/or group
-x, --exec <cmd>... Execute a command for each search result
-X, --exec-batch <cmd>... Execute a command with all search results at once
-c, --color <when> When to use colors [default: auto] [possible values: auto,
always, never]
-h, --help Print help (see more with '--help')
-V, --version Print version
Tech
- Rust
License
The project is released under Apache-2.0 and MIT licenses.