SAS - 调用执行

发布于 2025-01-12 03:58:11 字数 1118 浏览 0 评论 0原文

我已经使用 callexecute 调用宏来逐行写入输出文件,但它只写入了输出文件中的最后一行。

%Macro INPS(ipdsn);
     filea = &ipdsn;
     %put MACRO:     ipdsn -->: &ipdsn;
     %put MACRO:     ipdsn -->: filea;
     newHostDSN = prxchange (re_iPat, -1, trim(&filea));
 do i=1 to f_count;
    set Var_Patterns(keep=Pat_Name Pat_Value) point=i nobs=f_count;
    re_vPat = cats('s/\$', Pat_Name, '(\.)?/', Pat_Value, '/i');
    newHostDSN = prxchange (re_vPat, -1, trim(newHostDSN));
 end;
 %Mend;
 data _null_;
     files   = 'AAAAAAAAAAAAAA,BBBBBBBBBBBBBB';
     f_count = countw(files);
     do i=1 to f_count;
         file = scan(files, i, ',');
         put 'DATA STEP: ipdsn -->: ' file;
         CALL EXECUTE (cats( '%INPS(', file, ');' ));
    end;
run;

输出:

DATA STEP: ipdsn -->: AAAAAAAAAAAAAA
MACRO:     ipdsn -->: AAAAAAAAAAAAAA
MACRO:     ipdsn -->: filea
DATA STEP: ipdsn -->: BBBBBBBBBBBBBB
MACRO:     ipdsn -->: BBBBBBBBBBBBBB
MACRO:     ipdsn -->: filea

我想将数据步骤值(文件名)存储在宏(filea)内的变量中,并将其用于宏中的其他验证(newhostdsn 和 do)。知道我该怎么做吗?

I have used call execute to call a macro to write the output file line by line but it has written only the last line in the output file.

%Macro INPS(ipdsn);
     filea = &ipdsn;
     %put MACRO:     ipdsn -->: &ipdsn;
     %put MACRO:     ipdsn -->: filea;
     newHostDSN = prxchange (re_iPat, -1, trim(&filea));
 do i=1 to f_count;
    set Var_Patterns(keep=Pat_Name Pat_Value) point=i nobs=f_count;
    re_vPat = cats('s/\

output:

DATA STEP: ipdsn -->: AAAAAAAAAAAAAA
MACRO:     ipdsn -->: AAAAAAAAAAAAAA
MACRO:     ipdsn -->: filea
DATA STEP: ipdsn -->: BBBBBBBBBBBBBB
MACRO:     ipdsn -->: BBBBBBBBBBBBBB
MACRO:     ipdsn -->: filea

I want to store the data step value (file name) in a variable inside macro(filea) and use it for other validation in macro (newhostdsn and do). any idea how can i do this?

, Pat_Name, '(\.)?/', Pat_Value, '/i'); newHostDSN = prxchange (re_vPat, -1, trim(newHostDSN)); end; %Mend; data _null_; files = 'AAAAAAAAAAAAAA,BBBBBBBBBBBBBB'; f_count = countw(files); do i=1 to f_count; file = scan(files, i, ','); put 'DATA STEP: ipdsn -->: ' file; CALL EXECUTE (cats( '%INPS(', file, ');' )); end; run;

output:

I want to store the data step value (file name) in a variable inside macro(filea) and use it for other validation in macro (newhostdsn and do). any idea how can i do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

树深时见影 2025-01-19 03:58:11

它将立即执行。

如果 EXECUTE 例程参数是宏调用或解析为
一、宏立即执行。
SAS语句的执行
执行宏所生成的内容将被延迟,直到
步边界。 SAS宏语句,包括宏变量
引用,将立即执行。

致电EXECUTE 文档

使用您的示例:

data _null_;
    files   = 'AAAAAAAAAAAAAA,BBBBBBBBBBBBBB';
    f_count = countw(files);

    do i=1 to f_count;
        file = scan(files, i, ',');
        put 'DATA STEP: ipdsn -->: ' file;
        CALL EXECUTE (cats( '%INPS(', file, ');' ));
    end;

run;

%Macro INPS(ipdsn);
    %put MACRO:     ipdsn -->: &ipdsn;
%Mend;

日志输出:

DATA STEP: ipdsn -->: AAAAAAAAAAAAAA
MACRO:     ipdsn -->: AAAAAAAAAAAAAA
DATA STEP: ipdsn -->: BBBBBBBBBBBBBB
MACRO:     ipdsn -->: BBBBBBBBBBBBBB

It will execute immediately.

If an EXECUTE routine argument is a macro invocation or resolves to
one, the macro executes immediately.
Execution of SAS statements
generated by the execution of the macro will be delayed until after a
step boundary. SAS macro statements, including macro variable
references, will execute immediately.

CALL EXECUTE documentation

Using your example:

data _null_;
    files   = 'AAAAAAAAAAAAAA,BBBBBBBBBBBBBB';
    f_count = countw(files);

    do i=1 to f_count;
        file = scan(files, i, ',');
        put 'DATA STEP: ipdsn -->: ' file;
        CALL EXECUTE (cats( '%INPS(', file, ');' ));
    end;

run;

%Macro INPS(ipdsn);
    %put MACRO:     ipdsn -->: &ipdsn;
%Mend;

Log Output:

DATA STEP: ipdsn -->: AAAAAAAAAAAAAA
MACRO:     ipdsn -->: AAAAAAAAAAAAAA
DATA STEP: ipdsn -->: BBBBBBBBBBBBBB
MACRO:     ipdsn -->: BBBBBBBBBBBBBB
尴尬癌患者 2025-01-19 03:58:11

这里的问题是您并没有真正理解 SAS 变量(和宏变量)的工作原理。

CALL EXECUTE 的作用是告诉 SAS 在数据步骤完成后立即执行 callexecute 内的代码。您的示例代码包含数据步骤行,但它们不在数据步骤内 - 它们只是在开放代码内执行。将它们放入数据步骤中,它们就会正常执行。

%Macro INPS(ipdsn);
    data _null_;
     filea = "&ipdsn";
     %put MACRO:     ipdsn -->: &ipdsn;
     %put MACRO:     ipdsn -->: filea;
     newHostDSN = trim(filea);
     put newHostDSN=;
   run;
 %Mend;
 data _null_;
     files   = 'AAAAAAAAAAAAAA,BBBBBBBBBBBBBB';
     f_count = countw(files);
     do i=1 to f_count;
         file = scan(files, i, ',');
         put 'DATA STEP: ipdsn -->: ' file;
         CALL EXECUTE (cats( '%INPS(', file, ');' ));
    end;
run;

这会将正确的值放入 newHostDSN 中。

现在,还有其他重要的事情:宏语句确实在运行“之前”执行。如果您的宏比较复杂,这一点非常重要。

请参阅以下代码。

%Macro namesbyage(name);
    proc sql;
      select age into :age
      from sashelp.class
      where name = "&name";
    quit;
    data _null_;
      set sashelp.class;
      where age = &age.;
      put age= name=;
    run;
 %Mend;
/* do not run this at first:
%symdel age;
*/
data _null_;
     set sashelp.class;
     call execute('%namesbyage('||name||')');
run;

保留注释掉的部分,运行两次。第一次,它会起作用 - 除非你会收到一大堆警告 &age 不存在 - 但它会正确地将各种内容放入日志中,年龄 12,年龄 13,等等。

第二次就不会做同样的事情了!您不会收到有关 &age 不存在的警告 - 但每次执行都将具有相同的年龄,即早期运行的最后一个年龄。

这是因为,第一次,它不会将 &age 符号转换为任何内容 - 它将其保留为 &age,因为还没有值。这样,当 proc sql; 时select into 创建 &age,它有效 - 因为这样它就解决了。

然而,第二次,&age 存在!这意味着当它创建调用执行时,它就能够立即解析&age - 无需等待。因此,您将得到 15(任何运行之前 &age 的当前值)。这不是您想要的!

现在取消注释 %symdel Age; 。这会删除年龄符号,使每次运行都像第一次一样 - 它可以工作,但有关 &age 的警告无法解决。

你该如何解决这个问题?嗯,一种方法是使用 %symdel 语句。这会删除年龄,因此您确信它不会过早解决。这并不是最好的方法,但它确实有效。

更好的是使用 %nrstr,它对解析器“隐藏”年龄。

%Macro namesbyage(name);
    
    
    proc sql;
      select age into :age
      from sashelp.class
      where name = "&name";
    quit;
    
    
    data _null_;
      set sashelp.class;
      where age = %nrstr(&age);
      put age= name=;
    run;
%Mend;

现在,年龄直到稍后才得到解决 - 在调用执行调用宏之后。

第三,您可以在调用执行中执行相同的操作!

data _null_;
     set sashelp.class;
     call execute('%nrstr(%namesbyage('||name||'))');
run;

这将从调用执行解析器中隐藏整个宏。这在计划中是最简单的——一切都会在预期的时间稍后发生。

您还可以使用 callexecute 以外的其他方法来执行宏 - 例如,您可以使用我的 select into 语法来创建一个宏变量来执行所有执行。

proc sql;
  select cats('%namesbyage(',name,')') 
    into :namelist separated by ' '
    from sashelp.class;
quit;
&namelist.;

这里的 namelist 宏变量实际上包含所有宏执行的文本 - 因此只需在开放代码中运行该宏变量即可调用它们,就像您将它们全部键入一样。这有优点(同时执行)和缺点(特别是限制为 60k 字符)。您还可以将调用写入文本文件并%include 该文本文件。


为什么你的例子没有失败?因为它使用的宏参数不是宏变量,这就是原因。如果 ipdsn 作为单独的宏变量而不是参数存在,它将被解析 - 并且无法工作,这最有可能是您在实际中看到的情况例子。

The issue here is that you're not really understanding how SAS variables (and macro variables) work.

What CALL EXECUTE does is tells SAS to execute the code inside the call execute immediately after the data step completes. Your example code contains data step lines, but they're not inside a data step - they're just executed inside open code. Put them in a data step, and they'll execute fine.

%Macro INPS(ipdsn);
    data _null_;
     filea = "&ipdsn";
     %put MACRO:     ipdsn -->: &ipdsn;
     %put MACRO:     ipdsn -->: filea;
     newHostDSN = trim(filea);
     put newHostDSN=;
   run;
 %Mend;
 data _null_;
     files   = 'AAAAAAAAAAAAAA,BBBBBBBBBBBBBB';
     f_count = countw(files);
     do i=1 to f_count;
         file = scan(files, i, ',');
         put 'DATA STEP: ipdsn -->: ' file;
         CALL EXECUTE (cats( '%INPS(', file, ');' ));
    end;
run;

That puts the right value inside newHostDSN.

Now, there is something else important: the macro statements do execute "before" the run. This is pretty important if your macro is more complicated.

See the following code.

%Macro namesbyage(name);
    proc sql;
      select age into :age
      from sashelp.class
      where name = "&name";
    quit;
    data _null_;
      set sashelp.class;
      where age = &age.;
      put age= name=;
    run;
 %Mend;
/* do not run this at first:
%symdel age;
*/
data _null_;
     set sashelp.class;
     call execute('%namesbyage('||name||')');
run;

Leaving the commented out part out, run it twice. The first time, it will work - except you'll get a whole bunch of warnings that &age does not exist - but it will correctly put various things to the log, age 12, age 13, et cetera.

The second time, it won't do the same thing! You won't get the warning about &age not existing - but every single one of the executions will be the same age, the last age from the earlier run.

That is because, the first time, it's not converting &age the symbol into anything - it leaves it as &age, since there's no value yet. That way, when the proc sql; select into creates &age, it works - because then it's resolved.

However, the second time, &age exists! That means that when it creates the call execute, it is able to resolve &age right then - no need to wait. So you get 15 (the present value of &age before anything runs) substituted in. That's not what you want!

Now uncomment %symdel age; . That deletes the age symbol, making every run like the first - it works, but with the warnings about &age not resolving.

How can you fix this? Well, one way is to use that %symdel statement. That deletes age, so you know for sure it won't be prematurely resolved. That's not really the best way, but it works.

Better, is to use %nrstr, which "hides" age from the parser.

%Macro namesbyage(name);
    
    
    proc sql;
      select age into :age
      from sashelp.class
      where name = "&name";
    quit;
    
    
    data _null_;
      set sashelp.class;
      where age = %nrstr(&age);
      put age= name=;
    run;
%Mend;

Now, age is not resolved until later on - after the macro has been called by the call execute.

Third, you can do the same thing inside the call execute!

data _null_;
     set sashelp.class;
     call execute('%nrstr(%namesbyage('||name||'))');
run;

This would hide the whole macro from the call execute parser. This is easiest in the scheme of things - everything happens later, at the expected time.

You also could use something other than call execute to execute your macros - for example, you could use my select into syntax to create a macro variable to do all of the executions.

proc sql;
  select cats('%namesbyage(',name,')') 
    into :namelist separated by ' '
    from sashelp.class;
quit;
&namelist.;

Here the namelist macro variable actually contains the text for all of the macro executions - so just running that macro variable in open code calls them all as if you'd typed them all out. This has advantages (simultaneous execution) and disadvantages (limited to 60k characters in particular). You could also write the calls out to a text file and %include that text file.


Why didn't your example fail? Because it was using a macro parameter that wasn't a macro variable, that's why. If ipdsn had existed as a separate macro variable, not a parameter, it would be resolved - and fail to work, which is most likely what you're seeing in your real example.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文