Using SAS Macro to pipe a list of filenames from a Windows directory

42,662

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)) 

Share:
42,662
Allan Bowe
Author by

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, 2020

Comments

  • Allan Bowe
    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
    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
    Allan Bowe over 14 years
    although 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
    Allan Bowe over 14 years
    this works - but leaves an error message (WARNING: No logical assign for filename PIPE.)
  • user1774301
    user1774301 over 14 years
    just change filename pipe clear; to filename pipedir clear; to suppress the warning.
  • user1774301
    user1774301 over 14 years
    I 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
    lightning over 14 years
    i 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.