フィルタリングコマンド、peco

タグ: Linux   OpenSUSE   Ubuntu  

percolというrubyのツールがあります。標準入力に入ってきたテキストファイルを表示し、インクリメントの絞り込みを行ない、選択された行を標準出力に流すコマンドです。つまり、行単位での選択がCUIでグラフィカルにできるコマンドです。

ruby2系で動作しますが、現在linuxディストリビューションはruby3系が入ってきています。残念ながら、私の環境でも使用できません。

同じ機能を持つコマンド、pecoがあります。こちらはGo言語で書かれ、実行バイナリファイル一つで動作します。

*参照:GitHub:peco/peco

インストール方法も説明されています。まあ、Macの人はHomebrewで入れるんでしょうね。Go言語でコンパイルするのは面倒でもありませんが、Mac、Linux、Windowsに用意されている実行ファイルを落とすのが一番簡単です。

*参照:peco/releases

ダウンロードしたファイルを解凍すると、中に変更ログと実行ファイルが入っています。実行ファイル(peco)をパスの通ったディレクトリーに設置するだけで、準備は完了です。

日本語の情報は少ないですが、zshを使用している方なら、オリジナルのコマンドpercolで検索すると情報がヒットします。

キーバインド

Readmeの最後に、キーバインドについて説明されています。最低限を抜き出し、紹介しましょう。

  • 左右の矢印キー:ページの変更
  • 上下の矢印キー:選択行の移動
  • Ctrl+スペース:選択・非選択の切り替え
  • Ctrl+r:しぼり込み法法の変更(大文字小文字無視、大文字小文字認識、正規表現)
  • Enter:決定

その他のキーバインドも指定されています。詳細はReadmeをご覧ください。

キーバインドは設定ファイルで変更可能です。設定ファイルはいろいろなディレクトリーで指定できますが、一番実用的なのはホームディレクトリー下の.config/peco/config.jsonファイルでしょう。拡張子が表すように、JSON形式です。

指定できる内容などはReadmeをご覧ください。

操作方法:ハンズオン

インストールしたら、実際に使用してみましょう。

peco

入力が無いため、叱られます。

ls | peco

現在のディレクトリーの内容が選択できるようになります。文字列を指定し表示を絞り込み、Enterキーを押してください。選択行カーソルのある行の内容だけが表示されます。

同じコマンドを実行し、今度はCtrl+スペースで行を複数選択してみましょう。Enterを押せば選択した内容だけが表示されます。

注目: 行カーソルのある行も選択されることを確認してください。

もう一度、実行してください。今度はCtrl+rでしぼり込み法法を変更し、文字列を指定指定してください。大文字小文字を区別しないのがデフォルトです。一度切り替えれば区別するモードになります。もう一度切り替えれば、正規表現モードです。再度切り替えれば、区別しないモードに戻ります。

操作方法は以上です。簡単です。

ls | peco | xargs echo

選択したファイル/ディレクトリーの名前が1行にまとめて表示されます。例えばpecoでaaaとbbbを選択したとすると、両方の行がxargsに渡ります。xargsは渡された行の内容をその後のコマンドに引数として渡します。その結果、次のように実行されます。(実際は何が渡されるのか簡単に調べられるように、コマンドを省略すると、その内容をechoします。ですから、上記の場合はechoコマンドを指定しなくても同じ表示内容になります。)

echo aaa bbb

そこで"aaa bbb"が端末に表示されるというわけです。xargsを使用し、コマンドの最後ではなく、途中に入れたい場合はプレースホルダーを指定することで、行うことが可能です。

ls | peco | xargs -i {} echo あなたが選んだのは、{}です。

プレースホルダーには慣用的に{}が使用されることが多いです。プレースホルダーを明示的に指定しない場合も、{}が使用されます。もちろん、好きな文字列が使用できます。(ややこしいのは-Iでもプレースフォルダーが指定できますが、こちらは省略できません。)

複数の行を選択すれば分かりますが、プレースホルダーを指定した場合、各行の内容はコマンドの引数としてまとめられず、各行ごとにコマンドが実行されます。

現在のディレクトリーを取り扱う場合はlsコマンドの出力結果を使用しますが、現在のディレクトリー以下のファイルとディレクトリーをpecoで取り扱いたい場合はfindコマンドを使用しましょう。

find . | peco

ピリオドは現在のディレクトリーを示します。もちろん特定のディレクトリーを指定することもできます。利用価値があるのは、自分のホームディレクトリーを指定する方法でしょう。

find ~ | peco

findコマンドで、表示する対象を絞り込むこともできます。例えば、自分のホームディレクトリー下に存在するmarkdownファイルを表示する場合は、-nameオプションで指定します。

find ~ -name "*.md" | peco

このlsとfindの出力結果をパイプを使用し、他のコマンドに流しこむのはUnix/Linuxの基本で、とても応用できます。

さて、ちょっと長い例を見てみましょう。Bashには自分が実行したコマンドを履歴として保持し、それを利用できるようになっています。履歴を表示するコマンドは、historyです。

history | peco | head -1 | awk -e '{ gsub(/^ +[0-9]+/,"" ); print }' | bash

長いですね。:D

Bash向きに書きましたが、たぶんhistoryが使用でき、表示される内容同じシェルであれば、たいてい使用できると思います。pecoの実行時に選択したコマンドを実行します。もっと良い方法があるかも知れませんが、まあこの方法も勉強にはなりますよ。

pecoは複数行選択できるため、出力は複数行になる可能性があります。今回は簡単に選ばれたものを一つだけ実行する仕様にするため、head -1で最初の1行だけをフィルタリングします。

historyで表示される行のフォーマットは、いくつかの空白に続いて、数字(行番号)空白を挟んでコマンドです。例えば、" 43 ls -a"という形式になります。

Bashでは!に続いて空白を開けずに、その行番号を指定すれば、対応するコマンドが実行できます。残念ながら、この方法はスクリプトとして実行する場合には無理のようです。今回は単純に、実行対象のコマンドが番号の後に続いているのですから、そのコマンドを直接、何らかの形で実行すればよいわけです。

表示行後半の内容をコマンドとして実行するには、行番号の部分を削除してあげればよいわけです。そこで今回はawkを使用し、行番号の部分を空文字列に置き換えることで削除しています。他のツール、たとえばsedを利用しても可能です。最初はcutコマンドを使用しようと思いましたが、行番号の表示を整えるために行頭に詰められる空白の個数が変化するため、cutでは実現できないことに気が付きました。

さて、これでコマンドだけが入手出来ました。これを実行すればよいわけです。前回のようにxargsは使用できません。xargsは指定した実行コマンドの引数にしてくれるコマンドです。実行コマンドを指定しなければ、前述のようにechoするだけですので、コマンド実行ではなく、コマンドを表示するだけになってしまいます。

こうした場合、シェル自身に流しこむことができます。シェルもフィルターとして動作させることができます。実行したいコマンドの文字列を生成し、シェルにパイプにつないで標準入力としてコマンドを読みこませれば、それを実行してくれます。

今回は標準的なbashを使用しました。コマンドを実行させるだけでしたら、zshでもcshでもいけると思います。(試していません。)

では続いて、これを複数選択できるようにしてみましょう。

history | peco | awk -e '{ gsub(/^ +[0-9]+/,"" ); print }' | xargs -i {} bash -c {}

複数の選択を許すようにするのですから、headを使用し、1行に絞り込む必要は無くなりました。当初はhistoryからawkまでの部分の実行結果をbashのforで回せばよいかと気軽に思っており、実際に試してみたら思ったとおりには動きませんでした。

例えば"ls -a"というコマンド1行とっても、これをbashのfor文で取り扱うと、"ls"と"-a"に別れてしまいます。行として処理されず、空白により区切られた単語の並びとして扱われます。

そこで再びxargsの登場です。xargsでbashを起動し、前とは異なりコマンド文字列の引数として、各行の内容を指定しています。単一選択バージョンの場合は、コマンドを標準入力に流しこむことにより指定しました。今回は引数経由で指定します。

引数として指定する場合はオプション-cを使用します。それはそれとして、単純にコマンド+オプションの後ろに、実行コマンドを指定すればよいだけなのに、なぜわざわざプレースホルダーを最後に使用しているのでしょう。

もし、プレースホルダーを使用しない場合、例えば、”ls -a"と"ls -l"のコマンドが選択されていた場合、2つのコマンドが結合され"ls -a ls -l"が実行されてしまいます。もちろん、意図した結果とは異なります。プレースホルダーを指定することで、各行のコマンドごとにbashで実行しています。

ここまでの内容を応用すれば、自分でいろいろな便利コマンドが実現できます。出力し、pecoで選択、必要に応じて編集、xargs/bashで実行です。そのためのハンズオンでした。

サンプル

percolで検索してください。たくさんサンプルが見つかります。流用しましょう。コマンドをpecoに置き換えます。

ですが、見つかるものは大抵zshユーザー向きのものです。私のようなBashユーザーでは使用できないものが多いため、後ほど何か思いついたら、ここに追記しておきます。