• コマンドライン補完機構

WindowsのPowerShellではコマンドや引数の補完が可能だ。Linuxのbashでもコマンドや引数などの補完ができる。cmd.exeでは、ファイル名の補完機能はあるが、コマンドや引数の補完機能はない。Androidもadb shellでは、ファイル/ディレクトリ、PATHによる実行ファイルの補完はあるが、引数の補完はできない。

コマンドラインで補完が利用できると、コマンド名がうろ覚えでも問題ない。コマンド名さえわかれば、--helpオプションやシステムのヘルプ(Linux/UNIXのmanやPowerShellのget-helpなど)を使うことでコマンドの使い方がわかる。

コマンドラインにどんなに堪能でも、年に一度使うかどうかのコマンドを完全に覚えるのは、よほど記憶力が良くないと難しい。筆者も記憶力には自信があるほうだが、いつもLinuxのsytemctlコマンドをsyscontrlやsystemctrlだったか、と勘違いすることがある。しかし、bashで“sys”と打ち込んで、タブキーを2回たたけば、“sys”で始まるコマンドの一覧が出る。

コマンドラインの補完は、コマンドラインの使い方を大きく変えた。筆者は、1980年台にBSD系UNIXのcshのファイル補完の機能(set filec)を使ったときに、MS-DOSとの違いを思い知った。仮想メモリなど高度な機能を提供するOSがあれば、たとえコマンドラインでも進化するのものなのだと。

というわけで、いまどき、コマンドラインを使っていて、コマンドをフルスペルで高速打鍵するとか、まったくの時代遅れである。コマンドライン補完を活用すべきなのだ。というわけで、今回はシェルのコマンドライン補完を解説するが、残念ながらページ数の関係もあり、補完情報をカスタマイズする方法までは解説できないことはご了承いただきたい。

bashのコマンドライン補完

オリジナルUNIXに敬意を表して、その子孫であるbashのコマンドライン補完機能を先にみていくことにしよう。bashでは、タブキーを使ってコマンドラインの補完を行う。このとき、すでに入力された文字に応じて、補完方法が異なる。補完対象の文字列が「$」、「~」、「@」で始まる場合、シェル変数、ユーザー名、ホスト名として補完が行われ、それ以外はコマンド補完を試み、いずれも失敗するとファイル名補完を行う。なお、セキュリティ問題のため、カレントディレクトリの実行ファイルは、ユーザーが「./」を付けて明確にカレントディレクトリのファイルを指定しているときしか補完されない。また、補完に使われるキーは、以下のコマンドで確認できる。ディストリビューションなどにより違いがあるかもしれない(readlineの設定に依存)。'complet'は、completeとcompletionの両方にマッチさせるためのものなので、スペルミスではない。


bind -p | grep 'complet'

Ubuntu-18.04のbash、emacs割り当ての場合、(表01)のようなキー割り当てになる。

  • 表01

補完機能は、readlineで行われていて、complete、compopt、compgenコマンドなどのbash組み込みの補完補助コマンドを使ったシェルスクリプトをコマンドごとに定義する。設定は、/etc/bash_completionである。

コマンドが対応しているかどうかは、補完結果を得る「compgen」を使って調べることができる。たとえば、“sys”を含んだ補完されるコマンドを探したいときには「compgen -c '' | grep 'sys' 」 とする。ただし、WSLで、WindowsのPATH環境変数を起動時に読み込んでいる場合、キーワードを含むWindows側の実行ファイル名なども含まれてしまう点には注意されたい。これについては/etc/wsl.confで制御が可能だ。

なお、Linuxに含まれる多くのコマンドについては、GitHubにあるオープンソース・プロジェクトbash-completionが対応している。たいていのディストリビューションでは、bash-competionをパッケージとして導入している。前記のcompletionsディレクトリにある補完情報ファイルもこのパッケージからのものである。ディストリビューションなどにより、補完定義ファイルの場所に違いがある。Ubuntu/Debian系だと/etc/bash_completionは、/usr/share/bash-completion/bash_completionを読み込んでいて、補完定義ファイルは同じディレクトリのcompletionsディレクトリにあった(実際には、前記bash_completion内のスクリプトが決定している)。

scop/bash-completion: Programmable completion functions for bash
https://github.com/scop/bash-completion

PowerShellの補完

PowerShellにも、PSreadlineというbashのreadlineに似たモジュールがあり、コマンドライン補完が行える。PowerShellでは、Get-PSReadLineKeyHandlerでPSreadlineのキー割り当てを調べることができ、「コンプリート機能」にコマンドライン補完のキーが表示される。具体的には、「Ctrl+Space」、「Tab」、「Shift+Tab」の3つがある。TabとShift+Tabは、コマンド候補を1つ1つ表示していく。これに対してCtrl+Spaceは、候補をメニューとして表示し、カーソルキーとEnterキーで選択するもの。ただし、Windows Terminal(現行のVer.1.10安定版、1.11プレビュー版とも)では、候補すべてを表示するスペースがない場合にエラーになるようだ。

Set-PSReadLineKeyHandlerを使えば、キー割り当てを変更することも可能だ。標準では、Tabキーには、PowerShell標準のコマンドライン補完機能であるTabCompleteNextが割り当ててある。この関数では、第一候補でコマンドを補完してしまい、Tabを連打するごとに候補を切り替えていくが、PowerShellのコマンドは「動詞-対象」構造になっているため、多くのコマンドの前半は同じ。このため名前順にコマンドが切り替わっていく標準のコマンドライン補完は使いにくい。

Tabキーを「complete」関数に割り当てなおすと、複数候補がある場合に補完を候補の共通部分のみにとどめておくことができる(bashのcompleteコマンドに似た動作)。具体的には、以下のコマンドを実行するが、起動時に読み込まれるプロファイルなどで定義しておくといいだろう。なお、PSReadlineでキーに割り当て可能な関数は、「get-help about_PSReadline」で見ることができる。補完関連の関数には、(表02)のようなものがある。


Set-PSReadLineKeyHandler -Key TAB -Function Complete
  • 表02

PowerShellには、関数定義用に補完のための情報を示すことができるParamステートメントがある。これでユーザーが定義するPowerShellの関数(コマンドレット)のパラメーターやオプションを定義しておくと、自動的にコマンドラインやPowerShell IDE内での補完が働くようになっている。これとは別に、組み込みコマンドとして「Register-ArgumentCompleter」が用意されている。これは、補完のためのPowerShellプログラムをオプション文字ごとに登録するもの。これを使うと、すでに組み込まれているコマンドや外部コマンド(exeコマンドなど)に、コマンドライン補完を追加することができる。

最近では、多くのオープンソース・プログラムがLinuxだけでなくWindowsや他のOSでも動くのが普通になってきた。しかし、シェル側のコマンドライン補完に対するアプローチには、異なるものがある。Microsoftは、PowerShell用に外部コマンドをPowerShellコマンドでラップして補完などを行うオープンソース・プロジェクトCrescendoを昨年12月に立ち上げた。

シェルが違うからという理論も理解できなくもないが、似たような労力をあちこちでやるのも気になる点。どの場合でも、プログラムの補完スクリプトの作成は、ほとんど利用者に頼ることになるからだ。どんなプログラムにもエキスパートは少なくないが、コマンドライン補完についても詳しく、かつ、補完のためのスクリプトを書いてデバッグできる余裕があるとなると、かなり限られてしまう。なのに同じコマンドの補完に、異なるシステムを作るというのもどうなのか。もちろん、bashとPowerShellは異なるもので、同じスクリプトを走らせることもできない。さて、どうしたらいいものやら。