A feature of SAS/IML 13.2 (shipped with SAS 9.4m2, Aug 2014) is the ability to execute SAS/IML statements that are in a file. The feature is implemented by the new EXECUTEFILE subroutine.
This feature is similar to the CALL EXECUTE statement. The difference is that the EXECUTEFILE subroutine reads, parses, and executes statements from a file, rather than from a character string.
The EXECUTEFILE subroutine is also similar to the %INCLUDE statement, which is used by many SAS programmers to insert the contents of a file into a program. You can think of the EXECUTEFILE subroutine as a run-time equivalent of the %INCLUDE statement, which runs at parse time. For an explanation of parse time and run time, see "How to find and fix programming errors."
How does EXECUTEFILE differ from %INCLUDE?
The EXECUTEFILE subroutine differs from the %INCLUDE statement in two ways. First, the %INCLUDE statement unconditionally tries to read the file. Compare the results of the following IF-THEN statements:
proc iml; x=0; if x>0 then %include "DoesNoteExist.sas"; /* ERROR if file does not exist */ if x>0 then call executefile("DoesNotExist.sas"); /* 13.2: file conditionally executed */ quit; |
The first IF-THEN statement returns an error if the file does not exist: ERROR: Cannot open %INCLUDE file DoesNoteExist.sas. That is because the SAS pre-processor tries to insert the contents of the file when the IF-THEN statement is parsed. The pre-processor does not know about the value of x. In contrast, the second IF-THEN statement does not return an error. The IF condition is not true, so the CALL EXECUTEFILE statement is never run. Therefore it does not matter whether the DoesNoteExist.sas file exists.
The second way that the %INCLUDE and CALL EXECUTEFILE statements differ is when called inside a loop. If you use a %INCLUDE statement inside a loop, the code is inserted and parsed once. All macro values are resolved at parse time. In contrast, the EXECUTEFILE subroutine will load, parse, and execute the file during each iteration of the loop. This means that the contents of the file can change between iterations and that macro variables are resolved for each iteration.
How to use the EXECUTEFILE subroutine
To use the EXECUTEFILE subroutine, you can use your favorite text editor to create a plain text file in some location that SAS can read. The text file should contain valid SAS/IML statements, but should not include the PROC IML statement. Since SAS can write text files, this example uses the DATA step to create a text file that has two SAS/IML statements:
filename ExeFile "C:/temp/ShowTime.sas"; data _null_; file ExeFile; time = putn(datetime(), "DATETIME17."); cmd = catt('print "The file was created on ', time, '";'); put cmd; cmd = 'print "The current time is " (datetime())[F=DATETIME17.];'; put cmd; run; |
The DATA step creates a text file named ShowTime.sas. The actual text depends on when you run the DATA step, but it will look something like this:
print "The file was created on 11JUN15:06:11:33"; print "The current time is " (datetime())[F=DATETIME17.]; |
The first statement is PRINT statement that displays a literal string. The string was created by the DATA step to include the date and time at which the DATA step ran. The second statement is a PRINT statement that calls the DATETIME function and displays the time at which the statement is run.
The following SAS/IML program uses the EXECUTEFILE subroutine to run the two print statements in the ShowTime.sas file:
/* Execute statements in file in SAS/IML */ proc iml; run executefile("C:/temp/ShowTime.sas"); quit; |
When you run the PROC IML program, it executes the statements in the text file. Notice for my example that the program was run about 30 minutes after the file was created.
When to use the EXECUTEFILE subroutine
In 99% of the use cases, the classic %INCLUDE statement is sufficient to include statements into a SAS/IML program. The EXECUTEFILE subroutine becomes necessary when the statements refer to global statements or macro variables that you do not want to evaluate until run time.
Using macro variables in SAS/IML loops can be challenging. In a previous article, I wrote about calling global SAS statements within a SAS/IML loop. The example in that article uses the CALL EXECUTE statement to set the TITLE statement with the current value of the counter for an iterative loop. You can use the EXECUTEFILE subroutine to perform the same computation:
filename ExeFile "C:/temp/SetTitle.sas"; data _null_; file ExeFile; put 'TITLE "mu = &mu";'; /* TITLE includes current value of macro variable */ run; proc iml; x = j(50,1); do mu = 0 to 1; call randgen(x, "Normal", mu); /* x ~ N(mu, 1) */ call symputx("mu", mu); /* store counter in macro variable */ run executefile(ExeFile); /* can use fileref in PROC IML */ call histogram(x); end; |
The program displays two histograms. The first has the title "mu = 1", and the second has the title "mu = 2". The histograms are shown in my previous article. Notice that in PROC IML you can use a SAS fileref as the argument to the EXECUTEFILE subroutine. (That option is not support in SAS/IML Studio.)
For most situations, you can continue to use the %INCLUDE statement to include statements into a SAS/IML program. But programmers who need to conditionally read and execute statements can use the EXECUTEFILE subroutine. By using the EXECUTEFILE subroutine, SAS does not see the code until run time.