ほとんどの時間をターミナルで過ごしますか?もしそうなら、ファイルを検索するときに古典的な find、grep、cat コマンドに遭遇したことがあるでしょう。しかし、これらは使いにくいと感じており、もっと改善できるはずだと考えています。さらに、より高度な検索エクスペリエンスを提供するコマンドがあります。
早速本題に入ります。次のコマンドは、fzf、ripgrep、および Bat に基づいて構築され、これらを組み合わせて、最新のインタラクティブな検索インターフェイスを提供します。以下に各フラグの意味を説明しますので、ニーズに合わせて変更できます。
s() {
: | fzf \
--ansi \
--disabled \
--bind "change:reload:sleep 0.1; \
command rg --line-number \
--column \
--no-heading \
--color=always \
--smart-case {q} \
$* \
|| :" \
--bind "enter:execute:nano +{2},{3} {1}" \
--bind "ctrl-o:become:nano +{2},{3} {1}" \
--delimiter ":" \
--preview "command bat --style=full \
--color=always \
--highlight-line {2} \
{1}" \
--preview-window 'up:80%,border-bottom,~4,+{2}+4/3'
}
強力な検索機能を実現するバインド ripgrep
まずは、fzf の「–bind」フラグと、fzf がイベントを外部コマンドにどのように関連付けるかを見てみましょう。
イベントは、fzf 内で発生する重要なイベントです。たとえば、開始、ロード、変更、結果、フォーカスなどです。いくつかのイベントが利用できるため、詳細についてはマニュアルを参照してください。
man fzf
「change」イベントを使用すると、fzf に渡されたクエリが変更されたときにコマンドを実行 (「リロード」) できます。次の例では、「{q}」は fzf に入力するものです。
fzf --bind "change:reload:echo {q}"
echo を任意のコマンドに置き換えることができます。
fzf --bind "change:reload:rg {q}"
この例を構築して、ripgrep をさらに便利にしてみましょう。
--bind "change:reload:sleep 0.1; \
command rg --line-number \
--column \
--no-heading \
--color=always \
--smart-case {q} \
$* \
|| :" \
「change:reload:sleep 0.1」はデバウンサーと呼ばれ、過剰なコマンド実行を防ぐために使用されます。これを指定せずに入力すると、fzf はコマンドをすぐに再ロードするため、キーを押すたびに再実行にコストがかかります。最初に 100 ミリ秒スリープすると、代わりにキーストロークがそれを中断します。遅延が長い場合は、「rg」が通常どおり実行されます。
「command rg」部分は、シェルの変更(エイリアスなど)なしで「rg」を実行します。これにより、結果のレイアウトが予測可能になるため、fzf で処理できます。
「–column」フラグを指定すると、ripgrep は結果に列番号を含めます。これを使用して、一致した行と列でテキスト エディターを開きます。
ripgrep は主要な検索コンポーネントであるため、おそらく、検索の実行時にそれをすばやく構成する方法が必要です。 「$*」を使用すると、ファイル パスなどの引数を直接渡すことでこれを行うことができます。
他のフラグ「–no-Heading」、「–color=always」、「–smart-case」は、生活の質を向上させる機能です。スマートケースとは、クエリに大文字が含まれている場合に、大文字と小文字を区別して結果のみをフィルターすることを意味します。
fzf で色を正しく表示するには、「–ansi」フラグを指定します。また、「–disabled」を指定すると、fzf が結果に独自のフィルタリングを適用できなくなり、検索は完全に ripgrep に委ねられます。基本的に、fzf は「^」などの特殊記号を認識しなくなりますが、ripgrep は認識します。
最後に、コマンドの最後の「|| :」は「|| true」と同じです。コマンドから返されたエラーを飲み込むにはちょっとしたコツがあります。これにより、ripgrep によって fzf が終了するのを防ぎます。
テキストエディタをバインドして結果にすぐにアクセス
選択した結果でキーを押したときに、おそらくテキスト エディタを開くなどの処理を実行したいと思います。次の 2 行がそれを実行します。
--bind "enter:execute:nano +{2},{3} {1}" \
--bind "ctrl-o:become:nano +{2},{3} {1}"
最初の「バインド」式は Enter キーを押した後に Nano を実行しますが、2 番目の式は Ctrl+O キーを押した後に別の方法で開きます。
「execute」と「become」の違いは、「become」は fzf プロセスを置き換えますが、「execute」は置き換えないことです。簡単に言えば、テキスト エディタを終了すると、「実行」は終了して fzf メニューに戻りますが、「なる」は終了してシェルに戻ります。 fzf マニュアルには、これらのアクションが多数記載されています。
「+{2},{3} {1}」部分は、特定の行と列のファイルを開きます。これは、Nano コマンド構文と fzf プレースホルダー値を組み合わせたものです。自分のターミナルで自分で試すことができます。
nano +10,30 foo.txt # Line 10, column 30.
fzf コマンドでは、中括弧構文 (「{1}」など) がプレースホルダー値を表します。数字は、結果ウィンドウの N 番目の列を表します。
-
ファイルパス:「{1}」。
-
行番号 (「rg –line-number」で提供): 「{2}」。
-
列番号 (「rg –column」で提供): 「{3}」。
-
一致するテキストですが、これは無視できます。
Vim および Neovim を使用して特定の場所にあるファイルを開くには、次を使用します。
--bind "enter:execute:nvim '+call cursor({2},{3})' {1}" \
--bind "ctrl-o:become:nvim '+call cursor({2},{3})' {1}"
Emacs の場合は、次を使用します。
--bind "enter:execute:emacs +{2}:{3} {1}" \
--bind "ctrl-o:become:emacs +{2}:{3} {1}"
リッチリザルトを表示するには「–preview」フラグを使用します
Fzf を使用すると結果の選択が非常に速くなりますが、プレビュー ウィンドウを使用するとさらに便利になります。 Bat には豊富な表示オプションがあるため、bat を使用します。
--preview "command bat --style=full \
--color=always \
--highlight-line {2} \
{1}"
「–style」を使用して、プレビュー ウィンドウの外観を制御できます。 「完全」オプションでは、行番号、ファイルヘッダー、境界線を含むすべてが表示されます。オプションの完全なリストについては、bat のマニュアル ページを参照してください。
「–highlight-line」フラグは一致した行を強調し、「{2}」から行番号を取得します。
最後に、bat にファイル パス (「{1}」) を提供して、bat がデータを読み取れるようにします。
–プレビューウィンドウ
次のフラグ「–preview-window」はレイアウトを制御します。
--preview-window 'up:80%,border-bottom,~4,+{2}+4/3'
複雑そうに見えますが、分解してみるとそうではありません。
これらのセグメントの順序は柔軟です。 Fzf は、「~」や「+」などの構文要素に基づいて、その動作を決定できます。私が使用した一般的な形式は次のとおりです position:size,border-style,fixed-header,scroll-offset。
「位置」は、プレビュー ウィンドウが上 (上)、下 (下)、左、または右のどこに移動するかを定義します。 「サイズ」は、ウィンドウの何パーセントを占めるかを定義します。
「border-style」では、プレビュー ウィンドウを他のすべてから区切る境界線が表示されます。 「border-bottom」は賢明な値だと思います。
「固定ヘッダー」セグメントにより、プレビュー ウィンドウの上部に数行のメタデータが表示されます。「~4」が正確な数を定義します。これを使用してバットヘッダーを永続的に表示します(「–style=full」でアクティブ化)。
「scroll-offset」セグメントは、強調表示された行がプレビュー ウィンドウ内のどこに移動するかを制御します。
先ほどは「+{2}+4/3」を使用して定義しました。
-
最初の「+」は、セグメントをスクロール オフセットとして定義していると思います。
-
「{2}」は fzf によって提供される行番号プレースホルダーであるため、ファイル内のどこに移動するかを Bat に指示します。
-
「+4」は、「~4」行ヘッダーを考慮して表示を 4 行オフセットします。
-
「/3」は、強調表示された行を画面の上 1/3 に表示するように fzf に指示します。
マニュアルではそうではないと主張しているにもかかわらず、「1/3」という値を指定すると、強調表示された行がプレビュー ウィンドウのほぼ中央に表示されることがわかりました。若干の調整が必要でしたが、見た目は良くなりました。
Linux で Ghostty ターミナルを試してみました。誇大宣伝に応えられるでしょうか?
さらに別のターミナル アプリ。
ここで、スクリプトを bashrc (シェル構成ファイル) に配置し、リロードします。 「s」と入力して実行し、クエリの入力を開始します。パスを含む任意の ripgrep フラグを渡すことができます。
s --search-zip ~/
また、「: | fzf」(コマンドの先頭) により、fzf が最初は現在の作業ディレクトリのファイルを表示しないことに注意してください。これは単に null をパイプするだけです。
このコマンドの大きな欠陥は、 インデックス作成。ファイル システムの内容全体のインデックスは巨大になり、構築には永遠に時間がかかります。それにもかかわらず、ripgrep はまだ生の検索を迅速に実行しており、これは印象的です。検索範囲を狭めるパスを ripgrep に渡すことをお勧めします。また、不要なファイルを除外するためにいくつかのフラグを渡します。どちらも検索を大幅に高速化するはずです。
Linuxターミナルでコマンドのチートシートを入手する方法
時には不正行為も必要です。