Is there a system option or similar that will automatically echo macro invocations to the SAS log? When debugging code, I would like to see in the log every macro invocation, including what parameters were passed.
So that if I submit %Test(x=1) the log will show something like:
MACRO INVOKED: %TEST(x=1)
When calling a macro in open code, this is not an issue, because the macro call is shown in the usual log. But when outer macros call inner macros, the actual call to %inner is not shown by default. I tried turning on MLOGIC, MPRINT, etc, but couldn't find something that would show me the macro call itself. I think what I want is an MINVOCATION option.
Below I fake an MINVOCATION option by adding /parmbuff to macro definitions, but was hoping for a way to see macro calls without mucking with the macro definition.
%macro test(x=0,y=0,debug=0) /parmbuff ;
%if &debug %then %put MINVOCATION: %nrstr(%%)&sysmacroname&syspbuff ;
data _null_ ;
x=&x ;
y=&y ;
put x= y= ;
run ;
%mend test ;
%macro outer(debug=0) /parmbuff ;
%if &debug %then %put MINVOCATION: %nrstr(%%)&sysmacroname&syspbuff ;
%test(x=1,debug=&debug)
%test(x=1,y=2,debug=&debug)
%mend outer ;
options mprint mprintnest ;
%outer(debug=1)
Returns the desired:
908 options mprint mprintnest ;
909 %outer(debug=1)
MINVOCATION: %OUTER(debug=1)
MINVOCATION: %TEST(x=1,debug=1)
MPRINT(OUTER.TEST): data _null_ ;
MPRINT(OUTER.TEST): x=1 ;
MPRINT(OUTER.TEST): y=0 ;
MPRINT(OUTER.TEST): put x= y= ;
MPRINT(OUTER.TEST): run ;
x=1 y=0
MINVOCATION: %TEST(x=1,y=2,debug=1)
MPRINT(OUTER.TEST): data _null_ ;
MPRINT(OUTER.TEST): x=1 ;
MPRINT(OUTER.TEST): y=2 ;
MPRINT(OUTER.TEST): put x= y= ;
MPRINT(OUTER.TEST): run ;
x=1 y=2
I think you may be looking for option mlogic.
Example code:
option mprint mlogic ;
%macro y(blah);
%put &blah;
%mend;
%macro x();
%y(hello);
%put x;
%mend;
%x;
Gives:
MLOGIC(X): Beginning execution.
MLOGIC(Y): Beginning execution.
MLOGIC(Y): Parameter BLAH has value hello
MLOGIC(Y): %PUT &blah
hello
MLOGIC(Y): Ending execution.
MPRINT(X): ;
MLOGIC(X): %PUT x
x
MLOGIC(X): Ending execution.
You can see it tells you when the macro begins execution, which macro is executing, and also the value of any paramters passed in.
UPDATE
Based on your clarifications, this was the closest I could find. Basically you need to setup a libname for 'stored' macros. When you define your macro, add the options / store source to tell it to store the source code for the macro into the stored macro library:
libname mac "e:\temp";
option mstored sasmstore=mac;
%macro blah(something=whatever) / store source;
%put hi;
%mend;
You can later retrieve the source code by using the %copy macro (SAS v9+). This macro has options to write the source to a file rather than the log. You can then read in the file and extract the default parameter values yourself.
%COPY blah / source;
Gives:
%macro blah(something=whatever) / store source;
%put hi;
%mend;
This whitepaper goes into additional details.
It's a lot of extra steps I know but that seems to be a pretty unusual request.
You may be better off rethinking your strategy. For example, a far simpler method might simply be to define your defaults this way:
%macro hasDefaults(x=1,y=2);
%local default_x default_y;
%let default_x = 1;
%let default_y = 2;
%if &x ne &default_x %then %do;
%put The default for x was changed from &default_x to &x.;
%end;
%mend;
This is far from ideal as well, but you'll have to weigh up what will work better for your needs.
If you're willing to update all of your macros, which it sounds like you'd have to do anyways, then what about adding:
%put _local_;
at the top of each? At macro invocation, the only local macro variables defined will be those parameters, right?
%macro mymacro(x=,y=,z=);
%put _local_;
proc print data=sashelp.class;
run;
%mend mymacro;
%mymacro(x=1,y=2);
Gives a log of:
08 %mymacro(x=1,y=2);
MYMACRO X 1
MYMACRO Y 2
MYMACRO Z
NOTE: There were 19 observations read from the data set SASHELP.CLASS.
NOTE: PROCEDURE PRINT used (Total process time):
real time 0.05 seconds
cpu time 0.03 seconds
You could always also put the macro name in there:
%macro mymacro(x=,y=,z=);
%put MACRO INVOKED: &sysmacroname;
%put Parameters:;
%put _local_;
proc print data=sashelp.class;
run;
%mend mymacro;
%mymacro(x=1,y=2);
Though it's returned as part of the %put _local_ so it's probably extraneous.
Related
I am getting a generic 'Statement not valid or out of order' message with the below:
%macro test;
data _null_;
%if %sysfunc(fileexist("C:\report_201809.xlsx")) = 1 %then %do;
rc=%sysfunc(rename("C:\report_201809.xlsx",
"C:\report_201809.xlsx"_old.xlsx",'file'));
%end;
%mend;
%test;
The code below should get you what you need. While you can use %if statements in a data step you generally won't need to. I'm guessing the error is coming from the %sysfunc function around the fileexist and rename functions. %sysfunc allows you to call data step functions outside of a data step so it is not needed here.
%macro test;
data _null_;
if fileexist("C:\file.txt") then do;
rc = rename("C:\file.txt", "C:\file2.txt", 'file');
end;
run;
%mend;
Alternatively, you could use an X Command that allows you to execute Windows commands. You could replace the rename function with the following statement.
x move C:\file.txt C:\file2.txt;
Remove the DATA _NULL_ or proceed per #J_Lard.
Macro arguments used in %sysfunc invoked function calls are implicitly quoted and do not need additional ' or "
%macro test;
%local rc;
%if %sysfunc(fileexist(C:\report_201809.xlsx)) = 1 %then %do;
%let rc = %sysfunc(rename(C:\report_201809.xlsx,C:\report_201809_old.xlsx,file));
%end;
%test;
You original code may have worked (by way of non-obvious side effect) if the filename "C:\report_201809.xlsx"_old.xlsx" (having an extraneous ") was corrected to "C:\report_201809_old.xlsx"
I have searched some info about this error,but it seems none match mine,may someone familiar with this error take a look.
"Code generated by a SAS macro, or submitted with a "submit selected" operation in your editor, can leave off a semicolon inadvertently." it is still abstruse for me to explore in my code by this comment.although I got this error,the outcomes is right.may someone give any advice..thanks!
%let cnt=500;
%let dataset=fund_sellList;
%let sclj='~/task_out_szfh/fundSale/';
%let wjm='sz_fundSale_';
%macro write_dx;
options spool;
data _null_;
cur_date=put(today(),yymmdd10.);
cur_date=compress(cur_date,'-');
cnt=&cnt;
retain i;
set &dataset;
if _n_=1 then i=cnt;
if _n_<=i then do;
abs_filename=&sclj.||&wjm.||cur_date||'.dat';
abs_filename=compress(abs_filename,'');
file anyname filevar=abs_filename encoding='utf8' nobom ls=32767 DLM='|';
put cst_id #;
put '#|' #;
put cust_name #;
put '#|' ;
end;
run;
%mend write_dx;
%write_dx();
and if I am not using macro,there is no error.
data _null_;
options spool;
cur_date=put(today(),yymmdd10.);
cur_date=compress(cur_date,'-');
cnt=&cnt;
retain i;
set &dataset;
if _n_=1 then i=cnt;
if _n_<=i then do;
abs_filename=&sclj.||&wjm.||cur_date||'.dat';
abs_filename=compress(abs_filename,'');
file anyname filevar=abs_filename encoding='utf8' nobom ls=32767 DLM='|';
put cst_id #;
put '#|' #;
put cust_name #;
put '#|' ;
end;
run;
--------------------------------update----------------------------------
I add % to the keyword,but still get the same error
%macro write_dx;
options spool;
data _null_;
cur_date=put(today(),yymmdd10.);
cur_date=compress(cur_date,'-');
cnt=&cnt;
retain i;
set &dataset;
%if _n_=1 %then i=cnt;
%if _n_<=i %then %do;
abs_filename=&sclj.||&wjm.||cur_date||'.dat';
abs_filename=compress(abs_filename,'');
file anyname filevar=abs_filename encoding='utf8' nobom ls=32767 DLM='|';
put cst_id #;
put '#|' #;
put cust_name #;
put '#|' ;
%end;
run;
%mend write_dx;
%write_dx();
Why did you add () to the macro call when the macro is not designed to accept any parameters? If you do that then the () are NOT processed by the macro processor and so are passed along to SAS to interpret. That is the same error message as you would get if you submitted (); by itself.
1 %macro xx ;
2 data _null_;
3 put 'Running data step in macro';
4 run;
5 %mend xx;
6 %xx();
Running data step in macro
NOTE: DATA statement used (Total process time):
real time 0.02 seconds
cpu time 0.00 seconds
6 %xx();
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
7 ****;
8 ();
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
But if you define it with 0 or more parameters.
%macro param();
generated code
%mend ;
%put |%param()|;
The macro processor will use the () and so they are not passed onto SAS to use.
|generated code|
Change
%macro write_dx;
to
%macro write_dx();
When creating a macro, you have to include the () even if no values are passed.
Hi I am trying to rename variables using SAS Macro loop.
%Let t1=12Mth;
%Let t2=20;
%Let t3=30;
%Let t4=40;
%Let t5=50;
%Let t6=60;
%macro Re(time);
%Do I = 1 %to &time.;
data MilkNew;
set Milk;
rename MT&&t&I..Sp=MTSp&&t&I.;
run;
%end;
%mend Re;
%Re(6)
This loop is mean to rename MT...Sp to MTSp.... Eg:MT20SP to MTSp20.
When I run my loop, there was no error but the variable names were not changed in MilkNew at all.
Where does the problem come? Thanks!
If the only purpose of the macro is to rename the variables in the data set, then why read the data with a set statement. Your data set is probably really small so you don't even realize the inefficiency of doing that. Instead use the modify statement in proc datasets to accomplish the same thing, but more efficiently. Here's an alternative macro for you.
%macro renamevar(dsname, time);
%local lib ds i;
%let lib = %sysfunc(coalescec(%scan(&dsname, -2, %str(.)), work));
%let ds = %scan(&dsname, -1, %str(.));
proc datasets lib=&lib nolist;
modify &ds;
rename
%do i = 1 %to &time;
mt&&t&i..Sp=MTSp&&t&i.
%end;
;
quit;
%mend;
%renamevar(milk, 6);
Here's the log after the macro call:
NOTE: Renaming variable mt12MthSp to MTSp12Mth.
NOTE: Renaming variable mt20Sp to MTSp20.
NOTE: Renaming variable mt30Sp to MTSp30.
NOTE: Renaming variable mt40Sp to MTSp40.
NOTE: Renaming variable mt50Sp to MTSp50.
NOTE: Renaming variable mt60Sp to MTSp60.
NOTE: MODIFY was successful for WORK.MILK.DATA.
NOTE: PROCEDURE DATASETS used (Total process time):
real time 0.00 seconds
cpu time 0.01 seconds
You should move the loop so that it only generates just the RENAME statement (or even just the old=new name pairs). What is happening now is that you keep overwriting MilkNew so only the last RENAME has any effect.
%macro Re(time);
data MilkNew;
set Milk;
%do I = 1 %to &time.;
rename MT&&t&I..Sp=MTSp&&t&I.;
%end;
run;
%mend Re;
%Re(6)
You should have seen the last variable name in the loop (so the 6th) changed. That's because you repeated the same data step with the same source dataset but a different destination - so each time you 'forgot' the changes made in the earlier step.
So, this would've worked, though I'll get in a minute to why this isn't a good way to do this.
%Let t1=12Mth;
%Let t2=20;
%Let t3=30;
%Let t4=40;
%Let t5=50;
%Let t6=60;
%macro Re(time);
%Do I = 1 %to &time.;
data Milk;
set Milk;
rename MT&&t&I..Sp=MTSp&&t&I.;
run;
%end;
%mend Re;
data milk;
input
MT12mthSP
MT20SP
MT30SP
MT40SP
MT50SP
MT60SP
;
datalines;
12 20 30 40 50 60
;;;;
run;
%Re(6)
Here I had it make all changes to Milk and save them back in that dataset. If you want to preserve Milk then first make Milk_New then have that in both set and data statements.
Second, you should not do a new data step for each change. Macros don't have to have a data step in them; they can be run inside the datastep.
So for example:
%macro Re(time);
%Do I = 1 %to &time.;
rename MT&&t&I..Sp=MTSp&&t&I.;
%end;
%mend Re;
data milk_new;
set milk;
%Re(6);
run;
Even better would be generating this list outside of a macro entirely - look up "generating code SAS" for suggestions on that.
If you didn't see any renames at all, you also may have an issue where a label is present on the column(s). That won't affect your usage of the variable name, but it will make it confusing. Use
label _all_;
Or include a label-clearing statement (label <varname>; where you pop in the same variable name as the original variable name before rename) inside your macro loop to fix that.
I'm searching for a while an equivalent of the for in loop (like in Python or in R) in SAS 9.3 macro language. The DO loop seem's to be the solution but did't work exactly as I want.
I founded a way to do it in a data step with a DO loop but it don't work with the macro language.
For example, in a data step, this code is working :
DATA _NULL_;
DO i = 1,3,5,9;
PUT i;
END;
RUN;
And then the log prompt as expected :
1
3
5
9
When I try to do the same with an %DO loop in a Macro, I have an error.
%MACRO test();
%DO i = 1,2,4,9 ;
%PUT i = &i;
%END;
%MEND;
%test();
The log promp these messages :
ERROR: Expected %TO not found in %DO statement.
ERROR: A dummy macro will be compiled
I'm quite new in SAS and stackoverflow so I hope my question is no too stupid. It's so simple to do this in Python and R then it must have a simple way to do it in SAS.
Thank's for help - J. Muller
The closest I've ever come across to this pattern in SAS macro language is this:
%MACRO test();
%let j=1;
%let vals=1 2 4 9;
%do %while(%scan(&vals,&j) ne );
%let i=%scan(&vals, &j);
%put &i;
%let j=%eval(&j+1);
%end;
%MEND;
%test();
(Warning: untested, as I no longer have a SAS installation I can test this out on.)
You can certainly get around it this way:
options mindelimiter=,;
options minoperator;
%MACRO test();
%DO i = 1 %to 9 ;
%if &i in (1,2,4,9) %then %do;
%PUT i = &i;
%END;
%end;
%MEND;
%test();
However, I think you can usually avoid this sort of call by executing your macro multiple times rather than attempting to control the loop inside the macro. For example, imagine a dataset and a macro:
data have;
input x;
datalines;
1
2
4
9
;;;;
run;
%macro test(x);
%put &x;
%mend test;
Now you want to call %test() once for each value in that list. Okay, easy to do.
proc sql;
select cats('%test(',x,')') into :testcall separated by ' ' from have;
quit;
&testcall;
That works just as well as your %do in loop, except it's data driven, meaning if you want to change the calls you just change the dataset (or if your data changes, the call automatically changes!). In general, SAS is more effective when designed as data driven programming rather than as entirely written code.
%macro name_modal();
/*Create macro variables which contain the modalities of variables*/
%do i=1 %to &num_Var;
data _null_;
set &lib..Table_variable_modal_&i.;
call symputx('num_Mod'||compress(put(&i,4.)),_N_,"G");
call symputx('table'||compress(put(&i,4.))||'modal'||compress(put(_N_,4.)),compress(&&name_Var&i.),"G");
run;
%end;
/*Display modalities by variable*/
%do i=1 %to &num_Var;
%put &&name_Var&i. has &&num_Mod&i. modalities ;
%do j=1 %to &&num_Mod&i.;
%put %nrstr(&&tableb&i.modal&j.);
%end;
%end;
%mend name_modal;
%name_modal();
I hope the code is self-documenting.
I'll explain the problems here.
Everything is working fine until I pass to the second of the program which serves to display the modalities by variables.
For example when the name of modalities being stocked in the macro variables are like
$100% BLO,
100% COLOR,
AVON & RAGOBERT,
BALLANTINE'S,
L'OREAL,
AT&T,
U-V-A
etc
I fail to use %put properly.
I've tried using %bquote and %nrstr, but the problem persists.
So far, the only solution I can see is to modify the name of the modalities but since the names come from a client, I don't have the possibility to make amendment on the data.
Thank you
After trying a few times, I find %superq could solve this problem. Handling special characters in macro is troublesome. This page provides some useful tips on macro quoting.
I simplified your code here to the following
UPDATE: make it double-loop case.
data test;
input name ~ & $15.;
datalines;
100% BLO
100% COLOR
AVON & RAGOBERT
BALLANTINE'S
L'OREAL
AT&T
U-V-A
;
run;
%macro name_modal();
/*Create macro variables which contain the modalities of variables*/
%do i=1 %to 4;
data _null_;
set test;
call symputx('num_Mod1',_N_,"G");
call symputx('tableb'||compress(put(&i,4.))||'modal'||compress(put(_N_,4.)),name,"G");
run;
%end;
%do i=1 %to 4;
%do j=1 %to 7;
%put %superq(tableb&i.modal&j);
%end;
%end;
%mend name_modal;
%name_modal();
The result will display correctly.
Note that it is %superq(tableb&i.modal&j) not %superq(&&tableb&i.modal&j) as superq accepts macro variable name without extra ampersand.
It is hard to answer the question for all your sample data because I can't recreate your code (missing global macros).
[UPDATE] It is more difficult to get macros to resolve which include characters that need to be masked. Post some code that can be executed, and more help can be provided.
Here is a good link for macro quoting.
This has a really good diagram detailing which macro masking functions to use for different situations.
%Str will work even if a % or & are included in the string, as long as they are followed by a space. %NRSTR if either could be interpreted as a macro variable or macro call.
You can precede a single quote (') or single double quote (") with a percent sign (%), or use BQUOTE or NRBQUOTE (if your character string includes & or %).
Here is a start [UPDATE]:
%Macro Test(var=);
%Put &var;
%Mend test;
%Test(Var=%str($100% BLO)) ;
%Test(Var=%str(100% COLOR)) ;
%Test(Var=%nrstr(100 %COLOR)) ;
%Test(Var=%str(AVON & RAGOBERT)) ;
%Test(Var=%nrstr(AVON &RAGOBERT)) ;
%Test(Var=%str(BALLANTINE%'S)) ;
%Test(Var=%bquote(BALLANTINE'S)) ;
%Test(Var=%str(L%'OREAL)) ;
%Test(Var=%bquote(L'OREAL)) ;
%Test(Var=%nrstr(AT&T)) ;
%Test(Var=%str(U-V-A)) ;