今回もコマンド補完設定について述べるとしよう。これまでは1コマンドに対して1つの補完設定ファイルを作成してきた。今回は複数のコマンドに対して1つの補完設定ファイルを作成する方法について解説する。

cal/ncalコマンドの基本

まず、calコマンドとncalコマンドについて説明する。calコマンドはプロンプト1.1のようにカレンダーを出力するためのコマンドで、ncalコマンドはプロンプト1.2のようにカレンダーを出力するコマンドだ。この2つのコマンドには、出力が縦方向か横方向かという違いがある。

プロンプト1.1 cal(1)コマンドでカレンダーを出力


% cal 
     July 2007
Su Mo Tu We Th Fr Sa
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
% 

プロンプト1.2 ncal(1)コマンドでカレンダーを出力


% ncal
    July 2007
Mo     2  9 16 23 30
Tu     3 10 17 24 31
We     4 11 18 25
Th     5 12 19 26
Fr     6 13 20 27
Sa     7 14 21 28
Su  1  8 15 22 29
% 

inode番号を調べたところ、FreeBSDの場合、calとncalは実態は同じコマンドだった(プロンプト1.3)。さらにマニュアルではcalコマンドとncalコマンドについての解説は同じページに掲載されている(リスト1.1)。つまり、この2つのコマンドは出力形式に合わせて用意されたもので、コマンド名が違うだけということが分かる。また、オプションもそれぞれ若干異なっている。

プロンプト1.3 calとncalの実態は同じデータ - FreeBSDの場合


% ls -il =cal
25860577 -r-xr-xr-x  2 root  wheel  16540  7  7 01:32 /usr/bin/cal
% ls -il =ncal
25860577 -r-xr-xr-x  2 root  wheel  16540  7  7 01:32 /usr/bin/ncal
% 

リスト1.1 calとncalは同じマニュアルページで説明されている


CAL(1)                  FreeBSD General Commands Manual                 CAL(1)

NAME
     cal, ncal -- displays a calendar and the date of easter

SYNOPSIS
     cal [-jy] [[month] year]
     cal [-j] -m month [year]
     ncal [-jJpwy] [-s country_code] [[month] year]
     ncal [-Jeo] [year]

DESCRIPTION
     The cal utility displays a simple calendar in traditional format and ncal
     offers an alternative layout, more options and the date of easter.  The
     new format is a little cramped but it makes a year fit on a 25x80 termi-
     nal.  If arguments are not specified, the current month is displayed.

     The options are as follows:
...

ちなみにプロンプト1.3で使っている「=cal」や「=ncal」といった指定は、zshによってコマンドパスに置換される。例えば、この場合なら「=cal」は「/usr/bin/cal」に、「=ncal」は「/usr/bin/ncal」に展開されてから実行されている。typeやwhichコマンドを使ったりタブ補完で記述していく必要がないため、かなり便利に使える記述方法だ。

実態は同じだが名前は違っているコマンド、または実態も異なっているが使っているライブラリが同じでよく似たコマンドオプションを持っているといった場合などは、それぞれ個別に補完設定ファイルを作成するのではなく、1つの補完設定ファイルで複数のコマンドに対応する方法が便利である。

cal/ncalコマンドの補完設定

zsh 4.3.2に同梱されているcal/ncalコマンドの補完設定ファイルを見てみよう(リスト2.1)。これは、簡潔なファイルで読みやすい。

リスト2.1 zsh 4.3.2に同梱されているcal/ncalコマンドの補完設定ファイル


#compdef cal ncal

local args

case $service in
  cal)
    args=(
      '-3[three in a row]'
      '-m[Monday as first day of the week]'
    )
  ;;
  ncal)
    args=(
      '-J[display Julian calendar]'
      '-e[display date of western Easter]'
      '-o[display date of orthodox Easter]'
      '-p[assume as by ncal]'
      '-s[country code]'
      '-w[print number of the week below each column]'
    )
  ;;
esac

_arguments "${args[@]}" \
  '-j[display Julian days]' \
  '-y[display a calendar for the current year]' \
  '::month' \
  ':year'

まずは1行目の#compdef指定に注目しよう。補完対象とするコマンド名としてcalとncalの両方が列挙されている。このようにして複数のコマンドを補完対象とする場合にはコマンド名を列挙すればよい(リスト2.2)。

リスト2.2 #compdefで複数のコマンドを指定


#compdef cal ncal

次にローカル変数としてargsという名前の変数を用意している。ローカル変数にすることでほかに影響が出ないようにしている点に注目しておこう。補完設定を書く場合の基本的なテクニックだ(リスト2.3)。

リスト2.3 ローカル変数として変数を用意


local args

その次で実際に補完対象として使われたコマンドによって処理を切り替えている。ここがポイントだ。変数「service」に、この場合なら「cal」か「ncal」かのどちらかが保持されることになるので、同変数を使って処理をスイッチさせている。ここでそれぞれのコマンドに特有のオプションを、先ほど用意したローカル変数へ代入するわけだ(リスト2.4)。

リスト2.4 service変数にコマンドが保持されているので、これを使ってコマンド別の処理を記述


case $service in
  cal)
    calコマンド用オプションをargsローカル変数へ代入
  ;;
  ncal)
    ncalコマンド用オプションをargsローカル変数へ代入
  ;;
esac

あとは_argumentsの引数に先ほど設定したcal/ncalコマンドそれぞれのオプションを指定して、そのまま共通の補完設定を記述すれば完了である(リスト2.5、リスト2.6)。

リスト2.5 代入したローカル変数を補完設定に追加


_arguments "${args[@]}" \

リスト2.6 引き続き両コマンドに共通のオプションを追加


_arguments "${args[@]}" \
  '-j[display Julian days]' \
  '-y[display a calendar for the current year]' \
  '::month' \
  ':year'

ここで注目すべきは変数「service」に補完対象のコマンド名が代入されていることだ。これが分かれば分岐処理を実現できる。同じような設定が複数のファイルに分離していると扱いにくいので、同変数を使って1つの補完設定ファイルにマージするとよいだろう。