bash の TAB補完 をカスタマイズする
概要
bash はコマンド名や引数の補完機能を備えており,入力中に TAB キーを押すことで候補を表示したり残りの部分を入力したりできる.
ただし,デフォルトであらゆる状況に対して適切な補完が効くわけではないので,文脈に応じて補完候補を自分でカスタマイズしたいという状況が生じる. このようなとき,bash に備わっている programmable completion という機構を使うことで好きなように補完候補を指定することができる.
本記事では,lpコマンド(プリンタに印刷命令を発行するコマンド)を題材に自作の補完を実装してみた.
補完のしくみ
bash では補完を行う際,事前に設定されたシェルスクリプト関数を呼び出す. このシェルスクリプト関数はコマンド名や入力中の引数を受け取って解析し,文脈に応じて適切な補完候補を生成する.
bash-completion
というパッケージで便利な補完がかなりいろいろと定義されており,たいていはインストールされて使えるようになっているはずである.
GitHub - scop/bash-completion: Programmable completion functions for bash
もし入っていれば,/usr/share/bash-completion
などに補完用の関数の定義がある(~/.bashrc
内にこれらのファイルを読み込む設定があるはずである).
現在定義されている補完はcomplete -p
で確認できる.bash-completion
が入っていれば次のような表示が出るはずである.
complete -F _longopt mv complete -F _root_command gksudo complete -F _command nice complete -F _longopt tr complete -F _longopt head ...
この出力はそのまま bash で実行できるような形で出力されており,complete -F <function-name> <command-name>
で <command-name>
というコマンドの引数の補完方法に<function-name>
というシェルスクリプト関数を指定するという意味になる.
bash-completion
はかなり便利だがそれでもパターンが網羅されているわけではない.
自分で補完候補を定義したい場合は,自分でシェルスクリプト関数を書いて補完用の関数として指定すれば良い.
自作の補完関数は,~/.bash_completion
に保存しておけばbash-completion
が勝手に読み込んでくれる.
簡単な例
_dummy() { COMPREPLY=( one two ) } complete -F _dummy dummy
上の内容をdummy
などの名前でファイルに保存し,source dummy
などで定義内容を読み込む.
ここでdummy <TAB>
などと入力すると,one two
と補完候補が表示されるのがわかる.
_dummy
は補完用の関数で,COMPREPLY
という配列変数に補完候補を設定する.
complete -F _dummy dummy
でdummy
というコマンドに対する引数の補完方法として_dummy()
を使うように指定する.
補完用の関数名は _
+ コマンド名 とするのが通例となっているようである.
入力引数の取得
実際の状況では,特定のオプションの直後にオプションの設定値の候補を補完するなど現在入力中の引数に依存したより複雑な補完が要求される.
このために入力中の引数の数や引数の中身を取得するための _get_comp_words_by_ref
という関数がbash-completion
で用意されている.
これを使うには,次のように書けばよい.
local cur prev cword _get_comp_words_by_ref -n : cur prev cword
local
というのは関数のローカル変数を定義するための bash のキーワードである.
_get_comp_words_by_ref -n : cur prev cword
という書き方で,それぞれ
cur
: 現在入力中の引数prev
: ひとつ前の引数cword
: 引数の数
が設定される.
例えば,次のように補完を定義する.
_dummy() { local cur prev cword _get_comp_words_by_ref -n : cur prev cword echo echo cur: ${cur} echo prev: ${prev} echo cword: ${cword} } complete -F _dummy dummy
ここでdummy foo bar
と入力した段階で TAB キーで補完を試みると,各変数の内容は
cur
:"bar"
prev
:"foo"
cword
:"2"
のように設定される.
補完候補の絞り込み
入力途中の文字列にマッチする候補だけに絞りこむには compgen
という組み込み関数が使える.
comgen <option> <word>
という形で,オプションで指定した方法で生成された補完候補のうち先頭がword
にマッチするもののリストを生成する.
例えば,-W <word list>
という空白区切りの文字列を補完候補とするようなオプションを使って,
compgen -W "foo bar baz" ba
と実行すると,foo,bar,baz
のうちba
から始まるbar
とbaz
が補完候補として生成される.
典型的には,COMPREPLY
の値にcompgen
で生成したリストを設定することで候補の絞り込みを実現する.
実例: lp コマンドでプリンタ名の補完
lp コマンドは,プリンタに印刷命令を発行するコマンドで次のように使う.
lp <filename>
-d <printer-name>
というオプションでどのプリンタを使うか指定できるのだが,デフォルトでは適切に補完が効かない.
しかし,プリンタの型番など人間が覚えているはずがないので,なんとか補完候補を表示してほしいものである.
そこで,実装したのが次の関数である.
_lp() { local cur prev cword _get_comp_words_by_ref -n : cur prev cword if [[ "$prev" == "-d" ]]; then # generate available printer list local printers=$(lpstat -e | tr '\n' ' ') COMPREPLY=( $(compgen -W "${printers}" -- ${cur}) ) else COMPREPLY=( $(compgen -f -- ${cur}) ) fi } complete -F _lp lp
_get_comp_words_by_ref
で引数を取得する.
直前の引数を見て,-d
なら現在の補完候補としてプリンタ名のリストを生成する(lpstat -e
で取得できる).
tr '\n' ' '
というのは改行文字を空白文字に変換してcompgen -W
の引数として使えるようにするための処理である.
-d
オプション以外のときは,compgen -f
でファイル名を補完候補として使う.
たったこれだけのことだが結構快適になるものである. 困ったときはぜひ自分で補完を定義するのに挑戦してみてほしい.
参考
下の記事は内容が充実している.
公式な資料としてはman bash
の Programmable Completion というセクションやman bash-builtins
のcomplete
やcompgen
の項目を見ると良い.
実装例をいろいろと見たい場合には,/usr/share/bash-completion
以下にいろいろと置かれている.