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;
Related
I can't find the solution for this simple problem: I want to add a colum/variable in my data set. This variable will always have the same value, stored in the macro variable &value. And I am in a macro so I don't know if it change anything... This is the step before merging 2 data step.
So far, here's what I have:
%do i=1 %to 10;
data &new_data_set;
set &new_data_set;
Nom_controle=&Nom_Controle;
Partenaire=&Partenaire;
run;
%end;
I'm trying to add to my data-set (which was previously defined in the macro as &new_data_set) a column/variable named "Nom_Controle" which always takes the value stored in the macro variable &Nom_controle (previously defined too). I'm also trying to add a second column/variable named "Partenaire" which always takes the value stored in the macro variable &Partenaire (previously defined too).
Of course, as I'm posting here, my code doesn't work. Can you help me?
EDIT: after some ask me to in order to help me, here is the macro this code is from (the full thing):
%macro presence_mouvement (data_set_detail_mouvement, data_set_mouvement);
%if %sysfunc(exist(&data_set_mouvement)) AND %sysfunc(exist(&data_set_detail_mouvement)) %then %do; *Check if my data set actually exist;
%let suffix=_2;
%let new_data_set=&data_set_detail_mouvement&suffix; *Create the name of the new data set I'm going to save the result of the next proc sql in;
proc SQL noprint; *Proc to look for errors in a previous data set and print it in the new data set;
create table &new_data_set as
insert into &new_data_set
SELECT num_mouvement
FROM &data_set_detail_mouvement
EXCEPT
SELECT num_mouvement
FROM &data_set_mouvement);
%let Nom_controle=Presence_mouvement; *Creation of a new variable;
%if %sysfunc(length(&data_set_detail_mouvement))=29 %then %do; *Creation of a second variable (value conditional to the size of a previous variable);
%let Partenaire=%sysfunc(substr(&data_set_detail_mouvement, 9, 3)); %end;
%else %if %sysfunc(length(&data_set_detail_mouvement))=30 %then %do;
%let Partenaire=%sysfunc(substr(&data_set_detail_mouvement, 9, 4)); %end;
%else %do;
%let Partenaire=%sysfunc(substr(&data_set_detail_mouvement, 9, 6)); %end;
%do i=1 %to 10;
data &new_data_set;
set &new_data_set;
Nom_controle=&Nom_Controle;
Partenaire=&Partenaire;
run;
%end;
%end;*End of the actions to do in case the two data set in parameters exist;
%else %do; *Actions to do in case the two data set in parameters don't exist;
data _null_;
file print;
put #3 #10 "At least one of the data set does not exist";
run;
%end;
*This macro is aiming at pointing error in a previous data set, print them in a new data set and add two new variables/columns to this new data set (indicating their origin). The next set is going to be to merge this new data set to another one;
%mend presence_mouvement;
%presence_mouvement (sasuser.bgpi__detail_mouvement, sasuser.bgpi__mouvement);
I also wanted to say that I tested the rest of the macro before trying to add new variable so the rest of the macro shouldn't have any problem. But who knows...
Run a single data step, setting the new variables to the values setup in macro variables. If the values setup are character in nature the data step variables need to resolve those macro variables within double quotes.
data &new_data_set;
set &new_data_set;
retain
Nom_controle "&Nom_Controle"
Partenaire "&Partenaire"
;
* also works;
* Nom_controle = "&Nom_Controle";
* Partenaire = "&Partenaire";
run;
Note: The new data set variables lengths will be set to the length of the values stored in the macro variables.
A data set is a rectangle of values. It will have a certain number of rows and columns of numeric and / or character types. The SET statement in a DATA step reads one row of the table's column values into the running program data vector -- which are essentially the variables in the DATA step. A DATA step loops automatically and halts automatically on various conditions, such as the last row of a SET table being read.
I don't know why you have a macro loop %DO I=1 %TO 10. I might speculate you think you need to do this in order to 'update' 10 rows in &new_data_set.
What is it really doing ? Running the same code 10 times! Without macro the actual code run is akin to the following
data x; do r = 1 to 10; output; end; run; %* an original new_data_set;
data x; set x; z=1; run;
data x; set x; z=1; run;
data x; set x; z=1; run;
...
One additional concern is the code such as
%if %sysfunc(length(&data_set_detail_mouvement))=29 %then %do; *Creation of a second variable (value conditional to the size of a previous variable);
%let Partenaire=%sysfunc(substr(&data_set_detail_mouvement, 9, 3)); %end;
It appears you are grabbing the first 3, 4, or 6th letters of the data set name from a fully qualified libname.dataset where libname is presumed to be sasuser. A safer and more robust version could be
%let syslast = &data_set_detail_mouvement;
%let libpart = %scan(&syslast,1,.);
%let datapart = %scan(&syslast,2,.);
… extract 3, 4, or 6 preface of datapart …
%* this might be helpful;
%let Partenaire = %scan(&datapart,1,_);
Nothing seems to be wrong with the part of the code that creates the variables. There might be other issues that are difficult to tell from this extract without seeing the entire code or log. For example, if Nom_controle and Partenaire are meant to be character variables because the macro variables are characters but without quotes then there will definitely be errors. You should use symbolgen and mprint options and then post the log to help solve the problem.
I have a SAS dataset with values
yyyymm
201605
201606
201607
201608
201609
I am trying to find a way to pass these values one at a time to macro such that
do while dataset still has value
%macro passdata(yyyymm);
end
How can I do this in SAS. Can someone please help with a sample/code snippet.
As mentioned by the prior comment, a way to pass parameters is through call execute routine. Note that this must be done in datastep environment. The lines are read from the set you input.
You can input multiple variables. Just add more variables in '||' separators. Note that the variables may have a lot of whitespaces in them. (==Do comparisons with care.)
Here is a small sample code. Tested.
data start_data;
input date_var ;
datalines;
201605
201606
201607
201608
201609
;
run;
%macro Do_stuff(input_var);
%put 'Line generates value ' &input_var;
%mend do_stuff;
data _null_;
set start_data;
call execute('%do_stuff('||date_var||')' );
run;
Try this example and try modifying to meet your needs... From the "source" dataset we can use call symput() to assign a macro token to each observation (differentiated by the SAS automatic dataset variable n so My_token1, My_token2, etc.) Once you have a set of macro variables defined, just loop through them! This program will print all the individual records from source to the SAS log:
data source;
do var=1000 to 1010;
output;
end;
run;
data _null_;
set source;
call symput(compress("My_token"||_n_),var);
run;
%put &my_token1 &my_token4;
%Macro neat;
%do this=1 %to 11;
*Check the log.;
%put &&My_token&this;
%end;
%mend;
%neat;
I have problem saving a dataset using macro variables to a desired directory.
Basically, I want to save the dataset "_est" to library "sret" according to the values of &var and &age. I wrote the following code:
%let var=k;
%let age=2;
...
...
data sret.est_&var&age._b3;
set _est;
run;
What I want is a dataset named as "est_k2_b3.sas7bdat" in "sret". But what the code gives me is a dataset "est_k2.sas7bdat" saved in the folder I want and another dataset "_b3" in the working library. Both datasets are identical. I'm quite puzzled how to solve this.
As itzy pointed out you have a space after "2" that splits your dataset name in two.
I can replicate the issue only defining the macro variable age with a call symput:
data _null_;
age='2 ';
call symput('age',age);
run;
If this is the case you can solve it by removing the space in the data step with a strip(), using a call symputx() (to be used with numbers) or re-declaring your variable after the data step with a %let, that automatically removes spaces:
%let age= &age.;
Had a very similar problem. Somehow a space is added until you use strip(). Here's the example below.
data test;
input numdays;
datalines;
31
;
%macro monthly(months);
%let count=%sysfunc(countw(&months.));
%do i=1 %to &count.;
%let value=%qscan(&months.,&i,%str(,));
%let month=%sysfunc(strip(&value.));
%put &value.;
%put &month.;
data value_&value.;
set test;
run;
data month_&month.;
set test;
run;
%end;
%mend;
%monthly(%str(oct,jan));
I am trying to add prompt for missing or nonmissing options. This code doesn't work ,need to fix that. rec_And_issues is a new table that I created in the report. Need to pick rec_and_issues.SFVFDBK_FEEDBACK_COMMENTS is missing or not.
%macro missing_or_nonmissing;
%if "&sel_issue" eq "Missing" %then %do;
DATA rec_and_issues;
set rec_and_issues;
rec_and_issues.SFVFDBK_FEEDBACK_COMMENTS is null;
run;
%end;
%else %if "&sel_issue" eq "NonMissing" %then %do;
DATA rec_and_issues;
set rec_and_issues;
rec_and_issues.SFVFDBK_FEEDBACK_COMMENTS is not null;
run;
%end;
%mend missing_or_nonmissing;
You shouldn't put the data step inside the macro. How exactly you decide to do this depends on style - I like to not include the WHERE in the macro if it's easily avoided, as this makes it easier for someone to read and understand your code - but any variation on this should be fine. Only put parts of the datastep in the macro that actually vary.
%macro missing_or_nonmissing(sel=);
%let not = %sysfunc(ifc(&sel=NonMissing,not,));
SVFDBK_FEEDBACK_COMMENTS is ¬. null
%mend missing_or_nonmissing;
DATA rec_and_issues;
set rec_and_issues;
where %missing_or_nonmissing(sel=&sel_issue) ;
run;
No reason to do anything beyond that. Further, if you are using something as a parameter, use it as a parameter. Global variables shouldn't be used inside a macro in most cases, and definitely not in this case where it's clearly a parameter to the macro.
Further, you probably don't need to do this at all. If this is solely filtering the dataset, you almost certainly can do this whenever you actually use the dataset (or when it was created, depending on how it was created). For example, if your next step is a PROC SORT, as it often is, you should just do this in the PROC SORT - and this macro lets you do that. (This is why I like to leave WHERE out of it - since where syntax differs in data set options).
proc sort data=rec_and_issues(where=(%missing_or_nonmissing(sel=&sel_issue.)));
by idvar;
run;
Finally, if you're the one creating the prompt, I recommend having the underlying values be 1/0 not text. That way you don't have to worry about upcase/etc., and you can use them a bit more easily (since 1 is 'true' and 0 is 'false').
Your data-steps were not in the correct syntax. You've used some form of sql two-level variable naming that doesn't work in a SAS data-step. A WHERE statement would work. I've rewritten the macro:
%macro missing_or_nonmissing;
%if "&sel_issue" eq "Missing" %then %do;
DATA rec_and_issues;
set rec_and_issues;
where SVFDBK_FEEDBACK_COMMENTS is null;
run;
%end;
%else %if "&sel_issue" eq "NonMissing" %then %do;
DATA rec_and_issues;
set rec_and_issues;
where SFVFDBK_FEEDBACK_COMMENTS is not null;
run;
%end;
%mend missing_or_nonmissing;
Although you could make the macro even shorter, as only the operator changes:
%macro missing_or_nonmissing;
%if "&sel_issue" eq "Missing" %then %let op=;
%else %if "&sel_issue" eq "NonMissing" %then %let op=not;
%else %goto exit;
DATA rec_and_issues;
set rec_and_issues;
where SVFDBK_FEEDBACK_COMMENTS is &op. null;
run;
%exit:
%mend missing_or_nonmissing;
Add these lines below to run the macro:
%let sel_issue = Missing; * or NonMissing;
%missing_or_nonmissing;
Your code will overwrite the rec_and_issues table when this macro runs, either with the SVFDBK_FEEDBACK_COMMENTS values being missing or not. To make a new table instead (and keep the rec_and_issues table) put a different table name in the data statement.
Building on Joe's answer, you could even make it work without a macro at all, just using macro variables, as in the following:
%let not = %sysfunc(ifc(&sel=NonMissing,not,));
DATA rec_and_issues;
set rec_and_issues;
where SVFDBK_FEEDBACK_COMMENTS is ¬ null;
run;
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;