Macros to Manage your Macros? Garrett Weaver, University of Southern California, Los Angeles, CA

Similar documents
Storing and Reusing Macros

Utilizing the Stored Compiled Macro Facility in a Multi-user Clinical Trial Setting

Using a Control Dataset to Manage Production Compiled Macro Library Curtis E. Reid, Bureau of Labor Statistics, Washington, DC

Copy That! Using SAS to Create Directories and Duplicate Files

Functions vs. Macros: A Comparison and Summary

SAS 101. Based on Learning SAS by Example: A Programmer s Guide Chapter 21, 22, & 23. By Tasha Chapman, Oregon Health Authority

How to Create Data-Driven Lists

Going Under the Hood: How Does the Macro Processor Really Work?

SAS Macro Dynamics - From Simple Basics to Powerful Invocations Rick Andrews, Office of the Actuary, CMS, Baltimore, MD

PharmaSUG 2013 CC26 Automating the Labeling of X- Axis Sanjiv Ramalingam, Vertex Pharmaceuticals, Inc., Cambridge, MA

Better Metadata Through SAS II: %SYSFUNC, PROC DATASETS, and Dictionary Tables

New Macro Features Added in SAS 9.3 and SAS 9.4

SAS ENTERPRISE GUIDE USER INTERFACE

Program Validation: Logging the Log

ABSTRACT MORE THAN SYNTAX ORGANIZE YOUR WORK THE SAS ENTERPRISE GUIDE PROJECT. Paper 50-30

Arthur L. Carpenter California Occidental Consultants, Oceanside, California

Automated Checking Of Multiple Files Kathyayini Tappeta, Percept Pharma Services, Bridgewater, NJ

A Simple Framework for Sequentially Processing Hierarchical Data Sets for Large Surveys

Hidden in plain sight: my top ten underpublicized enhancements in SAS Versions 9.2 and 9.3

An Introduction to SAS/SHARE, By Example

PCKG: Managing SAS Macro Libraries. Magnus Mengelbier, Limelogic Ltd, London, United Kingdom

SAS Macro Dynamics: from Simple Basics to Powerful Invocations Rick Andrews, Office of Research, Development, and Information, Baltimore, MD

Get Started Writing SAS Macros Luisa Hartman, Jane Liao, Merck Sharp & Dohme Corp.

Create a Format from a SAS Data Set Ruth Marisol Rivera, i3 Statprobe, Mexico City, Mexico

Your Own SAS Macros Are as Powerful as You Are Ingenious

Introduction. Getting Started with the Macro Facility CHAPTER 1

Getting Up to Speed with PROC REPORT Kimberly LeBouton, K.J.L. Computing, Rossmoor, CA

Automation of makefile For Use in Clinical Development Nalin Tikoo, BioMarin Pharmaceutical Inc., Novato, CA

Customized Flowcharts Using SAS Annotation Abhinav Srivastva, PaxVax Inc., Redwood City, CA

SAS Macro Programming Tips and Techniques

ABSTRACT INTRODUCTION MACRO. Paper RF

Essential ODS Techniques for Creating Reports in PDF Patrick Thornton, SRI International, Menlo Park, CA

Creating and Executing Stored Compiled DATA Step Programs

The Output Bundle: A Solution for a Fully Documented Program Run

Paper ###-YYYY. SAS Enterprise Guide: A Revolutionary Tool! Jennifer First, Systems Seminar Consultants, Madison, WI

The DATA Statement: Efficiency Techniques

SAS/Warehouse Administrator Usage and Enhancements Terry Lewis, SAS Institute Inc., Cary, NC

PharmaSUG Paper PO12

KEYWORDS Metadata, macro language, CALL EXECUTE, %NRSTR, %TSLIT

Useful Tips When Deploying SAS Code in a Production Environment

Preserving your SAS Environment in a Non-Persistent World. A Detailed Guide to PROC PRESENV. Steven Gross, Wells Fargo, Irving, TX

Paper HOW-06. Tricia Aanderud, And Data Inc, Raleigh, NC

Quick Data Definitions Using SQL, REPORT and PRINT Procedures Bradford J. Danner, PharmaNet/i3, Tennessee

A SAS Macro Utility to Modify and Validate RTF Outputs for Regional Analyses Jagan Mohan Achi, PPD, Austin, TX Joshua N. Winters, PPD, Rochester, NY

The Ins and Outs of %IF

SAS Data Libraries. Definition CHAPTER 26

The Power of PROC SQL Techniques and SAS Dictionary Tables in Handling Data

Using GSUBMIT command to customize the interface in SAS Xin Wang, Fountain Medical Technology Co., ltd, Nanjing, China

What Do You Mean My CSV Doesn t Match My SAS Dataset?

Taming a Spreadsheet Importation Monster

SAS Programming Techniques for Manipulating Metadata on the Database Level Chris Speck, PAREXEL International, Durham, NC

Using PROC REPORT to Cross-Tabulate Multiple Response Items Patrick Thornton, SRI International, Menlo Park, CA

%MISSING: A SAS Macro to Report Missing Value Percentages for a Multi-Year Multi-File Information System

Validation Summary using SYSINFO

Macro Facility. About the Macro Facility. Automatic Macro Variables CHAPTER 14

Text Generational Data Sets (Text GDS)

Checking for Duplicates Wendi L. Wright

Lecture 1 Getting Started with SAS

A Quick and Gentle Introduction to PROC SQL

%MAKE_IT_COUNT: An Example Macro for Dynamic Table Programming Britney Gilbert, Juniper Tree Consulting, Porter, Oklahoma

A Macro that can Search and Replace String in your SAS Programs

Posters. Workarounds for SASWare Ballot Items Jack Hamilton, First Health, West Sacramento, California USA. Paper

The Impossible An Organized Statistical Programmer Brian Spruell and Kevin Mcgowan, SRA Inc., Durham, NC

An Efficient Method to Create Titles for Multiple Clinical Reports Using Proc Format within A Do Loop Youying Yu, PharmaNet/i3, West Chester, Ohio

ABSTRACT: INTRODUCTION: WEB CRAWLER OVERVIEW: METHOD 1: WEB CRAWLER IN SAS DATA STEP CODE. Paper CC-17

Tales from the Help Desk 6: Solutions to Common SAS Tasks

PharmaSUG Paper CC02

The GEOCODE Procedure and SAS Visual Analytics

Different Methods for Accessing Non-SAS Data to Build and Incrementally Update That Data Warehouse

A Generalized Macro-Based Data Reporting System to Produce Both HTML and Text Files

A Practical Introduction to SAS Data Integration Studio

Making do with less: Emulating Dev/Test/Prod and Creating User Playpens in SAS Data Integration Studio and SAS Enterprise Guide

Quick and Efficient Way to Check the Transferred Data Divyaja Padamati, Eliassen Group Inc., North Carolina.

An Efficient Tool for Clinical Data Check

A Macro to Keep Titles and Footnotes in One Place

Pros and Cons of Interactive SAS Mode vs. Batch Mode Irina Walsh, ClinOps, LLC, San Francisco, CA

Matt Downs and Heidi Christ-Schmidt Statistics Collaborative, Inc., Washington, D.C.

Macro Quoting: Which Function Should We Use? Pengfei Guo, MSD R&D (China) Co., Ltd., Shanghai, China

CHAPTER 7 Examples of Combining Compute Services and Data Transfer Services

Job Security: Using the SAS Macro Language to Full Advantage

ABSTRACT INTRODUCTION THE GENERAL FORM AND SIMPLE CODE

SAS Drug Development Program Portability

A Macro for Systematic Treatment of Special Values in Weight of Evidence Variable Transformation Chaoxian Cai, Automated Financial Systems, Exton, PA

Tracking Dataset Dependencies in Clinical Trials Reporting

CHAOTIC DATA EXAMPLES. Paper CC-033

SAS Viya 3.1 FAQ for Processing UTF-8 Data

Acknowledgments xi Preface xiii About the Author xv About This Book xvii New in the Macro Language xxi

Paper AP14 Modifying The LogParse PassInfo Macro to Provide a Link between Product Usage in Rtrace Log and Time Used in Job Log

SAS Viya 3.2: Self-Service Import

LST in Comparison Sanket Kale, Parexel International Inc., Durham, NC Sajin Johnny, Parexel International Inc., Durham, NC

SAS File Management. Improving Performance CHAPTER 37

PharmaSUG Paper TT11

A SAS Macro for Producing Benchmarks for Interpreting School Effect Sizes

FSEDIT Procedure Windows

Using the SQL Editor. Overview CHAPTER 11

Files Arriving at an Inconvenient Time? Let SAS Process Your Files with FILEEXIST While You Sleep

A Methodology for Truly Dynamic Prompting in SAS Stored Processes

Quality Control of Clinical Data Listings with Proc Compare

Utilizing SAS for Cross- Report Verification in a Clinical Trials Setting

PharmaSUG China Paper 059

Transcription:

Macros to Manage your Macros? Garrett Weaver, University of Southern California, Los Angeles, CA ABSTRACT SAS currently offers several methods for users to store and retrieve user-generated macros. The stored macro compiled facility is one such method that allows users to store a macro, along with the source code, to a location of their choice. To more effectively utilize the stored compiled macro facility, we developed a solution that enables a user to upload, delete, and search for macros generated by multiple users in a common directory. Along with presentation of this solution, we intend to highlight a number of coding techniques with uses beyond this particular example. Techniques include user management, automatic creation and deletion of files, regular expressions, use of PROC REPORT, and a number macro development techniques. Through the implementation of these macros, an organization can increase collaboration and code reuse without ever having to leave the SAS environment. INTRODUCTION For the individual user, SAS provides three primary methods to permanently store user created macros. The first and easiest method is to save your macro in its own program file and then to use the %INCLUDE statement in any other program that requires use of the macro. The %INCLUDE statement only requires that you provide the physical location of the program file that contains the macro as follows: %INCLUDE 'C:/.../MyMacro.sas'; The macro stored in the program will then be stored in an external.txt file that is then incorporated into your program at the position of the %INCLUDE statement. Another method is the Autocall Library, which stores the source code for a macro to a specific directory or SAS catalog for access at a later time. The Autocall method requires that the user specify a file where the macros will be stored using the SASAUTOS system option, and to set the system option MAUTOSOURCE to ON when they want SAS to search the assigned Autocall Library for saved macros. The last method, the Stored Compiled Macro Facility, will be the focus of our discussion for the remainder of the paper and is the technique that allows for the development of our macros. Similar to the Autocall Library, the Stored Compiled Macro Facility will compile and the store the source code for user compiled macros to a chosen SASMACR Catalog. A user simply needs to enable this macro facility, provide the location where the macros will be stored, and include a couple of additional options in their macro definition as shown below: *enable stored compiled macro facility, set location of stored macros to the location of the macro variable &libn; %LET libn = C:/ /macro_folder_location/; OPTIONS MSTORED SASMSTORE=&libn; *store the macro, %test, to &libn, store the macro and the source code; %MACRO test() / STORE SOURCE DES="Testing"; %put Testing; %MEND test; The option STORE tells SAS to permanently store the macro %test() to the SASMACR catalog location specified by &libn. SOURCE specifies that the source code for the macro should also be saved permanently as well. To utilize the stored macros at a later time, a user simply needs to reference the location of the SASMACR catalog by using the system option, MSTORED SASMSTORE=, again. Regardless of choice, one of the three methods will most likely meet the needs of an individual SAS user for storing their macros. However, in an environment that requires storage of macros from multiple users across a group or organization, the methods available may not be sufficient. A number of issues may be encountered that include overwriting macros of the same name across users, organization of macros in the shared catalog, and the ability to efficiently share macros between users. Thus, we have developed the following four macros that utilize the Stored Compiled Macro Facility to enable effortless storage of macros across multiple users to a common and searchable directory. The following paper will provide an in depth discussion how each macro works and how they can be utilized by a user after being implemented.

MACRO #1: USER LOGIN AND MACRO DIRECTORY CREATION - checkuser() In order for users to start sharing their macros with one another, we designed a simple user directory (Figure 1). Within a parent folder, called Macros, are two permanent SAS datasets that contain data on users (users.profiles), metadata for generated macros across all users (meta.metadata), and a subfolder called Users. Within the Users folder are additional folders, one for each user, that hold their SAS macro catalog and the corresponding source code, saved as.txt files, for each of their saved macros. To start uploading and searching for macros, both new and current users simply need to use the %INCLUDE statement to call the SAS program user_macros.sas, which holds all four macros reviewed in this paper. The expectation is that the common directory and the SAS program, user_macros.sas, will be placed on a server that is accessible to the entire user community. Macros Users Metadata User.Profiles. Meta.metadata User 1 User 2 User 3 Figure 1. User Directory Structure To determine whether the user is in the directory already we always run the %checkuser() macro first (this macro automatically runs when the %INCLUDE for user_macros.sas is executed). Within this macro, we take advantage of the system macro variable &SYSUSERID, which returns the user ID associated with the SAS installation. As an example, if I were to write and execute the following on my PC, the SAS log will show GarrettWeaver: %PUT &SYSUSERID; *GarrettWeaver; We use &SYSUSERID as both the user name in the user profile dataset (users.profile) and to identify the existence of a user in the directory. After assigning the location of the Users folder and &SYSUSERID to the local macro variables &loc and &uname using the %LOCAL command, we check for the existence of the user by using the FILEEXIST() function. FILEEXIST() accepts a file name as a string and will return one if the file exists and zero if it does not. If the user folder is found, we assign the library reference name associated with that user from the (users.profiles) dataset to a macro variable called &libn. The macro variable &libn is then used to assign the location of their folder in the directory through a LIBNAME statement. Lastly, we activate the stored compiled macro facility and assign to the same folder location by using the &libn macro variable as shown below: OPTIONS MSTORED SASMSTORE=&libn; 2

To better understand, let s assume that there are no users in the directory. When the macro executes, FILEEXIST() will first check to see if a file exist in the Users folder that has a user name that matches the current &SYSUSERID. Since the directory is empty, FILEEXIST() returns a zero and the macro proceeds on to the else statement to create a new user. When creating a new user, we must ensure that the (users.profiles) dataset exists prior to adding a new record. This is accomplished with the function EXIST(dataset_name), which again returns a one if the dataset exists and a zero if it does not exist. If the dataset exists, we determine the number of observations in the (users.profiles) dataset, which is equivalent to the number of unique users in the directory. Each user is assigned both a user name and a user ID that increments by one for every new user added (i.e. first user has userid = 1, second has userid = 2,.etc). By taking one plus the number of observations in (users.profiles) we can now assign the user name by using the &SYSUSERID, a unique user ID, and a permanent library reference name where all the user s macros will be stored for the new user. The library reference name takes on the form User(userID) (i.e. userid = 1 will have the associated library reference User1). All of this data is saved in the (users.profiles dataset). In the case that the users.profiles dataset does not exist yet, we generate the initial observation: username = &SYSUSERID, userid = 1, library = User1 After saving the new user to the (users.profiles) dataset, the library reference name is assigned to the file location where we will store the new user s macros. The stored compiled macro facility is activated and assigned to the same file location. Note in the code below where we use the SAS system option DLCREATEDIR. Invoking this system option forces the creation of a new folder when a library references a file location that does not actually exist. Use of this option turns out to be a simple way to automate the generation of each user s folder and removes the need for an administrator to manually create and assign folders in the directory. %MACRO checkuser(); %LOCAL uname numusers loc; *assign macro vars as local; *assign directory; %LET loc = C:/Users/GarrettWeaver/desktop/macros/users/; LIBNAME users "&loc."; %LET uname = &SYSUSERID; *determine if user exists in directory, if not, then create new folder for user; %IF %SYSFUNC(FILEEXIST("&loc&sysuserid./")) %THEN %DO; SET users.profiles; IF SYMGET('uname') = username THEN CALL SYMPUT('libn',library); STOP; LIBNAME &libn "&loc&sysuserid./"; OPTIONS MSTORED SASMSTORE=&libn; %*executed if user does not exist, adds user, folder and information to user profiles dataset; %ELSE %DO; %IF %SYSFUNC(EXIST(users.profiles)) %THEN %DO; IF 0 THEN SET users.profiles nobs=n; CALL SYMPUT('numusers',TRIM(LEFT(PUT(n,8.)))); STOP; run; %LET libn = user%eval(&numusers+1); PROC SQL; INSERT INTO users.profiles SET userid = "%EVAL(&numusers+1)", username = "&SYSUSERID.", LIBRARY = SYMGET('libn'); QUIT; OPTIONS DLCREATEDIR; 3

LIBNAME &libn "&loc.&sysuserid./"; OPTIONS MSTORED SASMSTORE=&libn; OPTIONS NODLCREATEDIR; %*condition executed only for creation of first user record and folder; %ELSE %DO; %LET libn = user1; DATA users.profiles; LENGTH userid $5; userid = "1"; username = SYMGET('sysuserid'); library = "user1"; OPTIONS DLCREATEDIR; LIBNAME &libn "&loc&sysuserid./"; OPTIONS MSTORED SASMSTORE=&libn; %MEND checkuser; %checkuser() MACRO #2: UPLOADING MACROS TO THE DIRECTORY macroupload() With the appropriate location assigned for where our macros will be compiled to, we are now ready to upload our first macro. Prior to using %macroupload(), a user needs to compile the macro with the options STORE, SOURCE, and DES= (des is optional). STORE tells SAS that we want to save the macro to the library specified when we activated the stored compiled macro facility. SOURCE specifies that we would like to also store the source code for the macro, and DES= allows for us to include a short description for the macro. The general format is as follows: %MACRO test() / STORE SOURCE DES="Testing"; %PUT Testing; %MEND test; After compiling the macro, we run %macroupload(macro_name, author_name, category) with parameters for macro name, author name, and category. To upload the test macro above, we would run the following: %macroupload (test, Garrett Weaver, Test) If completed correctly, the log will print out the statement, The macro was successfully uploaded! Note that when you initially compiled the macro, the macro and source code were already stored to the catalog in the user folder. By running %macroupload(), we complete three additional steps. 1. Check to see if the macro name is a duplicate of one already in the user catalog 2. Create a.txt file of the source code in the user folder 3. Generate metadata for the macro saved into a separate dataset (meta.metadata) to be used in the search function. There are a couple pieces of code we would like to highlight in %macroupload(). First, just as there was a function to check for the existence of a file and dataset, we also have the function LIBREF() to check for the existence of a library reference. In all four macros, we always check that the (user.profiles) and (meta.metadata) datasets have valid library references. You will also notice the use of the TRIM() and LOWCASE() functions when checking for duplicates. These are important to have when matching character variables to ensure trailing blanks or capitalization do not affect string matching. Lastly, after assigning the location where the.txt file will be stored (&dest), we make use of the %COPY macro function to copy the source code to that location, the default output of %COPY is a.txt file: 4

%COPY &macroname / SOURCE OUT=&dest; In the event that a macro already exists in the catalog with the same name, the user will receive a warning message in the log. By checking for duplicates, we preserve the source code in the.txt file, which allows the user to recompile the original macro from the.txt file and then select a unique name for the new macro. %MACRO macroupload(macroname,authorname,category)/store source; %*assign local macro variables; %LOCAL dest dup mname; %LET dest = "C:/Users/GarrettWeaver/desktop/macros/users/&sysuserid./%lowcase(&macroname).t xt"; %LET uname = &sysuserid; %*Assign library where user macros stored if not assigned already; %IF (%SYSFUNC(LIBREF(users))) NE 0 %THEN %DO; LIBNAME users C:/Users/GarrettWeaver/desktop/macros/users/; SET users.profiles; IF SYMGET('uname') = username THEN DO; CALL SYMPUT('libn',library); CALL SYMPUT('uid',userid); STOP; END; %*check if meta library assigned, if not, then assign to appropriate libary; %IF (%SYSFUNC(LIBREF(meta))) NE 0 %THEN %DO; LIBNAME meta 'C:/Users/GarrettWeaver/desktop/macros/metadata/'; %*check to see if metadata dataset exists, if it does, then add a new record to it with the new macro; %IF %SYSFUNC(EXIST(meta.metadata)) & %SYSFUNC(CEXIST(&libn..sasmacr.&macroname..macro)) %THEN %DO; %*assign local macro var for macro name; %LET mname = %SYSFUNC(TRIM(%LOWCASE(&macroname))); %*check to ensure macro name has not already been used; DATA _Null_; SET meta.metadata; IF SYMGET('mname') = LOWCASE(name) THEN DO; CALL SYMPUT('dup',1); STOP; END; %*upload macro if name is not a duplicate; %IF &dup NE 1 %THEN %DO; PROC SQL; INSERT INTO meta.metadata SET 5 userid="&uid.", author="&authorname.", category="&category.", name="&macroname."; QUIT; %*copy actual macro code to.txt file on server; %COPY &macroname / SOURCE OUT=&dest; %PUT The Macro was successfully uploaded!;

%*if name is a duplicate, warn user in log; %IF &dup = 1 %THEN %DO; %PUT This Macro Variable Name Already Exists!; %PUT Please change name of macro and rerun. Also, rerun source code of original macro; %ELSE %IF %SYSFUNC(CEXIST(&libn..sasmacr.&macroname..macro))%THEN %DO; DATA meta.metadata; length name $32 userid $5 author $50 category $50; userid = "&uid."; name = "&macroname."; author = "&authorname."; category = "&category."; %COPY &macroname / SOURCE OUT=&dest; %PUT The Macro was successfully uploaded!; %ELSE %DO; %PUT Macro upload failed.; %PUT Please ensure you have specified 'store' and 'source' in macro definition.; %MEND macroupload; MACRO #3: SEARCHING MACROS BY NAME AND KEYWORD macrosearch() Once you have a significant number of macros uploaded by a user group, you may be interested in viewing your colleagues macros for a number of reasons. Perhaps they have developed a new annual report, or you want to see how they developed a particular graphic using proc template, or maybe you want to identify different techniques to solve a particular problem. Regardless of the reason, you want to be able to view and potentially utilize macros within your user community without disturbing the original source code. The following macro allows you to search for macros by both macro name and keywords that are matched to text placed in the DES = option for any macro in the directory, without ever opening the source code. The search can be done by macro name or keywords as follows: %macrosearch(macro_name, keywords) The first challenge we encountered is to reference all the macro catalogs in a single library. To solve this problem, we reference multiple file locations with a single LIBNAME reference by separating the individual LIBNAME references with a comma: LIBNAME combined (library1,library2,..,etc); After compiling all user macros under one library reference, we format the keywords into a regular expression to search text in the des = option. The regular expression generated in this macro is simple and can be expanded upon by those with more experience in free text search techniques. In its simplest form, we want to find any macro that has one or more of the keywords that we input. The form of the regular expression is as follows, /word1 word2 word3 word(n)/i where i signifies that we will ignore the case of the words in the search. Thus, we need to collect the keywords entered into macrosearch() and place them into the correct form. To complete this task, we first determine the number of keywords entered and then use a do loop to concatenate the words together with a in between each word. To create the final search string, we must use the %STR() function to add the forward slashes into the string. %STR() is used to identify special characters as being part of the string and prevents misinterpretation of the characters when the code is executed. With a completed search string, we now use the PRXPARSE() and PRXMATCH() functions to complete a search the macro descriptions. PRXPARSE() creates a Perl regular expression from the search string we generated. myregex = PRXPARSE(&searchstr); 6

PRXMATCH() takes our search string and source text (the macro description) as parameters. PRXMATCH(myRegEx,desc); If one of the keywords is located in the macro description, PRXMATCH() returns the starting position where the match was found. Zero is returned if no matches are found. Now all that must be done is to output a dataset that contains the name and descriptions of all macros in the combined library: PROC CATALOG CATALOG=combined.sasmacr; CONTENTS out=mymacros; QUIT; We then subset the output dataset (mymacros) to those records that match either by macro name or description. With the final set of matching macros, we add additional information from (meta.metadata) and output a report that displays the macro name, author, category, and a link to view the macro code directly in SAS. The link is accomplished through the use of proc report. In order to generate the URL link in the report, we use CALL DEFINE() to modify the format of the column containing the link: COMPUTE view; href="c:/users/garrettweaver/desktop/macros/users/" STRIP(username) "/" STRIP(LOWCASE(name)) ".txt"; CALL DEFINE(_col_,"URL",href); ENDCOMP; We now have a report that allows us to directly view any matching macro without worrying about changing someone else s source code. *Macro to help combine all user libraries into one library to search all available macros; %MACRO libassign(); %IF (%SYSFUNC(LIBREF(&ulib.))) NE 0 %THEN %DO; LIBNAME &ulib. "C:/Users/GarrettWeaver/desktop/macros/users/&uloc."; %IF &libcomb= %THEN %LET libcomb=&ulib.; %ELSE %LET libcomb=%sysfunc(catx(&comma,&libcomb.,&ulib.)); %MEND libassign; *Main search macro; %MACRO macrosearch(name,terms) / STORE SOURCE; *Initialize macro variables; %LET libcomb=; %LET comma=%str(,); %*Assign library where users stored if not assigned already; %IF (%SYSFUNC(LIBREF(users))) NE 0 %THEN %DO; LIBNAME users C:/Users/GarrettWeaver/desktop/macros/users/; %*Create library of macros compiled from all user folders; SET users.profiles; CALL SYMPUT('uloc',name); CALL SYMPUT('ulib',library); CALL EXECUTE('%libassign'); LIBNAME combined (&libcomb.); %*Determine if meta library assigned, if not, assign library; %IF (%SYSFUNC(LIBREF(meta))) NE 0 %THEN %DO; libname meta "C:/Users/GarrettWeaver/desktop/macros/metadata/"; 7

%*Create search string from search terms entered; %LOCAL n count sep wordcnt searchstr word; %IF &name NE %THEN %LET n=%sysfunc(trim(%lowcase(&name))); %LET count=1; %LET sep=%str( ); %IF &terms NE %THEN %DO; %LET wordcnt = %SYSFUNC(COUNTW(&terms)); %DO i=1 %TO &wordcnt; %LET word = %SCAN(&terms,&count); %IF &count = 1 %then %LET searchstr = &word; %ELSE %LET searchstr = %SYSFUNC(CATX(&sep,&searchstr,&word)); %LET count = %EVAL(&count+1); %LET searchstr = "%STR(/)&searchstr.%STR(/)i"; %*output dataset with all user compiled macros; PROC CATALOG CATALOG=combined.sasmacr; CONTENTS out=mymacros; QUIT; %*Subset to macros that match by name or keyword search; DATA matches; SET mymacros; RETAIN myregex; %IF &searchstr NE %THEN %DO; IF _N_ = 1 THEN myregex = PRXPARSE(&searchstr); match = PRXMATCH(myRegEx,desc); mname = SYMGET('n'); IF match > 0 mname = LOWCASE(name); name = LOWCASE(name); LABEL name="macro Name" desc="description" crdate="date Created" moddate="date Modified" view="view" href="location" libname="library"; %*merge matching macros with metadata and user profiles (author,category,username,userid); PROC SORT DATA=matches; BY name; %IF %SYSFUNC(EXIST(meta.metadata)) %THEN %DO; PROC SORT DATA=meta.metadata; BY name; DATA macro_author; MERGE matches (IN=a) meta.metadata; BY name; IF a; %ELSE %DO; DATA macro_author; SET matches; %IF %SYSFUNC(EXIST(users.profiles)) %THEN %DO; PROC SORT DATA=users.profiles; BY userid; PROC SORT DATA=macro_author; BY userid; DATA macro_detailed; MERGE macro_author (IN=a) users.profiles; BY userid; href="c:/users/garrettweaver/desktop/macros/users/" STRIP(username) "/" STRIP(LOWCASE(name)) ".txt"; 8

view = "View Code"; IF a; LABEL author="author" category="category" view="view"; %*Print macros for viewing; TITLE1 "Search Results: Available User-Compiled Macros"; FOOTNOTE1 "Server Location: C:/Users/GarrettWeaver/desktop/macros/users/"; PROC REPORT DATA=macro_detailed NOWD; COLUMN name author category desc crdate username view; DEFINE desc / FLOW STYLE(COLUMN)=[CELLWIDTH=4in]; DEFINE username / DISPLAY NOPRINT; COMPUTE view; href="c:/users/garrettweaver/desktop/macros/users/" STRIP(usernam e) "/" STRIP(LOWCASE(name)) ".txt"; CALL DEFINE(_col_,"URL",href); ENDCOMP; %MEND macrosearch; Example: macrosearch() Let s take a look at simple example in which the directory contains two macros from a single user. The first macro, called %test(), contains a single %PUT statement with some text. The second macro, %regress(), provides simple linear regression and is saved with the description, Simple linear regression using proc glm. We run %macrosearch() to look for any macro with name test or a description that contains the word regression : The resulting output is shown below: Output 1. Results of %macrosearch(test,regression) From the generated report, a user can then review a macro by selecting the corresponding View Code in the last column of the report. As an example, if we select the %regress() macro, we are redirected to the following code: Output 2. View generated when View Code selected for regress() macro From this example, we see that users now have a simple method to access macros across their entire SAS user group by both a specific name and general keyword searches without accessing the source code. 9

MACRO #4: DELETING MACROS macrodelete() Within a short time span, a user is likely to generate a large number of macros and to continually improve upon their design and efficiency. As their macro library grows, there will of course be a need to remove obsolete code from the directory. Introduce %macrodelete(), which shares a number of techniques in common with those utilized in %macroupload(). To delete a macro from one s directory, simple type the name of the macro: %macrodelete(macroname); %macrodelete() will proceed to remove the macro from the user catalog, the.txt source file, and the associated metadata from (meta.metadata). Within this macro, we would like to bring attention to the technique used to delete the.txt file. fname = "tempfile"; rc = FILENAME(fname,"&sastxt."); rc = FDELETE(fname); We associate the location of the.txt file that needs to be deleted (&sastxt) with the file reference fname by using the FILENAME() function. After creating the reference, we use the FDELETE() function to remove the.txt file from the folder. FDELETE() is a simple function to use that accepts a string containing the file location and will proceed to delete the file if found. The user is provided with feedback as to whether the file was found and deleted in the log after execution of the code. With all four macros, user can now upload, delete, and search for macros across the directory. %MACRO macrodelete(macroname); %LOCAL uid userid mname sastxt; *Assign library where users stored if not assigned already; %IF (%SYSFUNC(LIBREF(users))) NE 0 %THEN %DO; LIBNAME users "C:/Users/GarrettWeaver/desktop/macros/users/"; %LET uname = &SYSUSERID; SET users.profiles; IF SYMGET('uname') = username THEN DO; CALL SYMPUT('libn',library); CALL SYMPUT('uid',userid); END; STOP; %IF %SYSFUNC(CEXIST(&libn..sasmacr.&macroname..macro)) %THEN %DO; %LET mname = %SYSFUNC(LOWCASE(%SYSFUNC(STRIP(&macroname)))); %LET userid = %SYSFUNC(STRIP(&uid)); %LET sastxt = C:/Users/GarrettWeaver/desktop/macros/users/&sysuserid./&mname..txt; proc CATALOG CATALOG=&libn..sasmacr FORCE; DELETE &mname / ET=MACRO; fname = "tempfile"; rc = FILENAME(fname,"&sastxt."); rc = FDELETE(fname); 10

%*Determine if meta library assigned, if not, assign library; %IF (%SYSFUNC(LIBREF(meta))) NE 0 %THEN %DO; LIBNAME meta "C:/Users/GarrettWeaver/desktop/macros/metadata/"; %*remove metadata for macro from dataset; DATA meta.metadata; SET meta.metadata; IF name = "&mname." AND userid = "&userid." THEN delete; %PUT Macro Successfully Deleted!; %ELSE %DO; %PUT Macro name not found! Please check spelling and try again.; %MEND macrodelete; CONCLUSION By implementing the above macros, a user will be able to maintain a personal directory of SAS macros and exchange macros across a local group. Given that the macros presented above are a first attempt at implementing a macro code collaboration solution, a number of improvements and modifications can be made in order to fit within a particular environment. This is just one technique from which I hope the SAS user community can expand upon to create more integrated coding environments within both academic and industry settings. REFERENCES Hemedinger, Chris. SAS trick: get the LIBNAME statement to create folders for you. The SAS Dummy. July 2, 2013. Available at http://blogs.sas.com/content/sasdummy/2013/07/02/use-dlcreatedir-to-create-folders/. McMahill, Allison. 2007. Beyond the Basics: Advanced PROC REPORT Tips and Tricks. Proceedings of the SAS Global Forum 2007 Conference. Cary, NC: SAS Institute Inc. Pless, Richard. 2004. An Introduction to Regular Expressions with examples from Clinical Data. Proceedings of the Twenty-Ninth Annual SAS Users Group International Conference, Cary, NC: SAS Institute Inc. SAS Institute Inc., Base SAS Procedures Guide, Second Edition, Cary, NC: SAS institute Inc.. 2012 SAS Institute Inc., SAS 9.2 Language Reference: Dictionary, Fourth Edition, Cary, NC: SAS Institute Inc. 2010 Stojanovic, Mirjana & Hollis, Donna. 2005. Ways to Store Source Codes and How to Retrieve Them. Proceedings of the SouthEast SAS Users Group, Portsmouth, VA: SouthEast SAS Users Group 11

ABOUT THE AUTHOR Garrett Weaver is a PhD student in Biostatistics at the University of Southern California. With only a little over a year of experience with SAS, he is interested in the continued expansion of his SAS skillset as one of his primary analysis and data management tools. AUTHOR CONTACT Your comments and questions are valued and encouraged. Contact the author at: Name: Garrett Weaver Enterprise: University of Southern California, Department of Preventive Medicine, Division of Biostatistics Address: 2001 N. Soto Street, SSB201A City, State, ZIP: Los Angeles, CA 90089 Email: gmweaver@usc.edu SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. indicates USA registration. Other brand and product names are trademarks of their respective companies. 12