SAS SYSCC Macro Variable Behaviour - sas

It is my understanding that if a SAS process hits an error that the value of &SYSCC. will not be 0. which is the success return code. I am writing said variable to a text file to act as a go/no go signal to a Python script that I am using to orchestrate some wider processes.
Whilst testing the behaviour of said variable by triggering some simple, deliberate errors I have noticed that it is still returning zero:
%macro test;
sdsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfs
%put &syscc. **********************************;
%mend;
%test;
Have I misunderstood something?
Thanks

This is a timing issue. No error has been generated at the point at which the %put statement executes. The %test macro generates some text (sdsfsdfs..) and writes a valid put statement. The semicolon after the macro invocation ; creates a statement boundary (sdsfsdf...;) which then throws an error.
Order of execution:
1) compile macro
2) execute macro (%test)
3) execute %put statement
4) send sdsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfs to input stack
5) finish executing macro
6) send final semicolon to input stack (;)
7) sdsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfs; is executed, which likely throws an error (it's too long to be a variable name but could be part of a valid %let statement);

Related

Get the filename and filepath of the code running

I need to get the current running SAS program's name and file path into local variables. I accomplished that using the SAS_EXECFILEPATH and SAS_EXECFILEPATH commands. I ran this through windows SAS and it worked.
But when i tried to run this on the server in batch mode, it failed. I then tried the &_SASPROGRAMFILE parameter, which ran fine on SAS EG, but fails when I trigger it on the server in batch mode.
Is there a possible way to accomplish this in batch mode on the server?
You might be looking for %sysfunc(getoption(sysin)) (Usage Note 24301: How to retrieve the program name that is currently running in batch mode or interactively), if you start the program with sas -sysin path/to/file.sas.
I know this is delayed but you could generate a macro that calls on the right code depending on if you are running the program in the editor or in batch mode. Art Carpenter created a great macro that solves this issue.
%macro ExecPrg;
%if %sysfunc(getoption(sysin)) ne %str() %then %do;
/* Batch Execution */
%sysfunc(getoption(sysin))
%end;
%else %do;
/* Interactive Execution */
%sysget(SAS_EXECFILEPATH)
%end;
%mend execprg;

How to write concise code for the conditional creation (if…then…) of a set of global macro variables

I've created 14 global macro variables like so:
data _NULL_;
set &medf;
if &class1='Catcher' then call symputx('MedianC',med);
if &class1='Catcher' then call symputx('FrequencyC',_FREQ_);
if &class1='First Baseman' then call symputx('MedianFB',med);
if &class1='First Baseman' then call symputx('FrequencyFB',_FREQ_);
if &class1='Outfielder' then call symputx('MedianO',med);
if &class1='Outfielder' then call symputx('FrequencyO',_FREQ_);
if &class1='Pitcher' then call symputx('MedianP',med);
if &class1='Pitcher' then call symputx('FrequencyP',_FREQ_);
if &class1='Second Baseman' then call symputx('MedianSB',med);
if &class1='Second Baseman' then call symputx('FrequencySB',_FREQ_);
if &class1='Shortstop' then call symputx('MedianS',med);
if &class1='Shortstop' then call symputx('FrequencyS',_FREQ_);
if &class1='Third Baseman' then call symputx('MedianTB',med);
if &class1='Third Baseman' then call symputx('FrequencyTB',_FREQ_);
run;
This seems like an inefficient code, and so I was wondering how I might be able to write this more concisely. I've looked around at the various uses of CALL SYMPUTX, and it seems like I might not need 14 lines of code for 14 global macro variables (i.e., a single line of CALL SYMPUTX might be able to produce multiple macro variables). However, I'm not sure how to retain the conditional nature of my variable creation in fewer lines of code.
If anyone could offer some guidance, I'd greatly appreciate it. Thank you!
Assuming that the first letter of each &class1 'word' can form a suffix to place at the end of the macro variable name, you can complete this task without any need for if-then logic:
data _NULL_;
set &medf;
length suff $8;
i=1;
/* Do loop pulls together the first letter of each word */
/* in the &class1 variable value into a new variable called suff */
do while (scan(&class1,i) ^= '');
suff=cats(suff,substr(scan(&class1,i),1,1));
i+1;
end;
/* The Median and Frequency words joined to suff make the new macro variable name, */
/* the second argument just holds the values */
call symputx(cats('Median',suff),med);
call symputx(cats('Frequency',suff),_FREQ_);
run;

Conditionally terminate SAS

I am trying to stop the processing of a SAS program if a certain condition has been met. I have a macro variable created and if that variable is > 0.5 then I want a hard stop of the program.
Current program looks like
data a1;
set Server.a2;
run;
%macro1(a1);
%macro2(_t1); /* _t1 generated from %macro1.
data _null_;
if %stopit(_t2) > 0.5 then `?????`; /* _t2 generated from %macro2.
run;
%macro3;
%macro4;
If %macro(_t2) > 0.5, I want to stop of whole program without running the rest (%macro3 and %macro4)
Use following statement:
abort abend;
Personally I tend to always use abort cancel; (or %abort cancel;) as it offers flexibility when running in interactive mode and in batch mode.
Interactively it will just cancel the submitted code (but leave your session open).
In batch mode it will stop the entire job.
There are additional options as well. You can find a full list in the documentation here.
There's also the endsas command but I don't like that as it closes the current interactive session (and it's more difficult to execute conditionally).
Here's an example:
%let x = 1;
data _null_;
if &x then abort cancel;
put "This won't print";
run;
%put This won't print either;
The results in the log will show:
ERROR: Execution terminated by an ABORT CANCEL statement at line 4 column 14.
_ERROR_=1 _N_=1

Strange error due to misuse of %sysrput in SAS

I am using AIX 6.1 with SAS 9.1.3
I have a program running in PC SAS 9.1
The program will rsubmit to unix.
Now, I convert the program to be totally running in AIX 6.1
The program fails very strangely.
After investigation, it is due to the %sysrput
Here is a simplified version of the program:
options mPrint mLogic symbolGen ;
%macro combine( startdate= , fullprefix= );
data _null_ ;
call symput( 'plength',compress(length(compress("&fullprefix."))));
run;
data _null_ ;
length balance 8. ;
balance= 1 + &plength.;
run;
%mEnd;
data _null_ ;
call symput( 'refdate', put(today(),date9.));
run;
%put &refdate.;
* -- If I forget to comment out the sysrput, the plength cannot be resolved -- ;
%sysrput refdate=&refdate.;
%put &refdate.;
%combine( startdate= "&refdate."d, fullprefix=a_filename_prefix );
(Sorry that the wordings are not meaningful, I just want to do a demo.)
Actually, in AIX, I should not use %sysrput
I just forget to comment it out.
But, if I forget this, there would be error in the plength macro variable in the balance= statement. This is very strange.
To solve, simply comment out the %sysrput is ok.
But, does anyone know why the %sysrput will cause failure in a macro variable in the macro ?
Alvin SIU
It's hard to tell from your simplified version of your question, but if you are asking why the macro variable plength is not present after your macro executes, it is because you must define it as global in your macro code itself. In other words:
%macro combine( startdate= , fullprefix= );
%global plength;
...
%mend;
And yes, if you use the %SYSRPUT command from a SAS session not under control of SAS/CONNECT, you will get a SAS ERROR; and getting a SAS error will put your non-interactive session into "syntax-check" mode, in which case the remaining statements in your program will not execute completely.
This last bit is a common misunderstanding when converting code from a SAS/CONNECT environment to "plain old SAS". When you use SAS/CONNECT, the "server" side of the connection is started with the "-NOSYNTAXCHECK" option.
After investigation with some little testing. maybe this is the answer.
Actually, I will use OPTIONS ERRORABEND in all batch SAS program to stop the SAS in case of error. This works for many errors and functions just perfectly.
The %SYSRPUT statement do give out error message saying ... options DMR ...
But the program does not abend stop here.
It does not give any message saying ... OBS=0 ...
It just go on.
So, I just 'think' that this is some minor error just like LIBNAME a non-exist directory and SAS will continue 'normally'.
(BTW, I think that the SYSRPUT error has turn on SYNTAX CHECK mode silently, without any notification nor hint.)
The next %PUT statement is normal, same value as previous one.
This mislead me that the program is running normally.
The next statements are also normal. There are many SYMBOLGEN, MPRINT and MLOGIC messages.
So, this further mislead me that the program is running very very normally.
After the call symput plength statement, there is a NOTE: Numeric values have been converted ...
This further mislead me the program is running normally.
Until now (after so many look-like normal messages), there is a NOTE saying SAS set option OBS=0 ...
(Maybe this NOTE will appear only after the RUN statement.)
This OBS=0 message is actually the hints which tell SAS is using SYNTAX CHECK mode.
Maybe this OBS=0 is caused by the SYSRPUT error. But, because there are so many look-like normal messages after the SYSRPUT error, I just overlook this OBS=0 message.
Actually, the call symput is so simply and should not cause any error.
This just further complicate the situation.
As SAS is in SYNTAX CHECK mode, this is why causing the look-like error in the plength variable in the balance statement.
This is the whole story.
Anyway, in order to prevent to enter into this complicated and misleading situation, just remember to comment out all the %SYSRPUT will do.

Can I change the execution order of the CALL EXECUTE stack in SAS?

I'm using SAS 9.1.3 to call a macro in a DATA step, but the macro generates a PROC REPORT step, so I am using CALL EXECUTE to call it, generate all those PROC REPORT steps, and then execute them all after the DATA step.
I'm using an array, and the macro is executed each time for every element in this array:
DATA macro_test;
ARRAY questions[3] $ 32 ('question1' 'question2' 'question3');
DO i=1 to 3;
a_question = questions(i);
CALL EXECUTE( "%report_by_question(a_question)" );
end;
RUN;
The thing is, the report outputs are coming out (usually) backwards - it will print question3 first, then 2, then 1.
Is there a way to modify the execution order of CALL EXECUTE so I can have the question reports print in order, or does it just do its own thing?
Thanks!
I assume you mean something more like this for yout call execute() line:
CALL EXECUTE( "%report_by_question(" || trim(left(a_question)) || ");" );
With a test macro I get some log lines like this, showing that the call execute()s are happening in the right order. Do you get anything similar?
Macro
%macro report_by_question(a);
data test_&a;
do i=1 to 10000000;
output;
end;
run;
%mend;
Logs
NOTE: CALL EXECUTE generated line.
1 + data test_question1; do i=1 to 10000000; output; end; run;
NOTE: The data set WORK.TEST_QUESTION1 has 10000000 observations and 1 variables.
NOTE: DATA statement used (Total process time):
real time 6.14 seconds
cpu time 0.45 seconds
1 + ;
2 + data test_question2; do i=1 to 10000000; output; end; run;
NOTE: The data set WORK.TEST_QUESTION2 has 10000000 observations and 1 variables.
NOTE: DATA statement used (Total process time):
real time 3.87 seconds
cpu time 0.53 seconds
2 + ;
3 + data test_question3; do i=1 to 10000000; output; end; run;
NOTE: The data set WORK.TEST_QUESTION3 has 10000000 observations and 1 variables.
NOTE: DATA statement used (Total process time):
real time 3.12 seconds
cpu time 0.45 seconds
A data step is compiled and then executed. The call execute(str); pushes the str into the input queue, so that they are popped after the data step is done execution. The order is preserved, period.
However, if you put your macro invocation in the double quoted string as you do in:
call execute("%report(q)");
then the macro is invoked when the data step is compiled, which is even before the data step starts running.
If you don't want to invoke the macro in the compile time, then either macro quote it or put it in the single quoted string. Below is an example. Hope this helps.
/* create a dataset with 1 var and 3 obs */
data qs;
input q $;
cards;
q1
q2
q3
;
run;
/* reporting macro -- a mockup */
%macro report(q=);
%put report for q=&q;
%mend report;
/* call the reporting macro for each q */
data _null_;
set qs;
macro = catx(q, '%report(q=', ')');
call execute(macro);
run;
/* on log
report for q=q1
report for q=q2
report for q=q3
*/
/* to show what happens when the
macro is invoked during the compile
time */
data _null_;
call execute("%report(q=q1)");
put "after call execute";
run;
/* on log
1 data _null_;
2 call execute("%report(q=q1)");
report for q=q1
3 put "after call execute";
4 run;
after call execute
*/
I prefer to do everything macro related using macro language. I guess the trade-off is that you have little macros scattered throughout your program. However, to prevent your program from generating reports, just comment out the macro call (*%loopit;) Also, you don't have to type out "question1", "question2", "question3", etc!!!
Hopefully this is useful to you!
%macro report_by_question(input);
%put "Question: " &input;
%mend;
%macro loopit;
%do i=1 %to 3;
%report_by_question("question&i.");
%end;
%mend loopit;
%loopit;