Over time we tend to collect a large number of program files. It never fails that after much time has past that you need to access some code in a file but can't recall which program the keyword was in.
I work on a Linux operating system so I can use the GREP command to handle the task but I never remember the syntax options and do not like the way the data is returned. That is why I wrote a macro named %findtext() that handles this for you in SAS via the use of a PIPE command.
If want to work for the keyword 'bitwise' in all SAS programs in a folder and any sub-folders that GREP command will be as follows:
> grep -nRi 'updatestagingdetails' '/em_data1/prod/macro' --include=*.sas
/em_data1/prod/macro/etl.sas:
/em_data1/prod/macro/
/em_data1/prod/macro/
/em_data1/prod/macro/
/em_data1/prod/macro/
Notice in the above output that the values are separated by a colon (:) and it is overall hard to read in my opinion. This is where the SAS SCAN and FIND functions come into play to create a cleaner easier to read result.
The SAS macro call will be as follows (only the first two parameters are required):
%findtext(
path = /em_data1/prod/macro
, text = updatestagingdetails
, ignorecase = Y
, exactmatch = N
, extension = sas
, outdsn = findtextresults
, printresults = Y
);
/*****************************
Program: findtext.sas
Author: Tom Bellmer
Responsible: Tom Bellmer
Created: 08/10/2020 @ 3:00:45 PM
SAS Version: SAS 9.4 (TS1M6)
OS: LIN X64
Purpose: read the contents of files searching for the string value
Usage: %findtext(path = /folder, string = sql);
Notes: colon is used as output separator. Scan() function had issues
with third column that contained colons.
Parameters: path = (folder path to be searched)
text = (text to be searched inside each file)
ignorecase = Y/N to ignore the case of the &string.
exactmatch = N/Y to match whole &string value
extension = sas (file extension, use * for all files)
outdsn = findtextresults (output data set name)
printresults = Y (proc print the results? Y/N)
Modifications in descending order
FL-YYYYMMDD
----------- ------------------------------
1 1 2 2 3 3 4 4 5 5 6 6 7 7 8
....5....0....5....0....5....
******************************
%macro findtext(
path =
, text =
, ignorecase = Y
, exactmatch = N
, extension = sas
, outdsn = findtextresults
, printresults = Y
);
%local options i;
%do i = 1 %to 100;
%local pathname&i;
%end;
%if %isblank(&path) %then %do;
%put %str(E)RROR: Must pass in a value for path.;
%return;
%end;
%if %isblank(&text) %then %do;
%put %str(E)RROR: Must pass in a value for text.;
%return;
%end;
/* i = ignores case, n = show matched line number
, R = recursively, w = match whole word */
%let options = -nR;
%if %upcase(&ignorecase) = Y %then %let options = &options.i;
%if %upcase(&exactmatch) = Y %then %let options = &options.w;
filename search pipe "grep &options '&text' '&path' --include='*.&extension'";
data &outdsn(drop = fullfilename findpos);
attrib
Path length = $256 label = 'Path'
Filename length = $128 label = 'Filename'
Lineno length = 8 label = 'Line No'
Code length = $256 label = 'Code'
Fullfilename length = $256 label = 'Full Filename'
;
infile search;
input;
fullfilename = scan(_infile_, 1, ':');
filename = scan(fullfilename, -1, "/");
path = substr(_infile_, 1, lengthn(fullfilename) - lengthn(filename));
lineno = input(scan(_infile_, 2, ':'), 8.);
findpos = find(_infile_, ":");
findpos = find(_infile_, ":", findpos + 1);
code = substr(_infile_, findpos + 1);
run;
filename search clear;
%if %getattr(dsn = &outdsn, attr = nlobs) = 0 %then %do;
proc sql;
insert into &outdsn
set path = "&path"
, filename = "N/A"
, lineno = 0
, code = "No results were found using '&text'"
;
quit;
%end;
%if %upcase(%substr(&printresults, 1, 1)) = Y %then %do;
proc sql noprint;
select distinct path
into :pathname1 -
from &outdsn;
quit;
title "Search results for: &text";
%if &sqlobs = 1 %then %do;
title2 "In Path: &pathname1";
%end;
proc print data = &outdsn;
%if &sqlobs = 1 %then %do;
var filename lineno code;
%end;
run;
title;
%end;
%mend;
/*EOF: findtext.sas */