How to expand the number of available SAS functions within the macro language

View more Problem Solvers posts

Have you ever been working in the macro facility and needed a macro function, but you could not locate one that would achieve your task? With the %SYSFUNC macro function, you can access most SAS® functions. In this blog post, I demonstrate how %SYSFUNC can help in your programming needs when a macro function might not exist. I also illustrate the formatting feature that is built in to %SYSFUNC. %SYSFUNC also has a counterpart called %QSYSFUNC that masks the returned value, in case special characters are returned.
%SYSFUNC enables the execution of SAS functions and user-written functions, such as those created with the FCMP procedure. Within the DATA step, arguments to the functions require quotation marks, but because %SYSFUNC is a macro function, you do not enclose the arguments in quotation marks. The examples here demonstrate this.

%SYSFUNC has two possible arguments. The first argument is the SAS function, and the second argument (which is optional) is the format to be applied to the value returned from the function. Suppose you had a report and within the title you wanted to issue today’s date in word format:

title "Today is %sysfunc(today(),worddate20.)";

The title appears like this:

"Today is July 4, 2018"

Because the date is right-justified, there are leading blanks before the date. In this case, you need to introduce another function to remove the blank spaces. Luckily %SYSFUNC enables the nesting of functions, but each function that you use must have its own associated %SYSFUNC. You can rewrite the above example by adding the STRIP function to remove any leading or trailing blanks in the value:

title "Today is %sysfunc(strip(%sysfunc(today(),worddate20.)))";

The title now appears like this:

"Today is July 4, 2018"

The important thing to notice is the use of two separate functions. Each function is contained within its own %SYSFUNC.

Suppose you had a macro variable that contained blank spaces and you wanted to remove them. There is no macro COMPRESS function that removes all blanks. However, with %SYSFUNC, you have access to one. Here is an example:

%let list=a b c;
%put %sysfunc(compress(&list));

The value that is written to the log is as follows:

abc

In this last example, I use %SYSFUNC to work with SAS functions where macro functions do not exist.

The example checks to see whether an external file is empty. It uses the following SAS functions: FILEEXIST, FILENAME, FOPEN, FREAD, FGET, and FCLOSE. There are other ways to accomplish this task, but this example illustrates the use of SAS functions within %SYSFUNC.

%macro test(outf);
%let filrf=myfile;

/* The FILEEXIST function returns a 1 if the file exists; else, a 0
is returned. The macro variable &OUTF resolves to the filename
that is passed into the macro. This function is used to determine
whether the file exists. In this case you want to find the file
that is contained within &OUTF. Notice that there are no quotation
marks around the argument, as you will see in all cases below. If
the condition is false, the %ELSE portion is executed, and a
message is written to the log stating that the file does not
exist.*/

%if %sysfunc(fileexist(&outf)) %then %do;

/* The FILENAME function returns 0 if the operation was successful;
else, a nonzero is returned. This function can assign a fileref
for the external file that is located in the &OUTF macro
variable. */

%let rc=%sysfunc(filename(filrf,&outf));

/* The FOPEN function returns 0 if the file could not be opened;
else, a nonzero is returned. This function is used to open the
external file that is associated with the fileref from &FILRF. */

%let fid=%sysfunc(fopen(&filrf));

/* The %IF macro checks to see whether &FID has a value greater
than zero, which means that the file opened successfully. If the
condition is true, we begin to read the data in the file. */

%if &fid > 0 %then %do;

/* The FREAD function returns 0 if the read was successful; else, a
nonzero is returned. This function is used to read a record from
the file that is contained within &FID. */

%let rc=%sysfunc(fread(&fid));

/* The FGET function returns a 0 if the operation was successful. A
returned value of -1 is issued if there are no more records
available. This function is used to copy data from the file data
buffer and place it into the macro variable, specified as the
second argument in the function. In this case, the macro variable
is MYSTRING. */

%let rc=%sysfunc(fget(&fid,mystring));

/* If the read was successful, the log will write out the value
that is contained within &MYSTRING. If nothing is returned, the
%ELSE portion is executed. */

%if &rc = 0 %then %put &mystring;
%else %put file is empty;

/* The FCLOSE function returns a 0 if the operation was successful;
else, a nonzero value is returned. This function is used to close
the file that was referenced in the FOPEN function. */

%let rc=%sysfunc(fclose(&fid));
%end;

/* The FILENAME function is used here to deassign the fileref
FILRF. */

%let rc=%sysfunc(filename(filrf));
%end;
%else %put file does not exist;
%mend test;
%test(c:\testfile.txt)

There are times when the value that is returned from the function used with %SYSFUNC contains special characters. Those characters then need to be masked. This can be done easily by using %SYSFUNC’s counterpart, %QSYSFUNC. Suppose we run the following example:

%macro test(dte);
%put &dte;
%mend test;

%test(%sysfunc(today(), worddate20.))

The above code would generate an error in the log, similar to the following:

1 %macro test(dte);
2 %put &dte;
3 %mend test;
4
5 %test(%sysfunc(today(), worddate20.))
MLOGIC(TEST): Beginning execution.
MLOGIC(TEST): Parameter DTE has value July 20
ERROR: More positional parameters found than defined.
MLOGIC(TEST): Ending execution.

The WORDDATE format would return the value like this: July 20, 2017. The comma, to a parameter list, represents a delimiter, so this macro call is pushing two positional parameters. However, the definition contains only one positional parameter. Therefore, an error is generated. To correct this problem, you can rewrite the macro invocation in the following way:

%test(%qsysfunc(today(), worddate20.))

The %QSYSFUNC macro function masks the comma in the returned value so that it is seen as text rather than as a delimiter.

For a list of the functions that are not available with %SYSFUNC, see the “SAS Functions Not Available with %SYSFUNC and %QSYSFUNC” section within SAS 9.4 Macro Language: Reference, Fifth Edition.

I hope this blog post has helped you understand the many SAS functions that are available within the macro facility. If you have SAS macro questions that you would like me to cover in future blog posts, please comment below.

How to expand the number of available SAS functions within the macro language was published on SAS Users.