I want to include sas code as text in a ODS pdf / proc odstext setting.
For that purpose I'd like to use a macro variable.
FILENAME msghtml FILESRVC FOLDERPATH='/Projects/SAS Viya training VWFS/' FILENAME='assign_brand.txt';
data _null_;
length text $32767;
retain text '';
infile msghtml flowover end=last;
input;
text=cats(text,_infile_);
if last then call symput('MSGBODY2',text);
run;
%put %nrquote(&MSGBODY2.);
%let user1=Arne;
proc odstext;
p "&user1.";
p "&MSGBODY.";
run;
If I comment the second P line in the proc odstext, it works.
Else it returns a blank window with the note:
NOTE: The quoted string currently being processed has become more than 2000 bytes long. You might have unbalanced quotation marks.
The macro variable resolves to:
82 %put %nrquote(&MSGBODY2.);
options VALIDMEMNAME=EXTEND VALIDVARNAME=ANY;length '_va_c_Marca'n $60;'_va_c_Marca'n=(KSCAN('DESC_TMAIMG_ES'n,1));;length
'_va_c_Marca_ok'n $10;if (('_va_c_Marca'n IN
('DIAVEL','DUACTI','Ducati','MULTISTRADA','DUCATI','MONSTER','SCRAMBLER','STREETFIGHTER')))then do;'_va_c_Marca_ok'n=
'Ducati';end;else do;if (('_va_c_Marca'n IN ('VOLKLSWAGEN','POLO','VOLKSWAGEB','VOLKSWAGEN','VOLSKWAGEN','VW')))then
do;'_va_c_Marca_ok'n= 'Volkswagen';end;else do;if (('_va_c_Marca'n IN ('TAYCAN','911','CARRERA','718','PANAMERA','PORSCHE')))then
do;'_va_c_Marca_ok'n= 'Porsche';end;else do;if (('_va_c_Marca'n IN ('A1','A3','A4','A5','A8','AUDI')))then do;'_va_c_Marca_ok'n=
'Audi';end;else do;'_va_c_Marca_ok'n= 'Other';end;end;end;end;;length _strfmt_ $60; drop _strfmt_;_strfmt_ = ' ';array
_tlevname_491_{5} $10 _temporary_ ( 'Audi''Ducati''Other''Porsche''Volkswagen');array _dt_fi_491_{5} _temporary_;_node_id_ =
0;_new_id_ = -1;nextnode_491:if _node_id_ eq 0 then do;_strfmt_ = left(trim(put(_va_c_Marca,$60.)));if _strfmt_ in
('LCV','PORSCHE','718','E','T','911','MII','CARRERA','ATECA','FABIA','COCHES','MS','MACAN','TAYCAN','PANAMERA','SEMINUEVO','MULTIVAN
','GOLD','MAN','CADDY','SEAT','CAYMAN','CAYENNE','CRAFTER','GOLF','CALIFORNIA','NEW','CUPRA','REFIN','SKODA','VARIOS') then
do;_new_id_ = 1;end;else if _strfmt_ in ('POLO','VW','VOLKLSWAGEN','VOLKSWAGEB','VOLKSWAGEN','VOLSKWAGEN') then do;_new_id_ =
2;end;else if _strfmt_ in ('AUDI','A3','A1','A4','A8','A5') then do;_new_id_ = 3;end;else if _strfmt_ in
('MONSTER','SCRAMBLER','DIAVEL','STREETFIGHTER','DUACTI','MULTISTRADA','DUCATI','Ducati') then do;_new_id_ = 4;end;else do;_new_id_
= 1;end;end;else if _node_id_ eq 1 then do;_strfmt_ = left(trim(put(_va_c_Marca,$60.)));if _strfmt_ in
('LCV','E','T','MII','ATECA','FABIA','COCHES','MS','MACAN','SEMINUEVO','MULTIVAN','GOLD','MAN','CADDY','SEAT','CAYMAN','CAYENNE','CR
AFTER','GOLF','CALIFORNIA','NEW','CUPRA','REFIN','SKODA','VARIOS') then do;_new_id_ = 5;end;else if _strfmt_ in
('PORSCHE','718','911','CARRERA','TAYCAN','PANAMERA') then do;_new_id_ = 6;end;else do;_new_id_ = 5;end;end;else if _node_id_ eq 2
then do;_leaf_id_ = 2;_new_id_ = -1;_dt_pred_lev_ = 4;_dt_pred_prob_ = 1;_dt_fi_491_{1} =
0;_dt_fi_491_{2} = 0;_dt_fi_491_{3} = 0;_dt_fi_491_{4} = 0;_dt_fi_491_{5}
= 1;end;else if _node_id_ eq 3 then do;_leaf_id_ = 3;_new_id_ = -1;_dt_pred_lev_ = 0;_dt_pred_prob_ =
1;_dt_fi_491_{1} = 1;_dt_fi_491_{2} = 0;_dt_fi_491_{3} =
0;_dt_fi_491_{4} = 0;_dt_fi_491_{5} = 0;end;else if _node_id_ eq 4 then do;_leaf_id_ =
4;_new_id_ = -1;_dt_pred_lev_ = 1;_dt_pred_prob_ = 1;_dt_fi_491_{1} = 0;_dt_fi_491_{2} =
1;_dt_fi_491_{3} = 0;_dt_fi_491_{4} = 0;_dt_fi_491_{5} =
0;end;else if _node_id_ eq 5 then do;_leaf_id_ = 5;_new_id_ = -1;_dt_pred_lev_ = 2;_dt_pred_prob_ =
1;_dt_fi_491_{1} = 0;_dt_fi_491_{2} = 0;_dt_fi_491_{3} = 1;_dt_fi_491_{4}
= 0;_dt_fi_491_{5} = 0;end;else if _node_id_ eq 6 then do;_leaf_id_ = 6;_new_id_ =
-1;_dt_pred_lev_ = 3;_dt_pred_prob_ = 1;_dt_fi_491_{1} = 0;_dt_fi_491_{2} =
0;_dt_fi_491_{3} = 0;_dt_fi_491_{4} = 1;_dt_fi_491_{5} = 0;end;if
_new_id_ >= 0 then do;_node_id_ = _new_id_;goto nextnode_491;end;I__va_c_Marca_ok = _tlevname_491_{_dt_pred_lev_+1};label
I__va_c_Marca_ok = 'Into: _va_c_Marca_ok';_i_ = 1;_dt_predp_ = _dt_fi_491_{_i_};P__va_c_Marca_okAudi = _dt_predp_;label
P__va_c_Marca_okAudi = 'Predicted: _va_c_Marca_ok=Audi';_i_+1;_dt_predp_ = _dt_fi_491_{_i_};P__va_c_Marca_okDucati =
_dt_predp_;label P__va_c_Marca_okDucati = 'Predicted: _va_c_Marca_ok=Ducati';_i_+1;_dt_predp_ =
_dt_fi_491_{_i_};P__va_c_Marca_okOther = _dt_predp_;label P__va_c_Marca_okOther = 'Predicted:
_va_c_Marca_ok=Other';_i_+1;_dt_predp_ = _dt_fi_491_{_i_};P__va_c_Marca_okPorsche = _dt_predp_;label P__va_c_Marca_okPorsche =
'Predicted: _va_c_Marca_ok=Porsche';_i_+1;_dt_predp_ = _dt_fi_491_{_i_};P__va_c_Marca_okVolkswagen = _dt_predp_;label
P__va_c_Marca_okVolkswagen = 'Predicted: _va_c_Marca_ok=Volkswagen';_i_+1;drop _dt_predp_;drop _i_;drop _dt_pred_lev_;drop
_dt_pred_prob_;drop _node_id_;drop _new_id_; drop '_va_c_Marca_ok'n '_va_c_Marca'n 'I__va_c_Marca_ok'n '_leaf_id_'n
'P__va_c_Marca_okAudi'n 'P__va_c_Marca_okDucati'n 'P__va_c_Marca_okOther'n 'P__va_c_Marca_okPorsche'n
'P__va_c_Marca_okVolkswagen'n;length 'I__va_c_Marca_ok_491_2'n
$10;'I__va_c_Marca_ok_491_2'n='I__va_c_Marca_ok'n;'P__va_c_Marca_okAudi_491_2'n='P__va_c_Marca_okAudi'n;'P__va_c_Marca_okDucati_491_
2'n='P__va_c_Marca_okDucati'n;'P__va_c_Marca_okOther_491_2'n='P__va_c_Marca_okOther'n;'P__va_c_Marca_okPorsche_491_2'n='P__va_c_Marc
a_okPorsche'n;'P__va_c_Marca_okVolkswag_491_2'n='P__va_c_Marca_okVolkswagen'n;'_leaf_id__491_2'n='_leaf_id_'n;
NOTE: The quoted string currently being processed has become more than 2000 bytes long. You might have unbalanced quotation marks.
This often happens when you do have earlier in your program a missing quotation mark. Best to try re-starting SAS and see if the program works.
Which brings me to a question. Why do you make your life much more difficult with variable names such as '_va_c_Marca'n?? It seems as if all of your variable names have the quotation marks and suffix N? This is difficult to type and could be the cause of your unbalanced quotation marks. And its completely unnecessary here, when the variable name _va_c_Marca would work just as well.
However ... from now on, when there are problems in the log, we expect you will provide the log in the proper code box ... copy the log as text, paste it into the window that appears when you click on the </> icon. No exceptions from now on.
OK, understood. I'll use the text box from now on.
The variable name convention is directly created from Visual Analytics when exporting a model, here a decision tree.
Changing the txt file thus the macro variable doesn't resolve this issue either.
The macro variable resolves to:
%put %nrquote(&MSGBODY2.);
Der Pissbahnhof-Vergleich hat Ludwigshafen dann doch geschmerzt. An öde Witze (Was ist das Schönste an Ludwigshafen? Die Brücke nach Mannheim!) und Schmähungen des Stadtbilds ( Betonversessene Planierraupen-Architektur ) ist man ja gewöhnt in der Heimstatt des weltgrößten Chemiekonzerns BASF. Dass der SWR seinen Odenthal-Tatort lieber in Baden-Baden dreht, obwohl er in Ludwigshafen spielt, wird achselzuckend hingenommen.Aber der Verriss im Spiegel , dessen Reporter nach Helmut Kohls Tod im Sommer 2017 ein knappes Stündchen durch die City geirrt war, empörte dann doch. Eine verwahrloste Geisterstadt sei Ludwigshafen, geeignet als Kulisse für osteuropäische Agentenfilme aus den 60ern: Verglichen mit dem Zentrum von Ludwigshafen wirkte jeder Pissbahnhof in Vorpommern, jede Autobahntankstelle in Sachsen-Anhalt wie ein blühender Zukunftsort.Als der Geburtsort von Helmut Kohl und Ernst Bloch, von André Schürrle und Daniela Katzenberger kurz darauf auch noch zur hässlichsten Stadt Deutschlands gekürt wurde, wenn auch nur von der TV-Satiresendung Extra 3 , hielt das Kulturamt die Zeit gekommen für eine gepfefferte Replik. Man aktivierte den Performance-Künstler Helmut van der Buchholz. Der leitet seither Rundgänge und Radtouren – zu den schlimmsten, unansehnlichsten, peinlichsten Ecken der Stadt. Ihr Titel: Germany’s Ugliest City Tours .
(text copied from https://www.welt.de/reise/staedtereisen/article243102677/Ludwigshafen-Eine-Tour-durch-die-haesslichs... )
But the ods output is not generated. That's true as well for wrapping a ods pdf statement around it. The pdf is still blank.
%let user1=Arne;
proc odstext;
p "&user1.";
p "%nrquote(&MSGBODY2.)";
run;
I think we need to see all of the log, before you get to PROC ODSTEXT as well as the log from within PROC ODSTEXT.
When I use this code
ods pdf file="/caslibs/marketing/&tab._topn.pdf" style=Style.Custom;
ods escapechar='^';
%let user1=Arne;
FILENAME refy FILESRVC FOLDERPATH='/Projects/SAS Viya training VWFS/' FILENAME='pic.png';
proc odstext;
p "&user1.";
/* p "%nrquote(&MSGBODY2.)"; */
P '^{style [preimage=refy}';
run;
ods pdf close;
the final pdf looks correct, with the macro variable user1 resolved to Arne and the image inserted.
If I comment the preimage line and uncomment the problematic macro variable line, the output is blank.
Thank you, the log.
1 %studio_hide_wrapper; 82 %studio_show_only_notes_wrapper; NOTE: ODS statements in the SAS Studio environment may disable some output features. 85 ods pdf file="/caslibs/marketing/&tab._topn.pdf" style=Style.Custom; WARNING: Apparent symbolic reference TAB not resolved. WARNING: Style STYLE.CUSTOM not found; Pearl style will be used instead. NOTE: Writing ODS PDF output to DISK destination "/caslibs/marketing/&tab._topn.pdf", printer "PDF". 86 ods escapechar='^'; 87 88 %let user1=Arne; 89 FILENAME refy FILESRVC FOLDERPATH='/Projects/SAS Viya training VWFS/' FILENAME='pic.png'; 90 proc odstext; 91 p "&user1."; 92 p "&MSGBODY2."; NOTE: The quoted string currently being processed has become more than 2000 bytes long. You might have unbalanced quotation marks. 93 /* P '^{style [preimage=refy}'; */ 94 run; NOTE: PROCEDURE ODSTEXT used (Total process time): real time 0.69 seconds cpu time 0.72 seconds 95 96 ods pdf close; NOTE: ODS PDF printed 1 page to /caslibs/marketing/&tab._topn.pdf. 97 98 %studio_hide_wrapper; 109 110
I think you need to fix this:
WARNING: Apparent symbolic reference TAB not resolved.
There does not seem to be any problems with the code in the macro variable (no double quotes inside, so it should work as a paragraph). So the problem may be with the length of the string, probably that you use CALL SYMPUT, with a string that has the maximum length, so the macro variable will get a length of 32767 characters (with probably quite a lot of blanks in the end).
If you change the call to CALL SYMPUTX (which trims blanks at the end), it may work.
Another possibility is to create a temporary file with statements for PROC ODSTEXT, and %INCLUDE that:
FILENAME msghtml FILESRVC FOLDERPATH='/Projects/SAS Viya training VWFS/' FILENAME='assign_brand.txt';
filename temp temp;
data _null_;
infile msghtml flowover end=last;
input;
file temp;
put 'p ' _infile_ $quote200. ';';
run;
proc odstext;
p "&user1";
%include temp;
run;
Thanks @s_lassen , it works like a charm
Why are you trying to write the whole file into one ODSTEXT paragraph? Wouldn't that generate unformatted gibberish (kind of like posting code into the main body of response on this forum)?
FILENAME msghtml FILESRVC FOLDERPATH='/Projects/SAS Viya training VWFS/' FILENAME='assign_brand.txt';
filename code tmep;
data _null_;
infile msghtml end=last;
file code ;
input;
put 'P ' _infile_ :$quote. ';' ;
run;
%let user1=Arne;
proc odstext;
p "&user1.";
%include code / source2;
run;
I'm happy, that's exactly what I need @Tom
I cannot mark your reply as a solution. I don't know why. Sorry
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
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.