Hi,
I am using a macro to combine RTF outputs. The macro runs without errors but the final file has only the first output.
The intermediate RTF files look correct.
Any suggestions?
thanks
KC
First a generic comment about debugging macros. When a macro doesn't do what you expect you can set
options mprint;
and if you are using complex macro variable constructions, like the indirect reference &&file&i you may want the SYMBOLGEN option as well.
Convoluted macro logic may call for the MLOGIC option.
Then re-run your macro to read more details of the generated code in the Log. Plus any messages will be closer to the actual generated code.
Second: Habitual use of code with the same data set as the input and output sets such as
data rtf&i; set rtf&i end=eof; if eof then line="\pard}}"; run;
Are extremely difficult to debug because the output set completely replaces in the input set. So you can't trace data related issues very well. Better to have a different name on the output side so you can see if the output matches what you expect for a given input at any given step.
If your data set RTF only has one file's worth of content then look to whether any of the Rtf&i were actually created and what their content looks like. But keep in mind the warning about about input/output sets with the same name. You no longer have, with your existing code, any idea of what the original Rtf&i sets looked like.
Instead of sticking that N and File& information into macro variables with this step:
proc sql noprint; select count(*) into:n from dirlist; select filename into:file1-:file%cmpres(&n) from dirlist; quit;
You can use the Dirlist data set directly, testing the _n_ variable instead of &n, and either use PUT statements to write a .SAS program file that you can read and verify for accuracy before %include it for execution or use the CALL Execute statement to write statements direct to the macro execution buffer.
I am little concerned about use of Macro functions in data step code such as in the following. How carefully did you test this before including it?
data rtf&i; set rtf&i end=eof; if _n_>29; if _n_=30 then line="\sect"||trim(left(line)); if eof then line=trim(left(line))||"%sysfunc(repeat(\pard,%eval(&n-1)))}"; run;
If you look at this example you can see no macro functions required:
%let n=3; data example; length x $ 50; x='Sometext'; x=strip(x)||repeat('\pard',&n-1); run;
Strip is equivalent to Trim(left(<text value>)) and has been around for like 20 years. And if you use the CAT functions such as CATS would not even be needed as CATS strips all the values involved.
%let n=3; data example; length x $ 50; x='Sometext'; x=cats(x,repeat('\pard',&n-1) ); run;
NO code attached so have no clue as to what you did or why it may not have all the output expected.
Generally I would say combining already created RTF files is a Word processor program job due to the volume of non-data elements that are in a typical RTF file.
@Kc2 wrote:
Hi,
I am using a macro to combine RTF outputs. The macro runs without errors but the final file has only the first output.
The intermediate RTF files look correct.
Any suggestions?
thanks
KC
%macro combine(pathin=, pathout=, fileout=);
filename dirlist pipe "dir ""&pathin*.rtf"" /b ";
data dirlist;
infile dirlist length=reclen ;
input filename_ $varying1024. reclen ;
if scan(filename_,2,'.') in ('rtf');
path="&pathin";
if filename_ ne "&fileout..rtf";
filename=strip(path)||'\'||strip(filename_);
run;
proc sql noprint;
select count(*) into:n from dirlist;
select filename into:file1-:file%cmpres(&n) from dirlist;
quit;
data rtf;set _null_;run;
%do i=1 %to &n;
filename rtf "&&file&i" ;
data rtf&i;
infile rtf missover length = l end = lastobs lrecl = 2000;
input line $varying2000. l;
run;
%if &i=1 %then %do;
data rtf&i;
set rtf&i end=eof;
if eof then line="\pard}}";
run;
%end;
%else %if &i=&n %then %do;
data rtf&i;
set rtf&i end=eof;
if _n_>29;
if _n_=30 then line="\sect"||trim(left(line));
if eof then line=trim(left(line))||"%sysfunc(repeat(\pard,%eval(&n-1)))}";
run;
%end;
%else %do;
data rtf&i;
set rtf&i end=eof;
if _n_>29;
if _n_=30 then line="\sect"||trim(left(line));
if eof then line="\pard}}";
run;
%end;
data rtf;set rtf rtf&i;run;
%end;
filename rtf "&pathout.&fileout..rtf" ;
data _null_;
set rtf;
file rtf lrecl=2000;
put line;
run;
%mend;
First a generic comment about debugging macros. When a macro doesn't do what you expect you can set
options mprint;
and if you are using complex macro variable constructions, like the indirect reference &&file&i you may want the SYMBOLGEN option as well.
Convoluted macro logic may call for the MLOGIC option.
Then re-run your macro to read more details of the generated code in the Log. Plus any messages will be closer to the actual generated code.
Second: Habitual use of code with the same data set as the input and output sets such as
data rtf&i; set rtf&i end=eof; if eof then line="\pard}}"; run;
Are extremely difficult to debug because the output set completely replaces in the input set. So you can't trace data related issues very well. Better to have a different name on the output side so you can see if the output matches what you expect for a given input at any given step.
If your data set RTF only has one file's worth of content then look to whether any of the Rtf&i were actually created and what their content looks like. But keep in mind the warning about about input/output sets with the same name. You no longer have, with your existing code, any idea of what the original Rtf&i sets looked like.
Instead of sticking that N and File& information into macro variables with this step:
proc sql noprint; select count(*) into:n from dirlist; select filename into:file1-:file%cmpres(&n) from dirlist; quit;
You can use the Dirlist data set directly, testing the _n_ variable instead of &n, and either use PUT statements to write a .SAS program file that you can read and verify for accuracy before %include it for execution or use the CALL Execute statement to write statements direct to the macro execution buffer.
I am little concerned about use of Macro functions in data step code such as in the following. How carefully did you test this before including it?
data rtf&i; set rtf&i end=eof; if _n_>29; if _n_=30 then line="\sect"||trim(left(line)); if eof then line=trim(left(line))||"%sysfunc(repeat(\pard,%eval(&n-1)))}"; run;
If you look at this example you can see no macro functions required:
%let n=3; data example; length x $ 50; x='Sometext'; x=strip(x)||repeat('\pard',&n-1); run;
Strip is equivalent to Trim(left(<text value>)) and has been around for like 20 years. And if you use the CAT functions such as CATS would not even be needed as CATS strips all the values involved.
%let n=3; data example; length x $ 50; x='Sometext'; x=cats(x,repeat('\pard',&n-1) ); run;
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.
Ready to level-up your skills? Choose your own adventure.