ハイパーマッスルエンジニア

Vim、ShellScriptについてよく書く

fzfで捗る自作コマンド一覧(zsh)

みんな大好きfzf

fzfは結果をインタラクティブに絞り込むだけのコマンドだが、組み合わせ次第でかなり使えるコマンドだ。日本ではpecoの方が有名だが海外ではfzfの方が人気らしい。

github.com

筆者が思うpecoと比較したときのfzfのメリットを述べる。

  • 画面クリアがなく目に優しい
  • fuzzy search(曖昧検索)が可能
  • 絞り込んだものをパイプつなぎでコマンドを適用でき、結果をpreviewとして画面に表示できる
  • カスタマイズが豊富

特にfuzzy searchとpreviewできる点が素晴らしい。またfzfの絞り込み結果の画面サイズを指定できたりとカスタマイズ性が高いのもGOOD。

以下はgit logの結果をfzfで絞り込み、previewにgit diffを表示、ENTERで詳細をless表示させているもの。(ちなみに別で全く同じ動作をするtiggコマンドというものがあるらしい)

f:id:rasukarusan:20180812205449g:plain

私のfzfのデフォルトの設定は以下。複数選択と色合いを変化、あとデフォルトだと絞り込み画面が下からでるのでreverseで直下に出している。

# fzfの設定
export FZF_DEFAULT_OPTS='--color=fg+:11 --height 70% --reverse --select-1 --exit-0 --multi'

実際に業務でも使っているコマンド一覧をつらつらと紹介していく。

爆速でディレクトリの移動をするfzf-cdr

これがない時のTerminalの移動はもう考えられない。cdrで過去に行ったことのあるディレクトリを表示し、fzfで絞り込んでディレクトリに移動する。 ちなみに私は「change directory direct」という意味で「cdd」というaliasをつけている。

f:id:rasukarusan:20180812211238g:plain

# fzf-cdr 
alias cdd='fzf-cdr'
function fzf-cdr() {
    target_dir=`cdr -l | sed 's/^[^ ][^ ]*  *//' | fzf`
    target_dir=`echo ${target_dir/\~/$HOME}`
    if [ -n "$target_dir" ]; then
        cd $target_dir
    fi
}

cdrの設定は以下のようにしている。ココらへんちょっと適当かもしれない(いつ書いたか忘れてた)。

# cdrの設定
autoload -Uz is-at-least
if is-at-least 4.3.11
then
  autoload -Uz chpwd_recent_dirs cdr add-zsh-hook
  add-zsh-hook chpwd chpwd_recent_dirs
  zstyle ':chpwd:*'      recent-dirs-max 500
  zstyle ':chpwd:*'      recent-dirs-default yes
  zstyle ':completion:*' recent-dirs-insert both
fi

ctrl+zでストレスなくバックグラウンドに入れる&出す

vimでファイルを開いていて、ふとterminalに戻って操作をしたくなる時があります。そんなときは大体ctrl+zで一旦バックに入れてfgで戻ってくる、という動作をすると思います。 ただ戻るのに一々fgを打つのも面倒くさいのでctrl+zで行ったり来たりするように設定しています。

f:id:rasukarusan:20180812211833g:plain

# fgを使わずctrl+zで行ったり来たりする
fancy-ctrl-z () {
  if [[ $#BUFFER -eq 0 ]]; then
    BUFFER="fg"
    zle accept-line
  else
    zle push-input
    zle clear-screen
  fi
}
zle -N fancy-ctrl-z
bindkey '^Z' fancy-ctrl-z

ただ、これだと直前のバックグランドに入れたものとしか行き来できないので、2個前のプロセスに戻りたい時などはfzfで選択しています。

# fgをfzfで
alias fgg='_fgg'
function _fgg() {
    wc=$(jobs | wc -l | tr -d ' ')
    if [ $wc -ne 0 ]; then
        job=$(jobs | awk -F "suspended" "{print $1 $2}"|sed -e "s/\-//g" -e "s/\+//g" -e "s/\[//g" -e "s/\]//g" | grep -v pwd | fzf | awk "{print $1}")
        wc_grep=$(echo $job | grep -v grep | grep 'suspended')
        if [ "$wc_grep" != "" ]; then
            fg %$job
        fi
    fi
}

agでヒットした行をvimで開く

これは最近vimプラグインで完結するようになったから使わなくなってしまったが、一時期はNOTE:とかFIXME:を探して修正しに行くのに結構使ってた。 f:id:rasukarusan:20180812213417g:plain

# agの結果をfzfで絞り込み選択するとvimで開く
alias agg="_agAndVim"
function _agAndVim() {
    if [ -z "$1" ]; then
        echo 'Usage: agg PATTERN'
        return 0
    fi
    result=`ag $1 | fzf`
    line=`echo "$result" | awk -F ':' '{print $2}'`
    file=`echo "$result" | awk -F ':' '{print $1}'`
    if [ -n "$file" ]; then
        vim $file +$line
    fi
}

各ブランチのgit logを見ながらcheckoutする

これももはや定番かもしれない、gitの操作をfzfに食わせるものだ。 gitの操作は手打ちでやると面倒くさいものも多いので、絞り込みで曖昧に覚えているものを打ち込んで選択するのがすごい楽。

f:id:rasukarusan:20180812213947g:plain

# git checkout branchをfzfで選択
alias co='git checkout $(git branch -a | tr -d " " |fzf --height 100% --prompt "CHECKOUT BRANCH>" --preview "git log --color=always {}" | head -n 1 | sed -e "s/^\*\s*//g" | perl -pe "s/remotes\/origin\///g")'

ちなみに「co」は「CheckOut」の略だ。 git checkoutの他にも

  • git add -pをfzfでdiffを見ながら選択
  • git checkout fileをfzfで選択
  • git resetをfzfでdiffを見ながら選択

といった操作をすべてfzfでやっている。

終わり

fzfのREADMEにあるExample集にもかなりの数があるので参考にしたい。 fzf最高。