Dear Miss SAS Answers,
I run a lot of programs that call other programs with %include. I was wondering if there was a way, perhaps through a SAS Automatic Macro variable, to determine 1) whether a program was called or "included" from another program? And if so, what is the name of the calling program and the directory where it's stored?
Sincerely,
Misplaced My Programs
p.s.I'm running SAS 9.1.3 on Windows XP.
Dear Misplaced,
If you had the platform for SAS Business Analytics or SAS Data Integration Studio installed, this would be much easier. Both SAS Managment Console and SAS Data Integration Studio have lineage components that allow you to trace the pieces that create a particular metadata object or to see all the pieces that use that metadata object down stream. Instead, I have a macro program solution for tracing all %INCLUDE statements in a particular directory.
What this macro code does is run through all the files in the directory looking for the "%include" token. When it finds that token, it writes out the directory path and the included program name to a SAS data set named work.IncludedFiles.
Let's walk through the code:
/* You can uncomment the following OPTIONS statement to see the macro code, the */
/* macro logic, and the macro variable resolved values displayed in the SAS log.*/
/* Several 'housekeeping' statements to declare local macro variables, upper */
/* case the value of &ext and open the directory */
%macro included(dir,ext);
%local fileref rc did dnum dmem memname didc dsdc dsid dir ext dirname;
%let ext=%upcase(&ext);
%let fileref=thisdir;
%let rc=%sysfunc(filename(fileref,&dir));
%let did=%sysfunc(dopen(&fileref));
/* If the directory does not exists, stop the macro from executing. */
%if &did=0 %then %do;
%put ERROR: Directory does not exist;
%return;
%end;
/* Otherwise, see if the work.IncludedFiles SAS data set exists. */
%else %do;
%let dsid = %sysfunc(open('work.includedfiles','I'));
/* If the data set does not exist, create it. */
%if dsid = 0 %then %do;
proc sql;
create table IncludedFiles
(program char(50),
calledFile char(50));
quit;
%end;
%let dsdc=%sysfunc(close(&dsid));
%end;
/* Find the number of entries in the directory for the subsequent DO loop. */
%let dnum=%sysfunc(dnum(&did));
%do dmem=1 %to &dnum;
/* Read the first entry name, search for the last "word" with a period as the */
/* delimiter. */
%let memname=%sysfunc(dread(&did,&dmem));
/* If the last word is the same as the requested value of &ext, the look for */
/* the first "word" with a period as the delimiter, and write the DATA step */
/* to the input stack. */
%if %upcase(%scan(&memname,-1,.))=&ext %then %do;
%let member=%scan(&memname,1,.);
%let member=%sysfunc(compress(member,' '));
/* DATA step creates a work data set by reading each line of code from the */
/* entry and checking for the word "%include. */
data &member&ext(keep=program calledfile);
length program $ 50 calledFile $ 50;
infile "&dir\&memname" truncover;
input @1 var $80.;
/* If %include is found, then write out the calling program (program) and the */
/* file included (CalledFile) to work.&member&ext. */
if lowcase(scan(var,1, " ")) = "%include" then do;
program = "&dir\&memname";
calledFile = scan(var,2, ' /"');
output;
end;
run;
/* Append the just created file to the work.IncludedFiles data set. */
proc append base=IncludedFiles
data=&member&ext;
run;
/* Delete the file just created. */
proc datasets lib=work nolist;
delete &member&ext;
quit;
%end;
%end;
/* Housekeeping to close the directory and clear the fileref. */
%let didc=%sysfunc(dclose(&did));
%let rc=%sysfunc(filename(fileref));
%mend included;
/* Sample call: value of dir parameter is c:\workshop\MissSASAnswers; */
/* value of ext parameter is sas. */
While this looks complicated if you aren't used to SAS macro code, it is very straightforward and fairly quick. The drawbacks are:
- You have to know the directory you want scanned.
- You have to call the macro multiple times if you have multiple directories or subdirectories to be scanned.
I hope this helps you trace your %INCLUDEd SAS code.
Happy tracking,
Miss SAS Answers
2 Comments
Doh! the example log with options source2 would look like:
I am not sure I understand the original question, since the name of the program and it's directory would appear in the SAS code (ie %include "c:\some path\include this.sas"). Maybe it's obfuscated through macros? Or possibly you have a whole slew of SAS code with multiple versions of programs, etc. And you want to easily tease out which ones are actually being called by the "driver" program?
The simplest thing I would recommend is using options source2; It will print out the name of the program in the log, and help you to follow the flow. It will create a log that looks something like:
Also, there are a few shortcomings with the %included() macro used to answer the question.
should be %if &dsid. The literal letters dsid will never = 0.
The if logic will fail if there is any comments preceding the include:
* this includes my favorite program; %include "c:\my favorites.sas";
The calledFile scan will also fail if %include is using a fileref, or a macro variable:
filename myFave "c:\inlude this file.sas";
%include myFave;
or
%let myFave = c:\my happy sas program.sas";
%include "&myFave";
So I would use options source2 and then write some code to scan the resulting log if it is too big to go through manually.
Hope this helps! -stephen http://www.sascoders.com