Resolving Macro Variables in a Server Session - macros

I have this code connecting to my server session in BASE SAS, but I cannot use this %LET command within the server session, is this because my macro is created locally and can only resolve locally? Do I need to use another %LET Statement in the RSUBMIT command, or should I use a command like %SYSLPUT?
EDIT: This code is still not resolving.
%LET yymm = 1606 ;
%PUT &yymm;
/* Identify the Month that you want the files to be named */
%LET mon = June ;
%PUT &mon;
/* Specifying the Recipients of the CRA Error files */
%LET recip = MYEMAILs
%MACRO CFPB(date , mon);
%LET remhost=MYSERVER MYPORT;
signon remhost user=&sysuserid. password= _prompt_;
rsubmit;
%syslput date=&date. ;
%syslput mon=&mon. ;
LIBNAME &mon "N4.BANKCRRG.CNSRLOAN.MTRO&date." disp=shr;
data &mon;
set &mon..file (keep= base_cis_id acct_num acct_type);
run;
Proc DOWNLOAD data = &mon.;
run;
endrsubmit;
signoff;
** INSERT SAS LOCAL STATEMENTS ***
%MEND;
%CFPB(&YYMM.,&MON.);
My Macro variables are STILL not resolving in the Remote Session. I, for the life of me, cannot see the error. I created my macro variables in the %LET statements, then used %SYSLPUT to create new macro variables inside my macro, inside my server session, and then when I called my macro I used the macros I created in the %LET statements to resolve to the ones I called, and furthermore resolve with %SYSLPUT.

You can use %syslput outside of the rsubmit block provided that you've already signed onto remote the server, or autosignon is set to yes. For example:
%let remhost=MYSERVER MYPORT;
signon remhost user=_prompt_ password=_prompt_;
%syslput yymm = 1066 / remote=rsession1;
rsubmit remote=rsession1;
LIBNAME june "LIBNAME" disp=shr;
data june;
set june.file (keep=base_cis_id acct_num acct_type);
run;
Proc DOWNLOAD data = june;
run;
endrsubmit;
signoff;**
Equivalently, you can use a %let statement within the rsubmit block. Either will generate a global macro variable on the remote server. I personally prefer %syslput and %sysrput to help visually keep track of which macro variables are in my session or the remote session. rsubmit and macro variables can quickly turn into Macroception.

Related

Output SAS macro to file or email attachment

I have some problems with getting basic file information send to me in an email.
From various scripts on the internet I have ended up with the following macro which works for outputting the information I want.
%macro list_files(dir);
%local filrf rc did memcnt name i;
%let rc=%sysfunc(filename(filrf,&dir));
%let did=%sysfunc(dopen(&filrf));
%let ymd = %sysfunc(putn(%sysfunc(today()).,yymmdd6.));
%if &did eq 0 %then %do;
%put Directory &dir cannot be open or does not exist;
%return;
%end;
%do i = 1 %to %sysfunc(dnum(&did));
%let name=%qsysfunc(dread(&did,&i));
%if %qscan(%qscan(&name,1,_,b),1,.) eq &ymd. %then %do;
%let rc=%sysfunc(filename(fref,&dir\&name));
%let fid=%sysfunc(fopen(&fref));
%let CreatedDT=%qsysfunc(finfo(&fid,Create Time));
%let ModifiedDT=%qsysfunc(finfo(&fid,Last Modified));
%put &dir\&name,&CreatedDT,&ModifiedDT;
%let fid=%sysfunc(fclose(&fid));
%let rc=%sysfunc(filename(fref));
%end;
%end;
%let rc=%sysfunc(dclose(&did));
%let rc=%sysfunc(filename(filrf));
%mend list_files;
%list_files(C:\logs\);
Notice: The macro does expect a certain file name format in order to work. If I for example have the following log files:
ProgramA_210215.log
ProgramA_210214.log
ProgramA_210213.log
ProgramB_210214.log
ProgramB_210213.log
ProgramC_210215.log
ProgramC_210214.log
ProgramC_210213.log
... and I run this on 2021-02-15, then I only get:
ProgramA_210215.log
ProgramC_210215.log
... which is perfect for my needs.
My problem is, that I can't figure out how to either send the output to me as an email or dump the output in a file, which I can then attach to an email.
Notice 2: I initially wrote the macro as "normal data calls"(?) and everything worked except for the CreateDT and ModifiedDT, which were static for all files. The macro version at least works and shows the correct (and different) CreateDT and ModifiedDT.
What I am trying to achieve in the end is, to send me an email with an attachment of todays log files and their creation datetime and last modified datetime.
I guess I'm just missing a simple step, but can't figure out which :(.
Calling all of those functions using %SYSFUNC() is just making it harder to to do what you want. Just call the functions in a data step instead so that you have an actual dataset with the files. Then you can use any of multiple methods to direct the list where ever you want, including sending an email.
I am pretty sure that FINFO() always returns character strings so make sure that the target variables are long enough for the value returned. Note that the information available from FINFO() depends on the operating system where SAS is running. So make sure the names you use are appropriate for your SAS session.
%let dir=c:\logs\;
data file_list;
length filrf fref $8 name $256 ymd $6 CreatedDT ModifiedDT $40;
ymd = put(today(),yymmdd6.);
rc=filename(filrf,"&dir");
did=dopen(filrf);
if did=0 then do;
put "Directory &dir cannot be open or does not exist.";
stop;
end;
do i = 1 to dnum(did);
name=dread(did,i);
if scan(scan(name,1,'_','b'),1,'.') eq ymd then do;
rc=filename(fref,"&dir\"||name);
fid=fopen(fref);
CreatedDT=finfo(fid,'Create Time');
ModifiedDT=finfo(fid,'Last Modified');
output;
fid=fclose(fid);
rc=filename(fref);
end;
end;
rc=dclose(did);
rc=filename(filrf);
keep name CreatedDT ModifiedDT ;
run;

SAS macro loop to read csv files with proc import

I have a directory of csv files, each with names that begin with the letter m and end with a number. There are twelve files - m6 to m17.
I'd like to read them in and process them as separate data sets. I've written two macros attempting to do so. Macro1 works. Macro2 breaks. I would prefer Macro2 if I can get it to work, to avoid unnecessary bits like my creation of %rawfiles, invocation of %sysfunc, etc.
Macro 1:
%let rawcsv = C:\ALL\dat\;
%let rawfiles = m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17;
%macro1;
%do i = 1 %to %sysfunc(countw(&rawfile));
%let rawfile = %scan(&rawfiles, &i);
proc import datafile="&&rawcsv.&&rawfile.csv"
out=&rawfile replace
dbms=csv;
guessingrows=500;
run;
%end;
%mend;
%macro1;
Macro 2:
%let rawcsv = C:\ALL\dat\;
%macro macro2(first=6, last=19);
%do i=&first. %to &last. %by 1;
proc import datafile="&&rawcsv..m&&i.csv"
out=m&i replace
dbms=csv;
guessingrows=500;
run;
%end;
%mend;
%macro2;
%macro2 is my bad imitation of this solution. It returns the following errors:
MPRINT(MACRO2): proc import datafile="C:\ALL\dat\m.6.csv" out=m.6 replace
dbms=csv;
MPRINT(MACRO2): ADLM;
MPRINT(MACRO2): guessingrows=500;
MPRINT(MACRO2): run;
ERROR: Library name is not assigned. /*repeats this error 14 times, once per file*/
Two questions:
What am I missing in %macro2?
Do you see a better solution that I am not using? The files are structured differently and not stackable, just a heads up.
From your log we can see a period is being inserted into the output dataset name. Just remove that extra period in your macro definition.
MPRINT(MACRO2): proc import datafile="C:\ALL\dat\m.6.csv" out=m.6 replace dbms=csv;
The extra & in the code is probably confusing you. When the macro processor sees two & it converts them to one and then reprocesses the string to further resolve the resulting macro variable references.
The period after a macro variable name is not required when the macro processor can tell that the name has ended. But the periods are needed in some places.
One place in your code is where it is required to make sure the macro processor knows where the name ends (the macro variable is named readcsv not readcsvm ). Another is where you want to place an actual period after the value of a macro variable. You will need to place two periods there since the first will be used by the macro processor when it evaluates the macro variable value.
In this version of macro2 I have removed the periods after the macro variable names in the places where they are not required just to emphasize the places where the period is required.
%let rawcsv = C:\ALL\dat\;
%macro macro2(first, last);
%local i ;
%do i=&first %to &last ;
proc import dbms=csv
datafile="&rawcsv.m&i..csv"
out=m&i replace
;
guessingrows=500;
run;
%end;
%mend macro2;
%macro2(first=6, last=19)
Small typo here, you need to use an & in front of LAST not the %.
%do i=&first. %to %last. %by 1;
Should be:
%do i=&first. %to &last. %by 1;
Unless you're using a separate macro called last to determine your end of the loop. But in that case you likely wouldn't also have a parameter called last.
If you're looking for alternate options I usually recommend reading all at once using a data step or CALL EXECUTE instead of macro loops as they're infinitely easier to debug in my opinion.
https://communities.sas.com/t5/SAS-Communities-Library/How-do-I-write-a-macro-to-import-multiple-text-files-that-have/ta-p/223627

SAS macros to save and list all macros in a session

I would like to save all the macros permanently defined as part of an exercise and list all the macros. I have 6 macros which I want to save and retrieve in this session:
%macro one(a,b,c)
%macro gchart(dseti,Weight,Gender)
%macro plot(dsetin,height,weight)
%macro one(a,b,c,strtpt,endpt)
%macro test
%macro name(dsetin,year,revenue)
%macro import_myfile(i=)
I have the code below and the error message follows it.
options mstored sasmstore=macross;
libname mjstore "C:\Users\komal\Desktop\Advanced SAS";
proc catalog cat=mjstore.macross;
contents;
title "Default Storage of SAS Macros";
quit
Error: Catalog "MJSTORE.MACROSS" not found
Please let me know your advice on how to solve it. Thank you for your time.
The default catalog is WORK.SASMACR
%macro one(a,b,c); %mend;
%macro gchart(dseti,Weight,Gender); %mend;
%macro plot(dsetin,height,weight); %mend;
%macro one(a,b,c,strtpt,endpt); %mend;
%macro test; %mend;
%macro name(dsetin,year,revenue); %mend;
%macro import_myfile(i=); %mend;
proc catalog cat=work.SASMACR;
contents;
title "Default Storage of SAS Macros";
quit;
The best way to find out which macros were defined during your program is to not use stored macro catalog options. Instead just let SAS default to storing them a WORK catalog. You can then use PROC CATALOG to see what macros have been compiled. Normally that will be WORK.SASMACR catalog, but if you are using alternative ways of running SAS (Enterprise Guide, SAS/Studio, stored process servers etc) it might be WORK.SASMACR1 catalog instead.
proc print data= sashelp.vcatalg width=min;
where libname='WORK' and memname='SASMACR'
and memtype='CATALOG' and objtype='MACRO'
;
var objname modified objdesc ;
run;
If you are using autocall macros newer versions of SAS will store the filename in the OBJDESC field in the metadata. Otherwise here is a link to a macro that will generate a list of the compiled macros and try to match them to a corresponding source file from the autocall library or other directories you might provide it. https://github.com/sasutils/macros/blob/master/maclist.sas

Get Data from SAS table to SAS Macro Variable

I want to create a macro that reads a value from a SAS table and stores that value in a Global variable.
The purpose is to use this value in SAS DIS JOBs.
I have tested following:
%GLOBAL &myMVar.;
%Macro Get_data(myDataset,myLine,myColumn,myMVar);
data _null_;
set &myDataset.;
if _N_ = &myLine.
then do;
call symputx(symget('myMVar'),&myColumn.);
end;
run;
%MEND Get_data;
*Calling a Macro program;
LIBNAME dtvault BASE "/sasdata/DataVault";
%Get_data(dtvault.codes,1,cod,myMVar);
%put &myMVar;
run;
**I do not have any results. The log: **
*Calling a Macro program;
38 LIBNAME dtvault BASE "/sasdata/DataVault";
NOTE: Libref DTVAULT was successfully assigned as follows:
Engine: BASE
Physical Name: /sasdata/DataVault
39 %Get_data(dtvault.codes,1,cod,myMVar);
NOTE: There were 2 observations read from the data set DTVAULT.CODES.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
40 %put &myMVar;
41 run;
42
43 GOPTIONS NOACCESSIBLE;
44 %LET _CLIENTTASKLABEL=;
45 %LET _CLIENTPROCESSFLOWNAME=;
46 %LET _CLIENTPROJECTPATH=;
2 The SAS System 10:06 Wednesday, October 25, 2017
47 %LET _CLIENTPROJECTPATHHOST=;
48 %LET _CLIENTPROJECTNAME=;
49 %LET _SASPROGRAMFILE=;
50 %LET _SASPROGRAMFILEHOST=;
51
52 ;*';*";*/;quit;run;
53 ODS _ALL_ CLOSE;
54
55
56 QUIT; RUN;
57
Thanks
The way you are initializing the global macro variable if what you want is a global macro variable called myVar. The %global statement does not require the ampersand.
Also, if you're going to instantiate that macro variable outisde of your macro definition anyway, then there's no need to specify it as a parameter of you macro definition:
%GLOBAL myMVar.;
%Macro Get_data(myDataset,myLine,myColumn);
data _null_;
set &myDataset.;
if _N_ = &myLine.
then do;
call symputx('myMVar',&myColumn.);
end;
run;
%MEND Get_data;
*Calling a Macro program;
LIBNAME dtvault BASE "/sasdata/DataVault";
%Get_data(dtvault.codes,1,cod);
%put &myMVar;
Note that the symputx call routine allows for specifying the symbol table in which you want your variable created. You can then do away with the %global statement:
%Macro Get_data(myDataset,myLine,myColumn);
data _null_;
set &myDataset.;
if _N_ = &myLine.
then do;
call symputx('myMVar',&myColumn.,'G');
end;
run;
%MEND Get_data;
*Calling a Macro program;
LIBNAME dtvault BASE "/sasdata/DataVault";
%Get_data(dtvault.codes,1,cod);
%put &myMVar;
EDIT: in answer to your comment, I used the above code on a test dataset:
libname dtvault (work);
data dtvault.codes;
cod='test';
run;
%Macro Get_data(myDataset,myLine,myColumn);
data _null_;
set &myDataset.;
if _N_ = &myLine.
then do;
call symputx('myMVar',&myColumn.,'G');
end;
run;
%MEND Get_data;
%Get_data(dtvault.codes,1,cod);
%put &myMVar;
The log produces this output:
42 %Get_data(dtvault.codes,1,cod);
NOTE: There were 1 observations read from the data set DTVAULT.CODES.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
2 The SAS System 10:04 Wednesday, October 25, 2017
cpu time 0.00 seconds
43 %put &myMVar;
test
If the %put statement, on your data, produces nothing; could it be that the value for of cod on line 1 of your dataset is blank?
I see two main mistakes that will make your program not work.
The & is not part of the macro variable name. It is the trigger that lets the macro processor know that you want it to replace the macro variable reference with the value of the macro variable referenced. So if you want to define a macro variable named mymvar your %global statement needs to look like this.
%global mymvar;
You are also getting impacted by the scoping of macro variables. If your program starts with these lines
%global mymvar;
%macro get_data(mydataset,myline,mycolumn,mymvar);
then you have defined a LOCAL macro variable mymvar that will hide the global macro variable mymvar while the macro get_data is running. So if you set mymvar to some value it will disappear when the macro finishes running.
Since you want your macro to generate a macro variable that will be available after the macro finishes running the you will want to make sure that the macro variable that it creates either already exists or is created in the GLOBAL macro space.
For added flexibility you might want to pass the NAME of the macro variable that you want the macro to use as a parameter to the macro. You could then use the %SYMEXIST() function to decide if you need to create a global macro variable or not. Notice that now we do want the & in the %global statement since we want to create a macro variable whose name is the value of another macro variable.
%macro get_data(dataset,line,column,mvar);
%if not %symexist(&mvar) %then %global &mvar ;
data _null_;
set &dataset ;
if _n_=&line then do;
call symputx("&mvar",&column);
stop;
end;
run;
%mend get_data;
Now when you call the macro you pass in the values that you want for the parameters. Note that one nice feature of SAS macros is that even for parameters that are defined as positional arguments you can use named parameters in the macro call.
%get_data(dataset=myDataset,line=1,column=myVar,mvar=myMvar)
%put &=myMvar;

SAS macro do loop-- import multiple flat files

I have a list of 17 flat files that I'm trying to import into different data sets. All of the files have the same data step, so I'm trying to write a do while loop to import all the files.
I've been trying to adapt some code from here without success:
http://www.sas.com/offices/europe/uk/support/sas-hints-tips/tips-enterprise-csv.html
http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a000543785.htm
I'm getting an error that says the %do statement is not valid in open code. Here is my code:
% let path1 = 'c:\path1'
% let path2 = 'c:\path2'
...
% let pathN = 'c:\pathN'
%let n=1;
%do %while (&n <= 17);
%let pathin = &path&n;
data retention&n;
infile &pathin;
<data step-->
run;
%let n=%eval(&n+1);
%end;
I've tested the data step outside of the do-while loop and it works fine for 1 file at a time using the %let pathin = &path&n code. The code still writes the datafile for the 1st data set; but, I need to be able to loop through all the files and can't figure out how. Sorry if this is a novice question; I'm just learning SAS.
Thanks,
-Alex
Welcome to SAS programming! The error message you got is a clue. "Open code" refers to statements that are executed directly by the SAS system. A %do statment is part of the SAS Macro Language, not "normal" SAS. A %let statement can be executed in open code and is use to create a macro variable (distinct from a compiled macro).
Compiled SAS macros are created by code that appears between the %macro and %mend statements. For example, using your code:
%macro run_me;
%let n=1;
%do %while (&n <= 17);
%let pathin = &path&n;
data retention&n;
infile &pathin;
<data step-->
run;
%let n=%eval(&n+1);
%end;
%mend;
But all that does is define/compile the macro. To execute it, you must issue the statement %run_me;. Note that the name run_me was just a name I made up.
For more info, please consult the SAS Macro Reference, especially the introductory section.
To convert your progma to macro, turn your macro variables declared by LET statement into macro arguments:
%macro readfile(n, pathin);
data retention&n;
infile &pathin;
<data step-->
run;
%mend;
A data step to repetitively call your macro.
Here the data included in CARDS statement, but also can be read from some table via SET statement.
The macro call is performed via call execute routine.
data _null_;
length path $200 stmt $250;
input path;
stmt = catt('%readfile(', putn(_N_, 3. -L), path, ')');
call execute(stmt);
cards;
c:\file1.txt
c:\file2.txt
c:\file3.txt
;
run;