Sas 为什么在直接运行宏错误时使用CALL EXECUTE运行宏错误会起作用?

Sas 为什么在直接运行宏错误时使用CALL EXECUTE运行宏错误会起作用?,sas,Sas,我最近继承了一个SAS程序,看起来像这样: %MACRO complexidestuff(GroupId=); %让FileId=%SYSFUNC(打开(Work.BigDataSet)); %将“使用&GroupId做一些非常困难的事情”放在一起。; %让CloseRC=%SYSFUNC(CLOSE(&FileId.)); %修补复杂的填充物; %复杂的duff(GroupId=ABC1); %complexidestuff(GroupId=DEF2); %复杂的duff(GroupId=3G

我最近继承了一个SAS程序,看起来像这样:

%MACRO complexidestuff(GroupId=);
%让FileId=%SYSFUNC(打开(Work.BigDataSet));
%将“使用&GroupId做一些非常困难的事情”放在一起。;
%让CloseRC=%SYSFUNC(CLOSE(&FileId.));
%修补复杂的填充物;
%复杂的duff(GroupId=ABC1);
%complexidestuff(GroupId=DEF2);
%复杂的duff(GroupId=3GHI);
%复杂的duff(GroupId=J4KI);
作为一名多面程序员,我看到了这一点,并认为“我当然可以让它更具动态性”。果然,我能够使用
callexecute
开发出我认为是一个简单的解决方案:

DATA Work.IDs;
输入ID$4。
;
数据线;
ABC1
DEF2
3GHI
J4KI
跑
数据工作.CommandDebug;
设置Work.id;
命令=猫(
“%complexidestuff(GroupId=”,ID“);”
);
调用EXECUTE(命令);
跑
我对这个解决方案很满意,直到将ComplexDstuff生成的文件FTP到另一台服务器。我们的SAS服务器在Unix上运行,SAS管理员创建了一个有用的小宏,让我们调用名为
%SAS\u sftp
(因为,我被告知,
x
的代码非常难看)。不幸的是,我不能发布
%sas_sftp
代码-它属于我的公司,我认为他们不希望这样

我试图调用
%sassftp
宏,就像调用
%complementdstuff
宏一样(作为同一数据步骤中的第二个
调用执行
和第二个数据步骤),但只有第一个文件(大约30个)才能到达目标。当我查看日志时,看起来第二个宏是在ftp完成之前开始执行的(ftp管道或其他任何东西在下一个ftp启动之前没有被释放),因此随后的ftp只是由于资源不可用而无声地失败(我猜)

我认为EXECUTE基本上会对我的宏调用进行排队,然后执行它们,就好像它们按顺序位于代码中一样(就像它们最初一样)-一次一个。显然,还有其他一些事情正在发生,因为尽管上述第一种方法毫无问题地工作,但我的动态解决方案失败了。我倒了很多酒,但恐怕我不明白他们在说什么

我最终找到了一个解决方法(或者说,是一位同事找到的),我在下面发布了一个“答案”,但我真的希望有人解释一下执行函数及其工作原理


为什么我的第一次尝试(使用
调用EXECUTE
)不起作用?

这是一个避免使用
EXECUTE
功能的解决方法。我把它贴出来是为了帮助未来的访问者,但它并没有真正回答我的核心问题

下面的代码通过利用SQLINTO:语法,使用我想要执行的命令创建宏变量。然后我创建了一个简单的宏,它基本上迭代宏变量并解析它们(使语句像在源代码中一样执行)

procsqlnoprint;
选择计数(*)
进入:CommandCount
从Work.CommandDebug
;
选择命令
进入:Command1-:命令%LEFT(&CommandCount.)
从Work.CommandDebug
;
退出
%宏执行命令;
%I=1%到&CommandCount。;
&&命令&I.;/*解析为%complexedStuff(GroupId=ABC1);,等*/
%结束;
%修补;
%死刑;

调用执行的工作原理与您在社区wiki中的代码类似,除了一些与计时有关的特定问题。我遇到的最常见问题是,当我在执行一个宏时,该宏包含一些定义宏变量的内容,例如该宏中的
PROC-SQL-select-into
,然后该宏创建宏中使用的宏文本-与您的答案没有什么不同。由于计时规则的原因,在调用EXECUTE完成构造要执行的代码之后才会执行,这意味着代码中的值没有正确更改

这里有一个例子

%macro mymacro(age=0);
proc sql noprint;
select quote(name) into :namelist separated by ',' from sashelp.class where age=&age.;
quit;

data name_age;
set sashelp.class;
where name in (&namelist.);
run;
proc print data=name_age;
var name age;
run;
%mend mymacro;

proc sort data=sashelp.class out=class nodupkey;
by age;
run;
好的,现在我有了一个控件数据集(
class
)和一个宏来运行它。下面是调用execute的
。这不能正常工作;第一次运行时,您将收到有关&namelist未定义的消息,第二次和以后的时间,您将获得all age=16(最后一个年龄),因为这是宏变量的定义

data _null_;
set class;
exec_val = cats('%mymacro(age=',age,')');
call execute(exec_val);
run;
下面是sql宏调用。这正如预期的那样有效

proc sql noprint;
select cats('%mymacro(age=',age,')') into :calllist separated by ' '
    from class;
quit;   
&calllist;

我不认为callexecute对数据生成代码的作用比PROC-SQL宏列表解决方案更大,除非在数据步骤中更容易构造代码,而且我没有做任何会导致计时问题的事情。

调用执行中宏计算计时的一个好办法是将代码放入临时文件,然后包含该文件。当您需要提交代码的硬拷贝时(只需将fileref切换为物理文件),这对于调试也很有用


你的问题不清楚你的问题在哪里。您提到您正在尝试执行另一个宏
%sas\u sftp
,但在代码中没有显示。您正在尝试向同一数据步骤添加第二个
调用EXECUTE
,还是有第二个数据步骤只运行
%sas\u sftp
部分?顺便说一句,我还有一个同名的实用程序宏!问题可能在于宏正在做什么。我不认为mind可以运行
CALL EXECUTE
,因为它创建并执行
expect
脚本。@BellevueBob-我不能发布sas_sftp宏,部分原因是我没有访问代码的权限,部分原因是我不能在公共论坛上发布。我尝试在数据步骤中添加第二个
调用EXECUTE
。当那不起作用时,我试着
%macro mymacro(age=0);
proc sql noprint;
select quote(name) into :namelist separated by ',' from sashelp.class where age=&age.;
quit;

data name_age;
set sashelp.class;
where name in (&namelist.);
run;
proc print data=name_age;
var name age;
run;
%mend mymacro;

proc sort data=sashelp.class out=class nodupkey;
by age;
run;

filename blah temp;

data _null_;
set class;
file blah;
exec_val = cats('%mymacro(age=',age,')');
put exec_val;
run;

%include blah;

filename blah clear;