確認環境
PC | Thinkpad X1 Carbon 2nd Gen |
OS | FreeBSD 12.0-RELEASE-p7 |
fzf | 0.18.0 (0b33dc6) |
sudo | version 1.8.27 |
課題
pkg search
やpkg info
コマンドで、目的の機能を持つパッケージを探すのは難しい。
キーワードでGrepかけた結果をless
やvim
とにらめっこ。。。
「うーん、無さそうだな」
というときは、またキーワードを変えてGrepから。
訂正したところで、また同じことの繰り返し。
もう、そういうのうんざりなんだよ。
対策
もっと楽に連続的に検索したい。選択したい。インストールしたい。
おや、、、、、、探して、選んで、何かする。。。
fzf
案件ですな。
ということで、pkg
のパッケージリストから、fzf
で選択して、インストールや削除するシェルスクリプトを作ってみよう。
以下は、作成したシェルスクリプトを起動して、イメージビューワーを探して、feh
というパッケージを選んで、インストールする様子の動画。
fzf
のプレビュー画面には、パッケージの詳細情報を表示するようにした。
また、fzf
なら綴りを間違えてもいい感じに拾ってくれる。
以降では、以下について説明する。
- コマンド概要
- シェルスクリプトの使用例
- 必要なもの
- コード
- 補足) fzfとの連携処理
- 補足) 他の環境のパッケージ管理コマンドに置き換える場合
1. コマンド概要
コマンドの概要は以下。
pkg search
またはpkg info
からパッケージリストを作成し、fzf
に渡すfzf
でパッケージを選ぶ。fzf
のプレビュー画面には、パッケージ詳細情報を表示- 選んだパッケージに対して、どんなアクションをするか選ぶ
- パッケージのインストール
- パッケージの削除
- パッケージ詳細情報を出力
- パッケージ名の出力
アクションは、コマンドオプションで予め選択できる。
"pkg" wrapper command with fzf USAGE: fpf [-l] [-nivIRv] [package-name] OPTIONS: [source] -l if set '-l', search installed local packages if not set, search from all packages [action] -n print name of selected packages -i print info of selected packages -I install selected packages -R remove selected packages -v print this command version
2. シェルスクリプトの使用例
以降、fpf
というファイル名で上記コードを保存した前提で説明する。
アクション未指定で起動
コマンドオプションでアクションを指定しなかった場合、fzf
で選んだ後にアクションを選ぶことになる。
> fpf # 全パッケージをパッケージリストにして起動 > fpf pkg-name # pkg-nameを含むパッケージに、対象リストを絞った状態で起動 > fpf -l # インストール済みパッケージのみを対象リストにする > fpf -l pkg-name # インストール済みパッケージで、pkg-nameを含む
fzf
での選択中は、プレビュー画面にカーソル行のパッケージの詳細情報を表示する。
?
キーで、表示・非表示は切り替えられる。
パッケージ選択後の画面例は以下。
選んだパッケージ名を表示して、アクションを選択を促すプロンプトが出る。
# Selected packages ------------------------------------------- p5-WWW-Scraper-ISBN-ORA_Driver-0.23 apache-openoffice-4.1.6_5 apache-openoffice-devel-4.2.1856382_2,4 # Please select action ------------------------------------------- n : print name of selected packages i : print info of selected packages I : install selected packages R : remove selected packages q : quit ------------------------------------------- >
基本的には、後述のオプションと一緒。
n
パッケージ名を出力i
パッケージの詳細情報を出力I
パッケージをインストールR
パッケージを削除q
何もせずに終了
このアクション選択画面は、標準エラーに出力している。
そのため、本コマンドの標準出力は、指定したアクションの出力しか出ない。はず。
アクションを指定して起動
アクションは、予めオプションで指定できる。
指定した場合は、fzf
選択後のアクション選択画面は出ない。
> fpf -n # パッケージ名を行区切りで出力 > fpf -i # パッケージの詳細情報を出力 > fpf -I # パッケージのインストール > fpf -R # パッケージの削除(インストール済みリストから探す) > fpf -I pkg-name # `-I`も`-R`も予めパケージ名から対象リストを絞って起動できる > fpf -IR # 複数のアクションを指定した場合は後勝ち > fpf -Il # `-I`指定時、`-l`は無効(インストール済みリストから探しても仕方ない)
3. 必要なもの
今回のスクリプトでは、プリインストールされていない、以下のコマンドを利用している。
fzf
今回のメインである対話的にフィジカル検索できる子sodo
管理者権限を一時的に授けてくれる子 (パッケージをインストール・削除するため)
どちらもpkg
でインストールできるので、持ってない人はインストールしておこう。
> pkg install fzf sodo
fzf
については、こちらの記事で紹介しているので、知らない人は覗いてみてねと。
4. コード
今回のコードはこちら。長い。
パスが通ったディレクトリに置いて、実行権限の追加もお忘れなく。
変なとこあれば、コメントでご指摘頂けると助かります。
ざっくり処理ブロックを説明すると以下。
2行目以降
ヘルプ表示関連25行目以降
オプション&引数処理58行目以降
デバッグ用出力(無効化中)63行目以降
fzf
との連携処理77行目以降
オプション指定がない場合の、アクション選択114行目以降
アクションの実行
オプションやら対話的なアクション指定を増やしたせいで長くなった。
最初は50行程度だったのに。
本質的な部分は63行目以降のfzf
との連携部分だと思う。
ここについて、ちょっと補足しておこう。
補足) fzfとの連携処理
fzf
との連携箇所は以下の部分。
# select packages with fzf PKG_NAME_PICK_PATTERN="s/^\\([^ ]*\\).*/\\1/" selected_pkgs=`$list_cmd $pkg_name \ | fzf -m -q "$search_query" \ --preview "echo {} \ | sed '$PKG_NAME_PICK_PATTERN' \ | xargs $PKG_INFO_CMD" \ --preview-window right:wrap \ --bind '?:toggle-preview' \ | sed -e "$PKG_NAME_PICK_PATTERN"` # cancelled? [ -z "$selected_pkgs" ] && exit 0
PKG_NAME_PICK_PATTERN
には、対象リストの1行から、パッケージ名のみ抜き出すsed
のパターンを指定。
selected_pkgs
に、fzf
の選択したパッケージ名リストが入る。
このとき、選択したパッケージはpkg1 pkg2 pkg3 ...
のように、空白区切りの1行になる。
$list_cmd $pkg_name
で、対象リストを作成するコマンドを指定。
pkg info -x -I
かpkg search -x
に、引数で指定したパッケージ名を渡す感じ。
fzf -m -q "$search_query"
の行からfzf
の起動オプションを指定。
-m
で複数のパッケージを選択を許可-q $search_query
は、空文字しかセットしていないので、今は実質使っていない--preview "echo {}"
からの3行で、プレビュー画面の設定をしているsed
でパッケージ名だけを抜き出して、xargs
でpkg search -f
へ渡している--preview-window right:wrap
で、プレビュー画面を右に表示する指定- プレビュー画面をデフォルトで非表示にする場合は、
wrap
をhidden
に変えるといい
- プレビュー画面をデフォルトで非表示にする場合は、
--bind '?:toggle-preview'
で、?
キーによってプレビュー画面の表示・非表示を切り替える
最後に、sed -e "$PKG_NAME_PICK_PATTERN"
により、選んだ各行をパッケージ名だけに置換えて、fzf
による選択したパッケージ名リストの作成終了。
[ -z "$selected_pkgs" ] && exit 0
で、fzf
で選択をキャンセルした場合(Escなど)、何もせずにスクリプトを終了する。
補足) 他の環境のパッケージ管理コマンドに置き換える場合
今回作成したものは、他の環境のパッケージ管理コマンドにも置換えやすいと思う。
(まぁ、きっと誰かがもう作っているだろうけど)
とりあえず、以下あたりを修正すれば良いと思われる。
4行目付近
: ヘルプ26行目(PKG_LIST_CMD_ALL)
: 全パッケージリストから検索するコマンド27行目(PKG_LIST_CMD_INSTALLED)
: インストール済みパッケージリストから検索するコマンド28行目(PKG_INFO_CMD)
: パッケージの詳細情報を表示するコマンド55行目(pkg_name)
: 初期値に、全リストにマッチするパターンを指定64行目(PKG_NAME_PICK_PATTERN)
: リストの1行からパッケージ名を抜き出すパターンを指定124行目
: パッケージをインストールするコマンドを指定128行目
: パッケージを削除するコマンドを指定
最後に
何かを「探す」という行為は、本来やりたいことの前処理だ。
理想は探さなくていいことなんだろうけど、fzf
を使えば「探す」こと自体がちょっと楽しくなる。
今回は正直、パッケージ検索を楽にしたいというよりは、fzf
と遊んでみたかっただけだった。
とはいえ、fzf
を活用するスクリプトは、今回と同じような流れだと使い勝手が良い気がする。
もう少し整理すればテンプレートになるかもしれない。
むしろfzf
連携シェルスクリプトを自動生成するスクリプトがいいのかも。
可変部分のコマンドやヘルプは別ファイルに書いておいて、スクリプトで自動生成。みたいな。
誰か作ってー。
参考
以上。