みなさん、こんにちは。

今回はまず「c.ファイル名複数指定」です。こんな形に対応します。

TACL> fl $vol1.user*.* $vol2.cust*.* $vol3.manage*.*

複数指定なので、#argument でテンプレートを取得できる限り「a.ファイル名にワイルドカードを指定」の処理を無限に繰り返せばいいのです。繰り返しは #LOOP built-in function でしたね。既にワイルドカードの展開で #LOOP を使っていますので、二重ループを構成することになります。

イメージ的には

[#loop |do|
    [#loop |do|        ---> この #loop 内が今の fl マクロの処理
        ワイルドカード展開
    |until| ワイルドカードが展開できない
    ]
    次のパラメータ取得
|until| END にヒット
]

という感じです。ほんの数行の追加で済みます。簡単ですね。

?section fl routine
#push fn1 fn2 sb2 ts1 ts2 err tmp breakvol arg
#push yyyy mm dd hh mmm ss ms us

[#if [#argument/value tmp/TEMPLATE END] = 2 |then|
#set tmp *
]
#set breakvol ----

[#loop |do|
#set fn1 [#filenames/maximum 1/ [tmp]]

[#loop |while| NOT [#EMPTYV fn1] |do|
#set  fn2 [#fileinfo/file/[fn1]]
#set  sb2 [#fileinfo/volume/[fn1]].[#fileinfo/subvol/[fn1]]
#set  ts1 [#fileinfo /LASTOPEN_GMT/[:fn1]]
#setmany err ts2,[#CONVERTTIMESTAMP [ts1] 0]

#setmany _ yyyy mm dd hh mmm ss ms us,[#interprettimestamp [ts2]]
[#if breakvol '<>' sb2 |then|
#output
#output [sb2]
#output /width 18,hold/
#output LAST OPEN
#set breakvol [sb2]
]
#output /width 18,hold/ [fn2]
#output /width 5,hold,fill zero,justify right/ [yyyy]/
#output /width 3,hold,fill zero,justify right/ [mm]/
#output /width 2,hold,fill zero,justify right/ [dd]

#output /width 1,hold,fill space/

#output /width 3,hold,fill zero,justify right/ [hh]:
#output /width 3,hold,fill zero,justify right/ [mmm]:
#output /width 3,hold,fill zero,justify right/ [ss].
#output /width 3,hold,fill zero,justify right/ [ms]
#output /width 3     ,fill zero,justify right/ [us]

#set fn1 [#filenames/maximum 1, previous [fn1]/ [tmp]]
]

#set arg [#argument/value tmp/TEMPLATE END]
|until| arg = 2
]

#pop yyyy mm dd hh mmm ss ms us
#pop fn1 fn2 sb2 ts1 ts2 err tmp breakvol arg

追加した

#set arg [#argument/value tmp/TEMPLATE END]
|until| arg = 2

は、上記のイメージに沿って2行で書いていますが、実は1行でも書けてしまいます。

|until| [#argument/value tmp/TEMPLATE END] = 2

arg variable を使わずにすむので、こちらの方がいいですね。それでこうなります。

?section fl routine
#push fn1 fn2 sb2 ts1 ts2 err tmp breakvol
#push yyyy mm dd hh mmm ss ms us

[#if [#argument/value tmp/TEMPLATE END] = 2 |then|
#set tmp *
]
#set breakvol ----

[#loop |do|
#set fn1 [#filenames/maximum 1/ [tmp]]

[#loop |while| NOT [#EMPTYV fn1] |do|
#set  fn2 [#fileinfo/file/[fn1]]
#set  sb2 [#fileinfo/volume/[fn1]].[#fileinfo/subvol/[fn1]]
#set  ts1 [#fileinfo /LASTOPEN_GMT/[:fn1]]
#setmany err ts2,[#CONVERTTIMESTAMP [ts1] 0]

#setmany _ yyyy mm dd hh mmm ss ms us,[#interprettimestamp [ts2]]
[#if breakvol '<>' sb2 |then|
#output
#output [sb2]
#output /width 18,hold/
#output LAST OPEN
#set breakvol [sb2]
]
#output /width 18,hold/ [fn2]
#output /width 5,hold,fill zero,justify right/ [yyyy]/
#output /width 3,hold,fill zero,justify right/ [mm]/
#output /width 2,hold,fill zero,justify right/ [dd]

#output /width 1,hold,fill space/

#output /width 3,hold,fill zero,justify right/ [hh]:
#output /width 3,hold,fill zero,justify right/ [mmm]:
#output /width 3,hold,fill zero,justify right/ [ss].
#output /width 3,hold,fill zero,justify right/ [ms]
#output /width 3     ,fill zero,justify right/ [us]

#set fn1 [#filenames/maximum 1, previous [fn1]/ [tmp]]
]

|until| [#argument/value tmp/TEMPLATE END] = 2

#pop yyyy mm dd hh mmm ss ms us
#pop fn1 fn2 sb2 ts1 ts2 err tmp breakvol

さあ、最後です。「d.作成日時や更新日時にも対応」を実装します。d. の要求仕様は次の通りでした。

TACL> fl macsrc
-------> 今まで通り最終オープン日時
TACL> fl /open/ macsrc
-------> これも今までと同じ動作
TACL> fl /create/ macsrc
-------> ファイル作成日時を印字
TACL> fl /mod/ macsrc
-------> 最終更新日時を印字

d. の要求仕様から、最初のパラメータは TEMPLATE END に加えて SLASH を許容しなければなりません。1番目のパラメータが SLASH にヒットしたということは /OPEN//CREATE//MOD/ のどれかということですので、2番目のパラメータは KEYWORD オプションで OPENCREATEMOD のいずれかに制限して取り込みます。最後の閉じる / も忘れないようにしましょう。

[#case [#argument/value tmp/SLASH TEMPLATE END]
|1|
    sink [#argument/value func/KEYWORD/WORDLIST OPEN  CREATE  MOD/]
    sink [#argument/value tmp/SLASH]
    [#if [#argument/value tmp/TEMPLATE END] = 2 |then|
        #set tmp *
    ]
|3|
    #set tmp *
]

という感じになります。閉じる / の後にもう一度 TEMPLATE END の検査をしているのがミソです。タイムスタンプ取得の部分はオプションに応じた改造が必要です。

[#case [func]
|OPEN|
    #set  ts1 [#fileinfo /LASTOPEN_GMT/[:fn1]]
    #setmany err ts2,[#CONVERTTIMESTAMP [ts1] 0]
|CREATE|
    #set  ts1 [#fileinfo /CREATION_GMT/[:fn1]]
    #setmany err ts2,[#CONVERTTIMESTAMP [ts1] 0]
|MOD|
    #set  ts1 [#fileinfo /MODIFICATION_LCT/[:fn1]]
    #set  ts2 [ts1]
]

MODIFICATION_LCTJST なので #CONVERTTIMESTAMP が不要ということに注意してください。

以上が要求仕様に基づく変更点ですが、今まで説明していない注意事項が1点ありますのでそれも考慮に入れることにしましょう。

fup create でファイルを作成した直後等、ファイルは実在していてもオープンされたことが一度もない、ということがあります。このようなファイルに対する #fileinfo /LASTOPEN_GMT/ の戻り値はゼロになります。このため

#setmany err ts2,[#CONVERTTIMESTAMP [ts1] 0]

がエラーになって ts2 に値が入らず #interprettimestamp で時刻に展開できません。この場合の処理も追加する必要があります。

以上をすべて反映すると、ソースは次のようになります。

?section fl routine
#push fn1 fn2 sb2 ts1 ts2 err tmp func breakvol title
#push yyyy mm dd hh mmm ss ms us


[#case [#argument/value tmp/SLASH TEMPLATE END]
|1|
    sink [#argument/value func/KEYWORD/WORDLIST OPEN  CREATE  MOD/]
    sink [#argument/value tmp/SLASH]
    [#if [#argument/value tmp/TEMPLATE END] = 2 |then|
        #set tmp *
    ]
|2|
    #set func OPEN
|3|
    #set tmp *
    #set func OPEN
]

#set breakvol ----

[#loop |do|
#set fn1 [#filenames/maximum 1/ [tmp]]

[#loop |while| NOT [#EMPTYV fn1] |do|
#set  fn2 [#fileinfo/file/[fn1]]
#set  sb2 [#fileinfo/volume/[fn1]].[#fileinfo/subvol/[fn1]]

[#case [func]
|OPEN|
    #set ts1 [#fileinfo /LASTOPEN_GMT/[:fn1]]
    #setmany err ts2,[#CONVERTTIMESTAMP [ts1] 0]
    #set title LAST OPEN
|CREATE|
    #set ts1 [#fileinfo /CREATION_GMT/[:fn1]]
    #setmany err ts2,[#CONVERTTIMESTAMP [ts1] 0]
    #set title CREATED
|MOD|
    #set ts1 [#fileinfo /MODIFICATION_LCT/[:fn1]]
    #set ts2 [ts1]
    #set title LAST MODIFIED
]

[#if breakvol '<>' sb2 |then|
#output
#output [sb2]
#output /width 18,hold/
#output [title]
#set breakvol [sb2]
]
#output /width 18,hold/ [fn2]

[#if [#emptyv ts2] |then|
#output  NEVER OPENED
|else|
#setmany _ yyyy mm dd hh mmm ss ms us,[#interprettimestamp [ts2]]
#output /width 5,hold,fill zero,justify right/ [yyyy]/
#output /width 3,hold,fill zero,justify right/ [mm]/
#output /width 2,hold,fill zero,justify right/ [dd]

#output /width 1,hold,fill space/

#output /width 3,hold,fill zero,justify right/ [hh]:
#output /width 3,hold,fill zero,justify right/ [mmm]:
#output /width 3,hold,fill zero,justify right/ [ss].
#output /width 3,hold,fill zero,justify right/ [ms]
#output /width 3     ,fill zero,justify right/ [us]
]
#set fn1 [#filenames/maximum 1, previous [fn1]/ [tmp]]
]

|until| [#argument/value tmp/TEMPLATE END] = 2
]

#pop yyyy mm dd hh mmm ss ms us
#pop fn1 fn2 sb2 ts1 ts2 err tmp func breakvol title

ずいぶん長いソースになってきましたね。説明していない細かい修正も入れています。技術的に新しい要素は特にないので、みなさんでチェックしてみてください。個人的にはこの fl マクロから派生させたものをよく使っています。みなさんも各自の業務に合わせて改造していってください。

え、なんですか。長いソースのくせにコメントの1つもない?そうですか、コメントを入れたいですか。コメントは次のように入れます。

TACL> == comment line
TACL> {comment block}

察してもらえると思いますが、== は行の最後まで全部がコメントで、{} は囲った間だけがコメントになります。単純にこんな書き方もできます。

TACL> comment any text here

comment はパラメータを読み捨てているだけの routine です。読み捨てると言えばこんなのもありましたね。

TACL> sink any text here

これでコメントを書くことはまずないでしょうけど。適切なコメントは好感度アップの秘訣です。センスを発揮してくださいね。

さて、長かった fl マクロも今回でおしまいです。次回はまた違う課題を取りあげましょう。Au revoir!