fd: 一款简单、快速且用户友好的 find 替代工具

fd 是一个用于在文件系统中查找条目的程序。它是一款简单、快速且用户友好的 find 命令的替代方案。虽然它并不旨在支持 find 的所有强大功能,但它为大多数使用场景提供了明智的(主观的)默认设置。

安装如何使用问题排查

特性

演示

Demo

如何使用

首先,要概览所有可用的命令行选项,您可以运行 fd -h 以获取简洁的帮助消息,或者运行 fd --help 以获取更详细的版本。

简单搜索

fd 旨在查找文件系统中的条目。您可以执行的最基本搜索是使用单个参数运行 fd:搜索模式。例如,假设您要查找旧的脚本(名称包含 netflix):

> fd netfl
Software/python/imdb-ratings/netflix-details.py

如果像这样仅使用一个参数调用,fd 将在当前目录中递归搜索 包含 模式 netfl 的任何条目。

正则表达式搜索

搜索模式被视为正则表达式。在这里,我们搜索以 x 开头并以 rc 结尾的条目:

> cd /etc
> fd '^x.*rc$'
X11/xinit/xinitrc
X11/xinit/xserverrc

fd 使用的正则表达式语法在此处进行了文档说明

指定根目录

如果我们要搜索特定目录,可以将其作为第二个参数提供给 fd

> fd passwd /etc
/etc/default/passwd
/etc/pam.d/passwd
/etc/passwd

递归列出所有文件

可以不带任何参数地调用 fd。这对于快速概览当前目录中的所有条目非常有用,递归地(类似于 ls -R):

> cd fd/tests
> fd
testenv
testenv/mod.rs
tests.rs

如果要使用此功能列出给定目录中的所有文件,则必须使用通配符模式,例如 .^

> fd . fd/tests/
testenv
testenv/mod.rs
tests.rs

搜索特定文件扩展名

通常,我们对特定类型的所有文件感兴趣。这可以通过 -e(或 --extension)选项完成。在这里,我们搜索 fd 存储库中的所有 Markdown 文件:

> cd fd
> fd -e md
CONTRIBUTING.md
README.md

-e 选项可以与搜索模式结合使用:

> fd -e rs mod
src/fshelper/mod.rs
src/lscolors/mod.rs
tests/testenv/mod.rs

搜索特定文件名

要查找具有完全提供的搜索模式的文件,请使用 -g(或 --glob)选项:

> fd -g libc.so /usr
/usr/lib32/libc.so
/usr/lib/libc.so

隐藏文件和忽略文件

默认情况下,fd 不搜索隐藏目录,也不在搜索结果中显示隐藏文件。要禁用此行为,我们可以使用 -H(或 --hidden)选项:

> fd pre-commit
> fd -H pre-commit
.git/hooks/pre-commit.sample

如果我们在作为 Git 存储库(或包含 Git 存储库)的目录中工作,fd 不会搜索与 .gitignore 模式之一匹配的文件夹(并且不显示文件)。要禁用此行为,我们可以使用 -I(或 --no-ignore)选项:

> fd num_cpu
> fd -I num_cpu
target/debug/deps/libnum_cpus-f5ce7ef99006aa05.rlib

要真正搜索 所有 文件和目录,只需组合隐藏和忽略功能以显示所有内容(-HI)或使用 -u/--unrestricted

匹配完整路径

默认情况下,fd 仅匹配每个文件的文件名。但是,使用 --full-path-p 选项,您可以匹配完整路径。

> fd -p -g '**/.git/config'
> fd -p '.*/lesson-\d+/[a-z]+.(jpg|png)'

命令执行

除了显示搜索结果之外,您通常还想 对它们执行某些操作fd 提供了两种为每个搜索结果执行外部命令的方法:

示例

递归查找所有 zip 归档文件并解压缩它们:

fd -e zip -x unzip

如果有两个这样的文件,file1.zipbackup/file2.zip,这将执行 unzip file1.zipunzip backup/file2.zip。这两个 unzip 进程并行运行(如果文件找到得足够快)。

查找所有 *.h*.cpp 文件,并使用 clang-format -i 自动就地格式化它们:

fd -e h -e cpp -x clang-format -i

请注意,如何将 clang-format-i 选项作为单独的参数传递。这就是为什么我们将 -x 选项放在最后的原因。

查找所有 test_*.py 文件并在您喜欢的编辑器中打开它们:

fd -g 'test_*.py' -X vim

请注意,我们在此处使用大写 -X 来打开单个 vim 实例。如果有两个这样的文件,test_basic.pylib/test_advanced.py,这将运行 vim test_basic.py lib/test_advanced.py

要查看文件权限、所有者、文件大小等详细信息,您可以告诉 fd 通过为每个结果运行 ls 来显示它们:

fd … -X ls -lhd --color=always

此模式非常有用,fd 提供了一个快捷方式。您可以使用 -l/--list-details 选项以这种方式执行 lsfd … -l

当将 fdripgrep (rg) 结合使用以在特定类别的文件中搜索时,-X 选项也很有用,例如所有 C++ 源文件:

fd -e cpp -e cxx -e h -e hpp -X rg 'std::cout'

将所有 *.jpg 文件转换为 *.png 文件:

fd -e jpg -x convert {} {.}.png

这里,{} 是搜索结果的占位符。{.} 与其相同,但不包含文件扩展名。有关占位符语法的更多详细信息,请参见下文。

使用 -x 从并行线程运行的命令的终端输出不会交错或乱码,因此 fd -x 可用于粗略地并行化在许多文件上运行的任务。一个例子是计算目录中每个单独文件的校验和。

fd -tf -x md5sum > file_checksums.txt

占位符语法

-x-X 选项采用 命令模板 作为一系列参数(而不是单个字符串)。如果要在命令模板之后向 fd 添加其他选项,可以使用 \; 终止它。

用于生成命令的语法类似于 GNU Parallel 的语法:

如果您不包含占位符,fd 会自动在末尾添加一个 {}

并行与串行执行

对于 -x/--exec,您可以使用 -j/--threads 选项来控制并行作业的数量。使用 --threads=1 进行串行执行。

排除特定文件或目录

有时我们想忽略来自特定子目录的搜索结果。例如,我们可能想搜索所有隐藏文件和目录 (-H),但排除来自 .git 目录的所有匹配项。我们可以使用 -E(或 --exclude)选项。它将任意 glob 模式作为参数:

> fd -H -E .git …

我们也可以使用它来跳过已挂载的目录:

> fd -E /mnt/external-drive …

..或跳过某些文件类型:

> fd -E '*.bak' …

要使此类排除模式永久生效,您可以创建一个 .fdignore 文件。它们的工作方式类似于 .gitignore 文件,但特定于 fd。例如:

> cat ~/.fdignore
/mnt/external-drive
*.bak

注意

fd 还支持其他程序(如 rgag)使用的 .ignore 文件。

如果您希望 fd 全局忽略这些模式,可以将它们放在 fd 的全局忽略文件中。这通常位于 macOS 或 Linux 的 ~/.config/fd/ignore 中,以及 Windows 的 %APPDATA%\fd\ignore 中。

您可能希望在 fd/ignore 文件中包含 .git/,以便在使用 --hidden 选项时,输出中不包含 .git 目录及其内容。

删除文件

您可以使用 fd 删除搜索模式匹配的所有文件和目录。如果只想删除文件,可以使用 --exec-batch/-X 选项调用 rm。例如,要递归删除所有 .DS_Store 文件,请运行:

> fd -H '^\.DS_Store$' -tf -X rm

如果不确定,请始终先调用不带 -X rmfd。或者,使用 rm 的“交互式”选项:

> fd -H '^\.DS_Store$' -tf -X rm -i

如果还要删除特定类别的目录,可以使用相同的技术。您必须使用 rm--recursive/-r 标志来删除目录。

注意

在某些情况下,使用 fd … -X rm -r 可能会导致竞争条件:如果您有一个像 …/foo/bar/foo/… 这样的路径,并且想要删除所有名为 foo 的目录,您可能会最终处于这样一种情况:外部 foo 目录首先被删除,从而导致 rm 调用中出现(无害的)"'foo/bar/foo': No such file or directory" 错误。

命令行选项

这是 fd -h 的输出。要查看完整的命令行选项集,请使用 fd --help,其中还包含更详细的帮助文本。

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/dir), symlink (l),
                  executable (x), empty (e), socket (s), pipe (p), char-device
                  (c), block-device (b)
 -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
   --format <fmt>        Print results according to template
 -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]
   --hyperlink[=<when>]     Add hyperlinks to output paths [default: never] [possible
                  values: auto, always, never]
 -h, --help            Print help (see more with '--help')
 -V, --version          Print version

基准测试

让我们在我的主文件夹中搜索以 [0-9].jpg 结尾的文件。它包含约 750.000 个子目录和约 400 万个文件。为了进行平均和统计分析,我使用了 hyperfine。以下基准测试是在“暖”/预填充磁盘缓存的情况下执行的(“冷”磁盘缓存的结果显示相同的趋势)。

让我们从 find 开始:

Benchmark 1: find ~ -iregex '.*[0-9]\.jpg$'
 Time (mean ± σ):   19.922 s ± 0.109 s
 Range (min … max):  19.765 s … 20.065 s

如果 find 不需要执行正则表达式搜索,则速度会快得多:

Benchmark 2: find ~ -iname '*[0-9].jpg'
 Time (mean ± σ):   11.226 s ± 0.104 s
 Range (min … max):  11.119 s … 11.466 s

现在让我们尝试对 fd 执行相同的操作。请注意,默认情况下,fd 执行正则表达式搜索。此处需要 -u/--unrestricted 选项才能进行公平比较。否则,fd 不必遍历隐藏文件夹和忽略的路径(参见下文):

Benchmark 3: fd -u '[0-9]\.jpg$' ~
 Time (mean ± σ):   854.8 ms ± 10.0 ms
 Range (min … max):  839.2 ms … 868.9 ms

对于此特定示例,fdfind -iregex 快约 23 倍,比 find -iname 快约 13 倍。顺便说一句,这两个工具都找到了完全相同的 546 个文件😄。

注意:这是 一个特定的 基准测试在 一台特定的 机器上。虽然我们已经执行了很多不同的测试(并且发现了始终如一的结果),但对于您来说,情况可能有所不同!我们鼓励每个人在自己的计算机上尝试一下。请参阅此存储库 以获取所有必要的脚本。

关于 fd 的速度,很大一部分归功于 regexignore crates,它们也在 ripgrep 中使用(查看一下!)。

问题排查

fd 找不到我的文件!

请记住,默认情况下,fd 会忽略隐藏的目录和文件。它还会忽略来自 .gitignore 文件的模式。如果要确保找到绝对每个可能的文件,请始终使用选项 -u/--unrestricted(或 -HI 以启用隐藏和忽略的文件):

> fd -u …

另请记住,默认情况下,fd 仅基于文件名进行搜索,而不将模式与完整路径进行比较。如果要基于完整路径进行搜索(类似于 find-path 选项),则需要使用 --full-path(或 -p)选项。

彩色输出

fd 可以像 ls 一样按扩展名对文件进行着色。为了使此功能正常工作,必须设置环境变量 LS_COLORS。通常,此变量的值由 dircolors 命令设置,该命令提供了一种方便的配置格式来定义不同文件格式的颜色。在大多数发行版上,应该已经设置了 LS_COLORS。如果您使用的是 Windows,或者正在寻找替代的、更完整(或更丰富多彩)的变体,请参见此处此处此处

fd 还遵循 NO_COLOR 环境变量。

fd 似乎无法正确解释我的正则表达式模式

许多特殊的正则表达式字符(例如 []^$ 等)也是 shell 中的特殊字符。如有疑问,请始终确保将单引号引起来正则表达式模式:

> fd '^[A-Z][0-9]+$'

如果您的模式以短划线开头,则必须添加 -- 以指示命令行选项的结束。否则,该模式将被解释为命令行选项。或者,使用带有单个连字符字符的字符类:

> fd -- '-pattern'
> fd '[-]pattern'

对于 aliases 或 shell 函数,“找不到命令”

Shell aliases 和 shell 函数不能用于通过 fd -xfd -X 执行命令。在 zsh 中,您可以通过 alias -g myalias="…" 使别名全局生效。在 bash 中,您可以使用 export -f my_function 使其可用于子进程。您仍然需要调用 fd -x bash -c 'my_function "$1"' bash。对于其他用例或 shell,请使用(临时)shell 脚本。

与其他程序集成

将 fd 与 fzf 结合使用

您可以使用 fd 为命令行模糊查找器 fzf 生成输入:

export FZF_DEFAULT_COMMAND='fd --type file'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"

然后,您可以在终端上键入 vim <Ctrl-T> 以打开 fzf 并搜索 fd 结果。

或者,您可能想遵循符号链接并包含隐藏文件(但排除 .git 文件夹):

export FZF_DEFAULT_COMMAND='fd --type file --follow --hidden --exclude .git'

您甚至可以通过设置以下内容在 fzf 中使用 fd 的彩色输出:

export FZF_DEFAULT_COMMAND="fd --type file --color=always"
export FZF_DEFAULT_OPTS="--ansi"

有关更多详细信息,请参见 fzf README 的提示部分

将 fd 与 rofi 结合使用

rofi 是一种图形启动菜单应用程序,能够通过从 stdin 读取来创建菜单。将 fd 输出通过管道传输到 rofi-dmenu 模式会创建可进行模糊搜索的文件和目录列表。

示例

创建一个不区分大小写的可搜索多选列表,其中包含 $HOME 目录下的 PDF 文件,并使用配置的 PDF 查看器打开选择。要列出所有文件类型,请删除 -e pdf 参数。

fd --type f -e pdf . $HOME | rofi -keep-right -dmenu -i -p FILES -multi-select | xargs -I {} xdg-open {}

要修改 rofi 显示的列表,请将参数添加到 fd 命令。要修改 rofi 的搜索行为,请将参数添加到 rofi 命令。

将 fd 与 emacs 结合使用

emacs 包 find-file-in-project 可以使用 fd 查找文件。

安装 find-file-in-project 后,将行 (setq ffip-use-rust-fd t) 添加到您的 ~/.emacs~/.emacs.d/init.el 文件中。

在 emacs 中,运行 M-x find-file-in-project-by-selected 以查找匹配的文件。或者,运行 M-x find-file-in-project 以列出项目中所有可用的文件。

将输出打印为树

要将 fd 的输出格式化为文件树,您可以使用带有 --fromfiletree 命令:

❯ fd | tree --fromfile

这可能比单独运行 tree 更有用,因为默认情况下 tree 不会忽略任何文件,也不支持像 fd 那样丰富的选项集来控制要打印的内容:

❯ fd --extension rs | tree --fromfile
.
├── build.rs
└── src
  ├── app.rs
  └── error.rs

在 bash 和类似的环境中,您可以简单地创建一个别名:

❯ alias as-tree='tree --fromfile'

将 fd 与 xargsparallel 结合使用

请注意,fd 具有内置功能,可以使用其 -x/--exec-X/--exec-batch 选项执行命令。如果您愿意,仍然可以将其与 xargs 结合使用:

> fd -0 -e rs | xargs -0 wc -l

在这里,-0 选项告诉 fd 使用 NULL 字符(而不是换行符)分隔搜索结果。以相同的方式,xargs-0 选项告诉它以这种方式读取输入。

安装

Packaging status

在 Ubuntu 上

...以及其他基于 Debian 的 Linux 发行版。

如果您运行的是 Ubuntu 19.04 (Disco Dingo) 或更高版本,则可以安装官方维护的软件包

apt install fd-find

请注意,由于二进制名称 fd 已被另一个软件包使用,因此二进制文件名为 fdfind。建议安装后,您通过执行命令 ln -s $(which fdfind) ~/.local/bin/fd 添加到 fd 的链接,以便以与本文档相同的方式使用 fd。确保 $HOME/.local/bin 在您的 $PATH 中。

如果您使用旧版本的 Ubuntu,则可以从发布页面下载最新的 .deb 软件包,并通过以下方式安装它:

dpkg -i fd_9.0.0_amd64.deb # adapt version number and architecture

请注意,此项目的发布页面上的 .deb 软件包仍将可执行文件命名为 fd

在 Debian 上

如果您运行的是 Debian Buster 或更高版本,则可以安装官方维护的 Debian 软件包

apt-get install fd-find

请注意,由于二进制名称 fd 已被另一个软件包使用,因此二进制文件名为 fdfind。建议安装后,您通过执行命令 ln -s $(which fdfind) ~/.local/bin/fd 添加到 fd 的链接,以便以与本文档相同的方式使用 fd。确保 $HOME/.local/bin 在您的 $PATH 中。

请注意,此项目的发布页面上的 .deb 软件包仍将可执行文件命名为 fd

在 Fedora 上

从 Fedora 28 开始,您可以从官方软件包源安装 fd

dnf install fd-find

在 Alpine Linux 上

您可以从官方源安装 fd 软件包,前提是您已启用相应的存储库:

apk add fd

在 Arch Linux 上

您可以从官方存储库安装 fd 软件包

pacman -S fd

您还可以从 AUR 安装 fd。

在 Gentoo Linux 上

您可以使用来自官方存储库的 fd ebuild

emerge -av fd

在 openSUSE Linux 上

您可以从官方存储库安装 fd 软件包

zypper in fd

在 Void Linux 上

您可以通过 xbps-install 安装 fd

xbps-install -S fd

在 ALT Linux 上

您可以从官方存储库安装 fd 软件包

apt-get install fd

在 Solus 上

您可以从官方存储库安装 fd 软件包

eopkg install fd

在 RedHat Enterprise Linux 8/9 (RHEL8/9)、Almalinux 8/9、EuroLinux 8/9 或 Rocky Linux 8/9 上

您可以从 Fedora Copr 安装 fd 软件包

dnf copr enable tkbcopr/fd
dnf install fd

EPEL8/9 存储库中还提供了一个使用较慢 malloc 而不是 jemalloc 的不同版本,软件包名为 fd-find

在 macOS 上

您可以使用 Homebrew 安装 fd

brew install fd

…或使用 MacPorts:

port install fd

在 Windows 上

您可以从发布页面下载预构建的二进制文件。

或者,您可以通过 Scoop 安装 fd

scoop install fd

或者通过 Chocolatey

choco install fd

或者通过 Winget

winget install sharkdp.fd

在 GuixOS 上

您可以从官方存储库安装 fd 软件包

guix install fd

在 NixOS 上 / 通过 Nix

您可以使用 Nix 软件包管理器 安装 fd

nix-env -i fd

通过 Flox

您可以使用 Floxfd 安装到 Flox 环境中:

flox install fd

在 FreeBSD 上

您可以从官方存储库安装 fd-find 软件包

pkg install fd-find

从 npm

在 Linux 和 macOS 上,您可以安装 fd-find 软件包:

npm install -g fd-find

从源代码

使用 Rust 的软件包管理器 cargo,您可以通过以下方式安装 fd

cargo install fd-find

请注意,需要 rust 版本 1.77.2 或更高版本。

构建也需要 make

从二进制文件

发布页面 包含 Linux、macOS 和 Windows 的预编译二进制文件。静态链接的二进制文件也可用:查找文件名中包含 musl 的存档。

开发

git clone https://github.com/sharkdp/fd
# Build
cd fd
cargo build
# Run unit tests and integration tests
cargo test
# Install
cargo install --path .

维护者

许可

fd 是在 MIT 许可证和 Apache 许可证 2.0 的条款下分发的。

有关许可证详细信息,请参见 LICENSE-APACHELICENSE-MIT 文件。