bashでもpercolをイイ感じに使う方法

[twitter:@stillpedant](mooz)さん作のpercolはシェルとは独立しているのですが、ググるzshと共に使っている人が多いようです。これはzshの強力な行編集機能を提供するzleに因るところが大きいためだと思います。

bashでもpercolを使っている人もいるのですが、

「絞り込んだパスへのcdを実行する」ことはできても、「絞り込んだパスを現在のコマンドライン行に挿入する」ことは実現できていない、つまりコマンド実行はできるけど行編集はできていないようでした。

そこで今回ちょっと調べてみたところ、bashでも(zleには及ばないにしても)変数READLINE_LINEとREADLINE_POINTで行編集できることがわかったので紹介します。ただし、bash 4以上が必要です。

READLINE_LINEとREADLINE_POINT

READLINE_LINEには現在のコマンド行の内容が、READLINE_POINTにはカーソルの位置が格納されます。

例えば、

_readline_test() {
  {
    echo "LINE=$READLINE_LINE"
    echo "POINT=$READLINE_POINT"
  } >&2
}

bind -x '"\C-xx":_readline_test'

とし、対話シェル上で C-x x を打つとこのような結果になります。「■」の位置がカーソルの位置を示します。

$ ■
LINE=
POINT=0

$ foo bar baz■
LINE=foo bar baz
POINT=11

$ foo bar■baz
LINE=foo bar baz
POINT=7

percol --query に $READLINE_LINE 全体もしくは $READLINE_POINT までの部分文字列を渡せば、検索文字列が入った状態で percol を呼ぶことができそうです。


またこの READLINE_LINE と READLINE_POINT は変更することもできます。変更した場合、対話シェル上のコマンドライン行をその内容とカーソルの位置に変更できます。

例えば、

_readline_test() {
  READLINE_LINE="$READLINE_LINE hok"
  READLINE_POINT=${#READLINE_LINE}
}

bind -x '"\C-xx":_readline_test'

とした場合、C-x x を押す度に「 hok」が追加されカーソルが行末に移動するはずです。


続いては実例です。

コマンドヒストリー

C-r を percol を使うものに置き換えています。念の為、C-r だった reverse-search-history を C-x r に当てています。

ヒストリーはpercolの結果で完全に置き換えて欲しいので、READLINE_LINEにはそのまま絞り込み結果を代入し、READLINE_POINTにはその長さを代入しています。

_replace_by_history() {
  local l=$(HISTTIMEFORMAT= history | tac | sed -e 's/^\s*[0-9]\+\s\+//' | percol --query "$READLINE_LINE")
  READLINE_LINE="$l"
  READLINE_POINT=${#l}
}
bind -x '"\C-r": _replace_by_history'
bind    '"\C-xr": reverse-search-history'

gitのディレクトリ{ にcd | を挿入 }

自分は git clone したディレクトリは ~/repos の直下もしくは1階層サブディレクトリを掘った下に置いているので、percolでそれを絞り込むシェル関数はこのようになります。

_select_gitdir() {
  find ~/repos -maxdepth 3 -type d -a -name '.git' | \
    sed -e 's@/.git@/@' | \
    percol
}

そこへ移動したい場合は、単純にcdすればよいです。

_cd_gitdir() {
  cd "$(_select_gitdir)"
}
bind '"\C-xg": "_cd_gitdir\n"'

次に、コマンドライン行の現在のカーソル位置に、絞り込んだ git ディレクトリのパスを挿入する場合はこのようになります。

現在の行の内容のカーソル位置のところに絞り込んだ結果を挟んで、カーソル位置を挟んだ末尾に移動しています。

_insert_gitdir() {
  local l=$(_select_gitdir)
  READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}${l}${READLINE_LINE:$READLINE_POINT}"
  READLINE_POINT=$(($READLINE_POINT + ${#l}))
}
bind -x '"\C-x\C-g": _insert_gitdir'

最近移動したディレクトリの履歴

自分は昔から Bash の小枝集 で紹介されている cdhist.sh を使っています。これを使っていると配列変数 CDHIST_CDQ に最近移動したディレクトリが記録されているので、percolから利用してみます。

_cd_cdhist() {
  cd "$(for i in "${CDHIST_CDQ[@]}"; do echo $i; done | percol)"
}
bind '"\C-xj": "_cd_cdhist\n"'

screenのアクティブwindowを選択する

screen 4.1以上なら -Q windows でリストが取れるようなのでそれでできそうです。

が、諸般の事情で tmux に乗り換え中です><

tmux の例は https://github.com/mooz/percol#tmux をどうぞ!

peco

goで書かれた peco というのもあるようです。

バイナリだけコピーすれば使えるので、いろんな環境で使いたい場合はいいかもしれませんね!