5

I'm just trying to concatenate two quoted macro variables but there doesn't seem to be an easy way.

Say we have:

%LET VAR1="This is not the greatest song in the world";
%LET VAR2="this is just a tribute.";

%LET TRIBUTE=%SYSFUNC(CATX(%STR( ),&VAR1,&VAR2));
%PUT &TRIBUTE;

I actually want:

  "This is not the greatest song in the world this is just a tribute."

But the above code actually yields:

"This is not the greatest song in the world"  "this is just a tribute."

So I try putting %QUOTE(),%BQUOTE,etc. around &VAR1 and %VAR2 in hopes of unmasking the quotes but I get the same result.

The only thing that works for me is:

 %LET TRIBUTE="%SUBSTR(&VAR1.,2,%LENGTH(&VAR1.)-2) %SUBSTR(&VAR2.,2,%LENGTH(&VAR2.)-2)"; 

But this is ugly and can get lengthy really fast. Is there not a better way to do this ?

Pane
  • 555
  • 2
  • 7
  • 20

2 Answers2

4

I'm going to paraphrase Joe's 'real answer' which is - do not store quotes in the macro variable. Single and double quotes in the macro language are no different to any other character. What you should do is delay introducing the quotes until you actually need them. This will result in much cleaner, more flexible, easier to read, and bug-free code.

Code:

Notice I've removed the quotes and to concatenate the strings I'm just listing them one after the other:

%LET VAR1=This is not the greatest song in the world;
%LET VAR2=this is just a tribute.;
%LET TRIBUTE=&VAR1 &VAR2;

Example 1

No quotes are needed to print out the desired string as we are using a %put statement in this first example - for this reason I left the quotes out:

%PUT &TRIBUTE;

Output :

This is not the greatest song in the world this is just a tribute.

Example 2

Quotes are required because we are now in data-step land:

data _null_;
  put "&TRIBUTE";
run;

Output :

This is not the greatest song in the world this is just a tribute.

Note that both of these examples assume you don't actually want to print the quotes to the screen.

Robert Penridge
  • 8,424
  • 2
  • 34
  • 55
  • Normally I would avoid quotes in macro variables as you say. However, I have a couple of general-use macros requiring parameters that may or may not contain commas. This might cause a positional parameter error if not quoted, so as a rule, whenever I write these macros I require the parameters be quoted, just to be safe. But then I might want to concatenate in the body of the macro or whatnot and here is where my question comes in play. – Pane Aug 28 '14 at 14:03
  • 1
    The correct way to deal with commas to avoid the problem you describe is to wrap the values with the `%bquote()` macro function. – Robert Penridge Aug 28 '14 at 15:21
  • @Pane Indeed, quote characters shouldn't be used for this purpose. – Joe Aug 28 '14 at 15:22
2

You can use COMPRESS to do this.

%LET VAR1="This is not the greatest song in the world";
%LET VAR2="this is just a tribute.";


%let VAR3=%sysfunc(compress(&VAR1,%str(%")));
%put &=var1 &=var3;

It's a bit tricky to remove quotes, but it works.

You could also do this in a FCMP function, or a function-style macro; here is one example.

%macro unquote_string(string=);
%sysfunc(compress(&string.,%str(%'%")))
%mend unquote_string;

%let VAR3="%unquote_string(string=&var1.) %unquote_string(string=&var2.)";
%put &=var3.;

Note you shouldn't use CAT functions to concatenate macro variables. They're just text, so typing one after the other automatically concatenates them.

However, the real answer to your question of 'Is there a better way', is to not store the quotes in the macro variable. Most of the time you should store the macro variable w/o quotes and use it within quotes when needed. SAS Macros do not respect quotes as anything special - they're just a character in a string - so they don't have a specific tool for dealing with this.

Joe
  • 62,789
  • 6
  • 49
  • 67
  • Two things... firstly - I didn't know about the `&=` syntax - thanks! Secondly, you could simplify your var3 assignment to `%let VAR3="%unquote_string(string=&var1 &var2.)";`. – Robert Penridge Aug 27 '14 at 20:10
  • You absolutely could simplify it that way. I like the above a bit more, though, as it's more clear what you're removing quotes from - sort of parallel to how `%UNQUOTE` works as far as macro quoting. (I think you'd have to call it twice if these were macro-quoted mvars). – Joe Aug 27 '14 at 20:12
  • Actually, now that I think about it, it's kind of a bad example of when to create an FCMP function or macro function, as you're simply wrapping calls to an existing function.... may as well just call the function directly. – Robert Penridge Aug 27 '14 at 20:12
  • The reason to call it as a macro is that it's less messy, and you can tell more readily immediately what it is doing (if you name your function/macro properly). `%SYSFUNC(COMPRESS(&string.,%STR(%"%')))` is both longer and takes a bit of mental processing to realize what it's doing, while `%UNQUOTE_STRING(&string.)` is immediately obvious what it's doing. – Joe Aug 27 '14 at 20:13
  • True. It's a shame `%sysfunc()` is such a mouthful. I usually tend to go for more of a self-documenting code approach. So I'd do something like define a var `%let chars_to_remove=%STR(%"%');` Which then simplifies the %sysfunc call to a more readable: `%let unquoted_string=%SYSFUNC(COMPRESS(&string.,&chars_to_remove));` – Robert Penridge Aug 27 '14 at 20:20
  • Neat, `%let VAR3=%sysfunc(compress(&VAR1 &VAR2,%str(%")));` will work just fine. I know that quoting macrovars is a really bad habbit but in some cases (see my reply to Robert's answer) it can become necessary. Also it seems that `&=` won't work for poor folk like me still on 9.2 :P – Pane Aug 28 '14 at 15:00