create a macro variable about filename in SAS - sas

This is the first step which would be used throughout all the project later:
(a) Create a macro variable using a %LET statement called ‘directory'where you can type the name of the directory that contains all the files of interest for this project.
(b) Create a temporary library called ‘datapath’ that links to this directory in part (a)
my code:
%let directory = C:\users\downloads;
%LET directory = 'C:\users\Downloads';
Libname datapath &directory.;
run;

in SAS 9.3 the following works:
%let libtest = test; /* &libtest --> name of library */
%LET directory = 'C:\users\Downloads'; /* &directory --> location on disk */
%let table = table; /* &table --> name of dataset in library test */
Libname &libtest &directory;
To test:
data &libtest..&table;
x=0;
run;

I don't really know what your question is, but if you are having issues, try using double quotes instead of single quotes. That works for me sometimes. Also, you created a macro variable called directory, then immediately overwrote it. You also don't need that "run;" statement at the end.

Related

Calling macros from within a datastep

Maintaining somebody else's SAS project, I found some code snippet which creates a table input-stats within a data step. The variable &all. contains a list of tables to examine. The data step is rather long which I shortened here with /* more code */:
%let all = "work.table1*mywork.table2";
data input-stats;
i = 1;
do while (scan(&all., i, '*') ne '');
name = scan(&all., i, '*');
/* more code */
output;
i = i + 1;
end;
run;
I want to expand the table input-stat with yet another column giving me the number of lines of each table. I found the following macro within the project to do exactly this:
%macro count_rows(ds);
%let DSID=%sysfunc(OPEN(&DS.,IN));
%let NOBS=%sysfunc(ATTRN(&DSID.,NOBS));
%let RC=%sysfunc(CLOSE(&DSID.));
&nobs
%mend;
I would like to now integrate this call within the above mentioned data step, but obviously, I cannot just simply add a rows=%count_rows(name) (e.g. instead of /* more code */) as I would in other programming languages.
How would you solve this issue with minimal code modifications? Is there a way without making a huge %while loop?
The functionality of the macro code can be replicated in DATA Step scope by invoking the same functions. No need for macro and intermingling scopes that can be confusing when using RESOLVE or CALL EXECUTE.
...
name = scan(&all., i, '*');
/* more code */
* add row counting code here;
_dsid = open (name,'IN');
nobs = attrn(_dsid,'NOBS');
_dsid = close (_dsid);
drop _:;
output;
...

Remove single quotes in list of values in macro variable

I have a project with multiple programs. Each program has a proc SQL statement which will use the same list of values for a condition in the WHERE clause; however, the column type of one database table needed is a character type while the column type of the other is numeric.
So I have a list of "Client ID" values I'd like to put into a macro variable as these IDs can change, and I would like to change them once in the variable instead of in multiple programs.
For example, I have this macro variable set up like so and it works in the proc SQL which queries the character column:
%let CLNT_ID_STR = ('179966', '200829', '201104', '211828', '264138');
Proc SQL part:
...IN &CLNT_ID_STR.
I would like to create another macro variable, say CLNT_ID_NUM, which takes the first variable (CLNT_ID_STR) but removes the quotes.
Desired output: (179966, 200829, 201104, 211828, 264138)
Proc SQL part: ...IN &CLNT_ID_NUM.
I've tried using the sysfunc, dequote and translate functions but have not figured it out.
TRANSLATE doesn't seem to want to allow a null string as the replacement.
Below uses TRANSTRN, which has no problem translating single quote into null:
1 %let CLNT_ID_STR = ('179966', '200829', '201104', '211828', '264138');
2 %let want=%sysfunc(transtrn(&clnt_id_str,%str(%'),%str())) ;
3 %put &want ;
(179966, 200829, 201104, 211828, 264138)
It uses the macro quoting function %str() to mask the meaning of a single quote.
Three other ways to remove single quotes are COMPRESS, TRANSLATE and PRXCHANGE
%let CLNT_ID_STR = ('179966', '200829', '201104', '211828', '264138');
%let id_list_1 = %sysfunc(compress (&CLNT_ID_STR, %str(%')));
%let id_list_2 = %sysfunc(translate(&CLNT_ID_STR, %str( ), %str(%')));
%let id_list_3 = %sysfunc(prxchange(%str(s/%'//), -1, &CLNT_ID_STR));
%put &=id_list_1;
%put &=id_list_2;
%put &=id_list_3;
----- LOG -----
ID_LIST_1=(179966, 200829, 201104, 211828, 264138)
ID_LIST_2=( 179966 , 200829 , 201104 , 211828 , 264138 )
ID_LIST_3=(179966, 200829, 201104, 211828, 264138)
It really doesn't matter that TRANSLATE replaces the ' with a single blank () because the context for interpretation is numeric.

JCL: How to read file name and find the specific string

I need to read the file name in JCL and find specific string in that. If string present then i need to set the flag variable.
Example:
000063 //SETVARS SET RUNMODE=AY,
000064 // MGRTMODE=M,
000065 // PARMFILE=BASXXXX.T1.XXXX.JIRA.T011746
If "PARMFILE" contains "JIRA", then set JIRAFLAG = Y else JIRAFLAG = N. I need to pass the JIRAFLAG to another JCL/SAS job to process further.
Thanks!
Bharathi
SAS:
000083 /**/
000084 %LET CHKFILE = %SCAN(&SYSPARM,1,+);
000085 %put &CHKFILE ;
000086 %GLOBAL JIRAFLG ;
000087 %MACRO CHK ;
000088 %LET TSTVAL = %SCAN(&CHKFILE,4,.) ;
000089 %PUT &TSTVAL;
000090 %IF &TESTVAL EQ 'JIRA' %THEN %LET JIRAFLG = 'Y' ;
000091 %ELSE %LET JIRAFLG = 'N' ;
000092 %PUT &JIRAFLG ;
000093 %MEND CHK;
000094 %CHK ;
JCL does not provide a scripting language per se although it is intuitive to think of it that way because of the JCL Symbols and conditionals.
I'm not a SAS buy but it seems like your SAS sample does the scanning. I'd suggest that you have a STEP where you parse the PARMFILE statement and set a known return code of your choice. For instance, choose a return code of 1.
Follow that with another step that when the return code is 1 executes the program you want to process when JIRA is in the PARMFILE. Since you mentioned a seperate job you could submit another job through INTRDR.
There are ways to accomplish what you want you just need to be creative. As I said, the conditionals and JCL Symbols lure many people into thinking scripting which is unfortunately not how it works.
SRCHFOR 'YOURSTRING',W (https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.f54u200/sfcmdfgrr.htm)
and then check here for conditional processing
https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.ieab600/iea3b6_Examples_of_IF_THEN_ELSE_ENDIF_statement_constructs.htm

Stop SAS from trying to resolve & reference within a macro string

I'm running a process that lists jobs I want to check the modification date on. I list the jobs in a dataset and then pass these to macro variables with a number.
e.g.
Data List_Prep;
Format Folder
Code $100.;
Folder = 'C:\FilePath\Job ABC'; Code = '01 Job Name.sas'; Output;
Folder = 'C:\FilePath\Job X&Y'; Code = '01 Another Job.sas'; Output;
Run;
%Macro List_Check();
Data List;
Set List_Prep;
Job + 1;
Call Symput (Cats("Folder", Job), Strip(Folder));
Call Symput (Cats("Code", Job), Strip(Code));
Run;
%Put Folder1 = &Folder1;
%Put Folder2 = &Folder2;
%MEnd;
%List_Check;
It prints the %Put statement just fine for foler 1, but folder 2 doesn't work right.
Folder1 = C:\FilePath\Job ABC
WARNING: Apparent symbolic reference Y not resolved.
Folder2 = C:\FilePath\Job X&Y
When I then go in to a loop to check the datasets, again, it work, so looks for Folder1, Code1 etc, but I still get the warnings.
How can I stop these warnings? I've tried %Str("&") instead, but still get the issue.
The %superq() macro function is a great way to mask macro triggers that are already in a macro variable. You could either remember to quote the values when using them,
%put Folder1 = %superq(Folder1) ;
or you could adjust your process to quote them right after creating them.
data List_Prep;
length Folder Code $100;
Folder = 'C:\FilePath\Job ABC'; Code = '01 Job Name.sas'; Output;
Folder = 'C:\FilePath\Job X&Y'; Code = '01 Another Job.sas'; Output;
run;
data List;
set List_Prep;
Job + 1;
length dummy $200 ;
call symputx(cats("Folder", Job), Folder);
dummy = resolve(catx(' ','%let',cats("Folder", Job),'=%superq(',cats("Folder", Job),');'));
call symputx(cats("Code", Job), Code);
dummy = resolve(catx(' ','%let',cats("Code", Job),'=%superq(',cats("Code", Job),');'));
drop dummy;
run;
P.S. Don't use FORMAT to define variables. Use statements like LENGTH or ATTRIB that are designed for defining variables. FORMAT is for attaching formats to variable, not for defining them. The only reason that using FORMAT worked is that it had the side effect of SAS defining the variable's type and length to match the format that you attached to it because it was the first place you referenced the variable in the data step.
You can prevent SAS from trying to resolve the ampersand in the value by using the %superq function
%put Folder2 = %superq(Folder2);

How to write batch file in SAS that automates the opening of files?

I was assigned a task that I don’t know where to start. Here’s the context:
There’s a variable in the data, say VAR1, indicating the directory to a bunch of image files. So for observation 1, VAR1 may look like D:\Project\Data\Images\Image1.tiff and so on. Of course, those image files exist in the computer.
What I need to do is to figure out SAS program(s) and later run them automatically using batch file. When the batch file runs, it will, in some way, opens the image files one by one. By “one by one”, I mean it firsts open one image file and, upon closing that file, it opens the next image file until the end of the list.
Better yet, the batch file will make a copy of the original image files and put them in some folder (e.g., D:\Project\Data\Temp images) before opening them. That is to make sure original data is left untouched.
Do you know how I can write such a program in SAS? I was given the following SPSS file for reference, which does that job nicely as described. I don’t know enough SPSS to understand every detail how it works. The two variables dir5 and tiff5 specify the location of the image files, and variables SCQID and ohhscqid are just ID variables.
string out2 (a200).
compute out2=concat('copy "', ltrim(rtrim(dir5)),"\", tiff5, '"',' "c:\temp\temp.tiff"').
write outfile='E:\Data\Outcome.bat'/'#echo SCQ ID ' ohhscqid .
write outfile='E:\Data\Outcome.bat'/out2.
write outfile='E:\Data\Outcome.bat'/'#"C:\Program Files\Microsoft Office\Office14\OIS.exe" "c:\temp\temp.tiff"'.
execute.
I did the homework and figured out one way that works as I want it to. Not the optimal way programmingly though, but the idea is like this.
data batwide;set have;
echo = '#echo SCQ ID '||ohhscqid;
predir = 'copy '||'"'||strip(dir5)||strip('\')||strip(tiff5)||strip('"');
preexec = '#'||strip('"')||strip('C:\Program Files\Microsoft Office\Office14\OIS.exe')||strip('"');
temp = '"'||strip('c:\temp\temp.tiff')||strip('"');
run;
data batwide; set batwide;
dir = catx(' ',predir,temp);
exec = catx(' ',preexec,temp);
run;
data batlong;set batwide;
format bat $200.;
bat = echo;output;
bat = dir;output;
bat = exec;output;
keep bat;
run;
data _null_;
set batlong;
file "E:\SAS codes and files\batchfile.bat";
put bat;
run;
Sounds like you are asking how to generate a series of OS commands into a text file? You can use a DATA step for that.
If you want to test if the specified files exist then use the FILEEXIST() function.
So if you have SAS dataset name HAVE with a variable named VAR1 that contains the filename then you probably want a program like this:
data _null_;
set have ;
file 'E:\Data\Outcome.bat';
if fileexist(VAR1) then do;
target=catx('\','D:\Project\Data\Temp images',scan(VAR1,-1,'\'));
put 'copy ' VAR1 :$quote. target :$quote. ;
put '"C:\Program Files\Microsoft Office\Office14\OIS.exe" ' target :$quote.;
end;
else putlog 'WARNING: File not found. ' VAR1=;
run;
I don't know SPSS, but will give you an example using unix commands, you can change them to Windows commands and probably do what you described.
In this example I'll only copy some files, but the logic to "open the files one by one" is the same. You will have to play with the code and adjust it to Windows.
First of all, we looking for csv files inside the /home/user directory. Again, adjust the command to windows.
This will create a sas dataset with all the files
filename dirlist pipe "find /home/user/ | grep csv";
data dirlist ;
infile dirlist lrecl=200 truncover;
input line $200.;
file_name = strip(line);
keep file_name;
run;
Then I'll create a macro variable with the file count, I'll all it cntfiles
proc sql noprint;
select count(*) into: cntfiles from dirlist;
quit;
%let cntfiles=&cntfiles;
%put cntfiles=&cntfiles;
The last thing I'm doing is, I'm looping, getting the filenames one by one and copying them to a new macro variable called &copyto
This data step (null) will only copy the files, if you want to do something else with them, you'll have to write the code for it.
%macro process_files;
%let copyto = /home/des/33889897/copyto;
%do i=1 %to &cntfiles;
data _null_;
set dirlist (firstobs=&i.);
put file_name=;
call system("cp -f " || file_name || " &copyto");
stop;
run;
%end;
%mend process_files;
%process_files;
Take a look at this link, maybe it can help you.
sample code I use frequently to parse list of files in a directory and extract metadata from the file names. Often feeds a step to generate a sequence of macro variables to use in a macro loop to process each file in turn. Just add any substringing of filenames to extract structured content as with the datetxt and date assigment statements in the example where the filename has a datestamp in it that I want to use.
%let extension=txt;
filename infiles "c:\a\b\c";
Data List_of_files
Not_ext
;
Length
path $255
filename $255
extension $10
;
d_open=dopen("infiles") ;
path=pathname("infiles") ;
nfiles=dnum(d_open) ;
do i=1 to nfiles;
filename =dread(d_open,i) ;
extension=scan(filename,-1,'.') ;
datetxt=scan(filename,2,"_");
date=input(scan(filename,2,"_"),date9.);
if upcase(extension) ne "%upcase(&extension)"
then output Not_ext ;
else output list_of_files ;
end;
d_open=dclose(d_open) ;
keep
filename
path
extension
;
Run ;
filename infiles clear;