BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
desertsp
Obsidian | Level 7

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;

 

 

1 ACCEPTED SOLUTION

Accepted Solutions
s_lassen
Meteorite | Level 14

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

5 REPLIES 5
Oligolas
Barite | Level 11

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 -

s_lassen
Meteorite | Level 14

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.

desertsp
Obsidian | Level 7

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

ChrisNZ
Tourmaline | Level 20
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.
Oligolas
Barite | Level 11

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

________________________

- Cheers -

SAS Innovate 2025: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

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