DATA Step, Macro, Functions and more

macro code check, please (scanning for trigger file)

Accepted Solution Solved
Reply
New Contributor
Posts: 4
Accepted Solution

macro code check, please (scanning for trigger file)

I've built a simple macro which checks for the existence of a file (any kind), and then populates a global macro variable with a 0 or 1 to indicate the result. It keeps testing for the file's existence at a user-specified interval until a user-specified timeout. Using SAS 9.4 running interactively on Windows.

 

Being that I'm relatively inexperienced writing macro code, I thought I'd run it by you folks for suggestions to make this more robust.

 

By the way, I intend to use this following execution of a Python script called using the "X" command. The Python script sets a trigger file if the process completed successfully, and SAS will be looking for that trigger file. If that seems like a shortsighted approach, please let me know!

 

Here's the macro code, and a call to check for a file every half second for 10 seconds.

 

 

%MACRO CHECK_FILE_EXISTANCE (FILENAMEANDPATH= , /* FILE TO CHECK */
                             INTERVAL_TIME= , /* HOW LONG TO PAUSE BETWEEN CHECKS, SECONDS */
                             TIMEOUT= ,  /* TOTAL TIME BEFORE GIVING UP, SECONDS */
                             RESULT_INDC_VAR= );  /* NAME OF MACRO VARIABLE TO POPULATE WITH BOOLEAN RESULT */

            /* DEFINE GLOBAL VAR TO HOLD OUTCOME */
          %GLOBAL &RESULT_INDC_VAR;

            /* DEFINE LOCAL MACRO VARS */
          %LOCAL MAX_INTERVALS;
          %LOCAL I;

            /* DETERMINE NUMBER OF ITERATIONS REQUIRED */
          %LET MAX_INTERVALS = %SYSEVALF(&TIMEOUT/&INTERVAL_TIME,CEIL);

          /* BEGIN LOOPING */
          %DO I = 1 %TO &MAX_INTERVALS;
                       
             %IF %SYSFUNC(FILEEXIST(&FILENAMEANDPATH)) %THEN  %DO;
                %LET &RESULT_INDC_VAR = 1; /* FILE EXISTS */
                %GOTO EXIT; 
                                                              %END;                       
             %ELSE        %DO;
                %LET &RESULT_INDC_VAR = 0; /* FILE WAS NOT FOUND (YET) */

                   /* PAUSE BEFORE TRYING AGAIN */
                 DATA _NULL_;
                   X = SLEEP(&INTERVAL_TIME, 1);
                 RUN;
                           %END;

           %END;  /* END OF ITERATION */


           %EXIT:

           %IF &RESULT_INDC_VAR = 1 %THEN  %DO;
              %PUT NOTE: FILE EXISTS (&FILENAMEANDPATH);
                                           %END;

           %IF &RESULT_INDC_VAR = 0 %THEN  %DO;
              %PUT WARNING: FILE DOES NOT EXIST (&FILENAMEANDPATH);
                                           %END;

%MEND;

%CHECK_FILE_EXISTANCE (FILENAMEANDPATH=C:\TESTFILE.TXT ,
                       INTERVAL_TIME= .5,
                       TIMEOUT= 10,
                       RESULT_INDC_VAR = FILE1_CHECK);

%PUT &FILE1_CHECK;

 

 


Accepted Solutions
Solution
‎04-10-2018 10:09 AM
PROC Star
Posts: 253

Re: macro code check, please (scanning for trigger file)

I am not a great fan of using global macro variables to return values. In this case it is not necessary; by using %sysfunc(sleep()) you can avoid the data step, and change the code to a function style macro:

%macro WaitForFile(FilenameAndPath , /* file to check */
                   interval_time=1, /* how long to pause between checks, seconds */
                   timeout=10  /* total time before giving up, seconds */
                   );
          %local i rc;

          /* begin looping */
          %do i = 1 %to %sysevalf(&timeout/&interval_time,ceil);
             %if %sysfunc(fileexist(&FilenameAndPath)) %then  %do;
                %put NOTE: File exists (&FilenameAndPath);
                %*;1
                %return;
                %end;
             /* pause before trying again */
             %let rc=%sysfunc(sleep(&interval_time, 1));
             %end;  /* end of iteration */
%put WARNING: File does not exist (&FilenameAndpath);
0
%mend;

So, you can use the macro in code like

%if %WaitForFile(x:\test.txt) %then...

or

%let FileOK=%WaitForFile(x:\test.txt);

I changed the name of the macro to WaitForFile, as that seems to explain the function better. I dropped the all-caps style as that makes the code easier to read (I once heard that there is a scientific study to prove that!). And I simplified the code: No reason to put the number of intervals in a variable, it is only used once. Do not use %GOTO when you can use %RETURN. And I dropped the named parameter requirement (the "=") for the first parameter, as the meaning seems obvious when you see the macro used in code, and added defaults for the other two parameters.

One finale note: the macro comment "%*;" before the 1 is just a way of avoiding spurious blanks in the return value.

View solution in original post


All Replies
Regular Contributor
Posts: 153

Re: macro code check, please (scanning for trigger file)

Hi,

 

I think it's fine of doing it this way, make sure to use triple ampersands to resolve the value of FILE1_CHECK

           %IF &&&RESULT_INDC_VAR = 1 %THEN  %DO;
              %PUT NOTE: FILE EXISTS (&FILENAMEANDPATH);
                                           %END;

           %IF &&&RESULT_INDC_VAR = 0 %THEN  %DO;
              %PUT WARNING: FILE DOES NOT EXIST (&FILENAMEANDPATH);
                                           %END;

Here is a little interesting paper explaining the situation:

 

https://www.lexjansen.com/phuse/2015/cc/CC08.pdf

________________________

- Cheers -

Solution
‎04-10-2018 10:09 AM
PROC Star
Posts: 253

Re: macro code check, please (scanning for trigger file)

I am not a great fan of using global macro variables to return values. In this case it is not necessary; by using %sysfunc(sleep()) you can avoid the data step, and change the code to a function style macro:

%macro WaitForFile(FilenameAndPath , /* file to check */
                   interval_time=1, /* how long to pause between checks, seconds */
                   timeout=10  /* total time before giving up, seconds */
                   );
          %local i rc;

          /* begin looping */
          %do i = 1 %to %sysevalf(&timeout/&interval_time,ceil);
             %if %sysfunc(fileexist(&FilenameAndPath)) %then  %do;
                %put NOTE: File exists (&FilenameAndPath);
                %*;1
                %return;
                %end;
             /* pause before trying again */
             %let rc=%sysfunc(sleep(&interval_time, 1));
             %end;  /* end of iteration */
%put WARNING: File does not exist (&FilenameAndpath);
0
%mend;

So, you can use the macro in code like

%if %WaitForFile(x:\test.txt) %then...

or

%let FileOK=%WaitForFile(x:\test.txt);

I changed the name of the macro to WaitForFile, as that seems to explain the function better. I dropped the all-caps style as that makes the code easier to read (I once heard that there is a scientific study to prove that!). And I simplified the code: No reason to put the number of intervals in a variable, it is only used once. Do not use %GOTO when you can use %RETURN. And I dropped the named parameter requirement (the "=") for the first parameter, as the meaning seems obvious when you see the macro used in code, and added defaults for the other two parameters.

One finale note: the macro comment "%*;" before the 1 is just a way of avoiding spurious blanks in the return value.

New Contributor
Posts: 4

Re: macro code check, please (scanning for trigger file)

Thank you very much - your solution is much better than mine!

PROC Star
Posts: 2,316

Re: macro code check, please (scanning for trigger file)

I would use %sysfunc (sleep()) instead of a data step. This way your macro generates no sas code and can be called from anywhere, including inside a data step.
Regular Contributor
Posts: 153

Re: macro code check, please (scanning for trigger file)

Hi, one little more thing, the right spelling is 'existence' with an -e

________________________

- Cheers -

☑ This topic is solved.

Need further help from the community? Please ask a new question.

Discussion stats
  • 5 replies
  • 140 views
  • 5 likes
  • 4 in conversation