DATA Step, Macro, Functions and more

How to define a dynamical path for FILE command

Reply
New Contributor
Posts: 4

How to define a dynamical path for FILE command

[ Edited ]

Hi everyone,

 

I've been reading some posts and similar problems but I don't find the solution.

 

The structure of my program.sas is:

 

%MACRO CREA_XML ;

        file " '/directory /folder/office1.xml ";                            <------- it works but not change

        *file &path1.;                                                                     <----- DOESN'T WORK with macro &path1.

        %put &path1.;                                                                    <------ &path1.=" '/directory /folder/office1.xml "

        PUT '<?xml version="1.0" encoding="UTF-8"?>';

        PUT ' ';

        PUT '<!DOCTYPE UPDATE SYSTEM "doc.dtd">';

        PUT ' ';

        PUT '<DOC>';

        PUT '   <JOB>';

        line = '      <APP FROM="'|| trim(&off) || '/>';

        PUT line;

        PUT '   </JOB>';

        PUT '</DOC>';

%MEND CREA_XML ;

 

data _null_;

       SET dataset;

       %LET off = office;

        path = cat('"',"'",'/directory /folder/',strip(&off.),'.xml"');

        %LET path1 = path;

         %CREA_XML;

run;

 

I want to create a new .xml document, in the path  '/directory /folder/, for each office, so I build the path by concatenating the office in a loop. 

 

If I check how is the path in %CREA_XML, it is correct, but with FILE &path1. it doesn't write anything. However, If i write all path (the same value that is inside the %path1.) it works. So..I am not able to dynamize it!!

 

Can you help me? I don't know why it is happening.

 

Thanks!

 

 

 

Super User
Posts: 6,928

Re: How to define a dynamical path for FILE command

Here:

data _null_;

       SET dataset;

       %LET off = office;
* this macro statement is dealt with before the data step is compiled;
path = cat('"',"'",'/directory /folder/',strip(&off.),'.xml"'); * this data step statement will create the data step variable path during data step execution!;
%LET path1 = path; * this macro statement, again, will be dealt with BEFORE data step compilation, so &path1 will contain the text "path" instead of the contents of the data step variable, as this does not yet exist;
%CREA_XML; run;

You need to make clear to yourself that the macro engine is a pre-processor, designed to create dynamic program text. It is NOT meant to handle data!

Avoid confusion by writing macro statements outside of data and proc steps, and only use macro calls inside procs and data steps. That way you visualize that macro processing occurs BEFORE the resulting program text is fed to the main SAS interpreter.

 

---------------------------------------------------------------------------------------------
Maxims of Maximally Efficient SAS Programmers
New Contributor
Posts: 4

Re: How to define a dynamical path for FILE command

I think that maybe I have problems of concepts, that's true. But I've done %LET to be able to use these values in %MACRO. If I don't use %LET statement, the value of office won't be visible in the %MACRO, am i wrong?

 

In fact, I understand that you are explaining me that the value (using macro engine) is not "assign" until the data step ends..but my put &path1. has the value I expected! Smiley Frustrated

 

Thanks!

Super User
Posts: 6,928

Re: How to define a dynamical path for FILE command

First of all, your path name contains a blank and has an unbalanced single quote, which makes it impossible to execute your code for test at all.

So I changed the path name to create a file in my typical location for SAS community tests:

%MACRO CREA_XML ;
        file "$HOME/sascommunity/office1.xml";
        *file &path1.;
        %put &path1.;
        PUT '<?xml version="1.0" encoding="UTF-8"?>';
        PUT ' ';
        PUT '<!DOCTYPE UPDATE SYSTEM "doc.dtd">';
        PUT ' ';
        PUT '<DOC>';
        PUT '   <JOB>';
        line = '      <APP FROM="'|| trim(&off) || '/>';
        PUT line;
        PUT '   </JOB>';
        PUT '</DOC>';
%MEND CREA_XML ;

data _null_;
%LET off = office;
path = "$HOME/sascommunity/&off..xml";
%LET path1 = path;
%CREA_XML;
run;

The log from this looks like that

16         %MACRO CREA_XML ;
17                 file "$HOME/sascommunity/office1.xml";
18                 *file &path1.;
19                 %put &path1.;
20                 PUT '<?xml version="1.0" encoding="UTF-8"?>';
21                 PUT ' ';
22                 PUT '<!DOCTYPE UPDATE SYSTEM "doc.dtd">';
23                 PUT ' ';
24                 PUT '<DOC>';
25                 PUT '   <JOB>';
26                 line = '      <APP FROM="'|| trim(&off) || '/>';
27                 PUT line;
28                 PUT '   </JOB>';
29                 PUT '</DOC>';
30         %MEND CREA_XML ;
31         
32         data _null_;
33         %LET off = office;
34         path = "$HOME/sascommunity/&off..xml";
35         %LET path1 = path;
36         %CREA_XML;
path
37         run;

NOTE: Numeric values have been converted to character values at the places given by: (Line):(Column).
      36:1   
NOTE: Variable office is uninitialized.
NOTE: The file "$HOME/sascommunity/office1.xml" is:
      Filename=/home/e9782/sascommunity/office1.xml,
      Owner Name=e9782,Group Name=sasadmin,
      Access Permission=rw-r--r--,
      Last Modified=Tue Sep 20 12:38:47 2016

NOTE: 9 records were written to the file "$HOME/sascommunity/office1.xml".
      The minimum record length was 1.
      The maximum record length was 38.

SAS complains about the unitialized variable probably because I omitted the set statement (I don't have your dataset), and I guess office is a variable in your dataset.

Note that the result of the %put &path1. comes BEFORE any message created by the running datastep, and that &path1 obviously contains the text "path" (without the quotes).

Now I changed the comments on the two FILE statements, and ran it again.

What changes is that I now get

ERROR: Physical file does not exist, 
       /usr/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/usr/dt/bin:/sbin:/usr/local/bin:/usr/java14_64/jre/bin:/usr/java14_64/bin.

which is a result of SAS trying to execute (in essence)

data _null_;
file path;
run;

as &path1 is replaced by the macro processor with the text "path" (without the quotes, of course).

Since path is (at least in my installation) a default SAS file reference that points to the execution of the operating system's path command, the output of that command is used in the file statement, and that fails.

 

Once again, all macro actions happen BEFORE the data step starts to run.

---------------------------------------------------------------------------------------------
Maxims of Maximally Efficient SAS Programmers
Super User
Posts: 6,928

Re: How to define a dynamical path for FILE command

[ Edited ]

If you want to define a dynamic file name from within the datastep, use the filevar= option in the FILE statement, and put your filename into the variable declared there. SAS will then automatically switch outfiles everytime the content of the "filevar" variable changes.

---------------------------------------------------------------------------------------------
Maxims of Maximally Efficient SAS Programmers
Frequent Contributor
Posts: 144

Re: How to define a dynamical path for FILE command

Hi,

 

I think that the problem is your macro variable path1. You could not introduce a variable content in a data step with a %let.

 

When you do that, you are not putting office or path contents into macro variable, you are literaly putting  &off is replaced by office and &path1 by path.

 


%MACRO CREA_XML ;
        
*Create number of observations;
proc sql noprint;
	select count(*) into :_nobs
	from dataset;
quit;
%local i;
*For each observation:;
%do i=1 %to &_nobs;
	*Keep path;
	data _null_;
		set dataset;
		if _N_=&i.;
		path = cat('"',"'",'/directory /folder/',strip(office),'.xml"');
		call symput('path1',path);
		*this put the content of the last observation of path into macro
		variable path1;
	run;

	*And generate xml with this info;
	data _null_;
	       SET dataset;                      
	        file "&path1.";                                                                    
	        PUT '<?xml version="1.0" encoding="UTF-8"?>';
	        PUT ' ';
	        PUT '<!DOCTYPE UPDATE SYSTEM "doc.dtd">';
	        PUT ' ';
	        PUT '<DOC>';
	        PUT '   <JOB>';
	        line = '      <APP FROM="'|| trim(office) || '/>';
	        PUT line;
	        PUT '   </JOB>';
	        PUT '</DOC>';
	run;
%end;
%MEND CREA_XML ;

Try something like this

 

Frequent Contributor
Posts: 103

Re: How to define a dynamical path for FILE command

Hi MissConde,

 

This is easy and bears a lot of possibilities.

If you want to generate the path "on the fly" using the input from 'dataset'.

This is how to do it:

%MACRO CREA_XML(path=);
   DATA _NULL_;
      file "'&path.";                                                                                         
      PUT '<?xml version="1.0" encoding="UTF-8"?>';
      PUT ' ';
      PUT '<!DOCTYPE UPDATE SYSTEM "doc.dtd">';
      PUT ' ';
      PUT '<DOC>';
      PUT '   <JOB>';
      line = '      <APP FROM="'|| trim(&off) || '/>';
      PUT line;
      PUT '   </JOB>';
      PUT '</DOC>';
   RUN;
%MEND CREA_XML ;

%LET off = office;
data _null_;
   SET dataset;
   path = cats('/directory /folder/',"&off.",'.xml');
   call execute('%'||'CREA_XML(path='||strip(path)||');');
run;

if you only want to create the xml once, just call

%CREA_XML(path=/directory/folder/&off..xml);

Cheers,

Oligolas

________________________

- That still only counts as one -

Ask a Question
Discussion stats
  • 6 replies
  • 326 views
  • 2 likes
  • 4 in conversation