はじめに
今回は作業効率化をすすめるにあたって有用な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で下部のリストから選択することができます。
(もちろんカーソルキーでもOK)
最初は『検索できるだけで、何が便利やねん』という感想を持たれた方も多いかと思いますが、CLIの環境において選択的インターフェースの提供は、工夫次第でかなり強力になります。
まずはfzfをインストールしましょう
fzfのインストール記事は他にいくらでもあるので割愛します。
たぶんこちらとかの記事を参考にすると良さそうです。
ちなみに私の設定っぽい設定は今のところこれだけです。(←zsh)
[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
export FZF_DEFAULT_COMMAND='rg --files --hidden --glob "!.git"'
export FZF_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"
}
なるほど、これなら自分でも実装できそうです。
自分が使っているfzfの例
git worktreeコマンドは皆さんお使いでしょうか。僕も最近使い始めて見たのですが、マルチタスクな日は便利すぎてかなりお世話になっています。
参考: ブランチの切り替えをしなくてよくなるコマンド git worktree がすごい!
でも、worktreeで作成したブランチまで移動するのがだるすぎる…。
ということでfzfを使って作業効率化してみます。
# worktree移動
function cdworktree() {
# カレントディレクトリがGitリポジトリ上かどうか
git rev-parse &>/dev/null
if [ $? -ne 0 ]; then
echo fatal: Not a git repository.
return
fi
local selectedWorkTreeDir=`git worktree list | fzf | awk '{print $1}'`
if [ "$selectedWorkTreeDir" = "" ]; then
# Ctrl-C.
return
fi
cd ${selectedWorkTreeDir}
}
選択した候補のpath部分をcdに渡して階層を移動しています。
長ったらしい関数名ですがcdw(tabキー)みたいな感じで使ってます。
Vimで使う
自分は普段使いのエディタがVimなのですが、Vimでfzfが使えるようにサポートされているところがpecoではなくfzfを使う理由の一つです。
そして本家でもvimプラグインとしても提供されています。
https://github.com/junegunn/fzf.vim
ctrlp.vimのようなファイル検索
大きいプロジェクトだとctrlp.vimよりも遥かに高速です。
" [Replace of ctrlp.vim] ========================================
"
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 = []
for i in qflist
if i.valid
let textList = add(textList, printf('%s|%d| %s',
\ bufname(i.bufnr),
\ i.lnum,
\ matchstr(i.text, '\s*\zs.*\S')
\ ))
endif
endfor
return textList
endfunction
" 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開くよりも軽いし、かなりおすすめです。
zコマンドと連携してディレクトリ移動を快適にする
zshでzの結果をpecoって爆速でディレクトリ移動する
pecoの記事ですけど。
zコマンドは自分が居たことのあるディレクトリの履歴のなかから、最もマッチするディレクトリ名のパスに移動できるプラグインです。
そのままで使うと、予測してない先に飛ぶこともあるので、zの履歴をfzfで検索してからcdするようにしてみます。
fzfでやるならこんな感じで自分は使ってます。
# zplugなどでzをインストールしとく
# zplug "rupa/z", use:z.sh
fzf-z-search() {
local res=$(z | sort -rn | cut -c 12- | fzf)
if [ -n "$res" ]; then
BUFFER+="cd $res"
zle accept-line
else
return 1
fi
}
zle -N fzf-z-search
bindkey '^f' fzf-z-search
まとめ
個人的にはfzfはシンプルかつVimサポートもあり、お陰様でCLI生活を豊かにしてくている(たぶん)ので好きです。
pecoもfzfも使ったことなかったよって人は、内容的にはbash(zsh)の部分に関してはpecoでもfzfでも同じことができると思うので、まずは自分で比較してみて好きになれそうな方を使ってみるのがおすすめです👍
追記
『~/.ssh/configからSSH先のホストをfzfで選んで接続する』例も書きました😊
https://qiita.com/kamykn/items/9a831862038efa4e9f8f