たまにはzshなネタを。
普段ターミナルで作業しているときに、「あれ、このコマンド前に打ったけどどんな感じだったっけか?」みたいな事象に(よく)遭遇します。 そんな時に一々1個ずつカーソルキーの上下とかでたどるのはかなり非効率なので、それをfzfを使ってスマートにやろう。というお話です。

※既にfzf使ってるよという人は本題までジャンプしてください。

fzfとは

ある標準入力をfzfに渡してあげると、その標準入力から一覧を作り、 それをターミナル上でユーザーが選択して、選択したものを標準出力として返してくれるというものです。
更にその一覧から検索して絞り込むことができたり、複数選択してそれを出力として返すことも可能です “インタラクティブフィルタ”とも呼ばれるようです。

詳しくはこちらの記事で紹介されています。

pecoと近い?

こういったインタラクティブフィルタとしてよく挙げられるのがpecoです。
こちらの方が有名?で使ってる人も多いかと思います。

そんなpecoと比べてfzfの良いところ(個人的にも好きな所)は、ファジー検索が優れている点です。
例えば、一覧の中に

path/to/src/sample_project/app.m

というものが含まれていた時に、

pathsrcsampap.m と打っても一覧から絞りこめるので、大変優秀です。

fzfを使う

まずはインストールから。 brew を使っている場合は、

brew install fzf

で完了です。 (fzf本家のinstall手順)

試しに使ってみる

例えば、現在のディレクトリ内にあるファイル一覧をfzfにかませて、選択したファイル名を出力してみます。

$ ls -a | fzf

本題

さて、ここからが本題です。
ターミナルで (^r) と打つだけで、今まで自分が入力したコマンド履歴をだしつつ、fzfで絞り込んで標準出力として返すようにします。
(返してターミナル上にコマンドが貼られるだけですぐには実行されないのでご安心を)
以下のコードを .zshrcに配置します

function select-history() {
  BUFFER=$(history -n -r 1 | fzf --no-sort +m --query "$LBUFFER" --prompt="History > ")
  CURSOR=$#BUFFER
}
zle -N select-history
bindkey '^r' select-history

やってること

history コマンドでまず逆順、履歴番号無しで全て列挙して、それを fzf の標準入力として渡してあげます。
そのときに、

  • fzfで絞込み中に検索結果をソートしない
  • 複数選択を無告に

といったオプションも付加してあげています。
あとは関数 select-history を、 (^r) にバインドしてあげます。

試してみる

こんな感じで過去に実行したコマンド一覧が最新順に並び、検索文字列を打っていくことで絞り込むことが出来ます。
最後にEnterを押すことで、選択したコマンドがターミナルに打ち込まれます

image
image

(本当はGIFで載せたかったのですが、色々と隠さないといけないものがあったのでスクショでの掲載とします…

他にも

fzfを使うことでこんなこともできるようになります

  • git branchの一覧をfzfにかませて、選択したbranchにcheckoutする
  • git branchの一覧をfzfにかませて、選択したbranchを削除する

また、fzfには様々なオプションが用意されていて、

  • 渡された標準入力を逆順で表示する
  • 複数選択可能にする
  • 検索文字列を絞り込む時に絞り込み結果をソートしない
  • 渡された入力が1項目だけならfzfを起動せずに選択する

といったことも可能になります。