I have written the following test code. The goal is to send an e-mail with the content of some variables which my contain any character (in this example, an &):
%macro WriteInLog(Text1, Text2);
data _null_;
%put "Text1 is &Text1";
%put "Text2 is &Text2";
run;
%mend WriteInLog;
%macro SendMail(Text1, Text2);
filename msg email
to = "moretest@sas.com"
from = "test@sas.com"
subject = "Test mail";
data _null_;
file msg;
put "Text1: &Text1"; put '0D0A'x;
put "Text2: &Text2";
run;
%mend SendMail;
data test;
Text1 = 'Test';
Text2 = 'Text&MoreText';
run;
data _null_;
set test;
call execute(cats('%WriteInLog(',Text1,',%nrstr(',Text2,'))'));
call execute(cats('%SendMail(',Text1,',%nrstr(',Text2,'))'));
run;
The execution of the macro WriteInLog shows no error and the following text in the log, as expected:
"Text1 is Test"
"Text2 is Text&MoreText"
The execution of the macro SendMail shows a warning when trying to resolve the variable &MoreText, even if I have called the macro using %nrstr in order to avoid that:
2 + filename msg email to = "moretest@sas.com" from = "test@sas.com" subject = "Test mail";
2 + data _null_; file msg;
put "Text1: Test"; put '0D0A'x; put "Text2: Text&MoreText"; run;
WARNING: Apparent symbolic reference MORETEXT not resolved.
What am I missing?
Thank you
Just add the extra level when writing the strings to CALL EXECUTE().
data test;
Text1 = 'Test';
Text2 = 'Text&MoreText';
run;
data _null_;
set test;
call execute(cats('%nrstr(%WriteInLog)(%nrstr(%nrstr(',Text1,')),%nrstr(%nrstr(',Text2,')))'));
run;
1 + %WriteInLog(%nrstr(Test),%nrstr(Text&MoreText)) MPRINT(WRITEINLOG): data _null_; MPRINT(WRITEINLOG): put "Text1=Test"; MPRINT(WRITEINLOG): put "Text2=Text&MoreText"; MPRINT(WRITEINLOG): run; Text1=Test Text2=Text&MoreText
Macro variable &MoreText has not been assigned a value.
MoreText is not intended to be a macro variable; it is part of text string assigned to a variable. I'm trying to show the text &MoreText as text in the email. Think of &MoreText as a company called Text &MoreText or Dallas&Sally, for example.
Interesting problem. Note that this isn't about emailing. In your first test of %WriteInLog you don't get the problem because you use a %PUT statement, instead of a PUT statement. If you get change to PUT, you'll get the same attempted resolution.
Call Execute timing, and automatic unquoting, is always tricky.
Could you settle for using:
data test;
Text1 = 'Test';
Text2 = '%nrstr(Text&MoreText)';
run;
That would allow to use your current code, e.g.:
%macro WriteInLog(Text1, Text2);
data _null_;
put "Text1 is &Text1. Text2 is Text2";
run;
%mend WriteInLog;
data test;
Text1 = 'Test';
Text2 = '%nrstr(Text&MoreText)';
run;
data _null_;
set test;
call execute(cats('%WriteInLog(',Text1,',',Text2,')'));
run;
I would probably use:
data _null_;
set test;
call execute(cats('%nrstr(%%)WriteInLog(',Text1,',%nrstr(',Text2,'))'));
run;
because delaying the macro execution gives a clearer log:
277 data _null_; 278 set test; 279 call execute(cats('%nrstr(%%)WriteInLog(',Text1,',%nrstr(',Text2,'))')); 280 run; NOTE: There were 1 observations read from the data set WORK.TEST. NOTE: CALL EXECUTE generated line. 1 + %WriteInLog(Test,%nrstr(Text&MoreText)) Text1 is Test Text2 is Text2
Sorry, my answer is wrong. Haven't woken up yet!
Tom's answer is (as usual) better than mine. But here is a corrected version of my prior approach, which requires adding quoting inside the text string, which looks weird, but works:
%macro WriteInLog(Text1, Text2);
data _null_;
put "Text1=&Text1";
put "Text2=&Text2";
run;
%mend WriteInLog;
data test;
Text1 = 'Test';
Text2 = '%nrstr(Text&MoreText)';
run;
data _null_;
set test;
call execute(cats('%nrstr(%%)WriteInLog(',Text1,',%nrstr(',Text2,'))'));
run;
Log:
13 data _null_; 14 set test; 15 call execute(cats('%nrstr(%%)WriteInLog(',Text1,',%nrstr(',Text2,'))')); 16 run; NOTE: There were 1 observations read from the data set WORK.TEST. NOTE: CALL EXECUTE generated line. 1 + %WriteInLog(Test,%nrstr(Text&MoreText)) Text1=Test Text2=Text&MoreText
Just add the extra level when writing the strings to CALL EXECUTE().
data test;
Text1 = 'Test';
Text2 = 'Text&MoreText';
run;
data _null_;
set test;
call execute(cats('%nrstr(%WriteInLog)(%nrstr(%nrstr(',Text1,')),%nrstr(%nrstr(',Text2,')))'));
run;
1 + %WriteInLog(%nrstr(Test),%nrstr(Text&MoreText)) MPRINT(WRITEINLOG): data _null_; MPRINT(WRITEINLOG): put "Text1=Test"; MPRINT(WRITEINLOG): put "Text2=Text&MoreText"; MPRINT(WRITEINLOG): run; Text1=Test Text2=Text&MoreText
@Tom wrote:
Just add the extra level when writing the strings to CALL EXECUTE().
Thanks Tom, agree, that's better.
Thanks everyone for your help. Just adding an extra level in the string processing did the trick for me, even when other characters as commas could cause trouble adding (apparently) extra parameters to the macro function.
No more annoying warnings in the log 🙂
You need to quote the string with the macro triggers if you don't want the macro processor to process them.
Many times it is much easier to just use actual quotes, like you did in the DATA step, than macro quoting. So if you just write the macro so it expects the quotes around the values.
%macro testing(Text1, Text2);
%put &=text1 &=text2 ;
data _null_;
put "Text1: " &text1 ;
put "Text2: " &text2 ;
run;
%mend testing;
options mprint;
data test;
Text1 = 'Test';
Text2 = 'Text&MoreText';
call execute(cats('%nrstr(%testing)(',quote(trim(text1),"'"),',',quote(trim(text2),"'"),')'));
run;
It works fine
NOTE: CALL EXECUTE generated line. 1 + %testing('Test','Text&MoreText') TEXT1='Test' TEXT2='Text&MoreText' MPRINT(TESTING): data _null_; MPRINT(TESTING): put "Text1: " 'Test' ; MPRINT(TESTING): put "Text2: " 'Text&MoreText' ; MPRINT(TESTING): run; Text1: Test Text2: Text&MoreText
Use this:
call execute(cats('%nrstr(%SendMail(',Text1,',',Text2,'))'));
to delay the execution of macro statements and references.
If that does not do it, try a macro quoting function.
In addition to what already have been said [especially by @Kurt_Bremser about adding %nrstr()] I would add: your input looks like an arbitrary string of text to me, in such case I would, instead using the "&" macro variable call, use the symget() function. It is more safe in such situations:
%macro WriteInLog(Text1, Text2);
data _null_;
length t1 t2 $ 32767;
t1=symget('Text1');
t2=symget('Text2');
put "Text1 is " T1;
put "Text2 is " T2;
run;
%mend WriteInLog;
%macro SendMail(Text1, Text2);
filename msg email
to = "moretest@sas.com"
from = "test@sas.com"
subject = "Test mail";
data _null_;
file msg;
length t1 t2 $ 32767;
t1=symget('Text1');
t2=symget('Text2');
put "Text1: " T1;put '0D0A'x;
put "Text2: " T2;
run;
%mend SendMail;
data test;
Text1 = 'Test';
Text2 = 'Text&MoreText';
run;
data _null_;
set test;
call execute(cats('%nrstr(%WriteInLog(',Text1,',%nrstr(',Text2,')))'));
call execute(cats('%nrstr(%SendMail(',Text1,',%nrstr(',Text2,')))'));
run;
Bart
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
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.