Macro系列导读

SAS Macro作为SAS高手不可或缺的一项技能,是因为它功能足够强大,能极大的提升程序开发效率;使你的时间和精力投入在更有价值的事情上。

Macro系列文章,将会逐一奉上SAS Macro的点点滴滴,带你踏上从认识Macro到熟练使用Macro之路。掌握SAS Macro,将会使你的SAS编程能力更上一层楼。

上期文章“”这一主题进行了介绍。在每种编程语言中都有一些字符具有特殊的含义,但是在某些情况下我们不需要他们有特殊的含义,只是单纯的当作一个字符来使用。在宏语言中也不例外,如果想要将具有特殊含义字符当作普通字符来使用,这时候局需要用到Macro Quoting。所以,简单来说Macro Quoting就是将宏语言中具有特殊功能字符的功能隐藏掉。

Macro系列(13)—Macro Storing

本期文章对Macro Storing这一主题进行介绍,如何将宏程序存储起来,方便每次调用。也可以将其共享给团队内部,使得整个团队提升工作效率。同时,也可以将有些核心的程序编译存储,在分享给他人使用的同时,对源代码也起到保护的作用。

一般情况下,我们在调用某个自定义的宏程序时,需要先对该宏程序的定义部分(即%MACRO语句和%MEND语句部分)运行一次。运行完这部分宏程序定义后,SAS会将该宏程序编译并存在WORK.SASMACR这个catalog中。如果不先运行自定义宏程序的定义部分,而直接调用该宏程序,SAS会提示没有该宏程序。

当关闭SAS,再次打开后。如果想要再次调用该自定义的宏程序,同样需要再次运行该宏程序的定义部分。那么,有没有办法可以比较方便的重复使用宏程序呢?

1. 最直接的方法

最为常用的方法也就是上面所讲的手动执行。将所有的宏程序定义保存在一个SAS程序中,在SAS中打开这个程序,运行整个程序编译所有的宏程序;或者选择性的运行部分宏程序的定义,编译部分需使用的宏程序。被编译后的宏程序会存储在WORK.SASMACR这个catalog中。例如:

2. 相对正规的方法

此种方法需要用到一个宏语句 %INCLUDE 。首先同样需要将所有的宏程序定义放在一个SAS程序中。在需要调用其中的宏之前,使用 %INCLUDE 语句将该程序文件包含到当前的SAS会话中。其本质与上面的方法是一样的,只不过相对简单。但是使用这种方法,就只能运行编译整个程序文件。

其用法如下:

%include "D:sasmacrosmacro_programexample.sas" ;

运行后日志如下:

可以看到,日志当中只显示了这一段代码,并无其他信息。 但其实SAS会运行程序文件中的所有程序。因为该程序中只有宏程序的定义,因此并无日志显示出来 。在WORK逻辑库可以看到的确生成了该程序文件中定义的3个宏。

接下来,就可以直接调用其中的宏使用了。在实际工作中此种方式也最为普遍,因为最为简单、方便。针对一个团队,可以将一些公共宏程序统一放到共享路径,由专门的人负责维护管理,他人负责使用即可。

3. 使用Autocall Macro

上面两种方法其实可以归结为同一种方法,只不过实现的路径不同。但都存在一个缺点:如果程序文件中包含很多宏程序的定义,而往往有时候只需要调用其中的一个宏,那么其他宏程序的编译其实就是多余的。

Autocall Macro就可以解决这个问题,通过使用这种方式,会在调用宏的时候,SAS自动搜索相对应宏程序的代码,然后对其进行编译( 在同个SAS会话中,只在第一次调用时编译,第二次调用会跳过编译过程 ),进而运行该宏。所以称之为Autocall。

此种方式又可以根据存储方式的不同分为两种,下面分别进行介绍。

3.1. 将目录作为Autocall库

要实现此种方式,需要以下三个步骤:

  • 将所有的宏程序分别存储到一个SAS程序文件中, 并且以宏程序的名字命名该程序文件 。然后将所有的宏程序文件放到一个特定的目录下;

  • 使用FILENAM语句将存储所有宏程序文件的目录关联一个FILEREF;

  • 使用MAUTOSOURCE系统选项打开Autocall机制(默认打开),并且通过SASAUTO=系统选项将该FILEREF指定为Autocall库;

  • 在完成上面三个步骤后就可以直接调用其中的宏使用,在调用的时候SAS会自动搜索该宏对应的宏程序文件(这也是为什么要求程序文件名必须跟宏的名字一致的原因);然后进行编译,存储到WORK.SASMACR中。

    将宏程序分别存储到不同程序文件中

    使用FILENAME语句

    filename mymacros "D:sasmacrosautocall_macros_directory" ;

    使用系统选项

    options mautosource sasautos =(sasautos mymacros);

    接下来,就可以直接调用宏程序使用了。例如:

    需要注意SASAUTOS=系统选项,因为SAS在启动时会初始化自带的一些Autocall库,所以SASAUTOS会有默认的值。如果直接覆盖SASAUTOS的值,那么SAS系统自带的宏将会不可用。因此在赋值的时候,要加上默认的SASAUTOS值。

    SASAUTOS的默认值可在SAS启动的配置文件“sasv9.cfg”中找到。若默认语言为中文,则该配置文件位于:

    {SASHOME}SASFoundation9.4nlszh

    打开该文件夹下的sasv9.cfg文件,即可找到默认的SASAUTOS对应的路径( 也可以直接将自己的存储宏程序的路径添加在此处,就省去以上的2、3步 )。

    3.2. 将SAS Catalog作为Autocall库

    此种方式与上面的方式基本一样,只是宏程序存储的方式不同。上面是将其存储在某个目录下的分散的程序文件中。而此种方式是将程序源码存储在SAS catalog中。需要以下四个步骤:

  • 使用LIBNAME语句将用于存储catalog的目录分配一个逻辑库;

  • 使用FILENAM语句在该逻辑库下创建一个catalog;

  • 将各宏程序的定义源码以SOURCE ENTRY的形式分别存入该catalog下, 个SOURCE ENTRY的名字与宏程序的名字保持一致

  • 使用MAUTOSOURCE系统选项打开Autocall机制(默认打开),并且通过SASAUTO=系统选项将该CATALOG指定为Autocall库;

  • 在完成上面四个步骤后就可以直接调用其中的宏使用,在调用的时候SAS会自动搜索该宏对应的SOURCE ENTRY(这也是为什么要求SOURCE ENTRY名必须跟宏的名字一致的原因);然后进行编译,存储到WORK.SASMACR中。

    分配逻辑库

    libname mylib "D:sasmacrosautocall_macros_catalog" ;

    创建CATALOG

    filename mymacros catalog 'mylib.myautos' ;

    将宏程序源码存入CATALOG

    filename macro1 catalog 'mylib.myautos.proc_print.source' ;

    data _null_ ;

    infile datalines truncover ;

    file macro1;

    input str $50. ;

    put str $;

    datalines4 ;

    %macro proc_print(dsn, var);

    proc print data=&dsn;

    var &var;

    %mend proc_print;

    run ;

    filename macro2 catalog 'mylib.myautos.proc_means.source' ;

    data _null_ ;

    infile datalines truncover ;

    file macro2;

    input str $50. ;

    put str $;

    datalines4 ;

    %macro proc_means(dsn, var);

    proc means data=&dsn;

    var &var;

    %mend proc_means;

    run ;

    filename macro3 catalog 'mylib.myautos.proc_freq.source' ;

    data _null_ ;

    infile datalines truncover ;

    file macro3;

    input str $50. ;

    put str $;

    datalines4 ;

    %macro proc_freq(dsn, var);

    proc freq data=&dsn;

    table &var;

    %mend proc_freq;

    run ;

    运行完毕后,即可再MYLIB逻辑库下看到MYAUTOS这个CATALOG,及该CATALOG下的三个宏程序对应的SourceEntry。

    使用系统选项

    options mautosource sasautos =(sasautos mymacros);

    接下来,就可以直接调用了:

    4. 存储编译宏

    Autocall的方式虽然能够自动搜索相应的宏程序进行编译运行,但是每次首次调用宏时仍然需要对宏程序源码进行编译,然后执行宏。

    但是通过存储编译后的宏,就可以在调用时省去宏程序编译的时间,提高效率。要实现存储编译宏,需要以下四步:

  • 使用LIBNAME语句定义存储编译宏的逻辑库;

  • 使用系统选项MSTORED打开存储编译宏功能,使用系统选项SASMSTORE=指定存储编译宏的逻辑库;

  • 在%MACRO语句中增加STORE选项;

  • 提交%MACRO-%MEND宏程序定义代码,编译并将其存储至指定逻辑库中;

  • 分配逻辑库

    libname macrolib "D:sasmacrosstored_macros" ;

    使用系统选项

    option mstored sasmstore =macrolib;

    增加STORE选项

    %macro proc_print(dsn, var) / store;

    proc print data=&dsn;

    var &var;

    %mend proc_print;

    提交运行代码

    提交上述代码,运行后日志如下:

    打开MACROLIB逻辑库,可以看到编译后的宏程序存储在MACROLIB.SASMACR中。

    利用以上方式存储有一个缺点,就是需要备份宏程序的源代码,因为存储的是编译后的宏,并不能从编译的宏得到源代码。如果后期对源代码升级优化后,需要重新编译存储。

    除了备份源代码,也可以在%MACRO语句中增加一个 SOURCE 选项,使其在存储编译宏的同时将源代码也一并存储。这样就可以不用手动备份源代码了。通过%COPY语句就可以将源代码读取出来,例如:

    %macro proc_means(dsn, var) / store source;

    proc means data=&dsn;

    var &var;

    %mend proc_means;

    % copy proc_means / source;

    运行后,日志如下:

    除此之外,还有一种需求,就是封装核心代码。他人只能使用,却看不到其中的源代码。通过存储编译宏的方式也可以达到,只需在%MACRO语句中增加 SECURE 选项即可。通过%COPY语句就无法查看其源代码。 例如:

    %macro proc_freq(dsn, var) / store secure;

    proc freq data=&dsn;

    table &var;

    %mend proc_freq;

    % copy proc_freq / source;

    运行后日志文件:

    可以看到日志显示因为SECURE的缘故,是无发显示源码的。 即使是使用MPRINT、MLOGIC选项,也不会将相关的代码及宏变量显示出来 ,只会显示运行的日志:

    本期文章到这里就结束了,主要针对Macro Storing这一主题进行了说明。在日常工作中比较多用的是%INCLUDE语句,因为最为方便。如果需要对某些敏感信息进行封装,可通过加密编译存储的方式,这样使用者就无法看到源码 更多内容,请持续关注SAS中文论坛——“SAS岩论-Macro系列”。

    作者:辛岩,从事了多年的SAS数据分析挖掘工作,担任过项目经理、技术顾问、培训讲师等职务,拥有丰富的项目实战经验。

    剑指SAS,尽在今朝。欢迎各位技术达人交流,可以长按下面群二维码入SAS中文论坛微信群@Slash,也可以发邮件:slash.xin@hotmail.com。

    更多SAS岩论系列文章