みなさん、こんにちは。
前回紹介した TACLBASE は TACL マクロサンプルの宝庫です。TACL マクロを自習する場合、このファイルを研究するのが王道です。ぜひ一度覗いてみてください。
今回の予告テーマは外部プログラムとの連携です。外部プログラムに fup
を使ってみましょう。ここにパーティション化されたファイル $VOLn.SUB.FILES
があるとしましょう。このファイルにパーティション毎にデータが何件あるか知りたいとしたらどうしますか。
ファイル関係の情報なので #fileinfo built-in function
を使おうと思った方はセンスがよい。ただ、#fileinfo
はパーティション関連の情報は扱えないのでこの場合は残念ながら不正解。ということで fup
を使うしかありません。そう、stat
オプションですね。
$VOL0.SUB.FILES xx xx 2014, 14:38 ENSCRIBE TYPE K FORMAT 1 CODE 256 EXT ( 20 PAGES, 20 PAGES ) REC 16 BLOCK 4096 KEYLEN 2 KEYOFF 0 PART ( 1, $VOL1, 14 PAGES, 42 PAGES, "25" ) PART ( 2, $VOL2, 14 PAGES, 42 PAGES, "50" ) PART ( 3, $VOL3, 14 PAGES, 42 PAGES, "75" ) MAXEXTENTS 16 OWNER 205,255 SECURITY (RWEP): NUUU DATA MODIF: 25 Apr 2014, 11:40 CREATION DATE: 24 Apr 2014, 16:19 LAST OPEN: 25 Apr 2014, 11:40 FILE LABEL: 200 (4.9% USED) EOF: 12288 (1.9% USED) EXTENTS ALLOCATED: 1 TOTAL TOTAL AVG # AVG AVG % LEVEL BLOCKS RECS RECS SLACK SLACK PART 1 1 1 1.0 4065 99 $VOL0 DATA 1 10 10.0 3884 95 FREE 0 BITMAP 1 1 1 1 1.0 4065 99 $VOL1 DATA 1 18 18.0 3740 91 FREE 0 BITMAP 1 FREE 0 $VOL2 BITMAP 0 1 1 1 1.0 4065 99 $VOL3 DATA 1 14 14.0 3812 93 FREE 0 BITMAP 1
情報は得られましたが余分な出力が多く、見やすいとは言えません。これをマクロで整形することにしましょう。次のような形式で出すことにします。
$VOL0.SUB.FILES 1 $VOL0 10 RECS 2 $VOL1 18 RECS 3 $VOL2 0 RECS 4 $VOL3 14 RECS
マクロで処理するためには、まず画面に出力されたメッセージを TACL に取り込む必要があります。fup
プログラムとの連携です。これには run
オプション のひとつである outv オプション
を使います。画面に結果を出力せず variable
に出力する オプション です。run
オプション なので、fup
の起動時に指定します。少し味見してみましょう。
TACL> #push dataout TACL> fup /outv dataout/ info $VOL0.SUB.FILES,stat
……何にも表示されませんでした。fup
の出力が dataout variable
に格納されたため、画面には何も出ないのです。fup
の出力は複数行に亘りますが、そのすべてが dataout variable
に格納されました。確かめてみましょう。
TAC> outvar dataout ↓ ここから展開結果 --------------------------------------------- ⇒ ここに空白行が1行あることに注意してください $VOL0.SUB.FILES xx xx 2014, 14:38 ENSCRIBE TYPE K FORMAT 1 CODE 256 EXT ( 20 PAGES, 20 PAGES ) REC 16 BLOCK 4096 KEYLEN 2 KEYOFF 0 PART ( 1, $VOL1, 14 PAGES, 42 PAGES, "25" ) PART ( 2, $VOL2, 14 PAGES, 42 PAGES, "50" ) PART ( 3, $VOL3, 14 PAGES, 42 PAGES, "75" ) MAXEXTENTS 16 OWNER 205,255 SECURITY (RWEP): NUUU DATA MODIF: 25 Apr 2014, 11:40 CREATION DATE: 24 Apr 2014, 16:19 LAST OPEN: 25 Apr 2014, 11:40 FILE LABEL: 200 (4.9% USED) EOF: 12288 (1.9% USED) EXTENTS ALLOCATED: 1 TOTAL TOTAL AVG # AVG AVG % LEVEL BLOCKS RECS RECS SLACK SLACK PART 1 1 1 1.0 4065 99 $VOL0 DATA 1 10 10.0 3884 95 FREE 0 BITMAP 1 1 1 1 1.0 4065 99 $VOL1 DATA 1 18 18.0 3740 91 FREE 0 BITMAP 1 FREE 0 $VOL2 BITMAP 0 1 1 1 1.0 4065 99 $VOL3 DATA 1 14 14.0 3812 93 FREE 0 BITMAP 1 ↑ ここまで展開結果 ---------------------------------------------
前記の fup
の出力がすべて格納されていることが判りますね。
複数行格納されているということで、単独行の場合と違う点があります。もう1つ variable
を宣言して
TACL> #push textcopy
それにコピーしてみましょう。
TACL> #set textcopy [dataout]
なにやらエラーになってしまいました。何故だか判りますか?
展開を理解していれば答えが判ります。dataout
を展開した形を考えてみましょう。#set textcopy [dataout]
が展開されるとこうなります。
#set textcopy
$VOL0.SUB.INQCMD xx xx 2014, 14:38
ENSCRIBE
TYPE K
FORMAT 1
CODE 256
EXT ( 20 PAGES, 20 PAGES )
REC 16
※ 以下省略
#set textcopy
の後ろが空白なのは、dataout
の先頭行が空白であるためです。次行以降はそのまま残り表示されていますね。
複数行ある variable
は展開時も複数行に展開されます。改行は tacl
の区切り文字の1つなので、#set
コマンドは dataout
の中の最初改行で終端します。#set
コマンドで消費されるのは先頭の1行目だけなのです。このため2行目以下はそのまま tacl
に渡ってしまいます。例えば2行目はこのように打ち込んだのと同じことになります。
TACL> $VOL0.SUB.INQCMD xx xx 2014, 14:38
もちろんコマンドとしては無効でエラーになります。3行目以下も同様で、すべてエラーになります。この動きは結構忘れてしまいがちなので頭の片隅においといてください。
さて、dataout
に取り込んだ fup
出力は、全部まとめては処理できないので1行ずつ取りだします。そのために #extract built-in function
を使います。類似の関数で #extract built-in function
というのもあります。関数名の最後に v
が付くかどうかの差ですが、それぞれ次のような使い方になります。
TACL> #set variable^to [#extract variable^from] TACL> #extractv variable^from variable^to
1行取り出して別の variable
に格納するのが #extractv
で、取り出した行を展開するのが #extract
です。どちらでもかまいませんが、ここでは v
の付かない方を使いましょう。なお、「取り出す」という点に留意してください。取り出された1行は、元の variable^from variable
から削除されるという意味です。「取り出し」を繰り返すと、最後は空になりますので、「空になるまで処理を繰り返す」処理を組む使い方になります。
まず1行取り出してみましょう。
TACL> #set textcopy [#extract dataout] TACL> outvar textcopy
何事も起きませんね……先頭が空行だったので、空白が表示されてしまいました。もう一度やりましょう。2行目です。
TACL> #set textcopy [#extract dataout] TACL> outvar textcopy
今度はファイル名が表示されました。fup
出力の2行目に当たる内容です。#extract/#extractv
を実行するたびに dataout
から1行ずつ取り出されて減っていきます。これを繰り返して1行ずつ取り出し、整形して出力するのが今回目標とするマクロになります。
もう一度出力したい表示形式を見てみましょう。
$VOL0.SUB.FILES 1 $VOL0 10 RECS 2 $VOL1 18 RECS 3 $VOL2 0 RECS 4 $VOL3 14 RECS
先頭の "$VOL0.SUB.FILES"
は2行目から取得できます。2行目は
$VOL0.SUB.INQCMD xx xx 2014, 14:38
なので、こんなロジックになります。
#set textcopy [#extract dataout] #setmany fullname,[textcopy] #output [fullname]
ここは説明不要ですよね。
次の "1 $VOL0 10 RECS"
はいろいろなところからの寄せ集めになります。要素は4つありますね。 "1"
、"$VOL0"
、 "10"
、 "RECS"
です。ま、最後の "RECS"
は単なる文字定数ですが。
最初の "1"
はどうやって出せばいいでしょうか。やりたいことはこんな感じです。
integer i i = 0 i = i + 1 print i
これを TACL に翻訳するとこうなります。
#push i #set i 0 #set i [#compute i + 1] #output [i]
#compute
というのは四則演算を処理する built-in function
です。これを使ってカウントアップすればOKですね。
$VOL0
の部分はどうでしょうか。元の fup
メッセージはこうです。
1 1 1 1.0 4065 99 $VOL0
一番最後、7番目にありますね。7番目まで項目がある行は fup
メッセージの中には数えるほどしかありません。例えば上記の次の行は
DATA 1 10 10.0 3884 95
となっていて、6項目しかありません。統計部分のタイトル行である
TOTAL TOTAL AVG # AVG AVG % LEVEL BLOCKS RECS RECS SLACK SLACK PART
より後ろで7番目まで項目がある行がディスク名を表示した行です。ロジックで書くとこうです。
#set textcopy [#extract dataout]
#setmany item1 item2 item3 item4 item5 item6 item7, [textcopy]
[#if not [#emptyv item7] |then|
#set diskname [item7]
#output [diskname]
]
なお、
[#if item1 = 1 |then|
#set diskname [item7]
]
でもいいじゃないかと思った人がいるかもしれませんが、これはだめです。ここは ENSCRIBE ファイルシステムの話になりますが、データ量が増えてLEVEL が2以上になると、ディスク名は次のように出力されます。
3 1 4 4.0 3925 97 $VOL0 2 4 441 110.3 1977 48 1 441 90194 204.5 170 4
ディスク名を得るには7番目が空じゃない行を取ってこないといけません。
これでディスク名が取得できました。最後の "10 RECS"
はどうでしょうか。次の fup
メッセージはこうです。
DATA 1 10 10.0 3884 95
先頭の項目が #DATA#
である行の2番目の項目を取得すれば、それがデータ件数です。ロジックで書くとこうです。
#set textcopy [#extract dataout]
#setmany item1 item2 item3 item4 item5 item6 item7, [textcopy]
[#if item1 '=' "DATA" |then|
#set rec^count [item2]
]
なお、データがゼロ件のディスクは DATA の行もないので、その場合は件数にゼロを代入する必要があります。
以上をまとめてマクロを作りましょう。マクロ名は武骨に fdatanum
とします。variable type
は 例によって routine
でいきます。次のロジックを macsrc
に追加してください。
?section fdatanum routine
#frame
#push fname dataout textcopy diskname fullname rec^count flag i
#push item1 item2 item3 item4 item5 item6 item7
sink [#argument /value fname/ filename]
sink [#argument end]
fup /outv dataout/ info [fname],stat
#set rec^count 0
#set i 1
#set flag 0
#set textcopy [#extract dataout]
#set textcopy [#extract dataout]
#setmany fullname,[textcopy]
#output [fullname]
[#loop |do|
#set textcopy [#extract dataout]
#setmany item1 item2 item3 item4 item5 item6 item7, [textcopy]
[#if not flag and item1 '=' "LEVEL" and item2 '=' "BLOCKS" |then|
#set flag 1
]
[#if flag and not [#emptyv item7] |then|
#set diskname [item7]
]
[#if flag and item1 '=' "DATA" |then|
#set rec^count [item2]
]
[#if flag and item1 '=' "BITMAP" |then|
#output/width 2,justify right,hold/ [i]
#output/width 1,fill space,hold/
#output/width 8,hold/[diskname]
#output/width 6,justify right,hold/[rec^count]
#output/width 1,fill space,hold/
#output RECS
#set rec^count 0
#set i [#compute i + 1]
]
|until| [#emptyv dataout]
]
#pop item1 item2 item3 item4 item5 item6 item7
#pop fname dataout textcopy diskname fullname rec^count flag i
#unframe
load
して
TACL> load/keep 1/$vol.subvol.macsrc
実行します。
TACL> fdatanum $VOL0.SUB.FILES
いかがですか?外部プログラムである fup
とうまく連携することで、マクロによってパーティション毎のデータ件数が表示できました。
ついでに、少し捻ったパターンを紹介してみます。今のマクロは fup
起動時に outv
オプションで処理結果を vaialble
に取り込んでいました。ここで outv
ではなく out オプション
を使うと
TACL> fup /out fileout/ info $VOL0.SUB.FILES,stat
となります。この場合 fileout
という名前の edit
ファイルに処理結果が出力されます。その edit
ファイルの内容を variable
に取り込めば outv
と同じことになりますよね。で、こうします。
TACL> filetovar fileout dataout
その名の通り、file to variable
でコピーしてくれるマクロです。取り込んだ後は outv
の時と全く同じです。まとめるとこんな感じです。
?section fdatanum routine #frame #push fname dataout textcopy diskname fullname rec^count flag i #push item1 item2 item3 item4 item5 item6 item7 sink [#argument /value fname/ filename] sink [#argument end] fup /out fileout/ info [fname],stat filetovar fileout dataout #set rec^count 0 #set i 1 #set flag 0 #set textcopy [#extract dataout] #set textcopy [#extract dataout] #setmany fullname,[textcopy] #output [fullname] [#loop |do| #set textcopy [#extract dataout] #setmany item1 item2 item3 item4 item5 item6 item7, [textcopy] [#if not flag and item1 '=' "LEVEL" and item2 '=' "BLOCKS" |then| #set flag 1 ] [#if flag and not [#emptyv item7] |then| #set diskname [item7] ] [#if flag and item1 '=' "DATA" |then| #set rec^count [item2] ] [#if flag and item1 '=' "BITMAP" |then| #output/width 2,justify right,hold/ [i] #output/width 1,fill space,hold/ #output/width 8,hold/[diskname] #output/width 6,justify right,hold/[rec^count] #output/width 1,fill space,hold/ #output RECS #set rec^count 0 #set i [#compute i + 1] ] |until| [#emptyv dataout] ] #pop item1 item2 item3 item4 item5 item6 item7 #pop fname dataout textcopy diskname fullname rec^count flag i #unframe
こちらを使うメリットは、fileout
が中間ファイルとして残るのでデバッグがやりやすい、といったところですか。ま、大差ありません。
filetovar
があるなら逆もしかり、vartofile
マクロは varialbe
の内容を edit
ファイルに吐き出すマクロです。vartofile variable
名 ファイル名という使い方ですね。
TACL> vartofile dataout newfile
dataout
に複数行格納されていても、そのすべてが newfile
ファイルに吐き出されます。またファイルへは追加モードで書きますので、既存の内容が消えたりしません。大概のファイルI/Oならこの2つのマクロでできてしまいます。
この filetovar
とか vartofile
を研究すると、TACL におけるファイルI/O機能の全貌がつかめます。今回の主題から逸れてしまうので、外部プログラム連携の話が終わったら改めて掘り下げてみることにします。
今回は外部プログラムの出力結果を TACL に取り込む話でしたが、次回は逆に外部プログラムに TACL からコマンドを与える話をしてみましょう。めったに見られない技が登場しますよ。Au revoir!