確認環境
PC | Thinkpad X1 Carbon 2nd Gen |
OS | FreeBSD 12.0-RELEASE-p7 |
fzf | 0.18.0 (0b33dc6) |
tcsh | tcsh 6.20.00 (Astron) |
課題
シェルにファイルの補完機能があるとはいえ、深い階層のパスを指定してエディタで開くのは面倒だ。
もっとサクッとファイルを開く方法はないだろうか。
対策
fzfというフィルタリングツールを使うと幸せになれるだろう。
まずは、以下の動画を見て頂きたい。
ナイスガイなあんちゃんが何を喋っているのかサッパリだけれども、
このツールは、以下のような動作の流れになる。
- 標準入力からのデータを受け取り
- ユーザが任意の行を選ぶ。このとき曖昧検索により、リアルタイムで対象行をフィルタリングできる
- 最終的に選んだ行を標準出力に出力する
これが恐ろしく汎用的。
例えば以下のようなことができる。
- コマンド履歴(
history
などの出力)から選択したコマンドを実行する - ディレクトリリスト(
find
などの出力)から選択したディレクトリへ移動する - ログから怪しい箇所(
grep
などの出力)を選択して、メールを送る - 関数の出現箇所(
grep
などの出力)のソースを眺める
、、、などなど。夢が広がりますな。
fzf
と似たようなものにpecoというツールもある。
今回は、主にfzf
とtcsh
との連携方法について紹介する。
どうやらtcsh
ではB系シェルのような関数が作れないようなので、各操作用のシェルスクリプトを作っていく。
fzf
とripgrep
のインストール~/.cshrc
の設定- ファイルを開く
- ディレクトリを移動する
- コマンド履歴から選んで実行する
1. fzfとripgrepのインストール
まずは、下記コマンドでfzf
とripgrep
をインストールする。
(管理者権限で実行してね)
> pkg install fzf ripgrep
ripgrep
はRust製の爆速Grepツール。
fzf
からripgrep
を利用する。(他のGrepツールも指定可能)
fzf
のインストールは、pkg
コマンドの他、githubにインストール用のスクリプトが用意されている。
そちらを使えば、fzf
起動後にキー操作により、検索対象をディレクトリに絞ったりということができるらしい。
興味がある人は、下記サイトあたりを参考にされると良いと思う。
2. ~/.cshrcの設定
fzf
で使うGrepツールと、デフォルトで有効にするオプションを指定しよう。
~/.cshrc
に下記を追記する。
setenv FZF_DEFAULT_COMMAND 'rg --files --hidden --follow --glob "!\.git/*"' setenv FZF_DEFAULT_OPTS "--height 80% --reverse --inline-info"
FZF_DEFAULT_COMMAND
FZF_DEFAULT_COMMAND
でripgrep
を使用するように指定している。
ripgrep
のオプションの意味は以下。
--files
ファイルを検索(find的な)--hidden
隠しファイルも対象--follow
シンボリックリンク先も対象--glob "!\.git/*"
gitディレクトリ配下は対象外
FZF_DEFAULT_OPTS
FZF_DEFAULT_OPTS
でfzf
でデフォルトで使いたいオプションを指定している。
fzf
のオプションの意味は以下。
--helght 80%
表示する高さを今のシェルの80%に指定--reverse
フィルタ文字列入力行を一番下に表示する(デフォルトは一番下の行)--inline-info
フィルタ中にマッチ件数を表示する
動作確認
ここまでできたら、一度動作確認をしておこう。
> source ~/.cshrc > fzf
Ctrl+j
or Ctrl+n
でカーソルを下に動かせる。
Ctrl+k
or Ctrl+p
でカーソルを上に動かせる。
Esc
or Ctrl+d
or Ctrl+c
で、選択せずにキャンセルできる。
Enter
キーで任意の行を選択すると、選択画面が閉じ、選択した行の文字列が標準出力に出ているはずだ。
3. ファイルを開く
以下をシェルスクリプトとして保存してパスが通ったディレクトリに置こう。
(実行権限を追加(chmod +x filename
)するのもお忘れなく)
カレントディレクトリ配下のファイルリストを作って、fzf
に渡している。
引数が1つの場合は、カレントディレクトリ配下を、引数のパターンで検索。
引数が2つの場合は、第1引数のディレクトリ配下を、第2引数のパターンで検索。
パターンは、fzf
起動後に打ち直せる。
fzf
のオプションの意味は以下。
--query="$1"
予めフィルタ文字列を指定--multi
複数ファイル選択可能にする(Tab/Shift+Tabで選択トグル)--select-1
対象ファイルが1行だけだったら、選択画面を出さずに1行を出力--exit-0
対象ファイルが0行だったら、即終了--preview "head -n 100 {}"
選択中のファイルの最初の100行をプレビュー画面に表示している。
プレビュー画面はシンタックスハイライトして欲しいという人は、batというコマンドを試してみると良いかも。
(ただ、head
に比べると若干ラグを感じる)
fzf
でEsc
キーなどによりキャンセルされたら、何もせずに終了する。
パスの通ったディレクトリに置いたら、実行権限を追加してコマンドを試してみよう。
(以下では、~/bin/fe
に保存した例)
> chmod +x ~/bin/fe > fe > fe py$
4. ディレクトリを移動する
シェルスクリプトの中で変数を変更しても、スクリプトを実行した親プロセスには影響しない。
つまり、スクリプトの中でcd
コマンドをしても、親プロセスには反映されない。
どうしたものかと考えてみたけど、source
コマンドで反映する方法で対応してみた。
もっと良さ気な方法があれば、ご教示下さいまし。
まずは、source
をかけるCシェルのスクリプトファイル。
引数がない場合は、カレントディレクトリ配下のディレクトリリストを作って、fzf
に渡す。
引数がある場合は、第1引数のパス配下のディレクトリリストを作って、fzf
に渡す。
fzf
でEsc
キーなどによりキャンセルされたら、何もせずに終了する。
fzf
で何か選択したら、ファイルを選択する。
find
コマンドでカレントディレクト配下のディレクトリリストを作って、fzf
に渡す。
(プレビューにツリーを表示するアイデアもあるみたい⇒こちら)
ちなみにgrep
をかませている理由は、findのエラーを除去するため。
(Cシェルの標準エラーだけ捨てて、標準出力を次のパイプへ渡すやり方が分からない)
あとは、上記ファイルにsource
をかけるエイリアスを~/.cshrc
に書くだけ。
パスの~/bin/fd.csh
は、各自置いた場所に置き換えて下さいな。
alias fd source ~/bin/fd.csh
保存したら、コマンドがちゃんと動くか試してみよう。
> source ~/.cshrc > fd
5. コマンド履歴から選んで実行する
最後のサンプルは、tcsh
の実行履歴をfzf
で選んで実行する例。
ここではfzf
をインストールしたら付いてくる、fzf-tmux
コマンドを使ってみる。
これは、fzf
での選択画面をtmux
の新しいペインに表示してくれる。
fzf
実行後は、作成したペインを閉じる礼儀正しい子なの。
fzf-tmux
に渡しているオプションの意味は下記。
--no-sort
入力データをソートしない。--nth 3..
3列目以降のフィールドを検索対象(今回の例では、実行コマンド文字列のみを対象にしている)-d 90%
fzf
で使用するペインの高さを90%に指定
sed
でごちゃごちゃやっているのは、履歴番号と時刻と空白を除去。
history -h
として、コマンドだけ表示にしてもいいかもしれない。
(その場合は、--nth 3
も要らなくなる)
あとは、上記ファイルにsource
をかけるエイリアスを~/.cshrc
に書くだけ。
パスの~/bin/fd.csh
は、各自置いた場所に置き換えて下さいな。
alias fh source ~/bin/fh.csh
ちなみに、このコマンドは#!/bin/tcsh
を先頭行に書いて実行権限を与えれば動くとは思う。
ただ、あくまでtcsh
の履歴なので、他の自作の汎用的なshスクリプトと混じらないように明示したかっただけ。
各自、お好きにして下され。
保存したら、コマンドがちゃんと動くか試してみよう。
> source ~/.cshrc > fd
最後に
対象リストを作って、絞って、選んで、何かを行う。
この恐ろしく汎用性の高いツールの使い道は、あなたの想像力次第。
FreeBSDで言えば、pkg
コマンドとfzf
を組み合わせると、検索&インストールが楽しくなりそう。
(2019/7/21 追記 ⇒ 作ってみたよ)
他に何か良さ気な使い方があれば、教えて下さいましまし。
Neovim
との連携については、そのうちまた紹介したい。
参考
以上。