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;
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.
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 -
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.
Thank you very much - your solution is much better than mine!
Hi, one little more thing, the right spelling is 'existence' with an -e
- Cheers -
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.
Ready to level-up your skills? Choose your own adventure.