Put them back the way you found them: restoring SAS titles

Has this ever happened to you? You have a SAS program with statements that you wrote, then you make use of a macro function or %include file supplied by a helpful colleague, and when your SAS code resumes, you find that all of your SAS titles have been changed out from under you!

To be a "good citizen" within a macro function, you should return the SAS environment to the way you found it. You'll occasionally see constructs like this to store and restore SAS option values:

%let _currValidName = %sysfunc(getoption(validvarname)); 
%put Overriding current VALIDVARNAME setting of &_currValidName; 
options validvarname=v7;
/* some statements that rely on VALIDVARNAME */
%put Restoring previous VALIDVARNAME setting to &_currValidName; 
options validvarname=&_currValidName.;

(Even more is possible with PROC OPTSAVE; see Denise Poll's paper on this topic.)

But what about the titles and footnotes? Is there a way to squirrel those values away before usurping them, and then put them back the way that you found them?

Yes, there is. Even though TITLE and FOOTNOTE are global SAS statements and not system options per se, you can programmatically query the current settings from one of the SAS dictionary tables: SASHELP.VTITLE. (Despite the "VTITLE" name, this table also contains FOOTNOTE settings.)

You can use these macros (or the code within them) to save and restore the current settings for TITLE and FOOTNOTE statements:

/* Define macro to save titles */
%macro saveTitles;
  data _savedTitles;
    set sashelp.vtitle;
/* Define macro to restore previously saved titles */
%macro restoreTitles;
  proc sql noprint;
    /* Using a SAS 9.3 feature that allows open-ended macro range */
    select text into :SavedTitles1- from _savedTitles where type="T";
    %let SavedTitlesCount = &sqlobs.;
    /* and footnotes */
    select text into :SavedFootnotes1- from _savedTitles where type="F";
    %let SavedFootnotesCount = &sqlobs.;
    /* remove data set that stored our titles*/
    drop table _savedTitles;
  /* emit statements to reinstate the titles */
  TITLE; /* clear interloping titles */
  %do i = 1 %to &SavedTitlesCount.;
    TITLE&i. "&&SavedTitles&i.";
  FOOTNOTE; /* clear interloping footnotes */
  %do i = 1 %to &SavedFootnotesCount.;
    FOOTNOTE&i. "&&SavedFootnotes&i.";

Sample use:

title "This is my tremendous title";
title2 "and this is a subtitle";
footnote "Created by ME";
proc means data=sashelp.class;
  var weight;
/* restore the old titles */
proc means data=sashelp.class;
  var height;

There is at least one idiosyncrasy of this approach: if your title or footnote includes a macro variable or expression, that macro will be resolved when the title is stored. So when you restore to the original value with %restoreTitles(), the value will be "stuck" as it was when you used %saveTitles(). If you dislike that limitation, then perhaps some hotshot macro programmer will add a solution here in the comments.

tags: macro programming, PROC OPTSAVE, SAS programming


  1. Posted October 2, 2013 at 8:56 am | Permalink

    This is very clever, and just what I need for a project that I am working on. An extra wrinkle for that "hotshot macro programmer" is that although SASHELP.VTITLE always exists, it might have zero observations. This occurs when the user submits a null TITLE statement:

    proc contents data=sashelp.vtitle ;

    • Chris Hemedinger Chris Hemedinger
      Posted October 2, 2013 at 4:35 pm | Permalink

      Good point Rick! I updated the example so that it clears TITLE and FOOTNOTE before attempting to restore a value that might not have ever been there.

      Our buddy Vince DelGobbo also pointed out that sequences like:

      TITLE1 "First title";
      TITLE3 "and a smaller title 3";

      Would not be restored properly using the current code. Easy enough to correct since it's all in SASHELP.VTITLE, but I leave it as a reader exercise for now.

  2. Rob Meekings
    Posted October 2, 2013 at 1:04 pm | Permalink


    This is very handy, thank you, and it set me to thinking, and I realised that it may be worth my trying a similar approach for log files.

    In some programs I set file destinations for log and listing output, using proc printto, and then close these destinations at the end of the program.

    So far, so good, ..., but this becomes messy when I %include files that use the same idea and proc printto statements become nested.

    Using an approach similar to this one, I can check sashelp.vextfl for log and listing files (this would require a naming convention for these files) before redirecting output. If there was an active destinations I could restore log and listing output to these instead of using the terminating proc printto; run;.

    Further, I could add a helpful note to tell the reader of the parent log where to find the missing middle chunk of logging.

    Finally, by using a push/pop stack, one should be able to accommodate multiple levels of nesting.

    I think I'm going to be busy tomorrow, thanks!


    • Chris Hemedinger Chris Hemedinger
      Posted October 2, 2013 at 1:08 pm | Permalink

      Rob - glad to have created more work for you!

    • Peter Crawford
      Posted October 13, 2013 at 11:47 am | Permalink


      if you want a copy of logs routed to an alternate location, have a look at the system option

  3. Peter Lancashire
    Posted November 15, 2013 at 1:03 pm | Permalink

    A structured way to achieve what is usually required would be push and pop title and footnote statements. This is a feature request. It would be a great idea for options too.

  4. Simon Ouellet
    Posted November 26, 2013 at 10:43 am | Permalink

    I have a problem with this macro.
    The file « _savestitles » has 3 observations (good!) but when invoking macro « restoretitles », with select type="T", only 1 observation was select (normaly, 2 observations would by selected)...see log below. Executing macros give me a title2 with blanks...

    Thank's for your help !

    • Chris Hemedinger Chris Hemedinger
      Posted November 26, 2013 at 1:20 pm | Permalink

      Simon, to save space I trimmed out your program log. The issue you're having is that when running SAS 9.1.3, you can't rely on the open-ended range for SELECT INTO -- that's a 9.3 feature (as commented in the code). You'll need to add a SELECT COUNT step to calculate the upper range of the number of titles. That technique is covered in this blog post.

      • Posted November 26, 2013 at 3:22 pm | Permalink

        Great ! It's work now ! Thank's for your fast and perfect answer !

  5. Kevin Viel
    Posted December 28, 2016 at 5:15 pm | Permalink

    This approach appears not work not to work if one has multiple justifications in the title or footnote:

    j = l 'LEFT'
    j = c 'CENTER'
    j = r 'RIGHT'

    ods pdf file = "O:\krviel\SAS\test1.pdf" ;

    data _null_ ;
    file print ;
    put "Test1" ;
    run ;

    ods pdf close ;

    data temp ;
    set sashelp.vtitle ;
    run ;

    title ;
    footnote ;

    data _null_ ;

    set temp ;
    call execute( cat( "title"
    , strip( put( number , 8. -L ))
    , " "
    , quote( strip( text ))
    , " ;"
    ) ;
    call symputx( "title1", text ) ;
    run ;

    ods pdf file = "O:\krviel\SAS\test1.pdf" ;

    proc print
    data = temp ;
    run ;

    title1 "&title1." ;

    proc print
    data = temp ;
    run ;

    ods pdf close ;

    Thank you,


Post a Comment

Your email is never published nor shared. Required fields are marked *


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>