fd
fd
是一个用于在文件系统中查找条目的程序。它是find
的一个简单、快速且用户友好的替代品。虽然它并不旨在支持find
的所有强大功能,但它为大多数使用场景提供了合理的(有主见的)默认设置。
特性
- 直观的语法:使用
fd PATTERN
而不是find -iname '*PATTERN*'
。 - 支持正则表达式(默认)和通配符模式。
- 非常快速,归功于并行化的目录遍历。
- 使用颜色突出显示不同的文件类型(与
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
。
-X
选项在将fd
与ripgrep(rg
)结合使用时也很有用,可以在特定类型的文件中搜索,比如所有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
[!NOTE]
fd
也支持.ignore
文件,这些文件被其他程序如rg
或ag
使用。
如果你希望 fd
在全局范围内忽略这些模式,你可以将它们放在 fd
的全局忽略文件中。
在 macOS 或 Linux 中通常位于 ~/.config/fd/ignore
,在 Windows 中位于 %APPDATA%\fd\ignore
。
如果你使用 --hidden
选项,你可能希望在 fd/ignore
文件中包含 .git/
,这样 .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
标志来删除目录。
[!NOTE] 在某些情况下,使用
fd … -X rm -r
可能会导致竞争条件:如果你有一个 像…/foo/bar/foo/…
这样的路径,并且想删除所有名为foo
的目录,你可能会遇到 外层foo
目录先被删除的情况,导致rm
调用中出现(无害的)*"'foo/bar/foo': 没有这样的文件或目录"*错误。
命令行选项
这是 fd -h
的输出。要查看完整的命令行选项集,使用 fd --help
,
其中还包括更详细的帮助文本。
用法: fd [选项] [模式] [路径]...
参数:
[模式] 搜索模式(正则表达式,除非使用'--glob';可选)
[路径]... 文件系统搜索的根目录(可选)
选项:
-H, --hidden 搜索隐藏文件和目录
-I, --no-ignore 不遵守 .(git|fd)ignore 文件
-s, --case-sensitive 区分大小写搜索(默认:智能大小写)
-i, --ignore-case 不区分大小写搜索(默认:智能大小写)
-g, --glob 基于 glob 的搜索(默认:正则表达式)
-a, --absolute-path 显示绝对路径而不是相对路径
-l, --list-details 使用长列表格式显示文件元数据
-L, --follow 跟随符号链接
-p, --full-path 搜索完整绝对路径(默认:仅文件名)
-d, --max-depth <深度> 设置最大搜索深度(默认:无)
-E, --exclude <模式> 排除匹配给定 glob 模式的条目
-t, --type <文件类型> 按类型过滤:文件 (f),目录 (d/dir),符号链接 (l),
可执行文件 (x),空文件 (e),套接字 (s),管道 (p),字符设备
(c),块设备 (b)
-e, --extension <扩展名> 按文件扩展名过滤
-S, --size <大小> 根据文件大小限制结果
--changed-within <日期|时长> 按文件修改时间过滤(比指定时间新)
--changed-before <日期|时长> 按文件修改时间过滤(比指定时间旧)
-o, --owner <用户:组> 按所有者用户和/或组过滤
--format <格式> 根据模板打印结果
-x, --exec <命令>... 为每个搜索结果执行命令
-X, --exec-batch <命令>... 一次性为所有搜索结果执行命令
-c, --color <何时> 何时使用颜色 [默认:auto] [可能的值:auto,
always, never]
-h, --help 打印帮助信息(使用'--help'查看更多)
-V, --version 打印版本信息
基准测试
让我们在我的主文件夹中搜索以 [0-9].jpg
结尾的文件。它包含约 750,000 个
子目录和大约 400 万个文件。为了进行平均和统计分析,我使用
hyperfine。以下基准测试是在
"热"/ 预填充磁盘缓存的情况下执行的("冷"磁盘缓存的结果显示相同的趋势)。
让我们从 find
开始:
基准测试 1: find ~ -iregex '.*[0-9]\.jpg$'
时间(平均值 ± σ): 19.922 秒 ± 0.109 秒
范围(最小值 … 最大值): 19.765 秒 … 20.065 秒
如果不需要执行正则表达式搜索,find
会快得多:
基准测试 2: find ~ -iname '*[0-9].jpg'
时间(平均值 ± σ): 11.226 秒 ± 0.104 秒
范围(最小值 … 最大值): 11.119 秒 … 11.466 秒
现在让我们对fd
进行同样的尝试。请注意,fd
默认执行正则表达式搜索。这里需要使用-u
/--unrestricted
选项来进行公平比较。否则fd
不必遍历隐藏文件夹和被忽略的路径(见下文):
基准测试3:fd -u '[0-9]\.jpg$' ~
时间(平均值 ± σ): 854.8 ms ± 10.0 ms
范围(最小值 … 最大值): 839.2 ms … 868.9 ms
在这个特定例子中,fd
比find -iregex
快大约23倍,比find -iname
快大约13倍。顺便说一下,两个工具都找到了完全相同的546个文件:smile:。
注意:这只是在一台特定机器上的一个特定基准测试。虽然我们进行了许多不同的测试(并得到了一致的结果),但对你来说情况可能会有所不同!我们鼓励每个人在自己的环境中进行尝试。所有必要的脚本可以在这个仓库中找到。
关于fd的速度,很大一部分功劳归功于regex
和ignore
crates,它们也被用于ripgrep(推荐你去看看!)。
故障排除
fd
找不到我的文件!
记住,fd
默认会忽略隐藏目录和文件。它还会忽略.gitignore
文件中的模式。如果你想确保找到所有可能的文件,请始终使用-u
/--unrestricted
选项(或使用-HI
来启用隐藏和被忽略的文件):
> fd -u …
彩色输出
fd
可以像ls
一样按扩展名为文件着色。为了使这个功能生效,需要设置环境变量LS_COLORS
。通常,这个变量的值由dircolors
命令设置,它提供了一个方便的配置格式来为不同的文件格式定义颜色。
在大多数发行版中,LS_COLORS
应该已经设置好了。如果你使用的是Windows,或者你在寻找其他更完整(或更丰富多彩)的变体,可以参考这里、这里或这里。
fd
也遵守NO_COLOR
环境变量。
fd
似乎没有正确解释我的正则表达式模式
许多特殊的正则表达式字符(如[]
、^
、$
等)在你的shell中也是特殊字符。如有疑问,请始终确保将正则表达式模式用单引号括起来:
> fd '^[A-Z][0-9]+$'
如果你的模式以短横线开头,你必须添加--
来表示命令行选项的结束。否则,该模式将被解释为命令行选项。或者,使用只包含一个连字符字符的字符类:
> fd -- '-pattern'
> fd '[-]pattern'
"找不到命令"对于alias
或shell函数
Shell alias
和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
命令添加参数。
在emacs
中使用fd
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
的输出格式化为文件树,你可以使用tree
命令的--fromfile
选项:
❯ fd | tree --fromfile
这可能比直接运行tree
更有用,因为tree
默认不会忽略任何文件,也不支持像fd
那样丰富的选项来控制打印内容:
❯ fd --extension rs | tree --fromfile
.
├── build.rs
└── src
├── app.rs
└── error.rs
在bash及类似的shell中,你可以简单地创建一个别名:
❯ 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
注意,二进制文件被命名为fdfind
,因为二进制名称fd
已被另一个包使用。
建议安装后,通过执行命令ln -s $(which fdfind) ~/.local/bin/fd
添加一个链接到fd
,以便像本文档中一样使用fd
。
确保$HOME/.local/bin
在你的$PATH
中。
如果您使用较旧版本的Ubuntu,可以从发布页面下载最新的.deb
包并通过以下方式安装:
dpkg -i fd_9.0.0_amd64.deb # 根据版本号和架构调整
请注意,此项目发布页面上的.deb包仍将可执行文件命名为fd
。
在Debian上
如果您运行的是Debian Buster或更新版本,可以安装官方维护的Debian包:
apt-get install fd-find
注意二进制文件被称为fdfind
,因为fd
这个二进制名称已被另一个包使用。
建议安装后,执行命令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
# 构建
cd fd
cargo build
# 运行单元测试和集成测试
cargo test
# 安装
cargo install --path .
维护者
许可证
fd
在MIT许可证和Apache许可证2.0的条款下分发。
有关许可详情,请参阅LICENSE-APACHE和LICENSE-MIT文件。