I am wondering is it possible to write a macro to read file
in different folders.
Like for example, I have 100 folders and within each folder,
it has another folder, within the sub folder It may contain a txt file that I
want to read with SAS. New folder can be created to include new file.
Folder1 folder2 file
A1 aa1 file1
A2 aa2 (nofile)
… .. …
A100 aa100 file100
Folder2 is within folder1 and file is stored in folder2.
Thanks
Chen Xu
The following macro might do the job;
%macro procesFilesInRoot(rootFolder,fileExt,procesMacro);
%put "dir ""%unquote(&rootFolder)"" /s /b";
filename pipeTree pipe "dir ""%unquote(&rootFolder)"" /s /b" lrecl=32767;
data fullNames;
infile pipeTree truncover;
input fullName $char1000.;
ext = upcase(scan(fullName,countw(fullName, '.'), '.'));
if ext = "%upcase(&fileExt)";
run;
filename pipeTree clear;
title 'files found';
proc print;
run;
proc sql noprint;
select count(*) into :nrFiles from fullNames;
%let nrFiles = &nrFiles; /** to strip the leading blanks **/
%if &nrFiles %then %do;
select fullName into :fullName1-:fullName&nrfiles from fullNames;
%end;
quit;
%do fileNr = 1 %to &nrFiles.;
%&procesMacro(&&fullName&fileNr);
%end;
%mend;
Before using it, you need to write a macro that processes a single input file;
%macro import1file(fullName);
%let fileName = %scan(&fullName,%sysfunc(countw(&fullName, '\')), '\');
%let fileExt = %scan(&fileName,%sysfunc(countw(&fullName, '.')), '.');
%let dataName = %substr(&fileName,1, %length(&fileName)-%length(&fileExt)-1);
proc import datafile = "&fullName" out=&dataName;
run;
%mend;
Then you can use it as follows;
%procesFilesInRoot(D:\Horsten,txt,import1file);
Related
I am trying to import all the CSV files in one folder and schedule the program in the SAS management console. Since it has empty CSV files in the folders, it failed to run the code automatically due to the errors. I was wondering if I can skip the steps of importing the empty file in the following codes.
`%macro drive(dir,ext);
%local cnt filrf rc did memcnt name;
%let cnt=0;
%let filrf=mydir;
%let rc=%sysfunc(filename(filrf,&dir));
%let did=%sysfunc(dopen(&filrf));
%if &did ne 0 %then %do;
%let memcnt=%sysfunc(dnum(&did));
%do i=0 %to &memcnt;
%let name=%qscan(%qsysfunc(dread(&did,&i)),-1,.);
%if %qupcase(%qsysfunc(dread(&did,&i))) ne %qupcase(&name) %then %do;
%if %superq(ext) = %superq(name) %then %do;
%let cnt=%eval(&cnt+1);
%put %qsysfunc(dread(&did,&i));
proc import datafile="&dir\%qsysfunc(dread(&did,&i))" out=dsn&cnt
dbms=csv replace;
guessingrows=max;
run;
data dsnnew&cnt (drop=old);set dsn&cnt (rename=(party_id = old)); party_id= input(old,12.);run;
%end;
%end;
%end;
%end;
%else %put &dir cannot be opened.;
%end;
%let rc=%sysfunc(dclose(&did));
%mend drive;`
PROC IMPORT cannot handle CSV files with fewer then 2 lines. You could just count how many lines the file has. No need to count beyond 2.
data _null_;
if eof then call symputx('nobs',_n_-1);
infile "&dir\%qsysfunc(dread(&did,&i))" obs=2 end=eof;
input;
run;
You can then test the value of the NOBS macro variable to decide whether or not to run PROC IMPORT>
%if &nobs>1 %then %do;
....
%end;
If the files all have the same structure then there is no need for the complicated macro code. Instead just read them all with one data step. You will have to write the code, but you will then have full control over how the variables are defined. You could include the actual name of the file as part of the dataset. Or perhaps extract some identifying information from the filename.
data want;
length fname filename $256 ;
infile "&dir/*.csv" filename=fname truncover dsd ;
input #;
* skip the header lines ;
if fname ne lag(fname) then delete;
* save the filename to a permanent variable;
filename=fname;
* Here is where you put the code to actually read the lines ;
input var1 var2 .... ;
run;
I have SAS code in which there is following line of code is included
% include "/sas/dev/compare.sas";
it is at location on server
/sas/cpm/dev/code
So, at present what I am doing manually go to above path and open each .sas code(in a Folder, you can say there are 4 codes location at /sas/cpm/dev/code) and check whether %include "/sas/dev/compare.sas" line of code is present or not
Can anyone help me without checking/open manually .sas code how to check whether %include "/sas/dev/compare.sas"; is exist or not
Can anyone , please help me how to write in sas,
We can pass each code through macro right
Thanks
You can do this by:
Listing all the .sas files in each folder,
Create a datasetp / infile statement for each file,
Search each line in the file for % include "/sas/dev/compare.sas";
If line is found, print to the log the file name and line number
Code:
%let extension=sas;
%let FolderPath=\\sas\SASDATA\momo\;
%macro check_file_path(f=);
DATA _null_;
infile "&f" dsd ;
length string $200.;
input string $;
if string=&check. then put "&f. " "includes # line " _N_= ;
run;
%mend;
Data List_files;
rc=FILENAME('FMyRep',"&FolderPath");
did=DOPEN('FMyRep');
memcnt=DNUM(did);
*count number of members - including subfolders;
DO i=1 TO memcnt;
*for each member, test extension and store filename;
filevar=LOWCASE(DREAD(did,i));
file_path=cats("&FolderPath.",filevar);
IF LOWCASE(SCAN(filevar,-1,".")) EQ LOWCASE("&extension") THEN
OUTPUT;
END;
rc=DCLOSE(did);
rc=FILENAME('FMyRep');
KEEP filevar file_path;
RUN;
data _null_;
set List_files;
call execute('%check_file_path(f='||file_path||')');
put file_path=;
run;
Output:
file_path=\\sas\SASDATA\momo\file1.sas
file_path=\\sas\SASDATA\momo\file2.sas
file_path=\\sas\SASDATA\momo\file3.sas
file_path=\\sas\SASDATA\momo\file4.sas
Log: file4.sas is the only one that doesn't have this % include "/sas/dev/compare.sas";
\\sas\SASDATA\momo\file1.sas includes # line _N_=1
\\sas\SASDATA\momo\file2.sas includes # line _N_=1
\\sas\SASDATA\momo\file4.sas includes # line _N_=1
you can do it by using fileexist function
%let myfile = "/sas/dev/compare.sas";
%macro filecheck;
%if %sysfunc(fileexist(&myfile))
%then % include "/sas/dev/compare.sas";
%else %put The external file &myfile does not exist.;
%mend;
%filecheck;
I am running the following
%global source_dir util_dir paramf workdir datadir
dataunitid
saserror
;
%let datadir = %str(I:....\KATER\DATA);
%let outdir = %str(I:...\KATER\Results);
I set my library
libname datain "&datadir";
options mstored sasmstore=datain;
and then
%global liste_reg;
%let liste_reg=22 31;
%do k=1 %to %sysfunc(countw(&liste_reg.));
%let reg=%scan(&liste_reg.,&k.);
data hh_record; set datain.hh_record_®.; run;
data person_record; set datain.person_record_®.; run;
%let outdir_ = &outdir.\output_®.;
proc printto log = "&outdir_.Log.txt"; run;
But I get an error:
ERROR: File DATAIN.PERSON_RECORD_.DATA does not exist.
ERROR: File DATAIN.HH_RECORD_.DATA does not exist.
why is this happening, How can I put my data in datain library?
I am new in SAS so i am little confused. In general I realised that there is nothing in both libraries?
EDIT full code:
%global source_dir util_dir paramf workdir datadir
dataunitid
saserror
;
%let source_dir = %str(I:...ONS_Transf_20170523);
*location of code;
%let util_dir = &source_dir.%str(\dsp_utils);
%let datadir = %str(...KATER\DATA);
%let outdir = %str(...\KATER\Results);
%let paramf = &datadir.%str(\parameter_file\param.csv);
options mautosource mrecall sasautos=(sasautos "&source_dir" "&util_dir") nolabel;
%reset_saserror;
libname datain "&datadir";
options mstored sasmstore=datain;
libname outdir "&outdir.\output";
options mstored sasmstore=outdir;
%macro sdc_super_control_KAT;
%global liste_reg;
%let liste_reg=22 31;
%do k=1 %to %sysfunc(countw(&liste_reg.));
%let reg=%scan(&liste_reg.,&k.);
data hh_record;
set datain.hh_record_®.;
run;
data person_record;
set datain.person_record_®.;
run;
%let outdir_ = &outdir.\output_®.;
proc printto log = "&outdir_.Log.txt";
run;
/
%sdc_control;
*copy files to permanent library;
proc copy in=work out=outdir_;
select sdcresults_hh_:;
run;
proc printto;
run;
%end;
data outdir.params;
set diagnostics_params;
run;
%mend sdc_super_control_KAT;
%sdc_super_control_KAT;
I don't think you're showing us the full code. The issue above appears to be due to the ® macro variable not resolving to a value but there is nothing shown to indicate why that would happen. Also, the error messages are in the wrong order (hh_record_) should come first in the log.
In summary, it's because your reg variable is resolving to a missing value, or because it is not found (if not found, the log should say that though).
I have a small STP which list all files/subdirectories in a given directory on our Unix-Server.
Problem is, my datastep only returns the name of the object, but not an information if this is a file or directory.
I tried some things and had some Ideas, but non is really foolproof, maybe someone can give me a hint how i can get this.
What i have tried so far:
using fileexists: This returns true for files and directories, so this does not work here
Checking for a dot. There could be files without extensions (and so dont have a dot) and also directories which have a dot in name, so this is not foolproof
using unixfunctionalities in sas: This is forbidden on our server, so no option for me
open an filehandle and do a finfo, this returns a warning if it is not a file, but
a)maybe it could be something else then a directory or file, or a warning because of other reasons
b)checking for a warning to detect type is not a good solution imo
Here a part of my code which i am using:
filename _folder_ "%bquote(&mydirectory/)";
data x (keep=filepath);
handle=dopen( '_folder_' );
if handle > 0 then do;
count=dnum(handle);
do i=1 to count;
filepath="&mydirectory"||dread(handle,i);
output;
end;
end;
rc=dclose(handle);
run;
filename _folder_ clear;
/*this part just makes a macrovariable with all results*/
proc sql noprint;
select filepath into: fpath separated by '#' from x;
quit;
/* this macro collects some additional fileinformation from all file in macrovariable fpath and creates a HTML-Ouput*/
%FileAttribs;
Use FILENAME() function to define a fileref to the file. This will return a code to indicate if the file exists or not. Then use the DOPEN() function to attempt to open the fileref as a directory. This will tell you if the file is a directory.
For example here is the function style macro that I use.
%macro direxist
/*----------------------------------------------------------------------
Test if directory exists
----------------------------------------------------------------------*/
(path /* Name of directory to test for existance */
);
/*----------------------------------------------------------------------
Test if directory exists. Returns value 1 or 0.
1 - Directory exists
0 - Directory does not exist
Global macro variable SYSRC will be set to 1 when a file is found
instead of a directory.
----------------------------------------------------------------------*/
%local return rc did fileref;
%*----------------------------------------------------------------------
Set up return values as normal failure to find path.
-----------------------------------------------------------------------;
%let return=0;
%let sysrc=0;
%*----------------------------------------------------------------------
If path is not specified or does not exist then return normal failure.
-----------------------------------------------------------------------;
%if (%bquote(&path) = ) %then %goto quit;
%if ^%sysfunc(fileexist(&path)) %then %goto quit;
%*----------------------------------------------------------------------
Try to open it using DOPEN function.
Return 1 if it can be opened.
Otherwise set SYSRC=1 to mean that PATH exists but is not a directory.
-----------------------------------------------------------------------;
%if (0=%sysfunc(filename(fileref,&path))) %then %do;
%let did=%sysfunc(dopen(&fileref));
%if (&did) %then %do;
%let return=1;
%let rc=%sysfunc(dclose(&did));
%end;
%else %let sysrc=1;
%let rc=%sysfunc(filename(fileref));
%end;
%quit:
%*----------------------------------------------------------------------
Return the value as the result of the macro.
-----------------------------------------------------------------------;
&return
%mend;
this is my code to read multiple multisheet excel in sas its giving an error which i am attaching in last.i am only reading first sheet named summary of every excel in that particular folder
%macro sks2sas01(input=d:\excels,out=work.tt);
/* read files in directory */
%let dir=%str(%'dir %")&input.%str(\%" /A-D/B/ON%');
filename myfiles pipe %unquote(&dir);
data list1; length fname $256.;
infile myfiles truncover;
input myfiles $100.;
/* put _infile_;*/
fname=quote(upcase(cats("&input",'\',myfiles)));
out="&out";
drop myfiles;
call execute('
PROC IMPORT DBMS=EXCEL2002 OUT= _1
DATAFILE= '||fname||' REPLACE ;
sheet="summary";
RUN;
proc append data=_1 base='||out||' force; run;
proc delete data=_1; run;
');
run;
filename myfiles clear;
%mend sks2sas01;
%sks2sas01(input=c:\sasupload\excels,out=work.tt);
hereby i am attaching error i am getting:
GOPTIONS ACCESSIBLE;
%macro sks2sas01(input=d:\excels,out=work.tt);
/* read files in directory */
%let dir=%str(%'dir %")&input.%str(\%" /A-D/B/ON%');
filename myfiles pipe %unquote(&dir);
data list1; length fname $256.;
infile myfiles truncover;
input myfiles $100.;
/* put _infile_;*/
fname=quote(upcase(cats("&input",'\',myfiles)));
out="&out";
drop myfiles;
call execute('
PROC IMPORT DBMS=EXCEL2002 OUT= _1
DATAFILE= '||fname||' REPLACE ;
sheet="summary";
RUN;
proc append data=_1 base='||out||' force; run;
proc delete data=_1; run;
');
run;
filename myfiles clear;
%mend sks2sas01;
%sks2sas01(input=c:\sasupload\excels,out=work.tt);
ERROR: Insufficient authorization to access PIPE.
ERROR: Error in the FILENAME statement.
I had this exact same problem the other day. I had no authorization for the pipe command, and dir using sftp wasn't working for me either. This alternative solution worked great for me. In a nutshell, you're going to use some oldschool SAS directory commands to read every file within that directory, and save only the ones that end in .xlsx.
You can consider the name of your Excel files .-delimited, scan the filename of each one backwards, and look at just the first word to obtain only those Excel files. For example:
File name.xlsx
Backwards:
Delimiter
v
xlsx.name File
^^^^
First word
Step 1: Read all XLSX files in the directory, and create a dataset of them
filename dir 'C:\MyDirectory';
data XLSX_Files;
DirID = dopen("dir");
do i = 1 to dnum(DirID);
file_name = dread(DirID, i);
if(upcase(scan(file_name, 1, '.', 'b') ) = 'XLSX') then output;
end;
rc=dclose(DirID);
drop i rc DirID;
run;
Step 2: Read all of those names into a pipe-delimited macro variable
proc sql noprint;
select file_name, count(file_name)
into :XLSX_Files separated by '|',
:tot_files
from XLSX_Files;
quit;
Step 3: Import them all in a macro loop
%macro import_all;
%do i = 1 %to &tot_files;
proc import file="C:\MyDirectory\%scan(&XLSX_Files,&i,|)"
out=XLSX_&i
dbms=xlsx replace;
run;
%end;
%mend;
%import_all;
You can then stack or merge them as you need.
data Master_File;
set XLSX_1-XLSX_&tot_files;
run;
OR
data Master_File;
merge XLSX_1-XLSX_&tot_files;
by key;
run;