fzf 做的事情,一句话能说完:从标准输入读一个列表,让你模糊地挑,把选中的写到标准输出。
就这一条管道模型,决定了它的适用范围——喂文件名进去就是文件选择器,喂历史命令就是搜索器,喂 Git 分支就是切换器,喂进程列表就是进程杀手,喂你自己的数据就是通用菜单。
它是 Unix 哲学做得最干净的一个现代工具:只干一件事,但能被组合进任何地方。2026 年它的最新稳定版是 0.74.x(2026-08 发布),每 4~8 周一个次版本,没有 LTS 线,跟着最新走就行。
一、为什么是它
先看它能替掉什么:
选它的三个理由:
单个 Go 二进制,零依赖。丢进 PATH 就能用,服务器上 scp 一个文件过去也行。优化过的匹配算法在毫秒级处理百万行。
可被管道组合。它不假设自己在找文件——它假设自己在过滤文本。这个抽象层级刚好卡在"足够通用"和"足够好用"之间。
可编程。基于事件-动作绑定(
--bind),你能把一个 shell 脚本、一个 rg 查询、甚至一个文件树变成带实时预览的 TUI,而不必去写 curses。
如果你只在终端里装两个效率工具,那就装 fzf 和 ripgrep——一个负责"挑",一个负责"搜",剩下的事情基本都能拼出来。
二、安装
包管理器一行搞定:
# macOS
brew install fzf
# Debian 9+ / Ubuntu 19.10+
sudo apt install fzf
# Fedora
sudo dnf install fzf
# Arch
sudo pacman -S fzf
# Windows
winget install fzf
scoop install fzf
choco install fzf
也可以用第 2 篇介绍过的 mise 统一管(跨平台一份配置):
mise use -g fzf@latest
或者用官方安装脚本,它会顺手帮你写好 shell 集成:
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
关键提醒:包管理器装完,你只是得到了 fzf 这个二进制。快捷键和模糊补全需要额外开启——这是"装完了但按 CTRL-R 没反应"的唯一原因。在 shell 配置里加一行:
# ~/.bashrc
eval "$(fzf --bash)"
# ~/.zshrc
source <(fzf --zsh)
# ~/.config/fish/config.fish
fzf --fish | source
Windows 上的两种情况
如果你的主力是 Git Bash 或 WSL,那跟 Linux 完全一样,eval "$(fzf --bash)" 加进 ~/.bashrc 就结束了。
如果主力是 PowerShell,注意官方的 shell 集成不覆盖 PowerShell,需要装社区模块 PSFzf:
winget install fzf
Install-Module PSFzf -Scope CurrentUser -Force
# 加进 $PROFILE
Import-Module PSFzf
Set-PsFzfOption -PSReadlineChordProvider 'Ctrl+t' -PSReadlineChordReverseHistory 'Ctrl+r'
两个容易翻车的点:PSReadline 必须先于 PSFzf 导入(PSFzf 是在模块导入时注册按键处理器的);如果报"禁止运行脚本",先执行 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser。
三、三个默认快捷键
开启 shell 集成后,你得到这三个键。它们覆盖了日常 80% 的使用:
三个都可以单独关掉或换列表来源:
# 只要 CTRL-R,把另外两个禁用
FZF_CTRL_T_COMMAND= FZF_ALT_C_COMMAND= eval "$(fzf --bash)"
# 换 ALT-C 的目录来源
export FZF_ALT_C_COMMAND='fd --type d --hidden --exclude .git'
CTRL-R 是性价比最高的那个。它直接替换掉 shell 内置的反向搜索——内置版本只做按顺序的子串匹配,你得记住命令开头是什么;fzf 是模糊匹配,记住中间某个词就能翻出来。半年零散敲过的长命令,两秒钟能找到。
四、**<TAB> 模糊补全
除了三个快捷键,shell 集成还会把 ** 变成补全触发器。光标前的单词以 ** 结尾时按 TAB,就弹出 fzf:
vim **<TAB> # 当前目录下的文件,可 TAB 多选
cd **<TAB> # 当前目录下的目录
vim ~/**<TAB> # 家目录下的文件
kill -9 **<TAB> # 进程 ID
ssh **<TAB> # 主机名(读 /etc/hosts 和 ~/.ssh/config)
不喜欢 ** 可以换:
export FZF_COMPLETION_TRIGGER='~~'
五、搜索语法速查(fzf 的隐藏考点)
fzf 默认开启扩展搜索模式:用空格分隔的多个条件是与(AND)关系。这一节是很多人用了几年都没打开的能力。
一行组合起来看:
# 精确含 wild、以 music 开头、以 .mp3 结尾、含 sbtrkt、排除 rmx
fzf --query "'wild ^music .mp3$ sbtrkt !rmx"
# 想要纯模糊、不要扩展语法
fzf --no-extended --exact
记忆点:' 是"别给我模糊",^ $ 是"卡头尾",! 是"排除",| 是"或"。
六、命令行参数速查
占位符(用在 --preview、--bind 的动作里):
注意:--tmux 这个老选项在 0.71 之后已被 --popup 取代,老 tmux 用独立的 fzf-tmux 脚本。
七、界面内按键速查
fzf 跑起来之后,界面里的键基本是 readline 那一套:
八、五个真实场景
场景 1:先把 CTRL-R 用起来
这是唯一一个"学会就再也回不去"的功能。压力测试一下:随便敲个关键词,比如 docker、ssh、uv,看半年前的命令怎么被捞出来。
TAB 还能多选——一次捞多条历史命令出来,比如按顺序把三条 ssh 命令拼成一行。
场景 2:文件预览,只打不含糊
# 临时用一次
fzf --preview 'bat --color=always --style=numbers {}' --preview-window right:60%
# 让 CTRL-T 永远带预览
export FZF_CTRL_T_OPTS="--preview 'bat --color=always {}' --preview-window right:60%"
bat 是 cat 的彩色带行号版本,跟 fzf 是固定搭配。装上它,挑文件时右边直接显示内容,不用"打开看了发现不是"。
场景 3:Git 分支与提交
# 切分支
git switch $(git branch --all | grep -v HEAD | fzf)
# 看某个历史提交干了什么,右边直接预览 diff 摘要
git log --oneline | fzf --preview 'git show --stat --color=always {1}'
官方作者还做了一个 fzf-git.sh,把 CTRL-G CTRL-B(分支)、CTRL-G CTRL-F(文件)这类组合键做成脚本,加载后就能在命令行里直接找 Git 对象——和第 1 篇的 lazygit 是两种路子,一个全程 TUI,一个只借用 fzf 当选择器。
场景 4:在脚本里当选择器
这是 fzf 真正的抽象价值:任何脚本要"让人选一个",都可以不用写菜单。
# 找出并杀掉一个进程
ps -ef | fzf | awk '{print $2}' | xargs kill -9
# 多选后一起打开
fzf --multi --bind 'enter:become(vim {+})'
# 多选后全选确认
fzf --multi --bind 'ctrl-a:select-all+accept'
场景 5:rg + fzf,内容搜索的正确组合
这是高阶用法,也是唯一一个"用对了比用错了快十倍"的地方。
RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
fzf --ansi --delimiter : \
--preview 'bat --color=always {1} --highlight-line {2}' \
--preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
--bind "change:reload:$RG_PREFIX {q} || true" \
--bind 'enter:become(vim {1} +{2})'
拆开看:rg 负责全项目内容检索;fzf 只做交互界面;--delimiter : 把 文件:行号:内容 切成字段,于是 {1} 是文件、{2} 是行号;--preview 用 bat 打开文件并高亮那一行;change:reload 让你边打字边用 rg 重新搜(所以大项目里不卡);enter:become 直接把 vim 起在准确的行号上。
性能上的取舍:fzf 本身很快,但它不适合直接搜大文件里的文本——那是 ripgrep 的活。上面这种写法把搜索完全交给 rg,fzf 只提供界面,代价是失去一部分模糊性,换来大项目下更好的性能和内存占用。另外 change:reload 最好加防抖,避免每敲一个字符就起一个子进程:
--bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true"
九、配置
默认行为靠环境变量调:
# ~/.bashrc 或 ~/.zshrc
export FZF_DEFAULT_COMMAND='fd --type f --strip-cwd-prefix --hidden --follow --exclude .git'
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
export FZF_DEFAULT_OPTS="--layout=reverse --height 40% --border --inline-info"
选项多了之后,写成文件更清爽:
export FZF_DEFAULT_OPTS_FILE=~/.fzfrc
# ~/.fzfrc
--layout=reverse
--height=40%
--border
--inline-info
此外,三个快捷键各自还有独立的变量:FZF_CTRL_T_OPTS、FZF_CTRL_R_OPTS、FZF_ALT_C_OPTS,可以只给其中一个加预览。
一个明确的禁区:不要把
--preview放进FZF_DEFAULT_OPTS。fzf 是通用文本过滤器,ps -ef | fzf这种输入下{}并不是文件名,预览命令会报错。预览只加在具体用途的*_OPTS里。
十、常见坑
十一、上手路径
不用一次学完,按四天分步来:
真正需要练的只有一件事:遇到"要在列表里选一个"的场景时,想到 fzf。想到了,剩下的都是查表。
十二、参考
项目主页:https://github.com/junegunn/fzf
完整手册:
man fzf,或仓库里的man/man1/fzf.1高级示例(rg 集成、日志跟踪、动态重载):仓库
ADVANCED.mdGit 集成脚本:https://github.com/junegunn/fzf-git.sh
PowerShell 集成模块 PSFzf:https://github.com/kelleyma49/PSFzf
配色与主题调试:https://vitormv.github.io/fzf-themes/