Lesson 10, Part 2: Macro Quoting Functions¶
SAS macro quoting functions are used to protect special characters, mnemonics, and macro triggers so that the macro processor does not misinterpret them as executable code instead of literal text during macro compilation or execution. In other words, macro quoting functions "remove the special meaning from those characters" (Carpenter, nodate).
... protecting characters ...
... disabling the normal syntactical meaning of the special characters ...
... removing the normal meaning of the special characters ...
& (ampersand known as macro trigger, e.g., &mvar to indicate a macro variable reference)
% (percent known as macro trigger, e.g., %mymacro to indicate a macro name)
blank
, (comma)
; (semicolon)
+ (plus)
- (minus)
* (multiplication operator)
/ (division operator)
< (less than operator)
> (greater than operator)
= (equal operator)
┑ (NOT symbol)
^ (NOT symbol)
~ (NOT symbol)
# (line pointer control within SAS INPUT and PUT statements)
| (or symbol)
' (unbalanced single quote)
" (unbalanced double quote)
( (unbalanced open parenthesis)
) (unbalanced close parenthesis)
AND
OR
NOT
EQ (equal to)
NE (not equal to)
LE (less than or equal to)
LT (less than)
GE (greater than or equal to)
GT (greater than)
IN (equal to one of a list)
Macro Quoting Functions: An Overview¶
- %STR masks special characters and mnemonics (except macro triggers - & and %) at compile time.
- %NRSTR masks special characters and mnemonics (including macro triggers - & and %) at compile time.
- %BQUOTE masks special characters and mnemonics (except macro triggers - & and %) at execution time.
- %NRBQUOTE masks special characters and mnemonics (including macro triggers - & and %) at execution time.
- %SUPERQ masks special characters and mnemonics during macro execution and also prevents further resolution of macro references that you do not want resolved.
- %UNQUOTE undoes quoting.
Without quoting functions, if you try to pass or store text containing those characters, mnemonics, or macro triggers, the macro processor might misinterpret them as executable code rather than literal text.
Macro quoting functions are of two types.¶
Compile-time macro quoting functions - %STR and %NRSTR (protecting constant text)
Execution-time macro quoting functions - - %BQUOTE and %NRBQUOTE and others like %SUPERQ (protecting resolved values - text that you cannot see until the macro variable is resolved)
A compile-time quoting function works on text passed to the macro processor, and an execution-time quoting function works on text values returned by macro processor code execution.
When to use which macro quoting function?¶
Russ Tyndall from SAS Technical support suggests a general rule of thumb to resolve this dilemma in the SAS User’s blog. A good rule of thumb is that if you can see the problem, it's a compile-time issue. Otherwise, it's an execution issue.
Readings¶
ESSENTIALS OF MACRO QUOTING FUNCTIONS IN SAS by Kaushal Raj Chaudhary
SAS Documentation: Summary of Macro Quoting Functions and the Characters That They Mask
Macro quoting made easy by Russ Tyndall
An Animated Guide: An Introduction to SAS Macro Quoting by Russ Lavery
Summary of Macro Quoting Functions
Demystifying Macro Masking Functions Q&A, Slides, and On-Demand Recording
%STR - Example 1¶
- %STR preserves the leading blank.
ods html close;
options nodate nonumber nosource nonotes;
ods html close;
%let x_course= Stat 4197;
%let Course=%str( Stat 4197);
%put &=x_course &=Course;
The SAS System
X_COURSE=Stat 4197 COURSE= Stat 4197
The SAS System
E3969440A681A2408885998500000005
%STR - Example 2¶
Line 3 (below): The %LET stops reading the value at the first unmasked semicolon, because that semicolon ends the %LET statement itself. The macro variable X_STEP stores the text PROC PRINT only.
Line 4 (below): The %STR function in the %LET statement correctly prevents the semicolon after PROC PRINT from being interpreted as the ending semicolon for the same statement.
ods html close;
options nodate nonumber nosource nonotes;
%let x_step = proc print; run;
%let step=%str(proc print; run;);
%PUT &=x_step;
%PUT &=step;
The SAS System
X_STEP=proc print
STEP=proc print; run;
The SAS System
E3969440A681A2408885998500000007
%STR vs. %NRSTR - Example 3¶
Line 3
- %STR only masks the special character, semicolon (;).
- However, it does NOT protect the macro trigger (&). So SAS still resolves &dsn to class.
Line 4
- Like %STR, %NRSTR masks the special character, semicolon (;) too.
- In addition, %NRSTR masks the macro trigger (&) and prevents resolution of macro triggers inside the text. So &dsn stays literal.
%let dsn=class;
%let p= %str(proc print data=sashelp.&dsn; run;);
%let p1= %nrstr(proc print data=sashelp.&dsn; run;);
%put &=p;
%put &=p1;
The SAS System
P=proc print data=sashelp.class; run;
P1=proc print data=sashelp.&dsn; run;
The SAS System
E3969440A681A2408885998500000010
%STR - Example 4¶
Line 3
- %STR is not strictly required in the %LET statement. Because there is no semicolon inside the macro variable's value and the quotes in the value are balanced.
Line 4
- In the % LET statement, %STR is more of a defensive habit here than a strict requirement.
ods html close;
options nodate nonumber nosource nonotes;
%let condition_x = age<15 and name='John';
%let condition = %str(age<15 and name = 'John');
%put &=condition_x;
%put &=condition;
The SAS System
CONDITION_X=age<15 and name='John'
CONDITION=age<15 and name = 'John'
The SAS System
E3969440A681A2408885998500000018
%STR - Example 5¶
Line 3 (below)
- In the macro variable (mv) value, the % before the apostrophe is an escape character used inside %STR to mask the unmatched single quote. Here, the apostrophe in Beth's is treated as a quote character. Since there is no matching opening single quote, %STR may interpret it as an unmatched quote unless you mark it.
ods html close;
options nodate nonumber nosource nonotes;
%LET mvar = %STR(Beth%'s Assignment Report);
%PUT &=mvar;
The SAS System
MVAR=Beth's Assignment Report
The SAS System
E3969440A681A2408885998500000021
%NRSTR¶
%NRSTR (NR stands for No rescan or Not Resolved) masks text with the macro triggers (& and %), preventing them from interpretation at compile time.
%NRSTR - Example 1¶
Line 3 (below)
- %NRSTR function masks the & in the constant text AT&T during compile time.
ods html close;
options nosource nodate nonumber;
%let text = "%nrstr(AT&T)";
%put &=text;
The SAS System
NOTE: Writing HTML5(SASPY_INTERNAL) Body file: _TOMODS1
TEXT="AT&T"
The SAS System
E3969440A681A2408885998500000005
Line 4 (below)
- %NRSTR function masks the & in the constant text &dsn and prevents resolution.
ods html close;
options nosource nodate nonumber;
%let dsn=class;
%let p= %nrstr(proc print data=sashelp.&dsn; run;);
%put &=p;
The SAS System
P=proc print data=sashelp.&dsn; run;
The SAS System
E3969440A681A2408885998500000019
%NRSTR - Example 2¶
Line 3 (below)
- %NRSTR masks the macro trigger (%). Without %NRSTR, SAS would interpret %showme as a macro invocation.
options nocenter nodate nosource;
ods html close;
%let ex=%nrstr(This macro is called %showme);
%put &=ex;
The SAS System EX=This macro is called %showme The SAS System E3969440A681A2408885998500000025
ods html close;
options nodate nonumber nonotes nosource;
%let txt = %nrstr(100% complete & verified);
%put &=txt
The SAS System
TXT=100% complete & verified
The SAS System
E3969440A681A2408885998500000007
%BQUOTE¶
The %BQUOTE (also called “Blind Quote) function masks special characters and mnemonics (except & and %) contained in the results from resolving macro expressions.
%NRSTR vs. %BQUOTE - Example 1¶
What happens line by line
%nrstr(Hello &name)
%nrstr operates during compile time: it prevents macro resolution and treats the text as literal, so &name is not expanded —msg1 stores the literal string Hello &name.
%bquote(Hello &name)
%bquote operates at execution time: it first resolves &name to John, the Great, then masks the resulting text (so commas, spaces, and other special characters are safely handled). Next, msg2 stores the resolved string Hello John, the Great.
ods html close;
options nodate nonumber nonotes nosource;
%let name = John, the Great;
%let msg1 = %nrstr(Hello &name);
%let msg2 = %bquote(Hello &name);
%put &=msg1;
%put &=msg2;
The SAS System
MSG1=Hello &name
MSG2=Hello John, the Great
The SAS System
E3969440A681A2408885998500000018
%BQUOTE - Example 2¶
- CALL SYMPUTX creates a macro variable: &m_store = Kids' Corner.
- First, &m_store resolves first → Kids' Corner.
- Next, %BQUOTE masks the special character (unmatched quote).
- %SUPERQ takes the macro variable name only (m_store), not &m_store. It then retrieves the stored value (Kids' Corner) directly.
*%BQUOTE vs. %SUPERQ;
options nonotes nosource nonumber nosymbolgen;
ods html close;
data test;
store="Kids' Corner";
call symputx('m_store',store);
run;
%put %bquote(&m_store);
%put %superq(m_store);
The SAS System 13:23 Friday, March 21, 2025
Kids' Corner
Kids' Corner
The SAS System 13:23 Friday, March 21, 2025
E3969440A681A2408885998500000005
ods html close;
options nodate nonumber nosource nonotes;
%LET mvar = %BQUOTE(Beth's Assignment Report);
%PUT &=mvar;
The SAS System
MVAR=Beth's Assignment Report
The SAS System
E3969440A681A2408885998500000010
Compare the %BQUOTE code example above with the %STR code example earlier.¶
%BQUOTE is superior to %STR for handling unmatched quotes because it masks them automatically at execution time without requiring extra characters, whereas %STR requires unmatched quotes to be escaped with a percent sign (e.g., %'). %BQUOTE is ideal for dynamic, runtime values (e.g., macro variables), while %STR is designed for static, compile-time text.
%BQUOTE - Example 3¶
Example: Filter Data Dynamically Use Case: Dynamic WHERE Clause in a Reusable Macro You want to:
- Pass a condition dynamically (using macro variables)
- Let macro variables resolve
- But still protect special characters (like > < =)
In this example, %bquote (&var > &limit) first resolves &var to weight and &limit to 100, then treats the resulting text as a quoted macro string. That matters when the resolved text contains special characters like &, %, commas, parentheses, or unmatched quotes. In this example, the resolved text is plain SQL condition text, so the quoting is effectively invisible.
ods html close;
options nodate nonumber nonotes nosource;
/* Sample macro variables */
%let var = weight;
%let limit = 100;
/* Build dynamic condition */
%let condition = %bquote(&var > &limit);
/* Use it in PROC SQL */
proc sql;
select name, weight
from sashelp.class
where &condition;
quit;
| Name | Weight |
|---|---|
| Alfred | 112.5 |
| Carol | 102.5 |
| Henry | 102.5 |
| Janet | 112.5 |
| Mary | 112 |
| Philip | 150 |
| Robert | 128 |
| Ronald | 133 |
| William | 112 |
%BQUOTE masks special characters during macro execution (runtime). It is especially useful when the value comes from:¶
- a macro parameter
- a macro variable
- %SYSFUNC
- CALL SYMPUTX
- PROC SQL INTO
- user-entered text
%NRBQUOTE¶
%NRBQUOTE masks special characters and mnemonics (including & and %) if present in the resolved value.
NRBQUOTE - Example 1¶
CALL SYMPUTX stores: Courses = STAT4197&6197.
%NRBQUOTE(&Courses) works like this:
- First, the macro variable reference, &Courses, resolves to STAT4197&6197.
- Next, %NRBQUOTE immediately masks the embedded &.
- %PUT writes the masked value STAT4197&6197.
options nocenter nodate nonotes nosource;
ods html close;
data _null_;
call symputx('Courses', 'STAT4197&6197');
run;
%put %nrbquote(&Courses);
The SAS System STAT4197&6197 The SAS System E3969440A681A2408885998500000035
%SUPERQ¶
%SUPERQ masks special characters and mnemonics during macro execution. However, it also prevents further resolution of any macros or macro variables. The %SUPERQ function accepts as its argument only the name of a single macro variable with no leading ampersand.
%SUPERQ -Example 1¶
- CALL SYMPUTX creates: Courses = STAT4197&6197.
- %SUPERQ passes the macro variable name (Courses), not the macro variable reference (&Courses).
- %SUPERQ retrieves the value directly from the macro symbol table and masks any special characters without resolving anything inside the value (such as & and %).
ods html close;
options nocenter nodate nonotes nosource;
data _null_;
call symputx('Courses', 'STAT4197&6197');
run;
%put %superq(Courses);
35 The SAS System STAT4197&6197 36 The SAS System E3969440A681A2408885998500000018
%SUPERQ - Example 2¶
- Line 8: %NRSTR(...) protects macro triggers when storing.
- Line 11: %SUPERQ() retrieves the stored value without triggering macro resolution or rescanning.
ods html close;
options nodate nonumber nonotes nosource mprint symbolgen;
%let product = Laptop;
%let price = 1200;
%let qty = 5;
/* Dynamic message with literal &TAX_RATE */
%let message = %nrstr(Expected Revenue for &product = &price * &qty * &TAX_RATE);
/* Print the message */
%put %superq(message);
The SAS System Expected Revenue for &product = &price * &qty * &TAX_RATE The SAS System E3969440A681A2408885998500000024
%NRBQUOTE vs. %SUPERQ - Example 3¶
- %NRBQUOTE(&Courses)
- The macro variable reference (&Courses) resolves to STAT4197&6197.
- %NRBQUOTE then tries to protect the macro trigger (&),but in this case, nothing further gets resolved visibly.
- The log prints: STAT4197&6197.
- %SUPERQ(Courses)
- There is no resolution of &Courses.
- The value is fetched directly and printed exactly as stored: STAT4197&6197.
ods html close;
options nocenter nodate nosource;
data _null_;
call symputx('Courses', 'STAT4197&6197');
run;
%put %nrbquote(&Courses);
%put %superq(Courses);
The SAS System STAT4197&6197 STAT4197&6197 The SAS System E3969440A681A2408885998500000045
%UNQUOTE¶
%UNQUOTE restores the meaning of any special characters and mnemonics masked by the quoting function during macro execution.
%UNQUOTE - Example 1¶
In the example below, %NRSTR (&Course) masks the &, preventing macro resolution. With %UNQUOTE, the resolved unquoted value of &u_course contains the correct text.
ods html close;
options nodate nonumber nonotes nosource;
%let course = STAT6197;
%let n_course = %nrstr(&course);
%let u_course = %unquote(%nrstr(&Course));
%put &=course;
%put;
%put &=n_course;
%put;
%put &=u_course;
%put;
The SAS System COURSE=STAT6197 N_COURSE=&course U_COURSE=STAT6197 The SAS System E3969440A681A2408885998500000057
%UNQUOTE - Example 2 (Source: Carpenter, 2016, and Tom SUPER USER at SAS Communities for some code below)¶
Macro variable resolution inside %STR / %NRSTR, and %BQUOTE¶
- Line 4: %STR does NOT mask &, and hence, &dsn gets resolved. However, it masks semicolons, so %LET can store a full statement safely.
- Line 5: The inner %NRSTR does mask &. Thus &dsn is protected, not resolved. It also masks semicolons, so %LET can store a full statement safely.
- Line 5: The outer %NRSTR prevents SAS from re-scanning the already-constructed string for macro triggers like &dsn during final assignment (ChatGPT).
- Lines 4 and 5: %unquote(...) removes the macro quoting, so the final value is plain text.
- Line 6: %BQUOTE is an execution-time quoting function. It masks various special characters, including semicolons, unmatched quotes, and parentheses (except & and %).
- Line 7: %STR is a compile-time quoting function. It masks various special characters, including semicolons (except & and %).
ods html close;
options nonotes nodate nonumber nosource;
%let dsn = class;
%let p1 = %unquote(%nrstr(%str(proc print data=sashelp.&dsn; run;)));
%let p2 = %unquote(%nrstr(%nrstr(proc print data=sashelp.&dsn; run;)));
%let p3 = %bquote(proc print data=sashelp.&dsn;run;);
%let p4 = %str(proc print data=sashelp.&dsn;run;);
%put &=p1;
%put &=p2;
%put &=p3;
%put &=p4;
%*unquote(&p3)
The SAS System P1=proc print data=sashelp.class; run; P2=proc print data=sashelp.&dsn; run; P3=proc print data=sashelp.class;run; P4=proc print data=sashelp.class;run; The SAS System E3969440A681A2408885998500000104
ods html close;
options nonotes nodate nonumber nosource nosymbolgen;
data _null_;
call symputx('dsnc','aaa&bbb');
run;
%let mvar3 = %nrbquote(proc print data=sashelp.&dsnc; run;);
%let mvar4 = %superq(proc print data=sashelp.&dsnc; run;);
%put &=mvar3;
%put &=mvar4;
The SAS System WARNING: Apparent symbolic reference BBB not resolved. WARNING: Apparent symbolic reference BBB not resolved. ERROR: Invalid symbolic variable name PROC DATA=SASHELP.AAA&BBB; RUN;. MVAR3=proc print data=sashelp.aaa&bbb; run; MVAR4= The SAS System E3969440A681A2408885998500000021
ERROR: Invalid symbolic variable name PROC DATA=SASHELP.AAA&BBB; RUN;. None
Summary¶
Compile time - %STR and %NRSTR¶
Execution time - %BQUOTE and others like %SUPERQ, %QUOTE, %NRQUOTE, and %NRBQUOTE
A good rule of thumb is that if you can see the problem, it's a compile-time issue. Otherwise, it's an execution issue.
Here are some compile-time examples:¶
- %let name=%str(Obrien%'s cafe); >>>Mask the unmatched single quote
- %if &state = %str(OR) %then ... >>>Mask OR so it's not treated as a logical operator 'or'
- %let name=%unquote(%str(%'&list%')); >>>Resolve the macro variable in single quotes
- %mac_name(%str(a,b,c)) >>>Mask commas so they're not treated as different parameters
- %let name=%nrstr(Ben&Jerry ice cream); >>>Mask the ampersand so it is not treated as a macro trigger
Here are some execution-time examples:¶
- %if %bquote(&state) = %str(OR) %then ... >>>Mask OR so not treated as a logical 'or'
- %if %scan(%bquote(&var),1) %then ... >>> Mask the value of &var in case it contains special characters such as commas
There have been numerous papers written through the years about macro quoting functions. Here's one I found https://www.lexjansen.com/mwsug/2015/RF/MWSUG-2015-RF-08.pdf that you may find helpful.
General rules¶
%STR masks special characters at compile time. The (&) acts normally, but if you wanted to hide its meaning you would use %NRSTR instead, as it works at compile time also.
%BQUOTE masks everything at macro execution time and treats (&) normally.
When compared with %BQUOTE, the only difference is that the %NRBQUOTE function will resolve down as far as it can, but does not resolve any ampersands or percent signs in the final result.