1.初めに
Macで標準シェルがbashからzshに変わったので、zshの補完について勉強してみました。bashでもプログラム可能な補完ができますが、zshはそれよりも高水準の機能の補完を標準で盛り込んでいます。主にbashではできないような補完機能について述べていきます。
・参考文献
zshの本
2.zshの補完システム
zshには大別してcompsys、compctlという2つの補完システムが存在する。バージョン3.1.6より導入された新補完システムがcompsys、それよりも前から存在するcompctlという旧補完システムである。
新補完システムであるcompsysについて今回は見ていく。compsysで用意されている全ての補完機能を有効にするには以下のコマンドを実行する。デフォルトではcompsysは有効ではないので注意。よく使うのでこのコマンドは~/.zshrcなどに書き込んでおくのも良い。
autoload -U compinit
compinit
3.補完のキーについて
既にシェルを使っている人は知っているとは思うが、補完をするにはtabを押す。このキーはCtrl-iでも同じなので、どちらの入力でもOK。
また、補完候補を表示するのは行末でCtrl-d。

なお文字上にカーソルがあると、Ctrl-dは1文字削除を表すので注意。また補完候補が複数ある時にtabを連打しても補完候補が表示される。
4.補完機能の概観
tabで補完できるのは、コマンド、コマンドのオプションやサブコマンド、ファイル名、変数、ユーザ名、ホスト名、プロセス、ジョブなど非常に幅広い。これらはcshやbashでも同じように補完できるのが大半だが、zshに特有のものを少し紹介する。
4.1 ファイル名補完
compsysのファイル名補完では、部分マッチによる補完が有効。予め決められた区切り記号(デフォルトでは/)の直前に*を自動で補って補完する。例えば/usr/local/binに行きたい時に/u/lo/bと打ってtabを押すだけで良い。

↓ tab

4.2 変数補完
行頭では環境変数の代入文も記入できる(その環境変数が一時的に代入された状態でそのコマンドを実行できる)という機能があるため、行頭での変数補完もある。

↓ tab

4.3 プロセス補完
killコマンドの引数位置でtabを押すと、プロセス一覧が表示される。

↓ tab

また、コマンド名をPID(プロセスID)に自動変換もしてくれる。

↓ tab

4.4 展開機能
tabには補完機能だけでなく、展開機能もついているので注意が必要である。
具体的には
- ファイル名展開
- ヒストリ展開
- 変数展開
- ブレース展開
などがある。これらはコマンドの実行時に内部的に変換処理が行われるものだが、コマンドラインで先にtabを打つことで前もって変換させることもできる。補完機能ではなく本筋からズレるため詳しく扱わないが、代表的なものだけ少し紹介する。
4.4.1 ファイル名展開
*は、任意の文字列に対応して、ファイル名展開を行う。
4.4.2 ヒストリ展開
ヒストリ機能には!!というコマンドがあり、これは一つ前に実行したコマンドを表す。

上の例ではls --colorを再び実行している。この!!を実行する前にtabを押すことで、先にls --colorを表示させることができる。

↓ tab

同じtabキーを使用するので混乱しがちだが、違う機能なので注意が必要である。
それでは補完の話に戻る。
5.メニュー補完
zshで便利な機能として、メニュー補完がある。これは補完候補がたくさんある時にtabを連打すると順番にその候補からコマンドラインに挿入してくれる。以下に具体例を載せる。
ls Dと入力した状態でtabを押すと候補一覧が表示

↓ tabで、一つ目のDesktopが挿入

↓ 再びtabで、二つ目のDocumentsが挿入

↓ 再びtabで、三つ目のDownloadsが挿入

↓ 再びtabで、一つ目のDesktopが挿入

以下サイクルになる。これもbashには標準で入っていない機能である。
6.補完スタイル
ここからはより細かく補完を制御する話になる。少し込み入っているので、初見の方は特に自分で実際にやってみることをお勧めする。
compsysではシェルオプションによりもきめ細かい設定ができるスタイルによって挙動を制御する。
スタイルとは、compsysが補完を行う時に「どのようなことをどう行うか」を指定するためのものである。これに与える値として、真偽値だけでなく文字列や条件も与えることができる。また、シェルオプションが補完を行う全ての局面で有効になるのに対して、スタイルによる指定はどのような状況で補完機能を呼び出したかを表すコンテクストに応じて設定値を変えることができる。
あるパターンPatternにマッチするコンテクストで、ある特定のスタイルStyleの値をValueに設定するにはzstyleコマンドを用いる。構文は以下。
zstyle Pattern Style Value
上記だけだと理解しづらいため、これから実際の例を見ていく。
・デフォルトではメニュー補完をするが、rmの引数の時にはメニュー補完をしない。
zstyle ':completion:*' menu true
zstyle ':completion:*:rm:*' menu false
・lessとcdの引数入力時、小文字で打った文字列で補完した時に、小文字大文字両方のファイル名にマッチさせる。
zstyle ':completion:*:(cd|less):*' matcher 'm:{a-z}={A-Z}'
zstyleの第一引数に与えているのがコンテクストで、実際の値は6つのフィールドをコロンで区切って並べたものになる。
completion:Func:Completer:Cmd-or-Context:Arg:Tag
-
completionは、補完システムで使うことを意味する固定文字列 -
Funcは、補完システムではなく名前付きウィジェットから呼ばれた場合の関数名(多くの場合空) -
Completerは、コンプリーター(補完ならcomplete、展開ならexpandなど) -
Cmd-or-Contextは、保管中のコマンド・サブコマンド・その他文脈を表すシンボル -
Argは、どの位置の引数を補完しているかを表すシンボル -
Tagは、どういう種類のものを補完しているかを表すシンボル
rmのメニュー補完の例では、completion:*:rm:*のように4つしか指定していないが、これは*が複数のフィールドにまたがっているだけである。ちゃんと書くならcompletion:*:*:rm:*:*となる。
初めのうちは各フィールドの意味を詳細に知る必要はなく、とりあえずcompletion:*あるいはcompletion:*:コマンド名:*で十分である。本記事ではFuncやArg、Tagについては詳しく扱わない。本記事を読んで気になる方は、最初に紹介した参考文献をご覧いただきたい。
6.1 代表的なスタイル
補完スタイルの変更は、「どんな状況で」「何を」「どのように」変えるかを組み合わせて指定する。先ほどの例であったようなmenuスタイルは「メニュー補完の挙動」、matcherスタイルは「マッチしたとみなす文字集合列」を制御するスタイルである。
スタイルは非常にたくさんあり、バージョン5.0.7現在で111ものスタイルがある。
(バージョン5.9現在で170あることを確認)

今回は適用用途の広い、代表的なスタイルについて示す。
6.1.1 補完提示方式の変更(menuスタイル)
menuスタイルは、メニュー補完に加えてメニュー選択も設定できる。これは候補一覧をカーソルで移動して選べる方式である。メニュー補完を有効化するtrueに加えて、メニュー補完の形式をメニュー選択にするselectも追加文字列として指定できる。
zstyle ':completion:*:setopt:*' menu true select
この状態でsetopt autoと入力してからtabを打つと、メニュー選択になり、矢印キーが使えるようになる。

6.1.2 一覧の表示属性設定(list-colorスタイル)
list-colorスタイルを用いれば、候補一覧を色や文字装飾付きで区別して表示できる。
例えば「ディレクトリは下線付きで、実行ファイルは黄色で、拡張子が.cのファイルは紫で表示」なら以下。
zstyle ':completion:*:default' list-colors di=4 ex=33 '=*.c=35'
設定値は「種別=表示属性」の形式を列挙する。種別には以下のようなものがある。
| 種別 | 意味 |
|---|---|
| fi | 通常ファイル |
| di | ディレクトリ |
| ex | 実行ファイル |
| ln | リンクファイル |
| =パターン | 「パターン」にマッチする候補 |
| 表示属性(XYという数字) | Y=0 | Y=1 | Y=2 | Y=3 | Y=4 | Y=5 | Y=6 | Y=7 |
|---|---|---|---|---|---|---|---|---|
| Xが空で属性設定 | 標準 | 太字 | 下線 | 点滅 | 反転 | |||
| X=1で属性解除 | 太字 | 下線 | 点滅 | 反転 | ||||
| X=3で文字設定 | 黒 | 赤 | 緑 | 黄 | 青 | 紫 | 水色 | 白 |
| X=4で背景設定 | 黒 | 赤 | 緑 | 黄 | 青 | 紫 | 水色 | 白 |
list-colorsにスタイルに空文字列を設定すると、GNU lsと同様の色分けになる。
6.1.3 マッチ仕様の調整(matcherスタイル)
補完時にはマッチするものを補う。この「マッチする」という定義自体を変えることができる。
例えばfooまで打って補完すると、fooという3文字が先頭から完全に一致するものだけが補完候補として残るが、これを「大文字と小文字を区別しないで一致とする」というように変更できる。以下これをマッチ仕様という。
マッチ仕様の変更は、全域的な設定はmatcher-listスタイルで行い、ある特定のタグだけに適用させたい時にはmatcherスタイルで行う。標準的な補完動作をmatcher-listに定義しておき、特定のコマンドの補完時だけより柔軟な変更したい、というような時には、そのコマンド名を含むコンテクストに対するmatcherスタイルの値を定義する。matcherスタイルに設定できる値は、matcher-listスタイルに適用できる値と同じである。
入力した単語の全ての文字を別のものに変えてマッチ試行させるには、
m:Lpattern=Tpattern
M:Lpattern=Tpattern
という書式を使う。これはコマンドライン上のLpatternにマッチする文字列をTpatternに置き換えたものでマッチングを行うことを指示する。ファイル名のように大文字と小文字が違うとエラーになるものはmを、シェルオプションのように大文字と小文字が同じように解釈されるものの補完ではMを使う。
例えば
zstyle ':completion:*' matcher-list 'm:to=2'
のように定義すれば元の単語のtoを2に置き換えてくれるので、以下のようにpdftoだけでなくpdf2も補完候補に含められる。
6.1.4 ホスト名・ユーザ名の候補(hostsスタイル、usersスタイル)
補完対象の単語の種類を表すのがタグであるが、sshやpingはどれも引数にホスト名を要求するように、別のコマンドでも同じタグを要求することがある。このような時は、このタグそのものの候補をまとめて変更することで、そのタグを要求するコマンド全ての補完候補を変えることもできる。
ホスト名の候補はhostsスタイル、ユーザ名の候補はusersスタイル、ユーザ名とホスト名を組み合わせた候補はusers-hostsスタイルで設定する。例えば以下。
zstyle ':completion:*:ping:*' hosts aa.example.ne.jp bb.example.com cc.example.ac.jp
この設定では、pingの際のホスト名の補完候補が上記3つのものになる。
zstyle ':completion:*:ssh:*' users-hosts user1@{host1,host2} users2@{host3,host4}
この設定では、sshの際にユーザ名にuser1を入れた時はhost1かhost2だけが、user2を入れた時はhost3かhost4だけが補完候補となる。
6.1.5 ソート(sortスタイル、file-sortスタイル)
候補一覧は通常文字コード順にソートさせるが、候補を好きな順番に登録してその順番に出して欲しい時もある。これにはファイル名以外のソートにはsortスタイルを用いる。
zstyle ':completion:*:ping:*' sort false
ファイル名のソートにはfile-sortスタイルで設定する。例えばlarge、small、very_large、very_smallという名前のファイルがある時、通常は文字コード順に候補が表示される。

これをファイルサイズ順に表示するには以下。
zstyle ':completion:*' file-sort size
これで候補一覧の順番がサイズ順になる。

設定値には以下のいずれかを含む文字列とする。文字列にreverseを追加すると、逆順でソートされる。
| 設定値 | ソートの基準 |
|---|---|
| size | ファイルサイズ |
| links | リンクカウント |
| modification, date, time | ファイル修正時刻 |
| access | 最終アクセス時刻 |
| inode, change | inode修正時刻 |
6.2 コンプリータ
これまでに述べた補完機能は、_completeというコンプリータによるもので、先頭部分を入力してそれにマッチするものを補うのが主な役割である。それ以外にも、綴り間違いを修正したり、シェルの展開機能を施すコンプリータがある。
コマンドラインで補完が要求された時に利用されるコンプリータは複数登録できる。それはcompleterスタイルで指定する。
zstyle ':completion:*' completer Completer1 Completer2 ...
このように設定すると、先頭に書かれたものから順次呼び出され、どれかのコンプリータでマッチが見つかると、以後のものは呼ばれない。
6.2.1 近似補完(_approximate)
_approximateコンプリータは近似マッチと同じ間違い修正をした上で補完する。このコンプリータを使う場合は、通常以下のように_completeコンプリータの後で稼働するように設定する。
zstyle ':completion:*' completer _complete _approximate
これにより、_completeによる通常補完でマッチするものが探せなかった時に限り近似補完をする。この近似マッチの精度を制御するスタイルはmax-errorsスタイルで、間違いの許容数を指定する。デフォルトでは以下のように許容数は2である。
zstyle ':completion:*:approximate' max-errors 2 NUMERIC
ここでいう間違いとは「文字違い(hogeがhage)」「順番違い(emacsがemasc)」「文字抜け(magickがmagic)」「文字超過(magicがmagick)」のことである。
この状態で試しにzshをzhsと打ち間違えてCtrl-dを押すと以下のようになる。

6.2.2 綴り修正(_correct)
_approximateが既に入力した部分文字列を修正してからさらに補完するのに対し、入力を終えたとみなせる単語の綴り修正のみを行うのが_correctコンプリータである。
zstyle ':completion:*' completer _complete _correct
6.2.3 単語途中の補完(_prefix)
例えば/usr/share/manを入力しようとし、/usr/maまで打ってからshareディレクトリに気付いて入力し直すとする。
この時以下のような状況になるが、この位置で補完してもパス区切りをまたいだマッチングは起きないので、shaの部分がshareに補完されることはない。

この場合、カーソル以降を無視してカーソル以前の部分だけで補完するコンプリータ_prefixが有用である。_prefixは他のコンプリータと違い、それ自身が独自の補完方式を持つのではなく、単語中のカーソル以降の文字を除去した単語に対して、その時点で呼び出されるべき全てのコンプリータを呼ぶ。
zstyle ':completion:*' completer _complete _approximate _prefix
とすると、最初に_completeと_approximateが呼ばれ、それでも補完されない時に_prefixが呼ばれて、カーソル以後の文字列を無視した状態でまた_completeと_approximateが呼ばれる。
_prefixコンプリータが発動する時に呼ばれるコンプリータを本来のものとは違うものにもできる。その場合は例えば
zstyle ':completion::prefix:*' completer _complete
のようにコンプリータフィールドにprefixを入れたものでcompleterスタイルの指定を行う。
7.終わりに
zshの補完には非常にたくさんの種類があります。特にスタイルなんかはたくさんあるので、man zshcompsysなんかで確認しましょう。




