我輩はブロガーではない。ネタもまだない

SASとかDelphiあたりの人様の役に立たないネタを提供します

バッチ実行ログから不要な改ページ行を削除する

sasプログラムを右クリックしてバッチ実行すると、同じフォルダに
ログが生成されますが、どうしてもログに改ページ、ページ番号、
タイトル、日付が出てしまいます。
(ページ番号はNONUMBERと日付はNODATEオプションで消せますが。)

今回はこれを削除してみようと思います。

まず、以下をsasの構成ファイルに追記します。
構成ファイルは通常
"C:\Program Files\SASHome2\SASFoundation\9.4\nls\ja\sasv9.cfg"
これかと思います。

(うまく行かなければ、レジストリエディタを開いて、
バッチファイルで指定されているcfgファイルを
[HKEY_CLASSES_ROOT\SAS.Program.701\shell\SAS940\command]
あたりで探してください。

C:\Program Files\SASHome2\SASFoundation\9.4\sasv9.cfg
が指定されており、その中に
-config "C:\Program Files\SASHome2\SASFoundation\9.4\nls\ja\sasv9.cfg"
のようにcfg本体が指定されているはずです。
この本体に追記をしてください)

<プログラム>

-TERMSTMT='
options noxwait xsync;
%let ls=%sysfunc(getoption(LINESIZE));
%let xfile=%sysfunc(getoption(LOG));
%let yfile=%sysfunc(pathname(work))\_tmp_.log;
%let zfile=%sysfunc(pathname(work))_mod_.log;
filename y "&yfile";
filename z "%sysfunc(pathname(work))_mod_.log";
x "copy ""&xfile"" ""&yfile""";
data _null_;
  retain posf;
  length logline $&ls. vl 8;
  infile y lrecl=&ls;
  input;
  file z;
  logline=_infile_;
  if logline="" then vl=0;
  else vl=length(logline);

  if _n_=1 then posf=0;
  pos=ANYCNTRL(logline);
  if pos^=1 and posf=0 then put logline $varying&ls.. vl;
  if posf=1 then posf=0;
  if pos =1 then posf=1;
run;

options noxsync;
x "timeout /T 1 & copy ""&zfile"" ""&xfile"" & del ""&zfile""";
'

<解説>

- TERMSTMT

https://documentation.sas.com/?docsetId=lesysoptsref&docsetTarget=n0rjd82dx13qi7n1mw6rsljm7x1o.htm&docsetVersion=9.4&locale=en
SAS終了時に実行するステートメントを指定できます。最大2048文字。

- %let ls=%sysfunc(getoption(LINESIZE));

オプションのLINESIZEから、ログの1行あたりの文字数を取得します
改ページコードが入っている行は切れますが、最後に落とすので気にする必要はありません。

options noxwait xsync;
x "copy ""&xfile"" ""&yfile""";

まず、バッチ実行時のログはSAS自身が掴んでいるので、そのままfilenameで読み取ったり
fcopyすることができません。そこで、Windowsに無理やりコピーさせます。
また、コピー完了を待つため、xsyncを指定します。

%let xfile=%sysfunc(getoption(LOG));

バッチ実行で生成されるログファイル名を取得します。

%let yfile=%sysfunc(pathname(work))\_tmp_.log;

ログのコピー先の一時ファイルとしてSASのworkライブラリ配下を指定します

%let zfile=%sysfunc(pathname(work))_mod_.log;

ログの一時ファイルを加工するファイルを指定します。(不要な行を削除する、等)
SASのworkライブラリの1つ上の階層に作成しているのは、意図的です。後で解説します。

  infile y lrecl=&ls;
  input;
  file z;

一時ファイルから加工ファイルに出力します。

  logline=_infile_;

_infile_で1行そのままを取得します。
input logline $と指定すると、デリミタで区切られちゃうので。

  pos=ANYCNTRL(logline);

ANYCNTRLで制御文字の位置を取得します。
改ページが1文字目に出力されるので、それを検出するためです。
https://support.sas.com/documentation/cdl_alternate/ja/lefunctionsref/67960/HTML/default/p1mt339n2zhb33n1uejmw7470orv.htm

  if pos^=1 and posf=0 then put logline $varying&ls.. vl;

そのまま「put logline」としてしまうと、前の空白が削除されてしまい、
「put logline $&LS..;」だと後ろが空白で埋められてしまうため、
$VARYINGwフォーマットを使って文字列長を指定して、必要な桁で出力します。
https://documentation.sas.com/?docsetId=leforinforref&docsetTarget=n0gyd5fptpa0ufn100pj7ghozy6i.htm&docsetVersion=9.4&locale=en

例えば、

data _null_;
  a='  abc';
  b=length(a);
  put a;
  put a $7.;
  put a $varying7. b;
run;

の結果は以下のようになります。

abc↩
  abc  ↩
  abc↩

1つ目はスペースが削除され、
2つ目は余計なスペースが追加され、
3つ目は変数長だけの長さになっていることが確認できます。

options noxsync;
x "timeout /T 1 & copy ""&zfile"" ""&xfile"" & del ""&zfile""";

加工ログファイルを最初のログファイルに上書きします。
この時、noxsyncとすることで、以下のように処理が実行され、
SASがログを掴んでいるために失敗する、ということを回避します。

  1. xコマンドが実行される
  2. xコマンドの終了を待たずにSASは処理を継続(=SASが終了する)
  3. xコマンドで1秒待つ(SASの終了処理を待つため)
  4. SAS終了後に、copyが実行される


後はいつも通り右クリックからバッチ実行するだけ。
あら不思議、2ページめ以降の改ページ行が消えてスッキリ。
これでgit等での差分比較がしやすくなりますね。

f:id:japelin:20190916172055p:plain
(画像ではcfgに-NOSTIMERも追記しており、実行時処理時間も消しています。※)


※ NOSTIMERはINITSTMT内ではなく、普通にconfig記載でOKだったので修正しました