ライブラリ参照名をテンポラリで作りたくないときがあります。そんなときにはディレクトリを指定してライブラリ参照名を定義しない使い方があります。古いTipsだけど忘れていた。
SASプログラムとログを解析するユーティリティを開発するための備忘録です。This is a memo to develop utility that analyzes the SAS log.
2025年5月29日木曜日
2025年5月13日火曜日
basename, dirname, nobs
良く使うけど、良く忘れるファイル名の操作のマクロをまとめておきます。DATAステップは使わずにマクロの文だけで作っているのでDATAステップの中でも値をはめこみ易いのです。高度なエディタだとマクロの終端を認識してくれないことがあるので、これらのマクロは最後の方に書きます。
このマクロの弱点は、括弧とか特殊文字がファイル名やディレクトリに混じると文字列の終端が判定できなくてERRORになることです。括弧を含む又は括弧の開始があって終了がないようなときはERRORになるので要注意です。
/* Strip directory and suffix from filenames. */ %macro basename(path); %local dequotePath orgLength dequoteLength lastSlash x; %let dequotePath=%sysfunc(dequote(&path)); %let orgLength=%length(&path); %let dequoteLength=%length(&dequotePath); %let lastSlash=%sysfunc(findc("&dequotePath", "/", -%length(&dequotePath))); %let x=%substr(&dequotePath, %eval(&lastSlash)); %quote(&x) %mend; /* Strip non-directory suffix from file name */ %macro dirname(path); %local dequotePath orgLength dequoteLength lastSlash x; %let dequotePath=%sysfunc(dequote(&path)); %let orgLength=%length(&path); %let dequoteLength=%length(&dequotePath); %let lastSlash=%sysfunc(findc("&dequotePath", "/", -%length(&dequotePath))); %let x=%substr(&dequotePath, 1, %eval(&lastSlash-2)); %quote(&x) %mend; /* Return number of obs. */ %macro nobs(mydata); %local NOBS; %let NOBS=0; %if %sysfunc(exist(&mydata)) %then %do; %let mydataID=%sysfunc(OPEN(&mydata., IN)); %let NOBS=%sysfunc(ATTRN(&mydataID, NOBS)); %let RC=%sysfunc(CLOSE(&mydataID)); %end; &NOBS %mend;
2024年2月14日水曜日
マクロ変数の名前と値をログに出力する。
何度も忘れているのでメモします。これでマクロ変数名と値をログに出力できます。DATAステップのPUTと書き方が違うので、ぱっと思い出せない。
%put &=var;
マニュアルで %put のセクションには書かれていない。出典がどこにあるのか探す。
https://go.documentation.sas.com/doc/ja/sasstudiocdc/3.8/pgmsascdc/mcrolref/n189qvy83pmkt6n1bq2mmwtyb4oe.htm
2024年2月13日火曜日
PROC HTTP で HTTPSではなくHTTPを使って通信するときのオプション
最近はHTTPではなくHTTPSが当たり前に使われているが、内部の通信だとHTTPが使われていることがある。そのときに必要な PROC HTTPのオプションは、sslparms "SSLREQCERT"="ALLOW"; です。
マニュアルからこのSSLREQCERT=ALLOWの意味を引くと、「ALLOWは、クライアントがサーバ証明書を要求することを指定するが、証明書が提供されなかったり、無効な証明書が提供されたりしても、セッションは正常に進行する」です。PROC HTTPのデフォルトは、SSLREQCERT=DEMANDです。意味は「サーバ証明書が要求され、有効な証明書が提供されなければセッションが終了することを指定する」です。システムオプションでも設定できます。
よく curl、sas-viya CLIで --insecure, -k のオプションありますが。これは SSLREQCERT=NEVER と同じです。証明書のチェックはしません。ALLOWの方が証明書あり、無し、どちらでも対応できるので、例としてはALLOWが使われているのが多いと思います(推測)。
2024年1月17日水曜日
PROC HTTPでトークンを取得するサンプル
PROC HTTPでユーザ名とパスワードを指定してアクセストークンを取得するサンプルです。通常はOAUTH_BEARER=SAS_SERVICES を使うことが多いですが、稀にトークン取得したいというニーズがあります。このコードで?な箇所は認証のヘッダーにsas.cli:をエンコードした値を使っているところです。コロンの前がクライアントのツール、コロンの後にパスワードを指定するのが普通ですが、パスワードは空の状態でエンコードしています。このパラメータはSAS Viya CLIからauth loginするときに--verboseで出力されるデバッグの情報を見てそれに倣っています。クライアントのパスワードが空なのは理解できませんが、ログを見るとそうなっています。時間あるときにPostmanでも同じようにパラメータを指定して動くか確かめます。
/* PROC Sample of using HTTP to obtain an access token. */ %let client=sas.cli; %let cpassword=; %let username=alex; %let password=hogehoge; %let host=%sysget(SAS_SERVICES_URL); filename resp "/tmp/foo.txt"; data _null_; attrib buf length=$64; buf=put("&client.:&cpassword.", $base64x64.); put buf=; call symputx('code', compress(buf), 'G'); run; proc http method="POST" url="&host./SASLogon/oauth/token" in="grant_type=password%str(&)username=&username.%str(&)password=&password." out=resp timeout=15; headers "authorization"="Basic &code" "Content-Type"="application/x-www-form-urlencoded"; run; data _null_; infile resp truncover; input; put _INFILE_; run;
2023年11月21日火曜日
SAS StudioからOpenSearchのログを検索するサンプル
Viyaのログの調査の効率を上げるためにSAS Studioからクエリを投入できないか思案しています。できたのが以下のコードです。
/* Sample code to search OpenSearch by PROC HTTP. */ filename myresult temp; data _null_; attrib code length=$64; /* Change the following user name (admin) and password (hogehoge) to match your environment. */ code=put("admin:hogehoge", $base64x64.); put code=; call symputx('code', compress(code), 'G'); run; proc http url="https://v4m-search.logging.svc.cluster.local:9200/_search" in='{ "query": { "simple_query_string": { "query": "setinit", "fields": [ "message" ], "default_operator": "and" }}}' out=myresult timeout=30 /* clear_cache*/; sslparms "SSLREQCERT"="allow" SSLCERTLOC="/opt/sas/viya/home/SASSecurityCertificateFramework/tls/certs/ca-bundle.pem"; headers "content-type"="application/json" "authorization"="Basic &code."; debug level=0; run; data _null_; infile myresult; input; put _INFILE_; run;このコードが意味するところは、messageフィールドからsetinitを検索しています。URLのホスト名、ポート番号とPEMのファイルはSAS Compute ServerのPodに接続して探したり、kubectl describe endpointsやservicesで探しました。それらのホスト名とかポート番号は公式なドキュメントには書かれていないので実機検証で調べてたものです。なので内部の実装は知らぬ間に変わる可能性があることに留意してください。
2023年10月4日水曜日
OpenSearch を curl から検索する例
SAS Viya Monitoring for Kubernetes でログを検索したい場合に以下のようにクエリを投入できる。結果はJSONなので、これを加工して欲しい情報を見やすく成型する。この curl のコマンドは OpenSearch の DEV Toolからコマンドを生成して、必要最小限のオプションまで削りました。
curl 'https://viya4.example.com/dashboards/api/console/proxy?path=_search&method=GET' \ -H 'osd-xsrf: opensearchDashboards' \ --data-raw '{ "query": { "simple_query_string": { "query": "setinit", "fields": [ "message" ], "default_operator": "and" }}}' \ --insecure -u 'admin:password'このコードが意味するところは、messageフィールドからsetinitを検索しています。-uでユーザ名とパスワードを指定しています。OpenSearch自体にクエリを投げる例はすぐに見つかりますが、クラスタ内部のサービスを指定する方法が判らず数時間調べました。
2023年4月25日火曜日
2023年4月11日火曜日
JSONのファイルから32KBを超えるSASプログラムを抽出する。
LIBNAMEのJSONエンジンの欠点は、32KBを超える長さのデータを抽出できない点です。できないものは仕方ないので、32KBを超えてもSASプログラムを抽出するコードを書きました。
字句解析の状態遷移を考えて "code": の値を抽出してバックスラッシュのエンコードを解いてファイルの保存します。入力のJSONファイルは整形されていないコンパクトや書式の前提です。昔、Lex, Yaccを使っていた記憶を掘り起こした。
/* Code extraction from JSON file of job definition */ %macro extractJobCode(infile=, outfile=, debug=0); %local/readonly DQ='22'x; %local/readonly BSL='5c'x; %local/readonly BS='08'x; %local/readonly FF='0c'x; %local/readonly NL='0a'x; %local/readonly CR='0d'x; %local/readonly TB='09'x; %local/readonly SP='20'x; data _NULL_; attrib filein length=8 label='input file id'; attrib fileid length=8 label='output file id'; attrib stat length=8 label='State of lexical analysis'; attrib token length=$256 label='Token'; attrib ucode length=$4 label='Unicode'; attrib count length=8; /* Open input/output files */ filein=fopen("&infile" , 'I', 1, 'B'); fileid=fopen("&outfile", 'O', 1, 'B'); count=0; stat=0; rec=&SP; do while(fread(filein)=0); rc=fget(filein, rec, 1); %if &debug=1 %then %do; if count < 512 then put stat=rec=token=; count=count + 1; %end; if stat eq 0 and rec eq &DQ then stat=1; else if stat eq 1 then do; if rec eq &DQ then do; /* Double quotes */ if token eq 'code' then stat=2; else stat=0; token=''; end; else do; token=catt(token, rec); stat=1; end; end; else if stat eq 2 then do; if rec in (&SP, &TB, &NL) then stat=2; else if rec eq ':' then stat=3; else stat=0; end; else if stat eq 3 then do; if rec in (&SP, &TB, &NL) then stat=3; else if rec eq &DQ then stat=4; else stat=0; end; else if stat eq 4 then do; if rec eq &DQ then stat=0; else if rec eq &BSL then stat=5; else do; rc=fput(fileid, rec); rc=fwrite(fileid); end; end; else if stat eq 5 then do; /* Handling of backslash-escaped characters */ if rec eq 'b' then do; rc=fput(fileid, &BS); rc=fwrite(fileid); stat=4; end; else if rec eq &DQ then do; rc=fput(fileid, &DQ); rc=fwrite(fileid); stat=4; end; else if rec eq &BSL then do; rc=fput(fileid, &BSL); rc=fwrite(fileid); stat=4; end; else if rec eq 'f' then do; rc=fput(fileid, &FF); rc=fwrite(fileid); stat=4; end; else if rec eq 'n' then do; rc=fput(fileid, &NL); rc=fwrite(fileid); stat=4; end; else if rec eq 'r' then do; rc=fput(fileid, &CR); rc=fwrite(fileid); stat=4; end; else if rec eq 't' then do; rc=fput(fileid, &TB); rc=fwrite(fileid); stat=4; end; else if rec eq 'u' then do; ucode=''; do i=1 to 4; rc=fread(filein); rc=fget(filein, rec, 1); ucode=catt(ucode, rec); end; if ucode eq '0026' then do; rc=fput(fileid, '&'); rc=fwrite(fileid); end; else if ucode eq '003c' then do; rc=fput(fileid, '<'); rc=fwrite(fileid); end; else if ucode eq '003e' then do; rc=fput(fileid, '>'); rc=fwrite(fileid); end; else do; rc=fput(fileid, &BSL); rc=fput(fileid, 'u'); rc=fput(fileid, ucode); rc=fwrite(fileid); end; stat=4; end; end; end; /* Close input/output files */ rc=fclose(filein); rc=fclose(fileid); run; %mend;
2022年12月19日月曜日
遅い PROC SQLのデバッグ
PROC SQLに 昔からあるオプション、機能だが使うのは初めて _METHOD _TREEを試しました。処理時間に問題ありということで、PROC SQL _METHOD _TREE; という具合にオプションを足すと、クエリを評価する順序や評価のツリー構造をログに出力してくれます。
これ DATAステップでインデクス作ったのに使っていないとかログを見ると判ります。性能調整のヒントに FULLSTIMERと一緒に合わせて使えます。
2022年11月1日火曜日
ユニークなライブラリ参照名、ファイル参照名を定義するマクロ
マクロを書いていて地味に困るのがテンポラリのファイル参照名、ライブラリ参照名を定義することです。ユニークでぶつからない名前を定義するために、小さいコードを書きました。参照するファイルが、SASファイルでも動くようにアンダースコアで始まるようにしています。filename関数でテンポラリの名前を作ると #LN00058 のような名前になります。これだと正しくアサインできないときがあるので、アンダースコア始まりにしています。
それと、マクロの定義を削除するステートメントを良く忘れるのでメモしておきます。%SYSMACDELETE で定義したマクロを削除します。AUTOEXECの中で使うけど最後には後始末しておきたいときに使います。
%macro _uname(); %local n; %let n=%sysfunc(monotonic()); _LN%sysfunc(putn(&n, z5.)) %mend; %put %_uname;
2022年10月25日火曜日
CPORTで移送ファイルを作成と圧縮
CPORTで移送ファイルを作成するときに圧縮できることを実験で確認した。移送ファイル形式は圧縮されてはいるが圧縮率は高くない。なので、FILENAMEステートメントでZIPの圧縮を試してみた。圧縮した移送ファイルからCIMPORTできて /tmp/new のディレクトリにもSASファイルがあることを確認できた。。
filename myfile1 '/tmp/foo.xpt'; proc cport lib=sashelp file=myfile1 memtype=data; select air baseball class; run; filename myfile2 zip "/tmp/foo.xpt.zip"; proc cport lib=sashelp file=myfile2 memtype=data; select air baseball class; run; libname newlib '/tmp/new'; proc cimport lib=newlib infile=myfile2; run;
2022年9月21日水曜日
SAS/ACCESSでIn-Database処理になるか?確認する方法
SAS/ACCESSのデータベースを乗り換えたときに、WHERE句の関数がプッシュダウンされているか知りたいという問合せがあった。DBの種類にもよるかもしれないが、OBS=1 を指定すると SQLにLMIT=1が効く場合がある。これでSASログを確認してプッシュダウンされない部分を特定していく。
options sastrace=',,t,d' sastraceloc=saslog stsuffix obs=1; proc sql; create table x as select * from hoge.xyz; quit; options sastrace=off obs=max;
2022年5月9日月曜日
SAS マクロ変数 /READONLY
https://support.sas.com/resources/papers/proceedings15/SAS1575-2015.pdf
2022年5月4日水曜日
SAS Viya CLI 入門 / SAS Viya REST API 入門
SAS Viya CLIの入門として資料を書き起こしました。CLIを使ってViyaのシステムを管理したい人向けです。内容を以下に示します。
- SAS Viya CLIのインストール
- プロファイル設定
- プラグインを使った操作
- OAuthプラグインでクライアントの登録
- SAS Viya CLI + jq
- curl + SAS Viya REST APIs
- SAS Viya REST API 最初に見るポイント
- SASコンテンツのフォルダ作成と権限の設定
2021年11月26日金曜日
JSONのファイルをPROC HTTPで読み込む
/* ジョブスケジューリングのJSONファイルとマップのファイルを定義 */ filename folders temp; filename jsonmap temp; /* JSONファイルを取得 */ proc http url="https://henteko.azure.com/jobFlowScheduling/flows" out=flows oauth_bearer=sas_services; headers 'Accept'='application/vnd.sas.collection+json'; run; /* JSONファイルの先頭をログに出力して確認するだけ */ data _null_; infile flows; length buf $4096; input buf 1-4096; put _all_; run; /* JSONエンジンで読み込ませて、マップは生成 */ libname flows json fileref=folders map=jsonmap automap=replace; /* 後はライブラリ参照名から要素と中身を見る */ proc print data=flows.items; run;
2021年1月17日日曜日
タイムスタンプの文字列生成
よく使うけど、すぐに忘れるタイムスタンプの文字列
%let dt=%sysfunc(putn(%sysfunc(datetime()), E8601DT20.));
2021-01-17T20:35:17
2021年1月8日金曜日
メタデータオブジェクトの検索例
SAS9でメタデータサーバの情報を探し出して、リストを作ることは稀にあります。たまにしかやらないから、どんな検索式を与えるか忘れてしまいます。備忘としていくつか例を残してい置きます。メタデータのAPIは、情報を1個ずつとると遅いのでできれば検索式で絞り込んで少ない回数で取得した方が良いです。
配置済みのジョブの検索
JFJob?@id contains '.' and @PublicType = 'DeployedJob'
2020年7月6日月曜日
スクリプトに組み込むための1行コード(one-liner)
1. Unixの場合には-stdio オプションで、コードを標準入力から投入できる。
echo "proc options option=dms value; run;" | sas -nodms -stdio2. Libnameを書かなくても任意のディレクトリにSASデータセットを出力できる。
echo "data '/var/tmp/foo.sas7bdat'; set sashelp.class; run;" | sas -stdio
2020年4月6日月曜日
LOCKDOWNで指定できるディレクトリを制限

これを実行すると、1つ目がエラーになります。検索に引っ掛かるようにエラーメッセージをタイプしておきます。この投稿を書いている時点では、日本語のエラーメッセージはヒットしません。ERROR: SAS がロックダウン状態の場合、パスc:\tempはアクセス可能なパスのリストに含まれません。