Using SAS Macro to pipe a list of filenames from a Windows directory
Solution 1
Here's another way of achieving the same result without needing to use a PIPE.
%macro get_filenames(location);
filename _dir_ "%bquote(&location.)";
data filenames(keep=memname);
handle=dopen( '_dir_' );
if handle > 0 then do;
count=dnum(handle);
do i=1 to count;
memname=dread(handle,i);
output filenames;
end;
end;
rc=dclose(handle);
run;
filename _dir_ clear;
%mend;
%get_filenames(C:\temp\);
%get_filenames(C:\temp\with space);
%get_filenames(%bquote(C:\temp\with'singlequote));
Solution 2
Make the following several changes and your code will work.
%macro get_filenames(location); %*--(1)--*;
filename pipedir pipe "dir ""%unquote(&location)"" /b" lrecl=32767; %*--(2)--*;
data filenames;
infile pipedir truncover;
input filename $char1000.;
put filename=;
run;
filename pipedir clear; %*--(3)--*;
%mend;
%get_filenames(d:\)
%get_filenames(d:\your dir) %*--(4)--*;
(1) End the %macro
statement with a semi-colon;
(2) Surround the macro variable resolution with doubled-up double quotes and %unquote
;
(3) Release the file handle by clearing it; and
(4) Don't single quote your input parameter. macro quote instead, if necessary.
Solution 3
Based on the last sample on this page, instead of the filename statement, try
%let filrf=pipedir;
%let rc=%sysfunc(filename(filrf,%bquote(dir "&location" /b),pipe));
and call the macro without using quotes:
%get_filenames(c:\temp\with spaces);
I also tried macro quoting, but couldn't get it to work.
Solution 4
here's a quick macro to pull windows-based directory listings into a sas data set.
%macro DirList(dir); /* %if &SUBDIR eq %then %let subdir=/s; */ /*** &SUBDIR not defined ****/ filename dirpipe pipe "dir &DIR.\*.* /s /-c"; data dir_list(label="Directory Listing [&DIR.]" drop=re_: _line_ date time); format Path File $250. ModDT datetime19. Size 16. _line_ $32000. ; if _N_ = 1 then do; re_path=prxparse("/Directory of (.+)/"); re_subd=prxparse("/(\d\d\/\d\d\/\d\d\d\d)\s+(\d\d:\d\d [A|P]M)\s+\s+(\S.*)/"); re_file=prxparse("/(\d\d\/\d\d\/\d\d\d\d)\s+(\d\d:\d\d [A|P]M)\s+(\d+)\s+(\S.*)/"); retain re_: path; end; infile dirpipe lrecl=32000; input; _line_ = _infile_; if lengthn(_line_)=0 then delete; else if prxmatch(re_path, _line_) then do; path=prxposn(re_path, 1, _line_); end; else if prxmatch(re_subd, _line_) then do; date=input(prxposn(re_subd, 1, _line_), mmddyy10.); time=input(prxposn(re_subd, 2, _line_), time6.); ModDT=dhms(date, 0, 0, time); File=prxposn(re_subd, 3, _line_); size = .D; /*mark subdirectory records*/ if file not in ('.', '..') then output; end; else if prxmatch(re_file, _line_) then do; date=input(prxposn(re_file, 1, _line_), mmddyy10.); time=input(prxposn(re_file, 2, _line_), time6.); ModDT=dhms(date, 0, 0, time); size=input(prxposn(re_file, 3, _line_), 16.); file=prxposn(re_file, 4, _line_); output; end; run; filename dirpipe clear; %mend;
and here's how they get called
%dirlist(c:); %dirlist(c:\temp);
notice there is no trailing backslash when specifying the base directory. C:
not C:\
.
Solution 5
it works for me if i call the original macro this way
%get_filenames(""C:\Program Files"")
of course i had to add the semicolon at the end of the %macro
statement.
if your directory contains a comma, bad things happen. to fix, use the %str()
macro
%get_filenames(%str(C:\temp\comma, fail))
Allan Bowe
SAS Consultant Projects: https://sasjs.io - Framework for building web apps on SAS https://sasensei.com - quiz game for SAS https://datacontroller.io - data capture, governance & approval https://rawsas.com - SAS blog
Updated on January 25, 2020Comments
-
Allan Bowe over 4 years
I am trying to amend the macro below to accept a macro parameter as the 'location' argument for a dir command. However I cannot get it to resolve correctly due to the nested quotes issue. Using %str(%') does not work, neither do quoting functions for some reason.
The macro will work fine when the filepath has no spaces (eg C:\temp\withnospace) as the middle quotes aren't needed. However I need this macro to work for filepaths with spaces (eg 'C:\temp\with space\').
Please help!
%macro get_filenames(location) filename pipedir pipe "dir &location. /b " lrecl=32767; data filenames; infile pipedir truncover; input line $char1000.; run; %mend; %get_filenames(C:\temp\) /* works */ %get_filenames('C:\temp\with space') /* doesnt work */
-
user1774301 over 14 years+1 - nice. You can also pass lrecl as a host option to this so it is just like his original: %let rc=%sysfunc(filename(filrf,%bquote(dir "&location" /b),pipe, lrecl=32767));
-
Allan Bowe over 14 yearsalthough this doesn't actually answer the question, this code is far superior as it can be used across environments - and fulfills the same purpose. thankyou...
-
Allan Bowe over 14 yearsthis works - but leaves an error message (WARNING: No logical assign for filename PIPE.)
-
user1774301 over 14 yearsjust change filename pipe clear; to filename pipedir clear; to suppress the warning.
-
user1774301 over 14 yearsI like this but I had to make a couple of changes for it to work on my system (win xp). I changed the re_subd= line to re_subd=prxparse("/(\d\d\/\d\d\/\d\d\d\d)\s+(\d\d:\d\d [A|P]M)\s+<DIR>\s+(\S.*)/"); as every line was otherwise being treated as a subdir for me. Also, I removed the %if &SUBDIR eq %then %let subdir=/s; line since &subdir macro variable isn't defined and isn't used elsewhere.
-
lightning over 14 yearsi recall having to make the change to re_subd regex pattern when os was upgraded, but never thought to save the prior version. the SUBDIR parameter is intended to be for specifying the /s switch for recursive subdriectory listings. someday.