はじめに
今回は作業効率化をすすめるにあたって有用なfzfの利用例を紹介したいと思います。
似たようなものでpecoというのもありますが、fzfの記事が少ないと思うので、今回はfzfについて書いていきたいと思います!
(あとfzfはVimでも使えるようにサポートされているので、Vimmerの方はpecoよりもfzfかなということもあり…。)
fzfとは
fzf
https://github.com/junegunn/fzf
fzfとはCLIでインクリメンタルに曖昧な検索が可能になるGO言語製のツールです。
標準出力をパイプでfzfコマンドで渡すだけで、標準出力の内容を対象に検索できます。
$ find . | fzf
上部の入力箇所でインクリメンタル曖昧検索しながら、(CLIとしては慣れ親しんだキーバインドの)Ctrl-n,Ctrl-pで下部のリストから選択することができます。
最初は『検索できるだけで、何が便利やねん』という感想を持たれた方も多いかと思いますが、CLIの環境において選択的インターフェースの提供は、工夫次第でかなり強力になります。
まずはfzfをインストールしましょう
fzfのインストール記事は他にいくらでもあるので割愛します。
たぶんこちらとかの記事を参考にすると良さそうです。
ちなみに私の設定っぽい設定は今のところこれだけです。(←zsh)
[ -f ~/.fzf.zsh ]&&source ~/.fzf.zshexportFZF_DEFAULT_COMMAND='rg --files --hidden --glob "!.git"'exportFZF_DEFAULT_OPTS='--height 40% --reverse --border'
fzfでヒストリー検索
さて、fzfをちゃんとセットアップできたら、まずはコマンドヒストリーの曖昧インクリメンタル検索を使ってみましょう。
デフォルトのヒストリー検索よりも、一覧を見ながらかつ柔軟に検索できるようになるので、一度使い始めるとデフォルトのコマンドヒストリー検索には戻れません。
参考:本家README.md
https://github.com/junegunn/fzf#key-bindings-for-command-line
fzfで実装する便利関数
さて早速、作業効率化を進めていきましょう。
まず何をすればよいかわからない方は、サンプルの宝庫である本家Wikiを見るのがおすすめです。
https://github.com/junegunn/fzf/wiki
ここではいくつか本家Wikiにある便利関数を紹介します。
fbr
下記のような記述を.bashrcや.zshrcなどに書きます。
# fbr - checkout git branch
fbr() {
local branches branch
branches=$(git branch -vv) &&
branch=$(echo "$branches" | fzf +m) &&
git checkout $(echo "$branch" | awk '{print $1}' | sed "s/.* //")
}
fbrコマンドで今ローカルに存在するbranchを選択して切り替えられるようになります。
※サンプルはvimのaleのgitリポジトリが丁度良さそうだったので勝手に使わせていただいています(私自身は全く貢献してません…)
※ 本家Wikiに書いてあるサンプルは基本的にfzf+(git) branch = fbrみたいなネーミングのノリです。
さぁ、どんどん行きましょう。
fbr
またしてもfbrという名前がついているのですが、今度はリモートブランチを含めた検索です。
fbr - checkout git branch (including remote branches)
fbr() {
local branches branch
branches=$(git branch --all | grep -v HEAD) &&
branch=$(echo "$branches" |
fzf-tmux -d $(( 2 + $(wc -l <<< "$branches") )) +m) &&
git checkout $(echo "$branch" | sed "s/.* //" | sed "s#remotes/[^/]*/##")
}
個人的にはfbrm(fzf+branch+remote)って関数名にしてfbrとは分けて使ってます。
fshow
# fshow - git commit browser
fshow() {
git log --graph --color=always \
--format="%C(auto)%h%d %s %C(black)%C(bold)%cr" "$@" |
fzf --ansi --no-sort --reverse --tiebreak=index --bind=ctrl-s:toggle-sort \
--bind "ctrl-m:execute:
(grep -o '[a-f0-9]\{7\}' | head -1 |
xargs -I % sh -c 'git show --color=always % | less -R') << 'FZF-EOF'
{}
FZF-EOF"
}
git log の--graphオプションもfzfに突っ込んでしまえば、立派なインターフェースの出来上がりです。
Enterでgit showの状態になります。
fd
もうちょっと簡単な例を見てどうやってカスタマイズしていけばいいか理解を深めてみましょう。
fdはfindコマンドで下層までをリスト化し、標準出力をパイプでfzfに渡し、最後にcdコマンドに結果を渡しているだけということがわかります。
# fd - cd to selected directory
fd() {
local dir
dir=$(find ${1:-.} -path '*/\.*' -prune \
-o -type d -print 2> /dev/null | fzf +m) &&
cd "$dir"
}
(※個人的にfcdって名前にしてみましたがあんまり使ってません…。)
なるほど、これなら自分でも実装できそうです。
自分が使っているfzfの例
git worktreeコマンドは皆さんお使いでしょうか。僕も最近使い始めて見たのですが、マルチタスクな日は便利すぎてかなりお世話になっています。
参考: ブランチの切り替えをしなくてよくなるコマンド git worktree がすごい!
でも、worktreeで作成したブランチまで移動するのがだるすぎる…。
ということでfzfを使って作業効率化してみます。
# worktree移動function cdworktree(){# カレントディレクトリがGitリポジトリ上かどうか
git rev-parse &>/dev/nullif[$? -ne 0];thenecho fatal: Not a git repository.returnfilocalselectedWorkTreeDir=`git worktree list | fzf | awk '{print $1}'`if["$selectedWorkTreeDir"=""];then# Ctrl-C.returnficd${selectedWorkTreeDir}}
選択した候補のpath部分をcdに渡して階層を移動しています。
長ったらしい関数名ですがcdw(tabキー)みたいな感じで使ってます。
Vimで使う
自分は普段使いのエディタがVimなのですが、Vimでfzfが使えるようにサポートされているところがpecoではなくfzfを使う理由の一つです。
そして本家でもvimプラグインとしても提供されています。
https://github.com/junegunn/fzf.vim
Ctrl-Pのようなファイル検索
大きいプロジェクトだとCtrl-pよりも遥かに高速です。
" [Replace of Ctrl-p] ========================================" nnoremap<C-p> :FZFFileList<CR>
command! FZFFileList call fzf#run({
\ 'source': 'find . -type d -name .git -prune -o ! -name .DS_Store',
\ 'sink': 'e'})
MRU(Most Recently Used)
よくあるMRUのFZFインターフェース化
" [MRU] ========================================"
command! Fmru FZFMru
command! FZFMru call fzf#run({
\ 'source': v:oldfiles,
\ 'sink': 'tabe',
\ 'options': '-m -x +s',
\ 'down': '40%'})
QuickFixの検索
なにかQuickFixに送って開かせるつもりだったもの(あまり使ってない)。
" [QuickFix] ==================================="
command! Fq FZFQuickfix
command! FZFQuickfix call fzf#run({
\ 'source': Get_qf_text_list(),
\ 'sink': function('s:qf_sink'),
\ 'options': '-m -x +s',
\ 'down': '40%'})" QuickFix形式にqfListから文字列を生成するfunction! Get_qf_text_list()let qflist = getqflist()let textList = []foriin qflistifi.validlet textList = add(textList, printf('%s|%d| %s',
\ bufname(i.bufnr),
\ i.lnum,
\ matchstr(i.text,'\s*\zs.*\S')
\ ))endifendforreturn textListendfunction" QuickFix形式のstringからtabeに渡すfunction! s:qf_sink(line)let parts = split(a:line,'\s')
execute 'tabe ' . parts[0]endfunction
Qiitaなどで紹介されているfzf活用例
fadd
Gitのunstageなファイルを選択してどんどんstageに移せます。
個人的には特に大きいプロジェクトではtig開くよりも軽いし、かなりおすすめです。
まとめ
個人的にはfzfはシンプルかつVimサポートもあり、お陰様でCLI生活を豊かにしてくている(たぶん)ので好きです。
pecoもfzfも使ったことなかったよって人は、内容的にはbash(zsh)の部分に関してはpecoでもfzfでも同じことができると思うので、まずは自分で比較してみて好きになれそうな方を使ってみるのがおすすめです👍