今天在闲逛博客 的时候偶然发现 crispgm 所写的文章,其中推荐了 fzf 这款工具,所以立马上手体验了一下。

fzf 是一个通用的命令行模糊搜索工具,用 golang 编写,大家的评价都是目前最快的 fuzzy finder,配合 ag 的使用,依靠模糊的关键词,可以快速定位文件。

Installation

git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install

or upgrade

cd ~/.fzf && git pull && ./install

Demo

最直接的使用方式就是在终端输入:

fzf

或者 fzf 可以接受 stdin,比如使用命令

find * -type f | fzf

此时会进入 fzf 的交互窗口,在交互窗口中可以使用 Ctrl + n 或者 Ctrl + p 来上下移动光标。使用 Enter 选中条目

其他常用:

  • Ctrl-j Ctrl-n 都能往下,不过 Ctrl-j 在我的配置中是切换到下 pane 所以我只用 Ctrl-n
  • Ctrl-k Ctrl-p 都能往上,不过 Ctrl-k 在我的配置中是切换到上 pane 所以我只用 Ctrl-p
  • Ctrl-c 或者 Ctrl-g 或者 Ctrl-q 或者 Esc 用来退出 abort
  • Ctrl-h 退格键 backspace,不过这个键和 tmux vim 有冲突所以一般不用
  • Ctrl-a 光标跳转到开头 Ctrl-e 跳转到行结尾
  • Ctrl-w 向前删以 word
  • Ctrl-u 删除所有输入

其实看到后面就发现很多快捷键其实是和 Bash/Shell 下一致的,其他更多的交互命令,可以参考 man fzf

Configuration

FZF_DEFAULT_COMMAND

fzf 默认查找文件使用的是系统的 find 命令,你可以通过自定义该环境变量来更改使用其他命令比如 ag 或者 fd

# 这行配置开启 ag 查找隐藏文件 及忽略 .git 文件
export FZF_DEFAULT_COMMAND='ag --hidden --ignore .git -l -g ""'
# or
export FZF_DEFAULT_COMMAND="fd --exclude={.git,.idea,.sass-cache,node_modules,build} --type f"

FZF_DEFAULT_OPTS

该环境变量定义了 fzf 的参数:

export FZF_DEFAULT_OPTS="--height 40% --layout=reverse --preview '(highlight -O ansi {} || cat {}) 2> /dev/null | head -500'"

常见的一些操作

历史记录搜索

在没有使用 fzf 之前都是 Ctrl + r 来快速输入历史命令,用起来倒也没有太大的问题,fzf 使得 Ctrl-r 变得更加好用,按下 Ctrl-r 之后立即就能够显示之前的历史命令,通过模糊搜索能够展示一个列表,从列表中选择即可。

Ctrl-r

在终端命令行下按下 Ctrl-r 会列出 history 命令目录,选中 Enter 离开 fzf 后,该条目会拷贝到命令行中。

打开文件 Ctrl-t

在命令行中按下 Ctrl-t 会打开 fzf 窗口,此时如果找到某文件,并选择 Enter,那么该文件名会被拷贝到命令行中。比如说想要打开某文件但是忘记了名字,那么可以先输入 vi 然后按下 Ctrl-t 那么会出现 fzf 的搜索窗口,在窗口中可以模糊搜索文件,然后将文件路径及文件拷贝到命令行中。

如果想要更简单 fzf GitHub wiki 中展示了非常多的 Bash Shell 的配置 1,比如可以定义命令 fe

fe() {
  local files
  IFS=$'\n' files=($(fzf-tmux --query="$1" --multi --select-1 --exit-0))
  [[ -n "$files" ]] && ${EDITOR:-vim} "${files[@]}"
}

那么在 shell 中直接输入 fe 然后回车会自动出现当前目录的 fzf,选择文件之后回车即可用默认 $EDITOR 打开文件。

更改路径

在 fzf 之前最原始的方法就是 cd 然后输入一个词,不断的按 Tab 直到完整的输入 Path,回车。但假如 path 比较长,那么可能需要费一些时间找到真正想要去的目录,而如果用 fzf 配置

fd() {
  local dir
  dir=$(find ${1:-.} -path '*/\.*' -prune \
				  -o -type d -print 2> /dev/null | fzf +m) &&
  cd "$dir"
}

在终端输入 fd 然后搜索一下目录回车就能直接到该目录下,效率提升 x 倍。

Kill Process

再比如说 fkill 用来 kill process

# fkill - kill process
fkill() {
  local pid
  pid=$(ps -ef | sed 1d | fzf -m | awk '{print $2}')

  if [ "x$pid" != "x" ]
  then
	echo $pid | xargs kill -${1:-9}
  fi
}

Tmux

再比如创建 Tmux session 一般都是 tmux new -s new-session 创建,如果使用 tmuxinator 则可能 mux name 比较快,但是如果在 fzf 这里只需要 tm new-session

# tm - create new tmux session, or switch to existing one. Works from within tmux too. (@bag-man)
# `tm` will allow you to select your tmux session via fzf.
# `tm irc` will attach to the irc session (if it exists), else it will create it.

tm() {
  [[ -n "$TMUX" ]] && change="switch-client" || change="attach-session"
  if [ $1 ]; then
	tmux $change -t "$1" 2>/dev/null || (tmux new-session -d -s $1 && tmux $change -t "$1"); return
  fi
  session=$(tmux list-sessions -F "#{session_name}" 2>/dev/null | fzf --exit-0) &&  tmux $change -t "$session" || echo "No sessions found."
}

如果 session 不存在则会自动创建,使用 fs 可以快速选择目前可用的 session.

# fs [FUZZY PATTERN] - Select selected tmux session
#   - Bypass fuzzy finder if there's only one match (--select-1)
#   - Exit if there's no match (--exit-0)
fs() {
  local session
  session=$(tmux list-sessions -F "#{session_name}" | \
    fzf --query="$1" --select-1 --exit-0) &&
  tmux switch-client -t "$session"
}

Alt-c

命令行中按下 Alt-c,会列出当前文件夹下的目录

扩展阅读

一些比较高效的搜索工具

  • find 目录搜索
  • fd 目录搜索
  • grep 文本内容搜索
  • ag 文本内容搜索
  • ripgrep 文本内容搜索

reference