Fd

fd #

中文 🇨🇳     English 🇬🇧 #

fd 是一个在你文件系统中查找条目的程序。它是一个简单、快速、友好的 find 替代品。虽然它的目标不是支持 find 的所有强大功能,但它为大多数的使用情况提供了合理的(有意见的)默认值。

快速浏览:

特点 #

  • 直观的语法:用 fd PATTERN 代替 find -iname '*PATTERN*'

  • 使用正则表达式(默认)和 GLOB 模式

  • 由于并行遍历目录, 速度非常快

  • 使用颜色来突出不同的文件类型(像 ls 一样)

  • 支持 并发执行命令

  • 智能大小写:默认情况下,搜索是不区分大小写的。如果模式包含一个大写字母,它将切换到大小写敏感 *

  • 默认忽略隐藏目录和文件

  • 默认忽略你 .gitignore 文件中的模式

  • 该命令比 find 短了 50% * :-)

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)。

匹配完整路径 #

默认情况下,fd 只匹配每个文件的文件名。然而,使用 --full-path-p 选项,你可以对全路径进行匹配:

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

命令执行 #

比起只是显示搜索结果,你往往还想对它们做另一些事情。fd 提供了两种方法来为你的每一个搜索结果执行外部命令

  • -x/--exec 选项为每个搜索结果运行一个外部命令(并行)。

  • -X/--exec-batch 选项启动一次外部命令,将所有搜索结果作为参数。

例子 #

递归找到所有的压缩文件并解压:

fd -e zip -x unzip

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

找到所有的*.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 选项,以这种方式执行ls:fd … -l

当把 fdripgreprg)结合起来时,-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 也支持其他程序使用的 .ignore 文件,如 rgag

如果你想让 fd 在全局范围内忽略这些模式,你可以把它们放在 fd 的全局忽略文件中。在 macOS 或 Linux 中,这个文件通常位于 ~/.config/fd/ignore,在Windows中则位于 %APPDATA%\fd/ignore

删除文件 #

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

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

如果你不确定是否需要删除,请先调用不带 -X rmfd。或者,使用 rm 的 “interactive” 选项:

> 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]  搜索模式 (正则表达式,除非使用了 '--glob'; 可选的)
  [path]...  文件系统搜索的根目录 (可选的)

Options:
  -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 <depth>          设置最大搜索深度 (默认: )
  -E, --exclude <pattern>          排除符合给定 glob 模式的条目
  -t, --type <filetype>            按类型过滤: file (f), directory (d), symlink (l),
                                   executable (x), empty (e), socket (s), pipe (p)
  -e, --extension <ext>            按文件扩展名过滤
  -S, --size <size>                根据文件的大小限制结果
      --changed-within <date|dur>  按文件修改时间过滤 (较新)
      --changed-before <date|dur>  按文件修改时间过滤 (较旧)
  -o, --owner <user:group>         按拥有文件的用户 (/或者) 
  -x, --exec <cmd>...              对每个搜索结果执行命令
  -X, --exec-batch <cmd>...        一次执行包含所有搜索结果的命令
  -c, --color <when>               什么时候使用颜色 [默认: auto] [可以使用的值: auto,
                                   always, never]
  -h, --help                       打印帮助信息 (使用 `--help` 获取更多细节)
  -V, --version                    打印版本信息

基准测试 #

让我们在我的主文件夹中搜索以 [0-9].jpg 结尾的文件。它包含大约190,000个子目录和一百万个文件。对于平均数和统计分析,我使用了 hyperfine。以下基准测试是使用 “warm”/pre-filled 磁盘缓存执行的(“cold” 磁盘缓存的结果显示相同的趋势)。

让我们从 find 开始:

Benchmark #1: find ~ -iregex '.*[0-9]\.jpg$'

  Time (mean ± σ):      7.236 s ±  0.090 s

  Range (min … max):    7.133 s …  7.385 s

如果不使用正则表达式搜索,find 的速度会快很多:

Benchmark #2: find ~ -iname '*[0-9].jpg'

  Time (mean ± σ):      3.914 s ±  0.027 s

  Range (min … max):    3.876 s …  3.964 s

现在让我们对 fd 做同样的尝试。注意,fd 总是 执行一个正则表达式搜索。为了进行公平的比较,需要有选项 --hidden--no-ignore,否则 fd 就不遍历隐藏的文件夹和忽略的路径(见下文):

Benchmark #3: fd -HI '.*[0-9]\.jpg$' ~

  Time (mean ± σ):     811.6 ms ±  26.9 ms

  Range (min … max):   786.0 ms … 870.7 ms

对于这个特定的例子,fdfind -iregex 快了大约9倍,比 find -iname 快了大约5倍。顺便说一下,两个工具都找到了完全相同的20880个文件 😄。

最后,让我们在没有 --hidden--no-ignore 的情况下运行 fd(当然,这会导致不同的搜索结果)。如果 fd 不必遍历隐藏的和 git 忽略的文件夹,它几乎快了一个数量级:

Benchmark #4: fd '[0-9]\.jpg$' ~

  Time (mean ± σ):     123.7 ms ±   6.0 ms

  Range (min … max):   118.8 ms … 140.0 ms

注意:这是一个特定机器上的一个特定基准。虽然我已经进行了相当多的不同测试(并发现了一致的结果),但对你来说,情况可能会有所不同!我鼓励大家自己尝试。我鼓励大家自己去尝试。所有必要的脚本请见 这个仓库

关于 fd 的速度,主要归功于 regexignore 模块,它们也被用在 ripgrep 中(快来看看吧!)。

排除故障 #

彩色化输出 #

fd 可以按扩展名给文件着色,就像 ls 一样。为了使其发挥作用,环境变量 LS_COLORS 必须被设置。通常,这个变量的值是由 dircolors 命令设置的,它提供了一个方便的配置格式来定义不同文件格式的颜色。在大多数发行版上,LS_COLORS 应该已经被设置了。如果你是在 Windows 系统上,或者你在寻找其他更完整(或更多彩)的变体,请看 这里这里这里

fd 也遵守 NO_COLOR 环境变量的规定。

fd没有找到我的文件! #

记住,fd 默认会忽略隐藏的目录和文件。它还会忽略 .gitignore 文件的模式。如果你想确保绝对找到所有可能的文件,一定要使用选项 -H-I 来禁用这两个功能:

> fd -HI …

fd 似乎不能正确地解释我的正则表达式模式 #

许多特殊正则表达式字符(如[]^$、..)也是shell中的特殊字符。如果有疑问,请务必在正则表达式模式周围加上单引号:

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

如果模式以破折号开始,则必须添加 -- 以表示命令行选项的结束。否则,该模式将被解释为命令行选项。或者,使用带有单个连字符的字符类:

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

执行alias或shell函数提示 “Command not found” #

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

与其他项目的整合 #

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 中的 提示部分

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 {}

emac 一起使用 #

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

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

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

将输出结果打印成树状 #

为了使 fd 的输出格式类似于 tree 命令,请安装 as-tree,并将 fd 的输出通过管道输送到 as-tree

fd | as-tree

这可能比运行 tree 本身更有用,因为 tree 默认不会忽略任何文件,也不像 fd 那样支持丰富的选项来控制打印的内容。

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

关于 as-tree 的更多信息,请参见 as-treeREADME

xargs 或 parallel 一起使用 #

请注意,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)或更新的版本,你可以安装 官方维护的软件包

sudo apt install fd-find

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

如果你使用的是旧版 Ubuntu,你可以从 发布页面下载最新的.deb包安装。

sudo dpkg -i fd_8.6.0_amd64.deb  # adapt version number and architecture

在Debian上 #

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

sudo apt-get install fd-find

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

在Fedora上 #

从Fedora 28开始,你可以从官方源安装 fd

dnf install fd-find

在Alpine Linux上 #

你可以从官方库安装 [fd 软件包]( Arch Linux - fd 8.6.0-1 (x86_64))。

pacman -S fd

在Gentoo Linux上 #

你可以使用官方库中的 fd ebuild

emerge -av fd

在openSUSE Linux上 #

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

zypper in fd

在Void Linux上 #

你可以通过 xbps-install 来安装 fd

xbps-install -S fd

在RedHat Enterprise Linux 8 (RHEL8), Almalinux 8, EuroLinux 8 或 Rocky Linux 8上 #

github 上的 sharkdp 中获取最新的 fd-v*-x86_64-unknown-linux-gnu.tar.gz 文件

tar xf fd-v*-x86_64-unknown-linux-gnu.tar.gz
chown -R root:root fd-v*-x86_64-unknown-linux-gnu
cd fd-v*-x86_64-unknown-linux-gnu
sudo cp fd /bin
gzip fd.1
chown root:root fd.1.gz
sudo cp fd.1.gz /usr/share/man/man1
sudo cp autocomplete/fd.bash /usr/share/bash-completion/completions/fd
source /usr/share/bash-completion/completions/fd
fd

在macOS上 #

你可以用 Homebrew 安装 fd

brew install fd

…或者用 MacPorts:

sudo port install fd

在Windows上 #

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

或者,你也可以通过 Scoop 安装 fd

scoop install fd

或者通过 Chocolatey

choco install fd

在GuixOS上 #

你可以从官方库安装 fd 软件包

guix install fd

在NixOS上/通过Nix安装 #

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

nix-env -i fd

在FreeBSD上 #

你可以从官方库中安装 fd-find 软件包

pkg install fd-find

通过npm #

在 Linux 和 macOS 上,你可以通过npm安装 fd-find 软件包。

npm install -g fd-find

通过源码 #

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

cargo install fd-find

注意,需要 1.60.0 或更高版本的 Rust。

构建时还需要 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 .

维护者 #

License #

Copyright (c) 2017-2021 The fd developers

fd is distributed under the terms of both the MIT License and the Apache License 2.0.

See the  LICENSE-APACHE and  LICENSE-MIT files for license details.