DATA Step, Macro, Functions and more

Reading quoted macro variables from data set SASHELP.VMACRO

Accepted Solution Solved
Reply
PROC Star
Posts: 1,561
Accepted Solution

Reading quoted macro variables from data set SASHELP.VMACRO

Hi,

 

I want to assign a value to a macro variable from the values stored in SASHELP.VMACRO.

 

Call symputx() seems to work, but I'd rather avoid creating 4GL code, and I'd prefer to keep all code as macro code so I can be called anywhere.

 

Function getvarc() doesnt quite conserve the quoted value though. Is there a way to do this?

 

 

%***  Assign a macro variable ***************;
%let a0=%str(     );

%***  Fetch macro variable value from data set ***************;
data T; set sashelp.vmacro;
  where NAME='A0';
  call symput ('a1',VALUE);
  call symputx('a2',VALUE);
run;
%let dsid=%sysfunc(open( SASHELP.VMACRO(where=(NAME='A0')), is));
%let rc  =%sysfunc(fetch(&dsid));
%let a3  =               %qsysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,VALUE))))   ;
%let a4  =%qsysfunc(trim(%qsysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,VALUE)))) ));
%let a5  =%trim(         %qsysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,VALUE)))) ) ;
%let rc  =%sysfunc(close(&dsid));

%***  Look at values fetched ***************;
%put A0 Len=%length(%superq(a0))*%substr(&a0,1,1)*%substr(&a0,2,1)*&a0*;
%put A1 Len=%length(%superq(a1))*%substr(&a1,1,1)*%substr(&a1,2,1)*&a1*;
%put A2 Len=%length(%superq(a2))*%substr(&a2,1,1)*%substr(&a2,2,1)*&a2*;
%put A3 Len=%length(%superq(a3))*%substr(&a3,1,1)*%substr(&a3,2,1)*&a3*;
%put A4 Len=%length(%superq(a4))*%substr(&a4,1,1)*%substr(&a4,2,1)*&a4*;
%put A5 Len=%length(%superq(a5))*%substr(&a5,1,1)*%substr(&a5,2,1)*&a5*;

A0 Len=5* * *     *
A1 Len=198* * *     Bad copy                                                                                                        

 

A2 Len=5* * *     *   Exact copy
A3 Len=198* * *     Bad copy                                                                                                        

A4 Len=1* ** *  Bad copy - Warnings
A5 Len=0****  Bad copy - Warnings

 

 

 

 

 

 

 

 

 

 


Accepted Solutions
Solution
‎06-03-2017 06:08 AM
PROC Star
Posts: 1,561

Re: Reading quoted macro variables from data set SASHELP.VMACRO

I finally decided to make it into a standalone macro, as Tom did for his.
The final final final version is below, in case anyone is interested, as an alternative to the one @Tom linked to (though its internal logic is not that different from Tom's).

 

/*********************************************************************************************************************

  Name                symget.sas
  ¯¯¯¯
  Description         This macro retrieves the value of a macro variable regardless if its scope 
  ¯¯¯¯¯¯¯¯¯¯¯         -even if it's hidden by a local macro variable- by reading table SASHELP.VMACRO.

                      Further help is available as part of the macro

  Used as a function  Yes  
  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  Calls another macro No
  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯   
  Generates SAS code  No 
  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  Limitations         
  ¯¯¯¯¯¯¯¯¯¯¯         
*********************************************************************************************************************
 Who             When        What             
*********************************************************************************************************************
 C Graffeuille   2017-06-01  Initial version             
*********************************************************************************************************************/

%macro symget( help
             , mvar     =
             , where    =
             , err_msg  =Macro symget could not find this variable
             , innercall=
             ) ;                        %* innercall is for internal use only.;

  %**************** Help screen *********************;
  %if %length(%superq(help)) or %length(%superq(mvar))=0 %then %do;
    %local mlogic pagesize;
    %let mlogic  =%sysfunc(getoption(mlogic  ));
    %let pagesize=%sysfunc(getoption(pagesize));
    options nomlogic pagesize=500;
    %put %nrstr(                                                                                      );
    %put %nrstr(**************************************************************************************);
    %put %nrstr(*  _____________________________                                                     *);
    %put %nrstr(*  Help screen for macro  symget                                                     *);
    %put %nrstr(*  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯                                                     *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*  This macros retrieves the value of a macro variable                               *);
    %put %nrstr(*  regardless if its scope, even if it%'s hidden by a local macro variable.           *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*  Parameters:                                                                       *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*    macvar=          REQD  Name of the macro variable to read.                      *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*    where=           OPTL  Specify conditions on the SCOPE.                         *);
    %put %nrstr(*                             Default=<blank> (i.e. nearest scope)                   *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*    err_msg=         OPTL  Value returned is teh variable is not found.             *);
    %put %nrstr(*                             Default=Macro symget could not find this variable      *);
    %put %nrstr(*  Examples:                                                                         *);
    %put %nrstr(*            1- %put Value=%symget(mvar=DSN12);                                      *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*            2- %macro outer;                                                        *);
    %put %nrstr(*                 %local mvar; %let mvar=outerval;                                   *);
    %put %nrstr(*                 %inner;                                                            *);
    %put %nrstr(*               %mend;                                                               *);
    %put %nrstr(*               %macro inner;                                                        *);
    %put %nrstr(*                 %local mvar; %let mvar=innerval;                                   *);
    %put %nrstr(*                 %put -> %Symget(mvar=mvar,where=&wh) <- (&=wh);                    *);
    %put %nrstr(*               %mend;                                                               *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let mvar=globalval;                                                 *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let wh=;                                                            *);
    %put %nrstr(*               %outer;                                                              *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let wh=SCOPE ne 'INNER';                                            *);
    %put %nrstr(*               %outer;                                                              *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let wh=SCOPE eq 'GLOBAL';                                           *);
    %put %nrstr(*               %outer;                                                              *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let wh=SCOPE eq 'ZZZ';                                              *);
    %put %nrstr(*               %outer;                                                              *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               -> innerval <- (WH=)                                                 *);
    %put %nrstr(*               -> outerval <- (WH=SCOPE ne 'INNER')                                 *);
    %put %nrstr(*               -> globalval <- (WH=SCOPE eq 'GLOBAL')                               *);
    %put %nrstr(*               -> Macro symget could not find this variable <- (WH=SCOPE eq 'ZZZ')  *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*            3- %symget(help) displays this help screen.                             *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(**************************************************************************************);
    %put %nrstr(                                                                                      );
    options &mlogic. ps=&pagesize.;
    %return;
  %end;

  %**************** Init *************************************;
  %local value scope myscope dsid rc result;

  %**************** Vet input parameters *********************;
  %if %sysfunc(notname(%superq(mvar))) 
    | %sysfunc(anydigit(%superq(mvar)))=1 
    | %length(%superq(mvar)) > 32 %then %do;
    %put %str(E)RROR: Value of parameter MVAR for macro SYMGET is not a valid SAS name.; 
    &err_msg
    %return;
  %end;

  %**************** Start processing *************************;
  %if %length(&innercall) %then %do;    %* Recursed logic. The work is done here.;       
    %let dsid=%sysfunc(open( SASHELP.VMACRO(where=(NAME=upcase("&mvar") 
                                                 and SCOPE ne 'SYMGET'
          %sysfunc(ifc(%length(%superq(where) ), and %superq(where), ))
                                           )), is));
    %if &dsid=0 %then %do;              %* Could not open SASHELP.VMACRO;
       %put %str(E)RROR: Value of parameter WHERE for macro SYMGET is invalid.; 
       &err_msg
       %return;                         %* This can only be due to a bad where clause;
    %end;                 
    %syscall set(dsid);                 %* Populate variables value and offset automatically;
    %let rc=%sysfunc(fetch(&dsid));     %* Fetch variables in data set;
    %if &rc. %then &err_msg.;           %* No macro variable with that name found in data set;
    %let myscope=&scope;                %* Ensure that only one scope is parsed;
    %do %while(&rc=0);&value            %* Write out fetched value;
      %let rc=%sysfunc(fetch(&dsid));   %* Fetch variables in data set; 
      %if &scope ne &myscope %then %let rc=1; %* Stop. Variable name found in higher scope;
    %end;                               %* Loop until no more records (or higher scope);                 
    %let rc=%sysfunc(close(&dsid));     %* Close table;
    %return;                            %* The recursion logic stops here;
  %end;   

  %* Use intermediate variable "result" to trim trailing blanks;
  %* Doing this means we must recurse for some reason;
  %let result=%symget(mvar=&mvar,where=%superq(where),innercall=1);
  &result
%mend;


 

View solution in original post


All Replies
PROC Star
Posts: 1,561

Re: Reading quoted macro variables from data set SASHELP.VMACRO

Mmm, more tests, and variable A6 seems to be correct.

 

It seems that the value has to dumped as is  using %sysfunc() rather than %qsysfunc() and the quoting is restored from the stored values probably using the stored quoting characters '01x and '02'x without having to quote again.

Interesting... Comments?

 

 


%***  Assign a macro variable ***************;
%let a0=%str(   ; ,  );

%***  Fetch macro variable value from data set ***************;
data _null_;
  set SASHELP.VMACRO;
  where NAME='A0';
  call symput ('a1',VALUE);
  call symputx('a2',VALUE);
run;
%let dsid=%sysfunc(open( SASHELP.VMACRO(where=(NAME='A0')), is));
%let rc  =%sysfunc(fetch(&dsid));
%let a3  =               %qsysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,VALUE))))   ;
%let a4  =%qsysfunc(trim(%qsysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,VALUE)))) ));
%*let a5  =%trim(         %qsysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,VALUE)))) ) ;
%let a6  =                %sysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,VALUE))))  ;
%let rc  =%sysfunc(close(&dsid));

%***  Look at values fetched ***************;
%put A0 Len=%length(%superq(a0))*%substr(%superq(a0),1,1)*%substr(%superq(a0),2,1)*%superq(a0)*;
%put A1 Len=%length(%superq(a1))*%substr(%superq(a1),1,1)*%substr(%superq(a1),2,1)*%superq(a1)*;
%put A2 Len=%length(%superq(a2))*%substr(%superq(a2),1,1)*%substr(%superq(a2),2,1)*%superq(a2)*;
%put A3 Len=%length(%superq(a3))*%substr(%superq(a3),1,1)*%substr(%superq(a3),2,1)*%superq(a3)*;
%put A4 Len=%length(%superq(a4))*%substr(%superq(a4),1,1)*%substr(%superq(a4),2,1)*%superq(a4)*;
%*put A5 Len=%length(%superq(a5))*%substr(%superq(a5),1,1)*%substr(%superq(a5),2,1)*%superq(a5)*;
%put A6 Len=%length(%superq(a6))*%substr(%superq(a6),1,1)*%substr(%superq(a6),2,1)*%superq(a6)*;

 

A0 Len=8* * *   ; ,  *
A1 Len=198* * *   ; ,                                                                                                               
A2 Len=8* * *   ; ,  *
A3 Len=198* * *   ; ,                                                                                                               
A4 Len=6* * *   ; ,*
A6 Len=8* * *   ; ,  *

 

PROC Star
Posts: 63

Re: Reading quoted macro variables from data set SASHELP.VMACRO

I experimented a bit with that some time ago. In order to get at the macro variables longer than 200 characters, it was necessary (or so I think) to use a data step - it was before DOSUBL, so I used a datastep view, which could be read using OPEN and FETCH.

 

Using DOSUBL, you can call a datastep in a macro function, so I created an updated version of my code:

%macro getmac(name,scope=);
  %local rc wherecls rval exist;
  %let scope=%upcase(&scope);
  %let name=%upcase(&name);
  %if %length(&scope) %then
    %let wherecls=scope="&SCOPE";
  %else
    %let wherecls=scope ne "GETMAC";
%let exist=0; %let rc=%sysfunc(dosubl(%str(
options nonotes; data _null_; length rval $32767; retain rval; set SASHELP.VMACRO(where=(name="&name" and &wherecls)); by scope notsorted; substr(rval,_N_*200-199,200)=value; if .<dif(offset)<0 or last.scope; /* The dif(offset) in case of a macro calling itself */ call symputx('rval',rval);
call symputx('exist',1); /* this will only be executed if variable exists */ stop; run; )));
%if not &exist %then %do;
%if %length(&scope)=0 %then
%put WARNING: macro variable &name not found;
%else
%put WARNING: macro variable &name not found in scope &scope;
%let rval=%nrstr(&)&name;
%end; &rval %mend;

It is a pure macro function, which will return the value assigned to macro variable &NAME in the most local scope (except %GETMAC itself), unless the SCOPE parameter is used.

 

Example of usage:

%let a1=GLOBAL;

%macro x(a1);
  %put GLOBAL A1: %getmac(a1,scope=global);
  %put LOCAL A1: %getmac(A1);
%mend;

%x(LOCAL);
Super User
Super User
Posts: 6,500

Re: Reading quoted macro variables from data set SASHELP.VMACRO

[ Edited ]

Use this macro.

https://github.com/sasutils/macros/blob/master/symget.sas

 

I created it originally to let me retrieve the value of a macro variable that is currently hidden by SAS's scoping rules by a similarly named local macro variable.

 

 

Super User
Super User
Posts: 6,500

Re: Reading quoted macro variables from data set SASHELP.VMACRO

If you try to store unquoted trailing spaces into the macro variable, say by using the older CALL SYMPUT() function.

* Create macro variable with unquoted trailing spaces ;
data _null_;
  call symput('badmvar','123     ');
run;

Then it will be impossible to recreate the exact value from the view. You will either get extra trailing spaces if you use CALL SYMPUT() or remove the unquoted trailing spaces if you use CALL SYMPUTX().

 

Also if the values can be long then you need to check the OFFSET variable in the VMACRO view.  In this case even quoted spaces might get trimmed if you are not careful when they fall at the end of one of the earlier observations in the view.  If you are willing to limit support to 32K character strings that a data step can handle then you could build the string into one data step variable and then write it back into a macro variable at the end.

PROC Star
Posts: 1,561

Re: Reading quoted macro variables from data set SASHELP.VMACRO

[ Edited ]

Thanks all for your interest.

Macro %symget fails on quoted values.

I think I have solved it. Unless I forgot to consider something?

Here is a test macro:

 

%***  Fetch macro variable value from data set ***************;
%macro read(mvar=);
  %*** Init**********************************;
  %global a6;
  %local is_quoted is_ended pos i tmp offset;
  %let a6    =;
  %let offset=-200;
  %let mvar  =%upcase(&mvar);
  %*** Read value of macro variable**********;
  %let dsid=%sysfunc(open( SASHELP.VMACRO(where=(NAME="&mvar")), is));
  %let rc  =%sysfunc(fetch(&dsid));         
  %do %while(&rc=0);                            %* Append successive $200 lengths;
    %let offset=%eval(&offset+200);             %* Remember latest offset;
    %let a6 =%superq(a6)%qsysfunc(getvarc(&dsid,%sysfunc(varnum(&dsid,VALUE)))) ;
    %let rc =%sysfunc(fetch(&dsid));            
  %end;
  %let rc  =%sysfunc(close(&dsid)); 
  %*** Trim variable length******************;
  %* See is variable is quoted. Quotes start with '01'x and end with '02'x;
  %let dsid=%sysfunc(open( SASHELP.VMACRO(where=(NAME="&mvar" and first(VALUE)='01'x)), is));
  %let is_quoted=^%sysfunc(fetch(&dsid));  
  %let rc=%sysfunc(close(&dsid));               
  %if &is_quoted %then %do;                     %* Trim quoted variable;
    %do i= 1 %to 200;                           %* Find position of quote-end;
      %let dsid=%sysfunc(open( SASHELP.VMACRO
         (where=(NAME="&mvar" and OFFSET=&offset and char(VALUE,&i)='02'x)), is));
      %let is_ended=^%sysfunc(fetch(&dsid));  
      %let rc=%sysfunc(close(&dsid));          
      %if &is_ended %then %do;
        %let quoted_length=%eval(&offset+&i-2); %* Store position of quote-end;    
        %let i=200;
      %end;
    %end;
    %let a6=%qsubstr(%superq(a6),1,&quoted_length); %* Trim;  
  %end;
  %else %do;                                    %* Trim unquoted variable;
    %let a6=%qsysfunc(prxchange(s/^(.*?)\s*$/$1/,1,%superq(a6)));
  %end;
%mend;


%***  Test unquoted variable  ;
%let a0=%sysfunc(repeat(a,190))           c ;
%read(mvar=a0);
%let a7=%symget(a0);

%put -&=a0-;
%put -&=a6-;
%put -&=a7-;
%put A0 Len=%length(%superq(a0));
%put A6 Len=%length(%superq(a6));
%put A7 Len=%length(%superq(a7));

%***  Test quoted variable  ;
%let a1=%str( ; , &a0 %"            b   );
%read(mvar=a1);
%let a7=%symget(a0);

%put -&=a1-;
%put -&=a6-;
%put -&=a7-;
%put A1 Len=%length(%superq(a1));
%put A6 Len=%length(%superq(a6));
%put A7 Len=%length(%superq(a7));

77         %put -&=a0-;

-A0=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa           c-
78         %put -&=a6-;
-A6=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa           c-
79         %put -&=a7-;
-A7=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa           c-
80         %put A0 Len=%length(%superq(a0));
A0 Len=203
81         %put A6 Len=%length(%superq(a6));
A6 Len=203
82         %put A7 Len=%length(%superq(a7));
A7 Len=203
83         

 

89         %put -&=a1-;
-A1= ; ,
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa           c "            b   -
90         %put -&=a6-;
-A6= ; ,
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa           c "            b   -
91         %put -&=a7-;
-A7=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa           c-
92         %put A1 Len=%length(%superq(a1));
A1 Len=226
93         %put A6 Len=%length(%superq(a6));
A6 Len=226
94         %put A7 Len=%length(%superq(a7));
A7 Len=203

 

 

 

 

PROC Star
Posts: 1,561

Re: Reading quoted macro variables from data set SASHELP.VMACRO

Ooops apologies.

%symget works fine.

I tested the wrong value.

 

86         %let a7=%symget(a1);
90         %put -&=a7-;
-A7= ; ,
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa           c "            b   -
A7 Len=226

PROC Star
Posts: 1,561

Re: Reading quoted macro variables from data set SASHELP.VMACRO

[ Edited ]

Using   %syscall set()  makes everything so much more simple and compact.

This is the final (is it ever?) version of the logic I'll use. It's a tiny macro now. Smiley Happy

%macro read(mvar);
  %local value dsid rc ;
  %let dsid=%sysfunc(open( SASHELP.VMACRO(where=(NAME=upcase("&mvar"))), is));
  %syscall set(dsid);                
  %let rc=%sysfunc(fetch(&dsid));       
  %do %while(&rc=0);&value%let rc=%sysfunc(fetch(&dsid));%end;
  %let rc=%sysfunc(close(&dsid));             
%mend;

%put Original Len=%length(%superq(a0));
%put Read     Len=%length(%read(a0  ));
%put Symget   Len=%length(%symget(a0));
%put Original Len=%length(%superq(a1));
%put Read     Len=%length(%read(a1  ));
%put Symget   Len=%length(%symget(a1));

Original Len=203
Read     Len=203
Symget   Len=203
Original Len=226
Read     Len=226
Symget   Len=226

 

PROC Star
Posts: 63

Re: Reading quoted macro variables from data set SASHELP.VMACRO

The problem with using CALL SET that way is that the macro variables will be padded with unquoted blanks, up to the nearest multiple of 200.

For instance, using the simple %READ macro:

65         %let a=55;
 66         %put "%read(a)";
 "55                                                                                                                                 
                                                                      "
 67         %put %length(%read(a));
 2

The %length function will trim these unquoted blanks, but they are still there:

 56         %put %length(%read(a) b);
 202

As Tom remarked, there is no way to get the "correct" number of unquoted trailing blanks (e.g. as created by SYMPUT) from SASHELP.VMACRO - I prefer the datastep SYMPUTX solution (even though it reduces the maximum length of the variable to 32767) which trims these blanks from the final result. The other way you will have a lot of spurious blanks popping up in unexpected places.

Super User
Super User
Posts: 6,500

Re: Reading quoted macro variables from data set SASHELP.VMACRO

@s_lassen There is no way to know whether the original macro value has unquoted leading/trailing blanks by looking at the data available in SASHELP.VMACRO view.  The %SYMGET() macro that I posted removes the extra unquoted blanks by basically adding a %LET statement.  That functionality is not in the simplified %READ() macro posted by @ChrisNZ. But if you are careful in how you use the macro then SAS will ignore those extra blanks.  So if you avoid constructs like this that would preserve the blanks:

title "Start %read(mvar) end";

And instead use constructs like this that remove them:

%let localvar=%read(mvar);
title "Start &localvar end";

You can avoid adding extra blanks.

 

 

PROC Star
Posts: 1,561

Re: Reading quoted macro variables from data set SASHELP.VMACRO

I am not too concerned about trailing spaces, but you are right, one might as well so things properly once and for all.

 

This (Final V2!) code makes it all clean and dandy.

 

%macro read(mvar,innercall);         
  %local value dsid rc result;
  %if %length(&innercall) %then %do;           
    %let dsid=%sysfunc(open( SASHELP.VMACRO(where=(NAME=upcase("&mvar"))), is));
    %syscall set(dsid);                
    %let rc=%sysfunc(fetch(&dsid));       
    %do %while(&rc=0);&value%let rc=%sysfunc(fetch(&dsid));%end;
    %let rc=%sysfunc(close(&dsid));    
    %return;
  %end;   
  %let result=%read(&mvar,1);
  &result
%mend;

I can now use this logic in my main macro. Smiley Happy

Thank you @s_lassen and @Tom.

Solution
‎06-03-2017 06:08 AM
PROC Star
Posts: 1,561

Re: Reading quoted macro variables from data set SASHELP.VMACRO

I finally decided to make it into a standalone macro, as Tom did for his.
The final final final version is below, in case anyone is interested, as an alternative to the one @Tom linked to (though its internal logic is not that different from Tom's).

 

/*********************************************************************************************************************

  Name                symget.sas
  ¯¯¯¯
  Description         This macro retrieves the value of a macro variable regardless if its scope 
  ¯¯¯¯¯¯¯¯¯¯¯         -even if it's hidden by a local macro variable- by reading table SASHELP.VMACRO.

                      Further help is available as part of the macro

  Used as a function  Yes  
  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  Calls another macro No
  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯   
  Generates SAS code  No 
  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
  Limitations         
  ¯¯¯¯¯¯¯¯¯¯¯         
*********************************************************************************************************************
 Who             When        What             
*********************************************************************************************************************
 C Graffeuille   2017-06-01  Initial version             
*********************************************************************************************************************/

%macro symget( help
             , mvar     =
             , where    =
             , err_msg  =Macro symget could not find this variable
             , innercall=
             ) ;                        %* innercall is for internal use only.;

  %**************** Help screen *********************;
  %if %length(%superq(help)) or %length(%superq(mvar))=0 %then %do;
    %local mlogic pagesize;
    %let mlogic  =%sysfunc(getoption(mlogic  ));
    %let pagesize=%sysfunc(getoption(pagesize));
    options nomlogic pagesize=500;
    %put %nrstr(                                                                                      );
    %put %nrstr(**************************************************************************************);
    %put %nrstr(*  _____________________________                                                     *);
    %put %nrstr(*  Help screen for macro  symget                                                     *);
    %put %nrstr(*  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯                                                     *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*  This macros retrieves the value of a macro variable                               *);
    %put %nrstr(*  regardless if its scope, even if it%'s hidden by a local macro variable.           *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*  Parameters:                                                                       *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*    macvar=          REQD  Name of the macro variable to read.                      *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*    where=           OPTL  Specify conditions on the SCOPE.                         *);
    %put %nrstr(*                             Default=<blank> (i.e. nearest scope)                   *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*    err_msg=         OPTL  Value returned is teh variable is not found.             *);
    %put %nrstr(*                             Default=Macro symget could not find this variable      *);
    %put %nrstr(*  Examples:                                                                         *);
    %put %nrstr(*            1- %put Value=%symget(mvar=DSN12);                                      *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*            2- %macro outer;                                                        *);
    %put %nrstr(*                 %local mvar; %let mvar=outerval;                                   *);
    %put %nrstr(*                 %inner;                                                            *);
    %put %nrstr(*               %mend;                                                               *);
    %put %nrstr(*               %macro inner;                                                        *);
    %put %nrstr(*                 %local mvar; %let mvar=innerval;                                   *);
    %put %nrstr(*                 %put -> %Symget(mvar=mvar,where=&wh) <- (&=wh);                    *);
    %put %nrstr(*               %mend;                                                               *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let mvar=globalval;                                                 *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let wh=;                                                            *);
    %put %nrstr(*               %outer;                                                              *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let wh=SCOPE ne 'INNER';                                            *);
    %put %nrstr(*               %outer;                                                              *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let wh=SCOPE eq 'GLOBAL';                                           *);
    %put %nrstr(*               %outer;                                                              *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               %let wh=SCOPE eq 'ZZZ';                                              *);
    %put %nrstr(*               %outer;                                                              *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*               -> innerval <- (WH=)                                                 *);
    %put %nrstr(*               -> outerval <- (WH=SCOPE ne 'INNER')                                 *);
    %put %nrstr(*               -> globalval <- (WH=SCOPE eq 'GLOBAL')                               *);
    %put %nrstr(*               -> Macro symget could not find this variable <- (WH=SCOPE eq 'ZZZ')  *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(*            3- %symget(help) displays this help screen.                             *);
    %put %nrstr(*                                                                                    *);
    %put %nrstr(**************************************************************************************);
    %put %nrstr(                                                                                      );
    options &mlogic. ps=&pagesize.;
    %return;
  %end;

  %**************** Init *************************************;
  %local value scope myscope dsid rc result;

  %**************** Vet input parameters *********************;
  %if %sysfunc(notname(%superq(mvar))) 
    | %sysfunc(anydigit(%superq(mvar)))=1 
    | %length(%superq(mvar)) > 32 %then %do;
    %put %str(E)RROR: Value of parameter MVAR for macro SYMGET is not a valid SAS name.; 
    &err_msg
    %return;
  %end;

  %**************** Start processing *************************;
  %if %length(&innercall) %then %do;    %* Recursed logic. The work is done here.;       
    %let dsid=%sysfunc(open( SASHELP.VMACRO(where=(NAME=upcase("&mvar") 
                                                 and SCOPE ne 'SYMGET'
          %sysfunc(ifc(%length(%superq(where) ), and %superq(where), ))
                                           )), is));
    %if &dsid=0 %then %do;              %* Could not open SASHELP.VMACRO;
       %put %str(E)RROR: Value of parameter WHERE for macro SYMGET is invalid.; 
       &err_msg
       %return;                         %* This can only be due to a bad where clause;
    %end;                 
    %syscall set(dsid);                 %* Populate variables value and offset automatically;
    %let rc=%sysfunc(fetch(&dsid));     %* Fetch variables in data set;
    %if &rc. %then &err_msg.;           %* No macro variable with that name found in data set;
    %let myscope=&scope;                %* Ensure that only one scope is parsed;
    %do %while(&rc=0);&value            %* Write out fetched value;
      %let rc=%sysfunc(fetch(&dsid));   %* Fetch variables in data set; 
      %if &scope ne &myscope %then %let rc=1; %* Stop. Variable name found in higher scope;
    %end;                               %* Loop until no more records (or higher scope);                 
    %let rc=%sysfunc(close(&dsid));     %* Close table;
    %return;                            %* The recursion logic stops here;
  %end;   

  %* Use intermediate variable "result" to trim trailing blanks;
  %* Doing this means we must recurse for some reason;
  %let result=%symget(mvar=&mvar,where=%superq(where),innercall=1);
  &result
%mend;


 

Super User
Super User
Posts: 6,500

Re: Reading quoted macro variables from data set SASHELP.VMACRO

The current logic in this version will concatenate the values from all of the instances of the same macro name in different scopes that happen to have the same scope name. That can happen if you use recursive or nested macro calls.  You need stop when you find another observation with OFFSET=0. That indicates you have started the value for a new macro variable.

PROC Star
Posts: 1,561

Re: Reading quoted macro variables from data set SASHELP.VMACRO

You are right of course @Tom. I was doing just that and then I "simplified" the code. Thank you!
PROC Star
Posts: 1,561

Re: Reading quoted macro variables from data set SASHELP.VMACRO

So the final final final final version....

 

 

  %**************** Init *************************************;
  %local value offset thisoffset dsid rc result; %* value and offset are populated by %syscall set();
  %let thisoffset=-1;

  %**************** Vet input parameters *********************;
  %if %sysfunc(notname(%superq(mvar))) 
    | %sysfunc(anydigit(%superq(mvar)))=1 
    | %length(%superq(mvar)) > 32 %then %do;
    %put %str(E)RROR: Value of parameter MVAR for macro SYMGET is not a valid SAS name.; 
    &err_msg
    %return;
  %end;

  %**************** Start processing *************************;
  %if %length(&innercall) %then %do;    %* Recursed logic. The work is done here.;       
    %let dsid=%sysfunc(open(SASHELP.VMACRO(where=( NAME  eq upcase("&mvar") 
                                                 & SCOPE ne 'SYMGET'    
                                                 & %sysfunc(coalescec(%superq(where),1))
                                           )), is));
    %if &dsid=0 %then %do;              %* Could not open SASHELP.VMACRO;
       %put %str(E)RROR: Value of parameter WHERE for macro SYMGET is invalid.; 
       &err_msg
       %return;                         %* This can only be due to a bad where clause;
    %end;                 
    %syscall set(dsid);                 %* Populate variables value and offset automatically;
    %let rc=%sysfunc(fetch(&dsid));     %* Fetch variables in data set;
    %if &rc. %then &err_msg.;           %* No macro variable with that name found in data set;
    %do %while(&rc=0);
      %if &thisoffset < &offset %then %do; %* Ensure that only one variable is parsed;
        %let thisoffset=&offset;&value  %* Write out fetched value;
        %let rc=%sysfunc(fetch(&dsid)); %* Fetch variable values in data set; 
      %end;
      %else %let rc=1;                  %* Stop. Same variable name found in higher scope;
    %end;                               %* Loop until no more records (or higher scope);                 
    %let rc=%sysfunc(close(&dsid));     %* Close table;
    %return;                            %* The recursion logic stops here;
  %end;   

  %* Use intermediate variable "result" to trim trailing blanks;
  %* Doing this means we must recurse for some reason;
  %let result=%symget(mvar=&mvar,where=%superq(where),innercall=1);
  &result
☑ This topic is SOLVED.

Need further help from the community? Please ask a new question.

Discussion stats
  • 14 replies
  • 216 views
  • 5 likes
  • 3 in conversation