Tracing calling programs for %INCLUDE

2

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.*/
 
    /* options mprint mlogic symbolgen; */

/* 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. */
 
 
    %included(c:\workshop\MissSASAnswers,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

Share

About Author

Miss SAS Answers

Technical Training Specialist

Linda Jolley has been a SAS software user since 1980. She has been an instructor for SAS since 1997, and is Base and Advanced SAS Programming certified and working on the Data Integration Developer certification. She has presented papers at several SAS user group venues on various SAS programming efficiency techniques and the SAS Scalable Performance Data Server.

2 Comments

  1. Doh! the example log with options source2 would look like:

    84
    85   data _null_;
    86     put 'this is just some sas code executing...';
    87   run;
    
    this is just some sas code executing...
    NOTE: DATA statement used (Total process time):
          real time           0.00 seconds
          cpu time            0.00 seconds
    
    
    88
    89   %include "c:\temp\prog2.sas";
    NOTE: %INCLUDE (level 1) file c:\temp\prog2.sas is file c:\temp\prog2.sas.
    90  +
    91  +data _null_;
    92  +  put 'this some other sas code';
    93  +run;
    
    this some other sas code
    NOTE: DATA statement used (Total process time):
          real time           0.00 seconds
          cpu time            0.00 seconds
    
    
    94  +
    95  +* and include even more levels deep;
    96  +filename prog3 "c:\temp\prog3.sas";
    97  +%include prog3;
    NOTE: %INCLUDE (level 2) file PROG3 is file c:\temp\prog3.sas.
    98  +data _null_;
    99  +  put 'this a third level';
    100 +run;
    
    this a third level
    NOTE: DATA statement used (Total process time):
          real time           0.00 seconds
          cpu time            0.00 seconds
    
    
    101 +
    102 +
    103 +* and include even more levels deep;
    104 +filename prog4 "c:\temp\prog4.sas";
    105 +%include prog4;
    NOTE: %INCLUDE (level 3) file PROG4 is file c:\temp\prog4.sas.
    106 +data _null_;
    107 +  put 'this is the final level';
    108 +run;
    
    this is the final level
    NOTE: DATA statement used (Total process time):
          real time           0.00 seconds
          cpu time            0.00 seconds
    
    
    NOTE: %INCLUDE (level 3) ending.
    NOTE: %INCLUDE (level 2) resuming.
    NOTE: %INCLUDE (level 2) ending.
    NOTE: %INCLUDE (level 1) resuming.
    NOTE: %INCLUDE (level 1) ending.
    
  2. 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:

    NOTE: DATA statement used (Total process time):
          real time           0.01 seconds
          cpu time            0.01 seconds
    
    
    NOTE: %INCLUDE (level 3) ending.
    NOTE: %INCLUDE (level 2) resuming.
    NOTE: %INCLUDE (level 2) ending.
    NOTE: %INCLUDE (level 1) resuming.
    NOTE: %INCLUDE (level 1) ending.
    

    Also, there are a few shortcomings with the %included() macro used to answer the question.

    /* If the data set does not exist, create it. */
    
                %if dsid = 0 %then %do;
    

    should be %if &dsid. The literal letters dsid will never = 0.

    if lowcase(scan(var,1, " ")) = "%include" then do;
                            program = "&dir\&memname";
                            calledFile = scan(var,2, ' /"');
    

    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

Back to Top