BookmarkSubscribeRSS Feed

SAS Studio Custom Task Tuesday: How to Create the Dataset Directory Search Task

Started ‎01-17-2017 by
Modified ‎08-04-2021 by
Views 5,233

Have you ever wanted to search a SAS library for a keyword, to find a specific dataset or list of datasets that include a phrase, prefix, or suffix in their name or variable names? If so, the SAS Studio custom task functionality is here to help. The task we are going to construct with this week’s post is a dataset directory search task. Custom Task Tuesday.jpg

 

The dataset directory search task allows the user to input the name of their SAS library and the key word they want to search for, and then outputs a list of datasets that contain the keyword in their name, and a list of datasets that contain the keyword in their variable names.

 

Here’s what the finished product looks like:

search.PNG

 

Step 1: Getting Started

 new task.png

In SAS Studio, under the Task and Utilities section, open a “New Task” as well as the “Sample Task.” We will copy and paste the necessary Velocity Template code from the Sample Task to our task.

 

 

Step 2: Naming and Saving the Task

 

Name: Dataset Directory Search

Description: Search a SAS library for user-specified key words.

At the top of the VTL code for your New Task, you will need to fill in the Name and Description portions as shown below:

 blah.png

After you’ve done that, you should save this task to your My Tasks folder, so you don’t lose it. Click the edity.pngbutton in the upper left corner of the task to bring up this option screen:

 

directory.png

 

Step 3: Creating the Metadata Portion of the Task

 

Just like in previous blog posts, we are going to use the built-in Sample Task to borrow VTL code. From our “finished product,” you can see that all we are going to need are two text boxes. Find the code that correspond with the text boxes in the Metadata section of the Sample Task, and copy and paste them into the same place in your task. Edit to code you copied to correspond with what we want as our finished product.

 

This is what your finished Metadata portion should look like:

           

 

<Metadata>                  
    <Options>
        <Option name="DATATAB" inputType="string">DATA </Option>
              <Option name="GROUPTEXT" inputType="string">TEXT SEARCH </Option>
              <Option name="labelTEXT1" inputType="string">Enter the libname of the library you would like to search. </Option>
              <Option name="LIBNAME" defaultValue="WORK" inputType="inputtext"
                     indent="1"
                     required="true"
                     promptMessage="Enter libname."
                     missingMessage="Missing text.">Libname: </Option>
              <Option name="labelTEXT2" inputType="string">Enter the keyword you would like to search for. </Option>
              <Option name="KEYWORD" defaultValue="KEY" inputType="inputtext"
                     indent="1"
                     required="true"
                     promptMessage="Enter keyword."
                     missingMessage="Missing text.">Keyword: </Option>
</Options> </Metadata>

 

 

Step 4: Creating the UI Portion of the Task

 

In Custom Tasks, each object that we just put code for in the metadata portion requires corresponding code in the UI section. Just like we did in step 3, find the code that correspond with text boxes in the UI section of the Sample Task, and copy and paste them into the same place in your task. Edit to code you copied to correspond with what we want as our finished product.

This is what your finished UI portion should look like:

 

 

<UI>
   <Container option="DATATAB">
       <Group option="GROUPTEXT" open="true">
           <OptionItem option="labelTEXT1"/>
           <OptionItem option="LIBNAME"/>
           <OptionItem option="labelTEXT2"/>
           <OptionItem option="KEYWORD"/>
       </Group>
   </Container>
</UI>  

 

 

Step 5: Creating the Code Template Portion of the Task

 

This is the portion of the task that contains your SAS Code, and the part that actually makes the options of the user work with your SAS code. Velocity Template Language has its own macro variables, and each of our UI elements has one. When you select an option in the UI, the value of the VTL macro variable will change immediately.

This SAS code uses proc contents to identify the presence of key words in the user’s library (in dataset names and variable names). Two separate datasets are created for the presence of the key word in the name, and the presence of the key word in the variable name. If the datasets are non-empty, they are printed out with a proc print. 

 

 

proc contents data = $LIBNAME._all_ noprint out=CONTENTS;
run;
data CONTENTS;
       set CONTENTS (keep = MEMNAME NAME);
       DATASET = upcase(MEMNAME);
       VARIABLE = upcase(NAME);
run;
data CONTENTS_dataset;
       set CONTENTS;
       where dataset contains upcase("$KEYWORD");
       drop MEMNAME NAME;
run;
data CONTENTS_variable;
       set CONTENTS;
       where variable contains upcase("$KEYWORD");
       drop MEMNAME NAME;
run;
title "Observations where $KEYWORD was in the dataset name:";
proc print data = CONTENTS_dataset;
run;
title "Observations where $KEYWORD was in the variable name:";
proc print data = CONTENTS_variable;
run;

 

Step 6: Run the Dataset Directory Search Task

 

You’re finished! You just created a custom user interface to search a library for a keyword in dataset names and variables names. Click the save.pngbutton to save, then click the run.pngbutton to open the task. Make your selections, then click run.pngagain to watch it run!

 

Want to try it yourself?

Get the code from the zip file at the end of this article or from GitHub.

 

Take Me to GitHub!

 

Your Turn

Can you think of a way to customize or improve the functionality of this task? Post it in the comments below!

Comments
this routine overwrites a work library table.
When you wish to avoid that and have no control over the environment, consider using _DATA_
See the SAS Global Forum paper 2120-2016 "More Hidden Base SAS® Features to Impress Your Colleagues"

Thank you @Peter_C for your feedback! That would be a great addition to this task. I'll consider using that in the future but if you are interested in adding it to this task yourself, you can easily download my task, edit it, and save a new version. 🙂

Thanks again for your interest!

I have attached a new version of the task that uses _DATA_ and will not create any datasets in the WORK library. Thanks again for the idea and the help @Peter_C! 🙂

Very nice explanation!   To anyone who wishes to expand the functionality to include data values, the following macro (part of the core library) may be of use: mp_searchdata

 Is there a way to search for the SAS Data set variable in all sub-directories of the parent folder as well ?

 

Thanks

Menaka

Hi @Menakabm! I'm sure there is a way to do that but I can't think of one off the top of my head. You would not be able to use the PROC CONTENTS approach for this because PROC CONTENTS only recognizes SAS files, and therefore doesn't recognize subfolders.

@Menakabm as this thread is about templates using proc contents your problem won't change the principles of this thread. However, it is an interesting challenge for the SAS programming environment. If you cross post into a relevant forum (SAS programming) someone might tell you that you need to submit a libname statement for each folder in the subdirectory structure under the start folder..... and there is a part solution for this at
https://www.google.co.uk/url?sa=t&source=web&rct=j&url=http://support.sas.com/kb/45/805.html&ved=2ah...

Thank you all for the response!

 

I have found a way to print members of folder (see code below) and later used proc contents to search for specific SAS dataset variables.

===================================================================================================

 

%macro getdirmembers_detail(dirpath,outdata=dirmembers,errdata=getdir_errors);

%global getdirrc subdirflag;
%let subdirflag=0;

data &outdata(keep=member memname memext memcount dirpath dirlevel memtype
                   owner group permissions lastmoddt filesize)
     &errdata(keep=member dirpath dirlevel errmsg);
   length member memname $ 300 memext $ 40 dirpath $ 600
          memtype $ 4 errmsg $ 200 
          owner group permissions lastmod $ 40;
   format lastmoddt datetime20. ;
   label member='Member' memname='Member Name' memext='Member Extension'
         memcount='Members in Folder' dirpath='Parent Path' memtype='Dir or File'
         owner='Owner' group='Group' permissions='Permissions'
         lastmoddt='Last Mod Date' filesize='File Size(bytes)';

   dirpath=strip("%str(&dirpath)");
   dirlevel=countw(dirpath,'/');
   call symputx('getdirrc','0','G');
   call symputx('getdirmsg',' ','G');

   /*-----------------------------------------------------------------
   * Check to see if the directory exists.  If so, proceed here. 
   *-----------------------------------------------------------------*/
   if fileexist(dirpath) ne 0 then do;
       rc=filename('extdir',dirpath);
       did=dopen('extdir');

       /*-----------------------------------------------------------------
       * After attempting to open the directory, see if it succeeded
       * If so proceed here.  Get the number of members found into memcount
       *-----------------------------------------------------------------*/
       if did then do;     
           memcount=dnum(did);

           /*-----------------------------------------------------------------
           * Loop through the members and figure out what they are.  
           *-----------------------------------------------------------------*/
           do i=1 to memcount;
              member=dread(did,i);
              /*-----------------------------------------------------------------
              * First, look for some bad actors.  May deal with these later.  
              *-----------------------------------------------------------------*/
              if countc(member,"'")=1 then do;
                 errmsg='Member contains a single quote and has been excluded from analysis';
                 output &errdata;
              end;
              else if countc(member,'"')=1 then do;
                 errmsg='Member contains a double quote and has been excluded from analysis';
                 output &errdata;
              end;
              else if index(member,'(') and not index(member,')') then do;
                 errmsg='Member contains a left parenthesis but no right parenthesis so has been excluded from analysis';
                 output &errdata;
              end;
              else if index(member,')') and not index(member,'(') then do;
                 errmsg='Member contains a right parenthesis but no left parenthesis so has been excluded from analysis';
                 output &errdata;
              end;
              else if index(member,'*') then do;
                 errmsg='Member contains an asterisk so has been excluded from analysis';
                 output &errdata;
              end;  
     
              /*-----------------------------------------------------------------
              * Process each member to see if a directory or a file
              *-----------------------------------------------------------------*/
              else do;  
                 tempname=strip(dirpath) || '/' || strip(member);
                 trc=filename('testfile',tempname);
                 *put 'NOTE:  Return code on filename function=' trc;
                 fid=fopen('testfile','I');
                 /*---------------------------------------------------------
                 * Attempt to open as a file.  If it is, get attributes
                 *---------------------------------------------------------*/
                 if fid then do;
                       memtype='FILE';
                       memext=scan(member,-1,'.');
                       memname=scan(member,1,'.');
                       /*---------------------------------------------------------
                       * Note that the position of these attributes are different
                       * per operating system.  This is set up for Unix/Linux
                       *---------------------------------------------------------*/
                       owner=finfo(fid,foptname(fid,2));
                       group=finfo(fid,foptname(fid,3));
                       permissions=finfo(fid,foptname(fid,4));
                       lastmod=finfo(fid,foptname(fid,5));
                       lastmoddt=input(lastmod,datetime20.);
                       filesize=finfo(fid,foptname(fid,6));
            
                       fc=fclose(fid);
                       output &outdata;
                 end;
                 /*-----------------------------------------------------------------
                 * If not a directory and cannot open as file, it is either an
                 * unreadable file or we do not have permission.  Write to error
                 * file. 
                 *-----------------------------------------------------------------*/
                 else do;
                    tid=dopen('testfile');
                    /*---------------------------------------------------------
                    * If the member opens as a diretory, then flag it and output.
                    *---------------------------------------------------------*/
                    if tid then do;
                       memtype='DIR';
                       memname=member;
                       memext=' ';
                       call symputx('subdirflag','1','G');
                       /*---------------------------------------------------------
                       * Note that the position of these attributes are different
                       * per operating system.  This is set up for Unix/Linux
                       *---------------------------------------------------------*/
                       owner=dinfo(tid,doptname(tid,2));
                       group=dinfo(tid,doptname(tid,3));
                       permissions=dinfo(tid,doptname(tid,4));
                       lastmod=dinfo(tid,doptname(tid,5));
                       lastmoddt=input(lastmod,datetime20.);
               
                       tc=dclose(tid);
                       output &outdata;
                    end;
                    else do;
                       put "WARNING: " member " could not be opened";    
                       errmsg='fopen and dopen failed for file ' || tempname;
                       output &errdata;
                    end;
                 end; 
     
                 rc=filename('testfile');
              end;  /* end of else do for each member */
           end;  /* End do i=1 to memcount */
           cid=dclose(did);

       end; /* End If did */
       /*-----------------------------------------------------------------
       * If Directory open failed, then issue a message.
       * probably permissions.  
       *-----------------------------------------------------------------*/
       else do;        
         getdirmsg=sysmsg();
         errmsg=getdirmsg;
         if errmsg ne ' ' then put errmsg;
         else put "WARNING: " dirpath  " could not be opened";
         call symputx('getdirmsg',strip(getdirmsg),'G');
         call symputx('getdirrc','4','G');
         output &errdata;
       
       end;
       rc=filename('extdir');

    end; /* End If fileexist */
    /*-----------------------------------------------------------------
    * If file could not be found, issue a message. 
    *-----------------------------------------------------------------*/
    else do; 
       put "ERROR: " dirpath " does not exist.";
       getdirmsg=sysmsg();
       errmsg=getdirmsg;
       call symputx('getdirmsg',strip(getdirmsg),'G');
       call symputx('getdirrc','12','G');
       output &errdata;
    end;
 run;

%mend getdirmembers_detail;

 

 

Version history
Last update:
‎08-04-2021 01:12 PM
Updated by:

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!

Free course: Data Literacy Essentials

Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning  and boost your career prospects.

Get Started

Article Tags