工欲善其事,必先利其器,常使用Linux命令终端的人都知道,bash默认情况下提供了很多便利的功能,比如TAB命令补全等,CTRL+R命令搜索等,但是这些功能往往有这样或者那样的问题致使不够尽兴。今天,虫虫给大家介绍一款强大通用系统模糊搜索补全工具Fzf,可以给这方面的功能锦上添花,趋于完美。
概述
Fzf是一个开源的交互式shell命令行模糊查询工具,可以用于对文件,历史记录(history),进程,主机名,书签,git commit等等列表和应用。
Fzf是用Golang语言开发的shell系统工具,源代码托管再Github上,截止目前该项目已经累积了25500星,Fork上千。
借助于Golang静态编译的特点,Fzf具有便携式,无依赖性;高性能,速度飞快的优点,同时还具有:
最全面的功能集;
布局灵活;
额外强大的插件支持:
比如Vim/Neovim插件,可支持键绑定和模糊自动补全。
安装
Fzf支持通过Homebrew、操作系统包管理器以及源码方式安装。其文件项目有四部分组成,包括以下组件:
fzf可执行文件;
fzf-tmux脚本,用于在tmux窗格中启动fzf;
Shell扩展:支持按键绑定(CTRL-T,CTRL-R和ALT-C),支持bash,zsh,fish。
模糊自动补全,支持bash,zsh。
Vim/Neovim插件。
其中,fzf可执行文件是必须的,其他组件依赖于它。如果不需要其他组件,可以单独下载fzf可执行文件运行。
发行版包管理器安装
Fzf最简单的安装方法就是通过OS发行版本的包管理器一键安装。Fzf支持了最广泛的发行版本安装:
Arch Linux:
sudo pacman -S fzf
Debian系:
fzf支持Debian Buster及更新的版本,可以使用apt-get包管理器安装。sudo apt-get install fzf
Fedora:
fzf支持Fedora 26及更新的版本,可以使用dnf安装:
sudo dnf install fzf
默认情况下,启用vim或neovim的Shell补全和插件。Shell键绑定已安装,但默认情况下未启用。有关更多信息,请参见Fedora的软件包文档(/usr/share/doc/fzf/README.Fedora)。
FreeBSD:
pkg install fzf
NixOS:
以使用Nix软件包管理器来安装fzf。
nix-env -iA nixpkgs.fzf
openSUSE:
对于openSUSE 支持Tumbleweed版本,可通过zypper安装:
sudo zypper install fzf
Windows:
Windows的预编译二进制文件可以项目发布页下载。fzf也支持以Chocolatey或Scoop软件包管理器安装:
choco install fzf
scoop install fzf
但是,该项目的其他组件可能无法在Windows上运行。已知问题和限制可以在Wiki页面上找到。可能要考虑在Windows子系统(适用于Linux)上安装fzf,以确保一切运行正常。
Homebrew安装
对于Mac系统可以Homebrew fzf。
brew install fzf
如果要安装用键绑定和模糊补全脚本使用:
(brew --prefix)/opt/fzf/install
也可以通过MacPorts:
sudo port install fzf
Git安装
如果你的用的发行版恰好不支持,或者你需要自定义源码安装最新版本,可以通过git clone 克隆项目,然后执行安装程序即可。
git clone --depth 1 github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install
Vim插件
安装好Fzf后,在Vim配置文件中将目录添加到&runtimepath,就可以在Vim启它,如下所示:
" 通过Homebrew安装的
set rtp+=/usr/local/opt/fzf
" 通过git安装的
set rtp+=~/.fzf
如果使用要vim-plug,则配置如下:
"通过Homebrew安装的
Plug '/usr/local/opt/fzf'
"通过git安装的
Plug '~/.fzf'
也可以使用vim-plug来完成安装工作,而不是在系统上单独安装fzf(使用Homebrew或git clone),然后通过配置在Vim中启用(添加到&runtimepath )。
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' }
升级fzf
Fzf在持续迭代开发,为了获取最新功能,则需要时时升级版本。Fzf的升级方法如下:
git安装:
cd ~/.fzf && git pull && ./install
brew:
brew update; brew reinstall fzf
chocolatey: choco upgrade fzf
vim-plug: :PlugUpdate fzf
Fzf源码编译
Fzf编译需要golang 1.11版本以上,还依赖下面的第三方库:
使用的第三方库
mattn/go-runewidth
mattn/ o-shellwords
mattn/go-isatty
tcell
构建fzf二进制文件并将其复制到bin目录,
make && make install
在目标中构建32位和64位可执行文件和tarball
make release
为目标中所有受支持的平台制作发布档案
make release-all
使用go get构建
Fzf也支持,直接通过go get命令,而无需手动克隆存储库。具体命令如下:
go get -u github.com/junegunn/fzf
Fzf用法大全
Fzf安装后,将启动交互式查找程序,从STDIN中读取列表,然后将所选项目写入STDOUT。
find * -type f | fzf > selected
如果没有STDIN管道,则fzf将使用find命令来获取文件列表(不包括隐藏文件)。(可以使用FZF_DEFAULT_COMMAND覆盖默认命令)
vim $(fzf)
使用finder
CTRL-J/CTRL-K(或CTRL-N/CTRL-P)可上下移动光标
输入键选择项目,按CTRL-C/ TRL-G/ESC退出
在多选模式(-m)中,使用TAB和Shift-TAB标记多个项目
Emacs样式键绑定
鼠标:滚动,单击,双击;在多选模式下按住Shift点击并Shift滚动。
布局
默认情况下,Fzf开题全屏模式,我们可以使用--height选项使其在光标下方开始。
vim $(fzf --height 40%)
如果喜欢"自上而下"的布局而不是默认的"自下而上"的布局,请使用--reverse和--layout选项。
vim $(fzf --height 40%)
可以将这些选项添加到$ FZF_DEFAULT_OPTS,以便默认情况下应用它们。例如,
export FZF_DEFAULT_OPTS ='-高度40%--layout = reverse --border'
搜索语法
除非有特殊说明,Fzf都会以扩展搜索模式启动,可以在其中键入多个以空格分隔的搜索词。比如:^music .mp3$ chongchong !fire
相关解释如下:
chongchong模糊匹配,匹配chognchong字条;
^music前缀精准匹配,匹配以music开头的字条;
.mp3$ 后缀精准匹配,匹配以.mp3结尾的字条;
'whild 精准匹配,匹配包含wild的字条;
!fire 反向精准匹配,匹配其中不包含fire的字条;!也支持对前后缀匹配的反向匹配。
上例子表示查找以music开头,以.mp3结尾,不包括fire,含有chongchong的字条。
如果不喜欢模糊匹配并且不想"引用"每个单词,可以用-e或--exact选项启动fzf。注意:设置为--exact时,"-prefix"选项会实效。
单个竖线字符可以充当OR运算符。比如,下面的查询匹配以core开头,以go,rb或py结尾的条目。
^core go$ | rb$ | py$
环境变量
FZF_DEFAULT_COMMAND
输入为tty时使用的默认命令
例如导出FZF_DEFAULT_COMMAND ='fd --type f'
FZF_DEFAULT_OPTS
默认选项
例如export FZF_DEFAULT_OPTS ="-layout = reverse --inline-info"
fzf-tmux脚本
fzf-tmux是一个bash脚本,用来在tmux窗格中打开fzf。
在水平分割中窗体中(15行))显示git分支
git branch | fzf-tmux -d 15
在左侧垂直分割的窗体中(屏幕宽度的20%)显示字典的内容:
cat /usr/share/dict/words | fzf-tmux -l 20% --multi --reverse
如果不是tmux模式,该插件也可以正常工作,只是-[udlr]选项会实效。通常模式下需要使用--height HEIGHT[%]选项,启动非全屏模式的fzf。
fzf --height 40%
命令行的键绑定
安装脚本将会在bash,zsh和fish添加以下按键绑定。
CTRL-T: 将选定的文件或者目录粘贴到命令行上
可通过设置FZF_CTRL_T_COMMAND覆盖默认命令;
可通过设置FZF_CTRL_T_OPTS设置其他选项;
CTRL-R : 从历史记录中将所选命令粘贴到命令行上
如果想按时间顺序查看命令,请再次按CTRL-R,按相关性切换排序
可通过设置FZF_CTRL_R_OPTS设置其他选项;
ALT-C cd进入所选目录
可通过设置FZF_ALT_C_COMMAND以覆盖默认命令;
可通过设置FZF_ALT_C_OPTS设置其他选项;
在tmux会话下,可以通过将FZF_TMUX设置为1来在拆分窗格中启动fzf,然后使用FZF_TMUX_HEIGHT更改窗格的高度(例如20%,50%等)。
如果在bash上使用vi模式,则需要在.bashrc中的source ~/.fzf.bash之前添加set -o vi,以使其正确设置vi模式的键绑定。
可以在Wiki页面上找到更多提示。
bash和zsh的模糊补全
文件和目录
如果光标前的单词以触发序列(默认为**)结尾,则可以触发文件和目录的模糊补全。
COMMAND [DIRECTORY/] [FUZZY_PATTERN] ** <TAB>
当前目录下的文件,可以使用TAB键选择多个项目
cat ** <TAB>
对父目录下的文件名补全
vim ../** <TAB>
父目录下匹配`fzf`的文件
vim ../fzf** <TAB>
用户主目录下的文件
cat ~/** <TAB>
当前目录下的目录(单选)
cd ** <TAB>
~/github下与`fzf`匹配的目录
cd ~/github/fzf** <TAB>
进程ID
PID的模糊补全提供给kill命令。在这种情况下,没有触发序列,只需在kill命令后按tab键即可。
可以使用<TAB>或<Shift-TAB>键选择多个进程
kill -9 <TAB>
主机名
对ssh和telnet命令,支持主机名的模糊补全。补全名称是从/etc/hosts和~/.ssh/config中获取的。
ssh ** <TAB>
telnet ** <TAB>
环境变量/别名
unset **<TAB>
export **<TAB>
unalias **<TAB>
默认配置项
触发关键字
比如使用~~作为触发关键字,不用默认的**,可通过设置:
export FZF_COMPLETION_TRIGGER='~~'
fzf命令的选项
export FZF_COMPLETION_OPTS='+c -x'
默认搜索工具
使用fd(github.com/sharkdp/fd)代替默认find命令列出路径
_fzf_compgen_path() {
fd --hidden --follow --exclude ".git" . "$1"
}
其中函数($1)的第一个参数开始便利的基础目录。
使用fd生成目录补全的列表:
_fzf_compgen_dir() {
fd --type d --hidden --follow --exclude ".git" . "$1"
}
增加命令支持
在bash上,仅对预定义的命令集启用了模糊补全,其详细里列表通过"complete | grep _fzf"查看。Fzf也支持对其进行扩展,方法是:
_fzf_setup_completion path|dir COMMANDS...
比如:
_fzf_setup_completion path ag git kubectl
_fzf_setup_completion dir tree
Vim插件
Fzf的Vim插件提供了两个核心功能fzf#run和fzf#wrap,其中:fzf命令基本文件选择器命令。
fzf#run([spec dict]):使用给定的规范在Vim中启动fzf,比如:
:call fzf#run({'source': 'ls'})
fzf#wrap([spec dict]) -> (dict) :采用fzf#run的规范,并返回其扩展版本以及用于处理全局首选项的其他选项(g:fzf_xxx)。
:echo fzf#wrap({'source': 'ls'})
通常在将规范传递给fzf#run之前将其包装在fzf#wrap
:call fzf#run(fzf#wrap({'source': 'ls'}))
:FZF [fzf_options string] [path string]:基本的模糊文件选择器,对那些不想编写VimScript来实现自定义命令的人的参考实现
用法
:FZF[!]
"在当前目录下查找文件
:FZF
在主目录下查找文件
:FZF ~
使用fzf命令行选项
:FZF --reverse --info=inline /tmp
与ctrlp.vim相似,使用Enter键,CTRL-T,CTRL-X或CTRL-V在当前窗口,新选项卡,水平分割或垂直分割中分别打开选定的文件。
注意,环境变量FZF_DEFAULT_COMMAND和FZF_DEFAULT_OPTS也起作用。
配置项
g:fzf_action:自定义的额外按键绑定,用于以不同方式打开所选文件
g:fzf_layout: 确定fzf窗口的大小和位置
g:fzf_colors: 自定义fzf颜色以匹配当前的配色方案
g:fzf_history_dir: 启用历史记录功能
fzf#run
fzf#run()函数是Vim集成的核心。它需要一个字典参数,一个规范,并相应地启动fzf进程。至少要指定接收器选项,以告诉它应该对所选条目执行的操作。
call fzf#run({'sink': 'e'})
上面没有指定源,所以这等效于在没有标准输入管道的情况下在命令行上启动fzf。fzf将使用find命令(或$ FZF_DEFAULT_COMMAND(如果已定义))列出当前目录下的文件。选择一个文件时候,它将使用接收器:e命令将其打开。如果要在新选项卡中打开它,则可以传递:tabedit命令作为接收器。
call fzf#run({'sink': 'tabedit'})
可以使用任何shell命令作为源,而不是使用默认的find命令。比如下面示例将列出git管理的文件。相当于shell命令行下运行git ls-files | fzf。
call fzf#run({'source': 'git ls-files', 'sink': 'e'})
可以将fzf选项指定为spec词典中的选项条目。
call fzf#run({'sink': 'tabedit', 'options': '--multi --reverse'})
不希望fzf窗口占据整个屏幕,还可以传递布局选项。
call fzf#run({'source': 'git ls-files', 'sink': 'e', 'left': '40%'})
call fzf#run({'source': 'git ls-files', 'sink': 'e', 'window': '30vnew'})
源不一定是外部shell命令,可以传递Vim数组作为源。下面的示例中,传递了颜色名称实现颜色方案选择器的源。
call fzf#run({'source': map(split(globpath(&rtp, 'colors/*.vim')),
\ 'fnamemodify(v:val, ":t:r")'),
\ 'sink': 'colo', 'left': '25%'})
fzf#wrap
前面说过:FZF命令的几个方面可以使用一组全局选项变量进行配置。使用不同的方式打开文件(g:fzf_action),窗口位置和大小(g:fzf_layout),调色板(g:fzf_colors)等不同的方式。那么,如何使我们的自定义fzf#run调用也能起作用呢?只需将其传递给fzf#run,然后用fzf#wrap修饰spec词典:
fzf#wrap([name string], [spec dict], [fullscreen bool]) -> (dict)
所有参数都是可选的。通常只需要传递一个规范字典。
name用于管理历史记录文件。如果未定义g:fzf_history_dir,则会被忽略。
fullscreeen 可设置为0或1(默认值:0)。
fzf#wrap接受一个规范,并返回它的扩展版本(也是字典),并带有用于解决全局首选项的其他选项。可以像这样检查它的返回值:
echo fzf#wrap({'source': 'ls'})
打包规范之后,就可将其传递给fzf#run。
call fzf#run(fzf#wrap({'source': 'ls'}))
现在,它支持CTRL-T,CTRL-V和CTRL-X键绑定,并根据g:fzf_layout设置打开fzf窗口。
为了使其易于使用,让我们定义LS命令使用。
command! LS call fzf#run(fzf#wrap({'source': 'ls'}))
然后就可以通:LS调用。
其他功能
性能问题
Fzf很快,而且会变得越来越快。在大多数情况下,性能都不是问题。但是有几个性能选项需要了解。
--ansi:告诉Fzf提取并解析输入中的ANSI颜色代码,这会使初始扫描变慢。因此,不建议将其添加到$FZF_DEFAULT_OPTS中。
--nth:该选项也会使fzf变慢,指定该选项后fzf必须对每行做标记。
--with-nth:该选项也会使fzf变慢,指定该选项后fzf必须每行标记并重新转载。
如果对想能有严苛要求,可以考虑使用--algo = v1(默认值为v2)使fzf使用更快的贪婪算法。但是,不能保证该算法找到最佳顺序的匹配,因此一般不建议使用。
执行外部程序
可以通过设置键绑定来启动外部进程,而无需离开fzf(execute,execute-silent)。
通过按F1键以较少的文件打开文件而不离开fzf,通过按CTRL-Y将行复制到剪贴板并中止fzf(需要pbcopy支持)
fzf --bind 'f1:execute(less -f {}),ctrl-y:execute-silent(echo {} | pbcopy)+abort'
预览窗口
设置--preview选项时,fzf会自动以当前行作为参数启动外部进程,并在拆分窗口中显示结果。
使用{}替换为焦点行的单引号字符串
fzf --preview 'cat {}'
由于预览窗口仅在该过程完成后才更新,因此该命令快速完成很重要。
使用head而不是cat,以便命令不会花费太长时间来完成
fzf --preview 'head -100 {}'
预览窗口支持ANSI颜色,因此可以使用语法突出显示文件内容的程序,比如和
bat、Highlight、CodeRay、Rouge等配合使用,比如下面的命令依次使用bat, highlight, coderay, rougify,然后回退到cat:
fzf --preview '[[ $(file --mime {}) =~ binary ]] &&
echo {} is a binary file ||
(bat --style=numbers --color=always {} ||
highlight -O ansi -l {} ||
coderay {} ||
rougify {} ||
cat {}) 2> /dev/null | head -500'
也可以使用--preview-window选项自定义预览窗口的大小和位置。例如,
fzf --height 40% --reverse --preview 'file {}' --preview-window down:1
fzf --height 40%--reverse --preview'file {}'--preview-window down:1
.gitignore
可以使用fd,ripgrep或silver搜索程序代替默认的find命令来遍历文件系统,搜索时候支持利用git忽略定义.gitignore文件排除文件和目录。
将fd的输出输入fzf
fd --type f | fzf
将fd设置为fzf的默认源
export FZF_DEFAULT_COMMAND='fd --type f'
设置后, fzf(不带管道)将使用fd代替find
也支持对CTRL-T命令使用:
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
如果要使该命令遵循符号链接,并且不希望其排除隐藏文件,可使用以下命令:
export FZF_DEFAULT_COMMAND='fd --type f --hidden --follow --exclude .git'
git ls-tree快速遍历
如果在大型git存储库中运行fzf,则使用git ls-tree可以提高遍历的速度。
export FZF_DEFAULT_COMMAND='
(git ls-tree -r --name-only HEAD ||
find . -path "*/\.*" -prune -o -type f -print -o -type l -print |
sed s/^..//) 2> /dev/null'
Fish Shell
Fish Shell 2.6.0之前的版本不允许在命令替换中从STDIN读取,所以简单的vim(fzf)将无法正常工作。 fish 2.5.0和更早版本需要使用使用read fish命令:
fzf | read -l result; and vim $result
对于多个结果:
fzf -m | while read -l r; set result $result $r; end; and vim $result
fish shell的glob系统系统和shell不同,所以**的补全功能将不起作用。但是,CTRL-T命令将使用命令行最后一个标记作为递归搜索的根文件夹。例如,在以下命令行的末尾按下CTRL-T
ls /var/
将列出/var/下的所有文件和文件夹。
使用自定义FZF_CTRL_T_COMMAND时,请使用未扩展的$dir变量来使用此功能。当最后不是有效目录时,$dir默认为.。 例:
set -g FZF_CTRL_T_COMMAND "command find -L \$dir -type f 2> /dev/null | sed '1d; s#^\./##'"
总结
Fzf是一款非常优秀、老少皆宜的开源系统工具,可以用来取代shell默认的搜索和补全行为。由于使用golang开发安装部署非常方便,性能也非常高。适合于开发者,运维和其他Linux用户,Github 2万多星就足以说明其多受欢迎了,非常值得大家使用。