fd: A simple, fast and user-friendly alternative to 'find'
fd: 一款简单、快速且用户友好的 find
替代工具
fd
是一个用于在文件系统中查找条目的程序。它是一款简单、快速且用户友好的 find
命令的替代方案。虽然它并不旨在支持 find
的所有强大功能,但它为大多数使用场景提供了明智的(主观的)默认设置。
特性
- 直观的语法:
fd PATTERN
而不是find -iname '*PATTERN*'
。 - 正则表达式(默认)和基于 glob 的模式。
- 由于并行目录遍历,因此速度非常快。
- 使用颜色突出显示不同的文件类型(与
ls
相同)。 - 支持并行命令执行
- 智能大小写:默认情况下,搜索不区分大小写。如果模式包含大写字符,则切换为区分大小写*。
- 默认情况下,忽略隐藏目录和文件。
- 默认情况下,忽略
.gitignore
中的模式。 - 命令名称比
find
短 50%* :-)。
演示
如何使用
首先,要概览所有可用的命令行选项,您可以运行 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
提供了两种为每个搜索结果执行外部命令的方法:
-x
/--exec
选项 为每个搜索结果(并行)运行外部命令。-X
/--exec-batch
选项启动外部命令一次,所有搜索结果作为参数。
示例
递归查找所有 zip 归档文件并解压缩它们:
fd -e zip -x unzip
如果有两个这样的文件,file1.zip
和 backup/file2.zip
,这将执行 unzip file1.zip
和 unzip 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.py
和 lib/test_advanced.py
,这将运行 vim test_basic.py lib/test_advanced.py
。
要查看文件权限、所有者、文件大小等详细信息,您可以告诉 fd
通过为每个结果运行 ls
来显示它们:
fd … -X ls -lhd --color=always
此模式非常有用,fd
提供了一个快捷方式。您可以使用 -l
/--list-details
选项以这种方式执行 ls
:fd … -l
。
当将 fd
与 ripgrep (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 的语法:
{}
:占位符令牌,将替换为搜索结果的路径 (documents/images/party.jpg
)。{.}
:与{}
类似,但不包含文件扩展名 (documents/images/party
)。{/}
:一个占位符,将被搜索结果的基本名称替换 (party.jpg
)。{//}
:发现路径的父级 (documents/images
)。{/.}
:基本名称,不带扩展名 (party
)。
如果您不包含占位符,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
还支持其他程序(如 rg
或 ag
)使用的 .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 rm
的 fd
。或者,使用 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
对于此特定示例,fd
比 find -iregex
快约 23 倍,比 find -iname
快约 13 倍。顺便说一句,这两个工具都找到了完全相同的 546 个文件😄。
注意:这是 一个特定的 基准测试在 一台特定的 机器上。虽然我们已经执行了很多不同的测试(并且发现了始终如一的结果),但对于您来说,情况可能有所不同!我们鼓励每个人在自己的计算机上尝试一下。请参阅此存储库 以获取所有必要的脚本。
关于 fd 的速度,很大一部分归功于 regex
和 ignore
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
似乎无法正确解释我的正则表达式模式
许多特殊的正则表达式字符(例如 []
、^
、$
等)也是 shell 中的特殊字符。如有疑问,请始终确保将单引号引起来正则表达式模式:
> fd '^[A-Z][0-9]+$'
如果您的模式以短划线开头,则必须添加 --
以指示命令行选项的结束。否则,该模式将被解释为命令行选项。或者,使用带有单个连字符字符的字符类:
> fd -- '-pattern'
> fd '[-]pattern'
对于 alias
es 或 shell 函数,“找不到命令”
Shell alias
es 和 shell 函数不能用于通过 fd -x
或 fd -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
的输出格式化为文件树,您可以使用带有 --fromfile
的 tree
命令:
❯ 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 与 xargs
或 parallel
结合使用
请注意,fd
具有内置功能,可以使用其 -x
/--exec
和 -X
/--exec-batch
选项执行命令。如果您愿意,仍然可以将其与 xargs
结合使用:
> fd -0 -e rs | xargs -0 wc -l
在这里,-0
选项告诉 fd 使用 NULL 字符(而不是换行符)分隔搜索结果。以相同的方式,xargs
的 -0
选项告诉它以这种方式读取输入。
安装
在 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
您可以使用 Flox 将 fd
安装到 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-APACHE 和 LICENSE-MIT 文件。