As technology expands, we have a similarly increasing need to create programs that can be handed off – to clients, to regulatory agencies, to parent companies, or to other projects – and handed off with little or no modification needed by the recipient. Minimizing modification by the recipient often requires the program itself to self-modify. To some extent the program must be aware of its own operating environment and what it needs to do to adapt to it.
When you employ portable programming techniques, you are practicing defensive programming. When your program is delivered and before it can be put into production, other programmers may need to ‘fine tune’ it. Robust code requires fewer modifications, therefore introduces fewer problems. But how do you write programs that are portable? How do you construct programs that are portable enough that they can run in a variety of situations with minimal (or better yet, without any) programmer intervention? It’s possible, and here are a few examples of effective portable programs.
Retrieving date values
The title in a sales report should reflect the current execution date. If date is hardcoded, the title will require a change each time the report is executed. To make this program portable, we need the ability to determine and insert the current date automatically.
title1 "Shoe Sales Report for August 20, 2016"; |
The user defined macro function %CURRDATE returns today’s date in WORDDATE form. The SAS date is retrieved using the DATE function, which is then formatted, trimmed and left justified.
%macro currdate; %qtrim(%qleft(%qsysfunc(date(),worddate18.))) %mend currdate; |
When used in the TITLE statement, this macro function assures that the date value displayed by the title will always be current, and it does it without user intervention.
title1 "Shoe Sales Report for %currdate"; |
Determining the operating System
When programs are moved from one platform to another they need to be adapted to the new operating environment. The first step in this process is determining the current operating system. The easiest way to do this is to take advantage of the automatic macro variables &SYSSCP and &SYSSCPL. The %PUT statement shown in this SAS Log was executed under the Windows (7 Pro) operating system for SAS 9.4.
45 %put &=sysscp &=sysscpl; SYSSCP=WIN SYSSCPL=X64_7PRO |
You can take advantage of the &SYSCP macro variable to make the path assignment in a LIBNAME statement, where the value held in &SYSSCP is used to determine the correct path. When the first argument is true (Windows OS), the second argument is returned, otherwise the non-windows path in the third argument is returned. In a 2016 SAS Dummy blog Chris Hemedinger also uses &SYSSCP in a similar way.
libname mydata "%sysfunc(ifc(&SYSSCP = WIN, c:\portableproject\data, /myloc/portproj/data))"; |
Returning the Name and Location of the Executing Program
At the operating system level, environmental variables are used in a similar manner to macro variables within SAS. We can access these OS level environmental variables through the use of the %SYSGET function. When executing a SAS program interactively under Windows, the environmental variable SAS_EXECFILEPATH takes on the value of the currently executing SAS file path and filename. When a program is run in batch mode, or on a UNIX platform, the path and name of the currently executing file can be obtained from the SYSIN system option. The GETOPTION function allows us to obtain the value of SYSIN.
The %EXECPRG macro detects whether the program is being executed with a batch process or as a part of an interactive session. It then uses this information to retrieve the path and name of the executing program. Notice that this macro has no input parameters. Because all of the inputs are either environmental variables or system options, we can simply place a call to %EXECPRG anywhere that we want to reference the path of the currently executing program.
For batch processes this information is stored in the SYSIN system option, which can be retrieved through the use of the GETOPTION function. Otherwise the %SYSGET macro function is used to retrieve the value from the environmental variable SAS_EXECFILEPATH. Either way this macro returns the full path and name of the executing program.
%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; |
This macro allows us to place the name and location of the executing program in a TITLE or FOOTNOTE statement.
footnote2 h=1 j=r "Executing Program: %execprg"; |
This blog uses content from a paper I coauthored with Mary Rosenbloom and presented at MWSUG 2016. You can read it in its entirety here.
You can also get more information on the macro language by perusing the new edition to my macro book, Carpenter’s Complete Guide to the SAS® Macro Language, Third Edition.
My dog Honey and I wish you a very Merry Holiday Season!
Here are a few pics to start your winter off right! Honey’s dream is to be a lead dog in the Iditarod Sled Dog race, but for now she has to settle for pulling me on my bicycle! :)
5 Comments
Excellent tips, as always.
A very merry Christmas, and happy 2017, to you Art!!
Honey is part husky. She loves the cold and she never gets so excited as when i bring out the harness.
Art, thank you for the examples.
...and thank you for sharing the pictures. Happy Holidays!
Great tips...and great photos. :-)
Always sharing wise tips. Thanks Art!
Similar to using OS level environment variables, I've found it helpful to have macro variables for dev, test and prod to help with migrating code between environments to minimize or completely reduce any between environment modifications.
With the summer heat we're having in Australia, your winter pictures with Honey are a cool relief! Have a great Christmas and hopefully see you at SAS Global Forum next year.