Tuesday, 18 May 2010

SAS Macro Programming is a pain in the butt!

I've been playing around with SAS macros these past few days and I must say I am totally shattered and depressed. It took me ages to find out why the SAS macros were not working in the first place - apparently macros are not meant to be run in "Open Space". They are meant to be placed inside data or proc steps... except that's not how you make global variables. However, it IS possible to run macros in open space though you need to add a function or two to get it working. Then I had issues with formatting and dates not being interpreted correctly. Finally I couldn't run a system command line command except on the local machine. Gah!

First. let's set some variables:

%let var_name = value;

The value can be a number or a string, but you're not meant to quote the string, like you usually do with SAS strings. If you want to see the value of var_name in the log (helps a great deal with debugging!):

%put &var_name.;

OK. Let's say you want to run a function to assign a value to a variable. Not straightforward - if you're in open space you need to call a special macro function %sysfunc:

%let var_name = %sysfunc( mdy(05, 18, 2010) ); * var_name holds date int value;

Note the command mdy() is in American Month-Day-Year mode.
What if we want to format this for output?

%let var_name = %sysfunc( putn( %sysfunc( mdy(05, 18, 2010) ), DATE7.) ); *holds 18MAY2010;

OK. Let's say I want to write a macro procedure/function or whatever they call it, and run it. Check out the silly if statements and loops.

%macro chk;
    * Check Sunday - yes, it counts the week days from Sunday - How annoying;
    * &yesterDate is already declared intnx(Day, "&sysdate"d, -1);
    %let weekday = %sysfunc( WEEKDAY( &yesterDate. ) );
    %if &weekday. = 1 %then %do;
        * intnx() is a great function - if its Sunday it sets yesterDate to Friday;
        %let yesterDate = %sysfunc( intnx(Day, &yesterDate., -2) );
    %end;
    * Now I want to declare an array... except macros don't do arrays, apparently!
    * Recommended guidlines say to do this:
    %let year = %sysfunc( YEAR(&yesterDate.) );

    %let Xmas1 = %sysfunc( mdy(12, 24, &year.) );
    %let Xmas2 = %sysfunc( mdy(12, 25, &year.) );
    %let Xmas3 = %sysfunc( mdy(12, 26, &year.) );
    %Do i = 1 %to 3;
        %if &yesterDate. = &&Xmas&i %then %do;
            %let yesterDate = %sysfunc( mdy(12, 23, &year.) );
            * Check its not Sunday again - Recursive Macro calling;
            %chk;
        %end;
    %end;
    * I have another loop to check for more holidays but I'm ommiting that;
%mend;
 
Now to run the macro I write:
 
%chk; 
 
but it would still work if I missed the ending semicolon - which is shocking because nothing else except multi-line comments work without an ending semicolon. Even single line comments and half finished statements need a semicolon on the end of a line.
 
OK. Now I had to run a command on the system. I had two options - the X command and the call system() command. X is a unixy command line thinga-ma-jig and can be called from anywhere - open space or not, while call system() can only be called inside a step. They are used like so:
 
X "move /path/to/file1.txt path/to/file2.txt"; *this is how you rename a file under unix/linux btw;
data _null_;
    call system("move /path/to/file1.txt /path/to/file2.txt"); *while windows does have the rename command, move works fine;
run;
 
An interesting thing is that you can send multiple commands through X seperated by semicolons while with call system() it does one command at a time.
 
Here's the problem... X didn't work on the remote server but call system() only worked on the local system. The rest of the script is meant to work on the remote server though. So this means that I'll need to run this part locally, comment it out temporarily, then run the whole script again remotely. Or I can skip that mess which took me ages to figure out why it wasn't working and manually change the file names.
 
SAS Sucks.

2 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete