zshで究極のオペレーションを

第5回zshの誇る花形機能“補完”

今回はzshの誇る機能の花形とも言える補完機能について紹介しよう。zshの補完は強大で、例示した設定の意味を略さず書こうとするとそれだけでになってしまう(約150ページ分)ので、細かい意味は読者の推測にまかせて、少し変えて便利にカスタマイズする場合のヒントを交えながら解説を進めたい。

補完の有効化

初期化ファイル、あるいはコマンドラインで、以下を入力することでzshのすぐれた補完機能が有効化される。

autoload -U compinit && compinit

補完に関するキー割り当てはいくつもあるが、最低限うまく利用するために以下の2つのキーバインドをまず覚えておけばよいだろう。

  • Tab (C-i) - expand-or-complete
    補完の実行。
  • ESC C-d (または行末のみ C-d) - list-choices (delete-char-or-list)
    マッチする候補の一覧表示。

ちなみに、正確には補完システムの機能ではないが、⁠標準では)ファイル名のみに働く以下のキーも覚えておくと有用である。

  • C-x* - expand-word
    カーソル位置に含まれる単語をシェルの展開規則によってその場で展開。
  • C-xg - list-expand
    カーソル位置に含まれる単語がどのように展開されるかを一覧表示。

Tabによる補完とESC C-dによる候補表示を使いながら標準装備されているきめ細やかな補完を確認してみよう。ただし、標準でONになっているシェルオプションautolistによってTab補完時、同時に候補一覧表示もなされる。普段OFFにしている場合で候補一覧を明示的に得る場合はESC C-dを利用することになる。 以下に、代表的な補完システムの挙動を示す。なお実行例はサンプル zshrcをロードした状態を前提としている部分がある。

1.ファイル名補完

他のシェルにもある最も基本的なファイル名補完は、基本的にはファイル名の先頭部分だけの入力を完全なものに補う機能だが、zshの場合は入力をさらに節約できる機能が備わっている。

% cd /usTab
% cd /usr/                      (/us で始まるファイル名に)

% cd /u/l/a/coTab
% cd /usr/local/apache/conf/    (/ の前に * を補ったパターンで補完)

このように、スラッシュを区切りとして直前に*があると見なして補完することで、一意に定まるパス名を極力短く指定することができる。この機能はEmacsのファイル名入力にも実装されている~/.emacsなどで (partial-completion-mode t)している場合)ので既になじんでいる人も多かろう。

tcshで `set complete=enhance' している場合も同様の部分補完ができる。tcshではピリオド、ハイフン、アンダースコアを区切りとして同様の補完をしたり、大文字と小文字を同一視して補完したりするが、zshでこれを行なうには以下のようにすればよい。

zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z} r:|[-_.]=**'

これは、⁠a-zをそれぞれ対応するA-Zに置き換えて、A-Zもそれぞれ対応するa-zに置き換えて補完してみるのと同時に、右側にハイフンかアンダースコアかピリオドが来る場所には * を補ったかのように補完してみる」ことを指示するもので、これにより以下のような補完挙動が得られる。

% ls -F
extra/  httpd.conf  magic  mime.types  original/
% less m.tTab
% less mime.types

タイプ数削減の大きな味方だ。ところが、やみくもに部分補完を行なう文字を増やすと逆に不便になる場合もある。例を示そう。

% ls
highperformance-std.conf        magic
highperformance.conf            mime.types
httpd-std.conf                  ssl-std.conf
httpd.conf                      ssl.conf

このようなファイルがある場合にhttpd.confを編集しようと以下のように補完すると期待どおりにいかない。

% vi httpd.Tab
% vi httpd-std.conf

これは、`httpd.' までタイプすればファイル名が一意に定まると思ってそこまでしっかり打ったのに、Tabを押したら想定外の httpd-std.conf が出てがっかり、という例である。zshでは「一意に決まるファイルがあるかもしれないから、まずそのまま補完せよ」と設定しておくこともできる。これには先のzstyle例を以下のように変更する。

zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z} r:|[-_.]=**'

中央付近に足した '' がそれで、マッチング規則の変更をまずなし''でやってみて、それでマッチするものがなければ次の引数の 'm:{a-zA-Z}={A-Za-z} r:|[-_.]=**' という規則適用でやれ、ということを意味している。

もうひとつ例を欲張っておこう。⁠大文字小文字を同一視」をONにしたときも、検索文字列としてわざわざ大文字をあらかじめ入れた場合は大文字にだけマッチして欲しい。その場合以下のよう設定が有用となる(大文字小文字に関する部分のみ記述⁠⁠。

zstyle ':completion:*' matcher-list '' 'm:{a-z}={A-Z}' '+m:{A-Z}={a-z}'

これは、以下のようなルール記述を意味する。

  • まず入力された文字そのままで補完してみて
  • それでマッチするものがなければ、小文字を大文字に変えつつ補完してみて
  • それでもマッチするものがなければ、大文字を小文字に変えるルールを追加(`+')して補完してみよ

以上文字の種別を変えて補完を試みる設定を示したが、これはファイル名補完に限らずその他の補完すべてでも有効化させられる。

2.変数補完

コマンドラインでシェル変数や環境変数を入力しようとしている場所では変数名を補完する。通常、コマンドライン先頭はコマンド名か、変数代入時の変数名が来るが、大文字の場合はマッチするものが変数に限られることが多い。

% MATab
Completing parameter
MACHTYPE   MAILCHECK  MAILDIR    MAILPATH   MANPATH
% MANTab
% MANPATH=

また、変数展開は$VARIABLEという形式だけでなく、たとえば ${HOME}あるいは$HOME:hのような形もよく利用する。zshではこれらも目の前に展開することなく正しく補完できる。

(ホームディレクトリの親ディレクトリにアクセスする例)
% ls ${HOTab
Completing parameter
HOME  HOST
% ls ${HOMTab
% ls ${HOME}/
($HOME値がパス名なので自動的に / が補われる)

(続けて : を入力する)
% ls ${HOME:
(}/ が自動的に消えるので h}/ を入力)

% ls ${HOME:h}/Tab
Completing files
....
($HOME/../ にあるファイル(ディレクトリ)一覧が表示される)

3.ユーザ名・ホスト名補完

SSHのように引数にユーザ名やホスト名を必要とする引数位置では、それにふさわしい補完が行なわれる。

% ssh yTab
Completing remote host name
yankee  yourpc.example.com
Completing login name
yas  yukita  yuuji

sshコマンドでは、以下の書式が許される。

ssh remotehost
ssh username@remotehost

そのため `ssh y' とだけ入力した y はホスト名、ユーザ名両方の可能性があり、補完候補も両方が提示される。ホスト名の候補は標準でgetent hostsコマンドの出力や~/.ssh/known_hostsファイルから自動的に取得され、ユーザ名の候補はシステムにホームディレクトリを持つユーザ一覧から取られるため、個人が利用するときの候補としては多すぎることが多い。

候補とするユーザ名、ホスト名を独自のものに設定するには、たとえば以下のようにする。

zstyle ':completion:*' users-hosts \
    user1@{host1,host2} {user2,user3}@{host3,host4}

この記述は、ログイン先ホスト名としてhost1, host2, host3, host4を候補として設定し、そのうちhost1とhost2にログインするときのユーザ名をuser1に、host3とhost4にログインするときのユーザ名をuser2とuser3として設定している。

上記の設定で補完を行なうと以下のようになる。

% ssh Tab
Completing remote host name
host1  host2  host3  host4
Completing login name
user1         user2
% ssh uTab
% ssh user
% ssh user1Tab
% ssh user1@
% ssh user1@Tab
% ssh user1@host
% ssh user1@hostTab
Completing remote host name
host1  host2

なお、⁠ユーザ@ホスト」の形式ではなくユーザ名だけ、あるいはホスト名だけが引数として認められるような場所は、それぞれ以下のようにusershostsスタイルを設定する。

(ユーザ名のみの場合の補完単語設定)
zstyle ':completion:*' users user1 user2 user3 user4 ...

(ホスト名のみの場合の補完単語設定)
zstyle ':completion:*' hosts host1 host2 host3 host4 ...

4.プロセス・ジョブ補完

killコマンドのように、引数位置にプロセスIDやジョブ記法を要求する位置で補完機能を呼ぶと、zshはその場で指定可能なプロセスを調べて候補として提示してくれる。

% jobs
[1]    suspended  sudo -H -s
[2]  - suspended  emacs -nw
[3]  + suspended  $EDITOR .zshrc
% ps
  PID TTY   STAT    TIME COMMAND
  356 ttyp0 Is+  0:00.07 -zsh
 1844 ttyp2 O+   0:00.00 ps
25114 ttyp2 Ss   0:00.23 -zsh 
26716 ttyp2 T    0:00.01 vim .zshrc 
28066 ttyp2 T    0:00.52 emacs -nw (emcws)

(上記のようなプロセス起動状況と仮定)

% kill Tab

Completing process ID
  356 ttyp0 Is+  0:00.07 -zsh
25114 ttyp2 Ss+  0:00.23 -zsh
26411 ttyp2 S+   0:00.00 -zsh
26716 ttyp2 T    0:00.01 vim .zshrc
27519 ttyp2 O+   0:00.00 ps
28066 ttyp2 T    0:00.52 emacs -nw (emcws)

(psを裏で起動してkillできるPIDが提示される
 さらに % まで入れるとジョブが補完候補となる)

% kill %Tab
Completing job
%1  -- sudo -H -s
%2  -- emacs -nw
%3  -- $EDITOR .zshrc

このように事前にpsコマンドなどを打たずとも、シグナルを送れるプロセスの指定方法を知ることができる。

しかし、zshユーザとしてはこれでもまだ不満を感じなければならない。psの結果がたくさん出てきたりすると、どのPIDがどのプロセスに対応しているのか見分けるのがたいへんだし、killでPIDを指定するときに番号を打ち間違ったりする危険性もある。

以下のようにプロセス指定の場所ではメニュー選択モードに移行するようzstyle指定を行なうと、メニュー中のカーソルを移動させて確実にPIDを選ぶことができる。

zstyle ':completion:*:processes' menu yes select=2

コマンドラインでもこれを設定してから実際に補完してみよう。

% kill Tab
Completing process ID
  356 ttyp0 Is+  0:00.07 -zsh
 1084 ttyp2 S+   0:00.00 -zsh                            
 9911 ttyp2 O+   0:00.00 ps
25114 ttyp2 Ss+  0:00.28 -zsh
26716 ttyp2 T    0:00.01 vim .zshrc
28066 ttyp2 T    0:00.52 emacs -nw (emcws)

(1行目が反転表示される。
 さらにもう一度Tabをタイプすると)
% kill Tab
Completing process ID
  356 ttyp0 Is+  0:00.07 -zsh
 1084 ttyp2 S+   0:00.00 -zsh                            
 9911 ttyp2 O+   0:00.00 ps
25114 ttyp2 Ss+  0:00.28 -zsh
26716 ttyp2 T    0:00.01 vim .zshrc
28066 ttyp2 T    0:00.52 emacs -nw (emcws)

(次の候補に進み2行目が反転表示される。
 C-n、C-p でも下、上に移動できる。Returnで確定。
 確定するとPIDの番号のみがコマンドラインに入る。)

このメニュー選択モードは、プロセス指定だけでなくファイル名やユーザ名など他の補完のときも使用できる。もし、PIDの選択だけでなくジョブ記法でもメニュー選択を有効化したい場合は以下のようにすればよい。

zstyle ':completion:*:(processes|jobs)' menu yes select=2

指定した値の最後の select=2 は、補完対象となる語が2つ以上ある場合にのみメニュー選択モードを有効化するという意味である。もっとも、メニュー選択のほうが使いやすいという局面は限られるので、やみくもに設定するのは得策でない。

5.文脈追跡補完

これまで例示した典型的な補完語種別のほかにも、文脈にふさわしい候補を提示するような補完関数が標準で多数用意されている。それらのうちとくに効果的と思われるものをいくつかピックアップし、実例で示そう。

([1]./configure や make は、その場のファイルを実際に解析して
    引数として適切な補完候補を設定)
% pwd
/home/yuuji/make/apache/apache22/httpd-2.2.13
(Apache2.2 のソースのディレクトリ)
% ./configure --Tab
zsh: do you wish to see all 272 possibilities (273 lines)? y
Completing no arguments
Completing option
--bindir           -- user executables (EPREFIX/bin)
--build            -- configure for building on BUILD # (guessed)
--cache-file       -- cache test results in FILE (disabled)
--config-cache     -- alias for `--cache-file=config.cache'
--datadir          -- read-only architecture-independent # data (DAT
--datarootdir      -- read-only arch.-independent data root # (PREFI
   :
   :
(以下省略)
([2] 処理対象ファイルに応じて補完候補を決定 - tarの場合)
% tar jxpf hTab
% tar jxpf httpd-2.2.13.tar.bz2 Tab
(補完候補取得にアーカイブ内部を調べるのに少し時間がかかる)
% tar jxpf httpd-2.2.13.tar.bz2 httpd-2.2.13/
(格納されているファイル名の共通プレフィクス(ディレクトリ)が出る
 続けて Tab)
% tar jxpf httpd-2.2.13.tar.bz2 httpd-2.2.13/tab
Completing file from archive
.deps             LICENSE           acinclude.m4      httpd.spec
.gdbinit          Makefile.in       apachenw.mcp.zip  include
ABOUT_APACHE      Makefile.win      build             libhttpd.dsp
Apache.dsw        NOTICE            buildconf         modules
BuildAll.dsp      NWGNUmakefile     config.layout     os
BuildBin.dsp      README            configure         server
CHANGES           README-win32.txt  configure.in      srclib
INSTALL           README.platforms  docs              support
InstallBin.dsp    ROADMAP           emacs-style       test
LAYOUT            VERSIONING        httpd.dsp
(格納されているファイル名一覧が出る)

% tar jxpf httpd-2.2.13.tar.bz2 httpd-2.2.13/INtab
% tar jxpf httpd-2.2.13.tar.bz2 httpd-2.2.13/INSTALL
([3] 第1引数にサブコマンドを要求するものの補完)
linux# aptitude Tab
autoclean       -- erase old downloaded package files
changelog       -- view a package's changelog
clean           -- erase downloaded package files
download        -- download the .deb file for a package
forbid-version  -- forbid aptitude from upgrading to a specific package version
forget-new      -- forget what packages are "new"
  :
  :
(以下省略)
linux# aptitude INTab
linux# aptitude install virTab
("vir"で始まるパッケージのうちインストールしていないものが候補となる)
virsh                        virtualbox
virt-goodies                 virtualbox-2.0
virt-manager                 virtualbox-2.2
virt-top                     virtualbox-ose
virt-viewer                  virtualbox-ose-dbg
virtaal                      virtualbox-ose-guest-source
virtinst                     virtualbox-ose-guest-utils
virtual-mobile-builder       virtualbox-ose-source
virtual-mysql-client         virtualbox-source
virtual-mysql-server         viruskiller

linux# aptitude remTab
linux# aptitude remove
linux# aptitude virTab
linux# aptitude remove virtualbox-3.0
(aptitude remove の場合は、
 "vir"で始まるパッケージのうちインストールしてあるものが候補となる.
 apt-get、yum や、BSD系の pkg_* なども知的補完が組み込まれている)

% svn Tab
Completing svn command
add         commit      import      mergeinfo   proplist    switch    
blame       copy        info        mkdir       propset     unlock    
cat         delete      list        move        resolve     update    
changelist  diff        lock        propdel     resolved              
checkout    export      log         propedit    revert                
cleanup     help        merge       propget     status                
(Subversionのサブコマンド一覧)

% hg Tab
add         export      locate      qfinish     qrestore    showconfig
addremove   forget      log         qfold       qsave       status    
annotate    glog        manifest    qgoto       qselect     strip     
archive     grep        merge       qguard      qseries     tag       
backout     heads       outgoing    qheader     qtop        tags      
bisect      help        parents     qimport     qunapplied  tip       
branch      identify    paths       qinit       recover     transplant
branches    import      pull        qnew        remove      unbundle  
bundle      incoming    push        qnext       rename      update    
cat         init        qapplied    qpop        resolve     verify    
clone       kwdemo      qclone      qprev       revert      version   
commit      kwexpand    qcommit     qpush       rollback              
copy        kwfiles     qdelete     qrefresh    root                  
diff        kwshrink    qdiff       qrename     serve                 

(Mercurialの場合は ~/.hgrc でロードしている拡張モジュールによって
 サブコマンドが増えるがそれらを自動検出して補完候補としている)

現在普及しているコマンドはzshの補完関数が既に用意されていると思ってよいだろう。どのコマンドの補完に対応しているかをざっと見たい場合は、zshをインストールしたトップディレクトリ($PREFIX)から見たshare/zsh/バージョン番号/functions/ディレクトリにあるファイル一覧を眺めるか、compinitで補完システムを有効化したzshが起動済みであれば、以下のようにすれば確認できる。

% echo ${(F)${(uo@)_comps}}|less
($_comps の値を見やすく加工して出力)

もし、標準でzshが対応していないコマンドの引数を補完させたい場合は自分で定義することになるが、これについては簡単な補完関数の作り方を後述する。

補完方式の選択

主役となる補完キーTabに標準で割り当てられているのはexpand-or-completeで、これは単語の展開と通常の補完を行なう。ここでいう「通常補完」とはいわゆるインクリメンタル補完で、入力された文字列が一意に定まる直前までは漸次的に補完して行くものである。

インクリメンタル補完はユーザに確実に正しい単語を選ばせるためには有用だが、似たような名前のファイルが複数ある場合にはゴールに辿り着くまでかえってたくさんキーを打たなければならないこともある。

メニュー補完は、それまで入力した文字列にマッチする候補が複数ある場合、それらを順番に提示する方式である。Tabキーを押すごとに順次切り替わるので、似たような名前のファイルがたくさんあるときでもTabだけ押していればそのうち目的のファイルが出てくることから、より怠惰な補完法とも言える。主な補完をインクリメンタル補完からメニュー補完にしたい場合はTabキーの割り当てを以下のようにmenu-expand-or-completeに変えるとよい。

この場合、Tabの連打で目的候補を通りすぎてしまった場合のために、逆方向に循環するためのreverse-menu-completeも別のキーに割り当てておくとよい。たとえば以下のように設定しておくと、行きすぎたときにESC Tab(またはESC C-iで戻れる。

bindkey '^i'	menu-expand-or-complete
bindkey '^[^i'	reverse-menu-complete
bindkey '^[i'	expand-or-complete

また、カメラの画像ファイルのように数字部分しか違いのないファイルを補完するときなどはメニュー補完で目的ファイルに辿り着くには何連打もしなければならないので、むしろ「インクリメンタル補完の方が便利」というケースもある。それらを考慮しexpand-or-completemenu-expand-or-completeの両方を好きなキーに割り当てて使い分けることでより素早くより手軽に補完できる。

独自コマンド補完の作成

zsh標準で知的に補完されないもの、あるいは標準の補完は厳密で候補が多すぎてかえって不便と感じる、などの場合は自分なりに便利と思える範囲で補完関数を定義して使うことができる。

ここでは、架空のコマンドに対する補完機能を作成つつ、典型的で簡単なものの作り方を解説しよう。

準備

早速独自の補完関数を作成してみよう。その前に、補完時の挙動を分かりやすくするため、以下の2つを実行しておく。/

% zstyle ':completion:*' format '%BCompleting %d%b'
% zstyle ':completion:*' group-name ''

もしサンプルの.zshrcファイルを利用している場合は既に入っているはずなので以下のようにして確認する。

% zstyle -L

では、定義に進む。

例題

コマンドライン引数で様々なオプションを指定して利用する流儀のコマンドの引数補完を定義してみよう。ここでは架空のコマンドgizの補完定義を試みる。gizコマンドは以下のようなコマンドライン文法を取るものと仮定する。

% giz
Usage: giz [ -fnqv ] [ -g grp ] [ -d {foo|bar|baz} ] file
Possible options are as follows:
	-f		Force overwrite
	-n		No exec, echo only
	-q		quiet
	-v		verbose
	-g grp		set group to grp
	-d kwd		set default keyword to kwd, which is one of:
			  foo, bar, baz

架空の例なので、Usageの文には全く意味がないが、補完定義をする立場から 文法的な特徴のみ記すと以下のようになる。

  • 第1引数にはファイル名を指定する
  • オプションには以下のものがあり、基本的には全て1字オプションである。
    • -f, -n, -q, -v の単体オプション
    • 引数を1つ取る -g, -d オプション
  • -q オプションと -v オプションは意味が反対なので同時に指定しても(間違いではないかもしれないが)意味がない

このような形式のコマンドをコマンドライン入力するときに、

% giz  

あるいは、

% giz - 

のようなカーソル位置で補完キー(Tab)を押したときの挙動を制御したい。

_arguments関数

典型的なコマンドラインオプション・引数の補完処理を包括的に司るのが、zsh補完システムに用意された_arguments関数である。

_arguments関数で補完できるようになるコマンドライン文法は幅広く非常に有用なのだが、_arguments 関数そのものの利用を完全に把握するためには膨大な事前知識を要する。そのためここでは典型的なコマンドライン文法を持つ、前述の架空コマンドgizの補完定義例を示し、同様のコマンド補完が定義できるようになることを目指そう。

先に定義例を示すと、gizの補完ができるようになる関数定義は以下のようになる。

function _giz () {
  _arguments -s : \
    '-f[Force overwrite]' \
    '-n[No exec]' \
    '(-v)-q[Quiet]' \
    '(-q)-v[Verbose]' \
    '-g[Group]:group:_groups' \
    '-d[Keyword]:keyword:(foo bar baz)' \
    '1:file:_files'
}

コマンドラインでも上記のとおり入力すれば、_giz 関数が定義できすぐに利用できる。まずは実際に動かしこの関数定義による補完挙動を理解しよう。実際に機能させるには以下のようにcompdefコマンドで補完定義を補完したいコマンドに割り当てる。

% compdef _giz giz

実際にコマンドラインのコマンド位置にgizを入力し、引数位置でいろいろ補完候補を示した例を示す。

% giz 
(通常引数位置での補完候補)
% giz ESC C-d
Completing file
tetris.html      z-1.html         z-3.html         z-5.html
tetris.png       z-2.html         z-4.html         z-anecdote.html

(ハイフンの直後での補完候補)
% giz -ESC C-d
Completing option
-d  -- Keyword
-f  -- Force overwrite
-g  -- Group
-n  -- No exec
-q  -- Quiet
-v  -- Verbose

(-fオプション入力後の補完候補からは-fが消える)
% giz -fESC C-d
Completing option
-d  -- Keyword
-g  -- Group
-n  -- No exec
-v  -- Verbose
-q  -- Quiet

(-gオプションを入れた後の補完候補)
% giz -f -g ESC C-d
Completing group
_httpd    _sdpd     daemon    guest     named     operator  staff     utmp
_pflogd   _timedc   dbus      kmem      nobody    polkit    sys       wheel
_proxy    authpf    dialer    mail      nogroup   postfix   tty       wsrc
_rwhod    bin       games     maildrop  ntpd      sshd      users
(システムに存在するグループ一覧が出力される)

その他、引数を分けて書いたりいろいろと実験してみよう。

_arguments関数の引数指定

gizの補完の挙動が分かったところで、_arguments関数への引数指定を紐解いていこう。

  _arguments -s : \
    '-f[Force overwrite]' \
    '-n[No exec]' \
    '(-v)-q[Quiet]' \
    '(-q)-v[Verbose]' \

    '-d[Keyword]:keyword:(foo bar baz)' \
    '-g[Group]:group:_groups' \
    '1:file:_files'

このコードの意味は以下のとおり。

1行目:

-sオプションは、補完対象となるコマンドの1字オプションをまとめて記述できることを指示する。たとえば、gizコマンドの-fオプションと-nオプションを同時に指定するのに-fnと書け、これを補完入力するときにも-fと入力したすぐ後ろの位置で他のオプション文字を補完するようにふるまう。

:_arguments関数自身へのオプションがここで終わることを明示する。

2行目、3行目:

-f[Force overwrite] の部分は、gizコマンドのオプション(のうち補完したいもの)-fがあり、補完候補提示時のコメント文を `Force overwrite' にすることを指示している。3行目の -n[No Exec] も同様。

4行目、5行目:

(-v)-q[Quiet]-q[Quiet] の部分は2、3行目と同様で補完したいオプションに-qがあり、その説明文が `Quiet' であることを指示する。その前に付いている(-v)はこのオプションの補完を-vオプションと排他的にすることの指示である。

この例のように(-v)-qと書いた場合、以下のことを指示する。

-qオプションが既にコマンドライン上にある場合、それより後ろでは-vオプションを補完候補から外す

括弧内には除外したいものを空白区切りで複数書ける。

6行目:

-d[Keyword]:keyword:(foo bar baz) の部分はコロンで区切られた3つのフィールドから成っている。最初の -d[Keyword] は上記同様-dオプションの補完登録である。第2フィールド以降の指定はそのオプションに対する引数があることを示し、第2フィールドにはオプション引数補完入力時のメッセージを、第3フィールドにはその補完候補の生成法を指定する。この例の (foo bar baz) のように括弧内に空白区切りの単語を書いた場合は、その単語を候補とする。

% giz -d ESC C-d
Completing keyword
bar  baz  foo

7行目:

'-g[Group]:group:_groups'も6行目と同様の「オプションとそのオプションに対する引数」の補完定義である。最後にある_groupsは補完関数の名前で、第3フィールドに関数名を指定するとその関数によって補完候補を入力するようになる。_groups関数はその名前が示すとおり、システムに存在するグループ名を候補として補完入力させる。

8行目:

'1:file:_files' は第1フィールドが自然数で始まっている。これはその位置での通常引数の補完方法を指定する。通常引数とはオプションやオプションへの引数を除いた引数で、たとえば以下のようなコマンドラインの場合、

% rsync -a --delete -e 'ssh -4' remote:foo bar

'ssh -4'までの部分はすべてオプションやオプションへの引数と考え、通常引数は、第1引数がremote:foo第2引数がbarとなる。

_giz関数の例の'1:file:_files'の場合は通常の第1引数入力時には、`file' というメッセージを出し、実際の単語を_files関数で補完入力させることを指示している。

補完定義のオートロード化

補完入力時の効果が確認できたら、次回ログイン時も有効にするための設定を行なおう。

手順は以下のとおりに進める。

  1. ファイルを保存するzshスクリプト用のディレクトリを決める
  2. そのディレクトリをシェル変数 fpath に設定する
  3. 補完用関数定義を関数と同名のファイル名で保存

ここでは仮に、保存ディレクトリを~/script/zsh に決めたものとして説明する。

% mkdir -p ~/script/zsh

このディレクトリに置いた関数定義ファイルを自動的に読み込むよう、~/.zshrccompinit呼出しより上の部分に以下の記述を追加する。

typeset -U fpath
fpath=($fpath ~/script/zsh)

これで準備完了。fpathはzshが利用する変数で、関数のオートロード時に参照するディレクトリを登録するためのものである。オートロード機能とは、実際に関数実行が必要になった時点で関数定義をファイルから読み込ませることができるものである。これまで補完機能の有効化にautoload -U compinit && compinitとしていたが、このautoloadが関数のオートロードを宣言するものである。これと同じように独自定義したzsh関数もオートロード化できる。先の例に示した_giz関数定義を含めた、以下のようなファイルを _gizというファイル名で作成し、~/script/zshディレクトリに置く。

_gizファイル
#compdef giz

function _giz () {
  _arguments -s : \
    '-f[Force overwrite]' \
    '-n[No exec]' \
    '(-v)-q[Quiet]' \
    '(-q)-v[Verbose]' \
    '-g[Group]:group:_groups' \
    '-d[Keyword]:keyword:(foo bar baz)' \
    '1:file:_files'
}

以上で、次回zsh起動時に_giz関数定義ファイルが、gizコマンドのコマンドライン補完のタイミングで自動的に読み込まれるようになる。1行目に書いた `#compdef giz' は、この関数定義ファイルがgizコマンドの補完時に使われることを示す特殊記法である。

では、その設定が生きているか確かめるためにzshを再起動しよう。

% exec zsh

コマンドラインで "giz -" まで入力した位置で補完が働くか確かめよう。

% giz -ESC C-d
Completing option
-d                -- Keyword
-f                -- Force overwrite
-g                -- Group
-n                -- No exec
-q                -- Quiet
-v                -- Verbose

関数作成の試行錯誤

定義した関数でうまく補完できることが分かったら、今度はそれを改良したくなるもの。関数定義を修正してはロードし直す方法を説明しておく。関数定義をコマンドラインで直接打ち込む段階が完了し、fpath変数に登録したディレクトリに定義ファイル(上の例では~/script/zshを保存した後、関数のデバッグはファイルをエディタで編集しながらやることになるだろう。その場合、修正してセーブしたら、それを現行zshに反映させるために以下のようにする。

% unfunction _giz; autoload +X _giz

unfunctionは既存の関数定義を抹消する。autoload +X は、関数のオートロードを直ちに行なう。

様々な補完アクション

上述の_giz関数定義の7行目:

-g[Group]:group:_groups

の第3フィールド _groups は補完候補としてグループ名を選ぶものであった。このようにzsh標準装備の関数で補完できるものがいくつかある。代表的なものを表に示しておく。

関数補完される語の種別
_aliasesエイリアス
_builtin内部コマンド
_groupsグループ
_filesファイル
_functions関数名
_gnu_generic--helpオプションを受け付けるもの
_hostsホスト名
_parameters変数名
_precommandtime や eval のように引数にコマンドラインを伴うもの
_usersユーザ名
_user_at_hostUSER@HOSTの形式

その他様々な補完形態を取り扱う関数が多数あり、それらはオンラインマニュアル zshcompsys(1) で参照可能だが、マニュアルから読み解くのはおそらく難解すぎるだろう。標準添付されている関数を真似して行くのが早道ではないだろうか。

その先へ

zshは標準で準備されている機能が豊富である。そしてそれらは同時にとことんカスタマイズ可能である。ちょっと感じた面倒を見逃さず調整して行くことで最高に気のきく、しもべを得ることができるだろう。

もちろんあまりカスタマイズを意識しなくとも、細かい気配りが行き届いていて十分に便利であることは間違いない。そして何より、shの制御構造や変数展開などの構文をコマンドラインで使用しても、正確に補完でき、不自由なくヒストリ呼出しや行編集ができるので積極的に利用することでシェルスクリプトの知識がどんどん身に付きスキルアップに繋がるという点は隠れた大きなメリットと言える。これを機会にぜひzshの世界へ飛び込んでみてほしい。

おすすめ記事

記事・ニュース一覧