I previously wrote about the best way to suppress output from SAS procedures. Suppressing output is necessary in simulation and bootstrap analyses, and it is useful in other contexts as well. In my previous article, I wrote, "many programmers use ODS _ALL_ CLOSE as a way to suppress output, but I do not recommend that you use the ODS CLOSE statement for this purpose." This article expands on that statement.
I can think of five reasons why using ODS EXCLUDE ALL is preferable to using ODS _ALL_ CLOSE for suppressing output from procedures. The ODS EXCLUDE statement is
- Easy. Put ODS EXCLUDE ALL before a procedure call. Put ODS EXCLUDE NONE after call. Done.
- Portable. It works regardless of which destinations are open.
- Reversible. You can easily undo the operation and send future output to all open ODS destinations.
- Repeatable. You can use it many times within a single SAS program without introducing side effects.
- Safe. It does not delete, overwrite, or change existing ODS output files.
Let me be perfectly clear: The ODS CLOSE statement is an important statement. It is used to close files that display SAS output. For many destinations (such as PDF and RTF), the output does not display until the destination is closed. However, when you want to temporarily suppress output, the ODS EXCLUDE statement is easier to use than ODS CLOSE.
A detailed look at ODS EXCLUDE
So that we can discuss the advantages of each method, let's write two programs. The first uses ODS EXCLUDE to temporarily suspend SAS output:
/* PROGRAM 1: The preferred approach */ ods graphics off; /* no graphics today */ ods html; ods rtf; /* open some ODS destinations */ title "ODS EXCLUDE: First output"; proc print data=Sashelp.Cars(obs=5); var make MPG_City; run; /* Use ODS EXCLUDE to temporarily suppress output */ ods exclude all; /* suspend all open destinations */ /* INSERT PROCEDURE CALL HERE. Output is suppressed */ ods exclude none; /* resume output to open destinations */ title "ODS EXCLUDE: Second output"; proc print data=Sashelp.Class(obs=5); var Name Age; run; ods _all_ close; /* DONE with program. Display results */
This program opens the HTML and PDF destinations. Behind the scenes, SAS creates two files to accumulate the results, which on my system are called sashtml.htm and sasrtf.rtf. Neither file is affected by the ODS EXCLUDE statements. When the program ends, the ODS CLOSE statements write a postamble to the files, closes them, and might even launch a viewer to display the output. At the end of the program, each file contains the results of both PROC PRINT calls.
The calls are ODS EXCLUDE calls are certainly easy. I do not need to know which destinations are open, which makes the technique portable. The call to ODS EXCLUDE NONE reverses the effect of ODS EXCLUDE ALL. I can include this ODS EXCLUDE "sandwich" many times in the same program, which makes the technique repeatable. Lastly, at the end of the program when I want to see the final output, there is one file for each destination, and both files are correct.
I like this technique because it requires minimal effort to get exactly the behavior I want. If you prefer inclusion to exclusion, feel free to use ODS SELECT NONE followed by ODS SELECT ALL. It is equivalent.
A detailed look at ODS CLOSE (and "OPEN")
The second program uses ODS CLOSE to close all ODS destinations. It then calls a SAS procedure (not shown). Before the program can generate more ODS output, it must reopen some ODS destinations, which is where the trouble begins:
/* PROGRAM 2: Not recommended */ ods graphics off; /* no graphics today */ ods html; ods rtf; /* open some ODS destinations */ title "ODS CLOSE: First output"; proc print data=Sashelp.Cars(obs=5); var make MPG_City; run; /* Use ODS CLOSE to close all open destinations */ ods _all_ close; /* close all open destinations */ /* INSERT PROCEDURE CALL HERE. Output is suppressed */ ods html; ods rtf; /* reopen some ODS destinations */ title "ODS EXCLUDE: Second output"; proc print data=Sashelp.Class(obs=5); var Name Age; run; ods _all_ close; /* DONE with program. Display results */
This program opens the HTML and PDF destinations. Behind the scenes, SAS creates two files to accumulate the results. The first ODS CLOSE statement write a postamble to the files, closes them, and might even launch a viewer to display the output.
When you want to resume output, you need to open some ODS destinations. All information about the previously open destinations is gone, so I hope that you wrote down which destinations were open before you closed them! This program reopens the HTML and PDF destinations, and SAS creates two files, which on my system are called sashtml1.htm and sasrtf.rtf. Notice that SAS has chosen a new name for the html file, but the same name for the RTF file. That means that the first RTF file has just been overwritten!
The remainder of the program is uneventful. The newly opened HTML and RTF files contain the output for the second PROC PRINT statement. They are properly closed (and potentially displayed) at the end of the program.
The ODS CLOSE technique as I've presented it has a few problems. Although you can CLOSE all destinations without knowing which are open, you must have knowledge of which destinations were previously open before you can reopen them. Also, the technique creates side effects. Although some destinations (like LISTING) can be opened and closed with impunity without creating additional physical files on disk, others must close and reopen a file. Obviously, if you repeat this process multiple times, the files proliferate.
Notice also that this straw-man program overwrites the RTF file that contained the output from the first portion of the program. You can use the FILE= option to specify a file name, which will ensure that all of your results are preserved. However, my point is that this second technique requires more typing, more knowledge about the state of ODS, more knowledge about how various destinations create files, and so forth.
What about the LISTING destination?
In the old days, when almost everyone used the LISTING destination, ODS LISTING CLOSE was a common technique for suppressing output. You see it in many old SAS Global Forum (née SUGI) papers that were written by excellent programmers. I've used it myself. The technique worked well because LISTING was the default destination in SAS.
However, it is time to retire the ODS LISTING CLOSE statement from your programming toolbox when your intention is to temporarily suspend output. The modern SAS programmer does not exclusively use the LISTING destination. It is not even the default destination when you run SAS in most GUI environments. If you are writing a program or macro that you intend to share with others, the program should work with whatever destinations are open at the time. The ODS EXCLUDE ALL statement is a simple approach that works for all destinations.
Can a careful programmer use the second technique (closing all destinations) successfully? Sure. It requires some effort, but the problems can be overcome.
However, the ODS EXCLUDE technique is simpler, safer, and can easily be repeated many times within a single program. Use it when you want to temporarily suspend output, and you intend to resume sending output later in the program. For even more control over suspending SAS output, use the %ODSOff and %ODSOn macros.