BookmarkSubscribeRSS Feed
RichardDeVen
Barite | Level 11

Here is a new twist on an old trope.  Recursive find files will create a data set listing all files below a root path to a certain depth.  Recursion in DATA Step made possible by maintaining state data (i.e. stack) in a hash object.  The only downside is that sub-folder detection in SAS is done via MOPEN returning a 0, which means every file detected has an attempted open and it takes a lot longer to get the results than say a bash find command.

 

Sample code:

 

%let root = G:;
%let maxdepth = 2;

data find_files (keep=folder filename);
  length folder $2000 fref $8 filename $2000 index dnum 8 ;

  call missing (depth, did, folder, index, dnum);

  declare hash state ();
  state.defineKey ('depth');
  state.defineData ('fref', 'did', 'folder', 'index', 'dnum');
  state.defineDone();

  folder = "&root";

  depth = 0;
  maxdepth = &maxdepth;

  link push;
  goto iterate;

push:
  depth = depth + 1;

  link fref;
  did = dopen (fref);

  if did = 0 then stop;

  filename = '';
  output;

  dnum = dnum (did);
  index = 0;

  state.add();

  if depth > maxdepth then do;
    link pop;
    goto iterate;
  end;
return;

%*-------------------------------------------------------;
iterate:
%*-------------------------------------------------------;
  if index >= dnum then do;
    link pop;
    goto iterate;
  end;

  index + 1;
  state.replace();

  filename = dread(did, index);

  mid = mopen(did, filename);

  if mid = 0 then do;
    folder = catx ('/', folder, filename);

    link push;
    goto iterate;
  end;

* add FINFO() calls here to obtain file size and mod date;

  mid = fclose(mid);
  output;

  goto iterate;

%*-------------------------------------------------------;
pop:
%*-------------------------------------------------------;
  did = dclose(did);
  rc = filename (fref);

  state.remove();

  depth = depth - 1;
  if depth = 0 then stop;

  state.find();  

  return;

%*-------------------------------------------------------;
fref:
%*-------------------------------------------------------;
  call missing (fref);
  rc = filename (fref, folder);

  if rc ne 0 then do;
    msg = sysmsg();
    put msg;
    stop;
  end;
  return;

run;
2 REPLIES 2
Tom
Super User Tom
Super User

If your subtree is too large to load into a hash object you can use the MODIFY statement to affect a recursive search.

https://github.com/sasutils/macros/blob/master/dirtree.sas

 

RichardDeVen
Barite | Level 11
The only issue I see with the recursion is if the number of allowed open
filerefs is exceeded. There is 1 fileref open per depth reached. No way
the hash will run out of resources.

hackathon24-white-horiz.png

The 2025 SAS Hackathon has begun!

It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.

Latest Updates

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
  • 2 replies
  • 1357 views
  • 2 likes
  • 2 in conversation