Understand the Dosubl Function in SAS
The Dosubl Function was introduced in SAS 9.3M2. According to the function documentation, the function: “Imports macro variables from the calling environment, and exports macro variables back to the calling environment.”. While this is true, it is only part of the story about Dosubl. In this post, I will try to help you understand the Dosubl Function and why you should make it part of your SAS toolkit. Furthermore, I will present an example or two and compare it to the Call Execute Routine.
The Dosubl Function
The Dosubl Function accepts a single string. The string is treated as SAS code and executed immediately. Take a look at the example below
data _null_; rc=dosubl("data a; b=1; call symputx('b', b); run;"); c=&b.; put c=; run;
A string with a simple data step is passed to the Dosubl Function. This data step is executed immediately. Therefore, I can use the macro variable b, which was created inside the Dosubl Function, directly after the execution of the function. As you can see, the value of c is successfully assigned and printed in the log.
Call Execute Comparison
The construction of the Dosubl Function naturally leads you to think of Call Execute Logic. And the comparison is fair. However, they differ in significant ways. Therefore, you can not blindly use one instead of the other. Let’s see why.
In the code below, I have three statements. The first statement prints a string in the log from the outer data step. The second statement prints a string from a Dosubl call. Finally, the third statement does the same as the first one. If you check the log, you see that the strings are printed in the same order as they appear in the data step below. This verifies the fact that Dosubl executes the code passed to the function immediately.
data _null_; put "1. This exeutes in the Data Step"; rc=dosubl("data _null_; put '2. This exeutes in Dosubl'; run;"); put "3. This exeutes in the Data Step"; run;
In the next example, I submit the exact same code as above. The only difference is that I use Call Execute instead of Dosubl. This time, the result is different. The two Put Statements from the outer data step is executed first and the Put Statement from the code submitted to Call Execute is executed last. This happens because Call Execute only runs SAS macro code immediately. Otherwise, the SAS stacks the code for later execution. This is a huge difference from the Dosubl Function.
data _null_; put "1. This exeutes in the Data Step"; call execute("data _null_; put '2. This exeutes in Call Execute'; run;"); put "3. This exeutes in the Data Step"; run;
For more information about Call Execute, see the article CALL EXECUTE made easy for SAS data-driven programming.
What You Can Not Do
As a final example, I want to point out what you can not do with the Dosubl Function. Consider the code below. Even though the Dosubl Function executes the data step immediately and creates the y data set, this yields an error. The error occurs because curing compilation of the outer data step, SAS tries to set up the PDV from the y data set. However, the y data set does not exist yet because the Dosubl Function creates it at execution. Not at compilation.
data x; rc=dosubl('data y; a=1; run;'); set y; run;
You can however do this. Which is exactly what the SAS documentation points out is the main purpose of the Dosubl Function: To import macro variables from the calling environment, and exports macro variables back to the calling environment.
data x; rc=dosubl("data y; a=1; call symputx('a', a); run;"); a=input(symget('a'), 8.); run;
This post is devoted to explaining how the Disubl Function works. I have presented a few examples and discussed that the Dosubl Function can and can not do. Furthermore, I discuss how the function is easily confused with the Call Execute Routine. However, they differ significantly. If you want to learn more about the Sas Dosubl Function, read the article Submitting SAS Code On The Side by Rick Langston. Also, Rick Langston presents the Function in detail in this video.
You can download the entire code from this post here.