In many SAS applications, there is a need to conditionally stop SAS code execution and gracefully (without generating an ERROR or a WARNING) terminate SAS session when no further processing is required. For example, your program processes large data and flags certain transactions as suspicious. If any suspicious transactions are found, then you continue further processing to gather more information about those transactions. However, if no transactions were flagged, you just want to stop your SAS job and augment the SAS log with a nice NOTE (not ERROR or WARNING), for example:
NOTE: No suspicious transactions are found. Further processing canceled. Exiting program execution.
Graceful termination techniques described in this post primarily apply to batch processing scenarios when SAS programs run unattended without human intervention. However, development of these programs is usually done in interactive SAS sessions, and we need to make sure SAS log is captured before our interactive application is “terminated”. Therefore, before we proceed reviewing and experimenting with SAS termination techniques, let’s make arrangements for capturing the SAS log.
Capturing SAS log
When you run a SAS program in batch you would usually submit it using OS command. For example, in UNIX/Linux you may submit or place in a shell script the following command:
sas /code/proj1/job1.sas -log /code/proj1/job1.log
The log file name is specified in the command itself (-log /code/proj1/job1.log), and it will record our “goodbye NOTE” generated before SAS session is terminated.
However, when you run/debug your program in an interactive application, e.g. SAS Enterprise Guide, the SAS log is captured in the Log Window. When SAS terminates its session, it will not only terminate your program execution, it will also terminate the application itself. That means your Enterprise Guide will close, along with its Log Window leaving you with no Log to see and inspect.
To capture SAS log to a file while debugging a SAS program containing session termination feature in interactive environment/application you can use PROC PRINTTO:
/* Beginning of SAS program */ proc printto log='C:\PROJECT1\program1.log'; run; resetline; <YOUR SAS PROGRAM> /* End of SAS program */ proc printto; run;
In this code statement RESETLINE makes sure program line numbers in the SAS log always start with 1.
Let’s take a look at the following coding techniques of SAS session termination.
ABORT Statement - not so graceful termination
ABORT Statement is an executable statement that can be used as part of IF-THEN/ELSE conditional processing in a DATA step. Its action, however, extends beyond the DATA step as it not only stops executing the current DATA step, but also stops executing SAS session.
There are several flavors of the ABORT statement in SAS 9.4 – ABORT, ABORT ABEND, ABORT CANCEL, ABORT RETURN, and ABORT <number>. All of them are useful when something goes bad in your program (e.g. database library is not available at the time of run, or a data set is locked preventing you from writing to it, etc.), and you need to kill your program to avoid an even bigger snafu. All such situations are real, and ABORT statement handles them properly – stopping further program execution, killing SAS session, and generating an ERROR message in the SAS log.
But that ERROR message in the SAS log is what effectively disqualifies ABORT statements from graceful termination status. Think about it: “no suspicious transactions” is an occasion to celebrate, not a reason to cry an ERROR. Besides, in many mission-critical production-quality applications having an ERROR or even a WARNING in the SAS log is not an option.
Fortunately, for SAS® Viya users, there is a new ABORT EXIT option available in SAS Viya which ends SAS session without generating an ERROR in the log (it still generates WARNING).
ENDSAS statement - pitfalls
Let’s explore ENDSAS statement which stops SAS program execution and terminates the SAS session.
ENDSAS statement is a global statement, it takes effect as soon as it is encountered in a SAS program. It can be placed anywhere in a SAS program, except where only executable statements are allowed. For example, if you place it as part of an IF-THEN/ELSE statement you get a syntax ERROR because IF-THEN/ELSE statements require executable statements. Try running this code:
data _null_; if 1=1 then endsas; run;
SAS log will show a syntax error:
2 data _null_; 3 if 1=1 then endsas; ------ 180 ERROR 180-322: Statement is not valid or it is used out of proper order.
However, if you place it in a conditionally executed DO-block it will not generate a syntax ERROR, but it will not produce what we wanted either because of the following.
First, it will execute even when the condition if FALSE. Second, since it executes inside the DO-block, it will end SAS data step and SAS session right there, without even giving DO-block a chance of completing its compilation, thus generating an ERROR. Here is the code illustration:
data _null_; if 0 then do; put 'Nooo!'; endsas; end; run;
If you dare to run this code, here is what you will see in the SAS log after your SAS session gets killed:
2 data _null_; 3 if 0 then 4 do; 5 put 'Nooo!'; 6 endsas; ERROR 117-185: There was 1 unclosed DO block.
No, it is not what we are after.
ENDSAS statement – data step solution
Using coding technique described in my previous post How to conditionally execute SAS global statements, we can make ENDSAS to be conditionally generated within data step and executed after the data step.
Suppose we have a data set SUSPICIOUS_CASES which may have either zero or some positive number of observations. We want to stop further processing and terminate SAS session in case it has 0 observations.
Here is how we can achieve this:
data _null_; set SUSPICIOUS_CASES nobs=n; if n=0 then call execute('endsas;'); stop; run;
Here we conditionally (if n=0) invoke CALL EXECUTE routine which un-quotes its character argument and pushes it outside the data step boundaries, thus generating the following code after RUN statement (as shown in the SAS log):
NOTE: CALL EXECUTE generated line. 1 + endsas;
Thus, we conditionally generated global ENDSAS statement and placed it after the data step. This global statement will terminate SAS session without a fuss; no ERROR, no WARNING, and even no NOTE.
If you want it to be not so “silent goodbye”, you can add some informative NOTES using PUT statement executed under the same condition as CALL EXECUTE (we combine them in a DO-block):
data _null_; set SUSPICIOUS_CASES nobs=n; if n=0 then do; put 'NOTE: No suspicious cases were found. Further processing is terminated.'; call execute('endsas;'); end; stop; run;
This code will conditionally output NOTE to the SAS log during data step. Obviously, you can generate any number of NOTE lines making your exit more verbose. We can place PUT statement either before or after CALL EXECUTE within the DO-block, because it will be executed within the data step, while generated ENDSAS statement will be executed after the data step.
ENDSAS statement – SAS macro solution
Another way of conditionally generating SASEND statement is by using SAS Macro Language.
The following macro language code will conditionally generate ENDSAS statement which then will gracefully terminate SAS session. For demonstration purposes, I explicitly specified TRUE condition as %if 1:
%if 1 %then %do; %put NOTE: Ending SAS session gracefully.; endsas; %end;
SAS log will show:
2 %if 1 %then 3 %do; 4 %put NOTE: Ending SAS session gracefully.; NOTE: Ending SAS session gracefully. 5 endsas; NOTE: SAS Institute Inc., SAS Campus Drive, Cary, NC USA 27513-2414
- How to conditionally execute SAS global statements
- How to conditionally terminate a SAS batch flow process in UNIX/Linux
- Running SAS programs in batch under Unix/Linux
- CALL EXECUTE made easy for SAS data-driven programming
Do you find this blog post useful? How do you handle graceful termination of your SAS programs? Please share with us below.