2013年8月15日木曜日

データセットのI/Oを補足するコード(私家版#2)

これ自分用のコードです。

function libname(dsn)
{
        i = index(dsn, ".");
        if (i > 0) {
                tmp = substr(dsn, 1, i - 1);
        }
        else {
                tmp = "WORK";
        }
        return toupper(tmp);
}

function memname(dsn)
{
        i = index(dsn, ".");
        if (i > 0) {
                tmp = substr(dsn, i + 1, length(dsn));
        }
        else {
                tmp = dsn;
        }
        return toupper(tmp);
}

function ilog(path, dsn)
{
        printf("R,%s,%s,%d,%s,%d\n", path, dsn, FNR, FILENAME, NR);
        return 1;
}

function olog(path, dsn)
{
        printf("W,%s,%s,%d,%s,%d\n", path, dsn, FNR, FILENAME, NR);
        return 1;
}

/NOTE: ライブラリ参照名/ && /を次のように割り当てました。/ {
        libref = $3;
        getline;
        while ($1 != "物理名") {
                getline;
        }
        lib[libref] = $3;

}

$1 ~ /NOTE:/ && $2 ~ /データセット/ && $NF ~ /オブザベーションを読み込みました。/ {
        dsn = $3;
        libref = libname(dsn);
        mem = memname(dsn);

        if (libref != "WORK" && libref != "SASHELP") {
                dir = lib[libref];
                if (dir != "") {
                        tmp = sprintf("%s/%s.sas7bdat", dir, tolower(mem));
                        status = ilog(tmp, dsn);
                }
                else {
                        printf("ERROR %s\n", dsn);
                        exit;
                }
        }
}

$1 ~ /NOTE:/ && $2 ~ /データセット/ && $NF ~ /変数です。/ {
        dsn = $3;
        libref = libname(dsn);
        mem = memname(dsn);

        if (libref != "WORK" && libref != "SASHELP") {
                dir = lib[libref];
                if (dir != "") {
                        tmp = sprintf("%s/%s.sas7bdat", dir, tolower(mem));
                        status = olog(tmp, dsn);
                }
                else {
                        printf("ERROR %s\n", dsn);
                        exit;
                }
        }
}

$1 ~ /MPRINT\([A-Z0-9_]+\):/ && $2 ~ /^set$/ {
        match($3, /([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+/);
        if (RLENGTH > 0) {
                dsn = substr($3, RSTART, RLENGTH);
                libref = libname(dsn);
                mem = memname(dsn);
                if (libref != "WORK" && libref != "SASHELP") {
                        dir = lib[libref];
                        if (dir != "") {
                                tmp = sprintf("%s/%s.sas7bdat", dir, tolower(mem));
                                status = ilog(tmp, dsn);
                        }
                        else {
                                printf("ERROR %s\n", dsn);
                                exit;
                        }
                }
        }
}

END {
        print "_EOF_";
}

SASデータセットのI/Oを捕捉するコード(私家版)

SASログからI/Oを追いかける私家版のSASプログラム。入力のCSVファイルはAWKで作成しています。お客様先で大量のSASログから、入力と出力を特定するためのものです。入力ファイルで1件もデータを読まないで、僅かにMPRINTのログにしか手掛かりがないものが厄介です。
その他に、SASHELP.VTABLEを通してファイルの存在チェックをしているものなど、SASログには現れない入力があります。

/*
*++
* import io log-file
*--
*/

data iods;
 infile "c:\temp\iods.csv" dsd firstobs=2;
 attrib RW length=$1;
 attrib FILENAME length=$64;
 attrib DSN length=$32;
 attrib FNR length=8;
 attrib LOG length=$61;
 attrib NR length=8;
 input rw filename dsn fnr log nr;
run;

/*
*++
* input data-set
*--
*/

proc sort data=iods out=ids;
 by filename nr rw;
run;

data ids;
 set ids;
 by filename nr rw;
 if first.filename and rw eq 'R' then output;
run;

proc sort data=ids;
 by nr;
run;


/*
*++
* output data-set
*--
*/

proc sort data=iods out=ods;
 where rw eq 'W';
 by filename nr;
run;

data ods;
 set ods;
 by filename nr;
 if last.filename then output;
run;

proc sort data=ods;
 by nr;
run;

/*
*++
* make compare procedure statement
*--
*/

data _null_;
 set ods;
 attrib comment length=$256;
 attrib libstat length=$256;
 attrib titstat length=$256;
 attrib comproc length=$256;
 attrib mem length=$32;

 basedir = "/sumdata/bk02";

 comment = "/* dsn=" || compress(upcase(dsn)) || " logfile=" || compress(log) || " line=" || compress(put(fnr, best.)) || " */";
 put comment;

 i = 1;
 imax = length(filename);
 pos = 0;

 do i=1 to imax;
  if substr(filename, i, 1) eq '/' then pos = i;
 end;

 put "options nocenter;";
 libstat = "libname tmp1 '" || compress(basedir) || substr(filename, 1, pos-1) || "' access=readonly;";
 put libstat;

 libstat = "libname tmp2 '" || substr(filename, 1, pos-1) || "' access=readonly;";
 put libstat;

 titstat = "title 'dsn=" || compress(upcase(dsn)) || " log=" || compress(log) || "';";
 put titstat; 

 mem = substr(dsn, index(dsn, ".")+1);
 comproc = "proc compare base=tmp1." || compress(mem) || " compare=tmp2." || compress(mem) || " maxprint=5;";
 put comproc;
 put "run;";
 put "title;";
 put ;

run;