diff --git a/all.sas b/all.sas index 543fbcf..b35baee 100644 --- a/all.sas +++ b/all.sas @@ -3070,20 +3070,13 @@ run; @brief Returns all files and subdirectories within a specified parent @details When used with getattrs=NO, is not OS specific (uses dopen / dread). - If getattrs=YES then the doptname / foptname functions are used to scan all - properties - any characters that are not valid in a SAS name (v7) are simply - stripped, and the table is transposed so theat each property is a column - and there is one file per row. An attempt is made to get all properties - whether a file or folder, but some files/folders cannot be accessed, and so - not all properties can / will be populated. - Credit for the rename approach: https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003 usage: - %mp_dirlist(path=/some/location,outds=myTable) + %mp_dirlist(path=/some/location, outds=myTable, maxdepth=MAX) %mp_dirlist(outds=cwdfileprops, getattrs=YES) @@ -3097,11 +3090,19 @@ run; X CMD) do please raise an issue! - @param path= for which to return contents - @param fref= Provide a DISK engine fileref as an alternative to PATH - @param outds= the output dataset to create - @param getattrs= YES/NO (default=NO). Uses doptname and foptname to return - all attributes for each file / folder. + @param [in] path= for which to return contents + @param [in] fref= Provide a DISK engine fileref as an alternative to PATH + @param [in] maxdepth= (0) Set to a positive integer to indicate the level of + subdirectory scan recursion - eg 3, to go `./3/levels/deep`. For unlimited + recursion, set to MAX. + @param [out] outds= the output dataset to create + @param [out] getattrs= (NO) If getattrs=YES then the doptname / foptname + functions are used to scan all properties - any characters that are not + valid in a SAS name (v7) are simply stripped, and the table is transposed + so theat each property is a column and there is one file per row. An + attempt is made to get all properties whether a file or folder, but some + files/folders cannot be accessed, and so not all properties can / will be + populated. @returns outds contains the following variables: @@ -3111,8 +3112,12 @@ run; - filename (just the file name) - ext (.extension) - msg (system message if any issues) + - level (depth of folder) - OS SPECIFIC variables, if getattrs= is used. +

SAS Macros

+ @li mp_dropmembers.sas + @version 9.2 @author Allan Bowe **/ @@ -3121,14 +3126,27 @@ run; , fref=0 , outds=work.mp_dirlist , getattrs=NO + , maxdepth=0 + , level=0 /* The level of recursion to perform. For internal use only. */ )/*/STORE SOURCE*/; %let getattrs=%upcase(&getattrs)XX; -data &outds(compress=no - keep=file_or_folder filepath filename ext msg directory +/* temp table */ +%local out_ds; +data;run; +%let out_ds=%str(&syslast); + +/* drop main (top) table if it exists */ +%if &level=0 %then %do; + %mp_dropmembers(&outds, libref=WORK) +%end; + +data &out_ds(compress=no + keep=file_or_folder filepath filename ext msg directory level ); length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200; + retain level &level; %if &fref=0 %then %do; rc = filename(fref, "&path"); %end; @@ -3186,8 +3204,8 @@ data &outds(compress=no run; %if %substr(&getattrs,1,1)=Y %then %do; - data &outds; - set &outds; + data &out_ds; + set &out_ds; length infoname infoval $60 fref $8; rc=filename(fref,filepath); drop rc infoname fid i close fref; @@ -3228,12 +3246,37 @@ run; run; proc sort; by filepath sasname; - proc transpose data=&outds out=&outds(drop=_:); + proc transpose data=&out_ds out=&out_ds(drop=_:); id sasname; var infoval; by filepath file_or_folder filename ext ; run; %end; + +/* update main table */ +proc append base=&outds data=&out_ds; +run; + +data &outds; + set &outds(where=(filepath ne '')); +run; + +/* recursive call */ +%if &maxdepth>&level or &maxdepth=MAX %then %do; + data _null_; + set &out_ds; + where file_or_folder='folder'; + code=cats('%nrstr(%mp_dirlist(path=',filepath,",outds=&outds" + ,",getattrs=&getattrs,level=%eval(&level+1),maxdepth=&maxdepth))"); + put code=; + call execute(code); + run; +%end; + +/* tidy up */ +proc sql; +drop table &out_ds; + %mend mp_dirlist;/** @file @brief Creates a dataset containing distinct _formatted_ values diff --git a/base/mp_dirlist.sas b/base/mp_dirlist.sas index e827f5a..23b0df5 100644 --- a/base/mp_dirlist.sas +++ b/base/mp_dirlist.sas @@ -3,20 +3,13 @@ @brief Returns all files and subdirectories within a specified parent @details When used with getattrs=NO, is not OS specific (uses dopen / dread). - If getattrs=YES then the doptname / foptname functions are used to scan all - properties - any characters that are not valid in a SAS name (v7) are simply - stripped, and the table is transposed so theat each property is a column - and there is one file per row. An attempt is made to get all properties - whether a file or folder, but some files/folders cannot be accessed, and so - not all properties can / will be populated. - Credit for the rename approach: https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003 usage: - %mp_dirlist(path=/some/location,outds=myTable) + %mp_dirlist(path=/some/location, outds=myTable, maxdepth=MAX) %mp_dirlist(outds=cwdfileprops, getattrs=YES) @@ -30,11 +23,19 @@ X CMD) do please raise an issue! - @param path= for which to return contents - @param fref= Provide a DISK engine fileref as an alternative to PATH - @param outds= the output dataset to create - @param getattrs= YES/NO (default=NO). Uses doptname and foptname to return - all attributes for each file / folder. + @param [in] path= for which to return contents + @param [in] fref= Provide a DISK engine fileref as an alternative to PATH + @param [in] maxdepth= (0) Set to a positive integer to indicate the level of + subdirectory scan recursion - eg 3, to go `./3/levels/deep`. For unlimited + recursion, set to MAX. + @param [out] outds= the output dataset to create + @param [out] getattrs= (NO) If getattrs=YES then the doptname / foptname + functions are used to scan all properties - any characters that are not + valid in a SAS name (v7) are simply stripped, and the table is transposed + so theat each property is a column and there is one file per row. An + attempt is made to get all properties whether a file or folder, but some + files/folders cannot be accessed, and so not all properties can / will be + populated. @returns outds contains the following variables: @@ -44,8 +45,12 @@ - filename (just the file name) - ext (.extension) - msg (system message if any issues) + - level (depth of folder) - OS SPECIFIC variables, if getattrs= is used. +

SAS Macros

+ @li mp_dropmembers.sas + @version 9.2 @author Allan Bowe **/ @@ -54,14 +59,27 @@ , fref=0 , outds=work.mp_dirlist , getattrs=NO + , maxdepth=0 + , level=0 /* The level of recursion to perform. For internal use only. */ )/*/STORE SOURCE*/; %let getattrs=%upcase(&getattrs)XX; -data &outds(compress=no - keep=file_or_folder filepath filename ext msg directory +/* temp table */ +%local out_ds; +data;run; +%let out_ds=%str(&syslast); + +/* drop main (top) table if it exists */ +%if &level=0 %then %do; + %mp_dropmembers(&outds, libref=WORK) +%end; + +data &out_ds(compress=no + keep=file_or_folder filepath filename ext msg directory level ); length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80 ext $20 msg $200; + retain level &level; %if &fref=0 %then %do; rc = filename(fref, "&path"); %end; @@ -119,8 +137,8 @@ data &outds(compress=no run; %if %substr(&getattrs,1,1)=Y %then %do; - data &outds; - set &outds; + data &out_ds; + set &out_ds; length infoname infoval $60 fref $8; rc=filename(fref,filepath); drop rc infoname fid i close fref; @@ -161,10 +179,35 @@ run; run; proc sort; by filepath sasname; - proc transpose data=&outds out=&outds(drop=_:); + proc transpose data=&out_ds out=&out_ds(drop=_:); id sasname; var infoval; by filepath file_or_folder filename ext ; run; %end; + +data &out_ds; + set &out_ds(where=(filepath ne '')); +run; + +/* update main table */ +proc append base=&outds data=&out_ds; +run; + +/* recursive call */ +%if &maxdepth>&level or &maxdepth=MAX %then %do; + data _null_; + set &out_ds; + where file_or_folder='folder'; + code=cats('%nrstr(%mp_dirlist(path=',filepath,",outds=&outds" + ,",getattrs=&getattrs,level=%eval(&level+1),maxdepth=&maxdepth))"); + put code=; + call execute(code); + run; +%end; + +/* tidy up */ +proc sql; +drop table &out_ds; + %mend mp_dirlist; \ No newline at end of file diff --git a/tests/crossplatform/mp_dirlist.test.sas b/tests/crossplatform/mp_dirlist.test.sas new file mode 100644 index 0000000..ca0becc --- /dev/null +++ b/tests/crossplatform/mp_dirlist.test.sas @@ -0,0 +1,50 @@ +/** + @file + @brief Testing mp_ds2cards.sas macro + +

SAS Macros

+ @li mf_nobs.sas + @li mf_mkdir.sas + @li mp_dirlist.sas + @li mp_assert.sas + +**/ + +/** + * make a directory structure + */ + +%let root=%sysfunc(pathname(work))/top; +%mf_mkdir(&root) +%mf_mkdir(&root/a) +%mf_mkdir(&root/b) +%mf_mkdir(&root/a/d) +%mf_mkdir(&root/a/e) +%mf_mkdir(&root/a/e/f) +data "&root/a/e/f/ds1.sas7bdat"; + x=1; +run; + +%mp_dirlist(path=&root, outds=myTable, maxdepth=MAX) + +%mp_assert( + iftrue=(%mf_nobs(work.mytable)=6), + desc=All levels returned, + outds=work.test_results +) + +%mp_dirlist(path=&root, outds=myTable2, maxdepth=2) + +%mp_assert( + iftrue=(%mf_nobs(work.mytable2)=5), + desc=Top two levels returned, + outds=work.test_results +) + +%mp_dirlist(path=&root, outds=myTable3, maxdepth=0) + +%mp_assert( + iftrue=(%mf_nobs(work.mytable3)=2), + desc=Top level returned, + outds=work.test_results +) \ No newline at end of file