Vim Functions for SAS Programming
Introduction
I wrote these three functions to help me program SAS with the Vim editor in GUI mode. The first allows me to run SAS in batch mode on a program I'm currently editing in Vim. In addition, there are functions to load the .log and .lst files for a SAS program being edited, and to run a log checking program on the current buffer, allowing the use of Vim's QuickFix functionality to check SAS log files (see the Vim quickfix help, which covers defining the file to be used and how to move around the issues list).The code to add the three functions to the Vim Tools menu is given as well as code to make the assignments to function keys. All three Vim functions for using SAS, the menu and function key code, a SAS log check script and an example wrapper script are available for download in this zip file.
Regarding the use of these functions with Windows and PC SAS: two suggestions I received (thank you Laurie Samuels) that appear to work fine are removing the fnameescape() calls from the LoadSASLogLst() function (it turns out these calls were overkill and are not needed for Unix use, either) and updating the RunSAS() function to work with PC SAS. The latter modification is a comment in the function that can be uncommented. Note that these functions may take a little bit of adjustment (well, the ChkSASLog function, especially) depending on your local environment.
Usage Notes
These functions were developed for use with SAS in a Unix environment so they reference some Unix paths and programs. But they could be adapted to Vim and SAS in a Windows environment quite easily (using batch mode to run SAS). File extensions are used for determining the file types and follow SAS' defaults (.sas for program, .log for log file, and .lst for list output). These can be changed if you use other extensions for some reason.I keep the code for these functions in my $HOME/.gvimrc file. I haven't tried these functions in a console Vim environment. They should work in console vim. Terminal menus get set up using :tlmenu instead of :menu. The functions use Vim tabs when loading files for editing, thus I prefer to use them in GUI Vim (console Vim can do tabbed editing, as well).
One option for Vim that may help you use these functions is to set the autoread option (set autoread in .gvimrc/.vimrc). Thus you should get your already-loaded .log and .lst files automatically updated when they change, such as when you rerun your .sas file. This may take a few seconds depending on how busy your Unix server (or Windows computer) is.
Below you'll find descriptions of each of the functions and the actual code used.
Run SAS on Current File/Buffer
SAS runs against a program file on disk, so this function, RunSASonCurrentFile(), will save the file before the run if the file has been modified in the editor.
The function will automatically load the .log and lst files that get created from the SAS run (or possibly just the log file if you have no list output) into separate tabs, if they are not already open in the editor (note: RunSASonCurrentFile() calls LoadSASLogList()).
The function gives its messages at the bottom of the Vim screen where other Vim messages appear. Any messages from SAS on the command line should appear there, as well.
If the .log and .lst files are already open in the editor, Vim will prompt to load the changed files after SAS runs as it normally does when files being edited are changed on disk by another process (such as another SAS run). This is Vim behavior, not a behavior of the function. In some cases, you may need to force your window manager to refocus on the Vim window for this to happen (e.g. by opening a pull down menu).
Be sure to set the location of SAS on your machine in the system() call below.
" Run SAS on the current file/buffer. Assumes you have a SAS program in " the current buffer. File will be saved before running SAS, if it has " been modified. SAS will run on the saved file on disk. Two tabs will " be opened, one for the log and one for the list, assumed to be in the " same location and with the same basename as the SAS program file. function! RunSASonCurrentFile() " Make sure this is a SAS program file (ends with .sas) so that " we don't run SAS on a log file or similar. :let checkSASpgm=match(expand("%"),"\.sas") " If we did not match .sas in the file name, end this function with " a warning msg if checkSASpgm==-1 :echo "*** Current file is not a SAS program. SAS run has been canceled." :return endif " Ask user if we want to run SAS so we don't accidentally run it. :let l:answer = input("Run SAS? Y/N ") :if (l:answer == "Y" || l:answer == "y") " If file has been modified, save it before running if exists(&modified) :echo "*** Saving modified file before SAS run..." :w endif " Run SAS on path/file name (modify to your location of sas) :echo "*** Running SAS..." let returntxt = system("/usr/local/bin/sas -nodms " . shellescape(expand("%:p"))) " Shows the return messages from the SAS commandline (may be useful " if no log produced) :echo "*** SAS commandline: " . returntxt :call LoadSASLogList() :else :echo "SAS Run cancelled." " endif for the Run SAS? check :endif endfunction
Load SAS Log and Lst Files The LoadSASLogLst function loads the SAS .log and .lst files for a SAS program being edited in the current buffer. It will load these files into separate tabs in Vim. This is handy if you want to see those files for a SAS program file you are examining but don't want to run in SAS. It uses the basename of file being edited to find the associated .log and .lst files.
function! LoadSASLogLst() " Assumes you are editing a SAS program. The log and lst file names will " be based on the SAS program basename (the file in the current buffer) and " are assumed to end in .log and .lst, as produced by SAS by default. " Load the SAS log file in a tab, if it is not already loaded & it exists :let log=bufexists(fnameescape(expand("%:p:r") . ".log")) :if log==0 :if filereadable(fnameescape(expand("%:p:r") . ".log")) :execute "tabedit " . fnameescape(expand("%:p:r") . ".log") :else :echo "*** SAS log file does not exist." :endif :endif " Load the SAS lst file in a tab, if it is not already loaded & it exists :let lst=bufexists(fnameescape(expand("%:p:r") . ".lst")) :if lst==0 :if filereadable(fnameescape(expand("%:p:r") . ".lst")) :execute "tabedit " . fnameescape(expand("%:p:r") . ".lst") :else :echo "*** SAS lst file does not exist." :endif :endif endfunction
Checking the SAS Log File The CheckSASLog function helps in debugging a SAS program, allowing you to link a list of errors to the lines in the log where the errors occurred and jump to specific errors. This is also the function that requires the most modification to make work the way you want.
The Vim QuickFix functionality (see :he QuickFix in the Vim help system) makes jumping from error to error (or warning to warning) in the log easy, so it is a good idea to take advantage of this. I've had it working in at least two different ways over the years: using Vim's internal grep capability (see :he grep and set grepprg=internal), and using an external script or program that does the log checking and returns a list of errors, warnings, and other questionable messages from the SAS log.
In order to use Vim's QuickFix mechanism fully, the list of errors and warnings needs to be linked to the SAS log file via the file name of the log. (Imagine if multiple SAS programs and logs were open in the gvim editor, how would Vim know which list of errors goes with which log file? By the file name). The example image above shows a SAS program (testdate2.log) in one gvim tab, the .lst file in another tab, and the log file with QuickFix window open and stopped on an error.
Note: a SAS log may contain dozens of errors and warnings, but they may be caused by the first error and cascaded into other errors from that point, so having a complete list of errors may not be indicative of the amount of debugging necessary. Always start with the first error found. Vim's quickfix feature will automatically jump to the first error found when the quickfix window opens.
The Unix grep program can provide a file name (GNU grep's -H option, for example) in the list of errors/warnings. Below, I present a simple SAS Log checking shell script using the GNU grep program that works with Vim's QuickFix mode.
I also have a SAS log checking program in Perl that can be used called cklg.pl That program provides the filename and line number information for use with vim's QuickFix facility and can be used as a grepprg program.
If you use another external log-checking program, make sure it returns the file name for each error, in addition to the line number and error text (and modify the grepformat in the CheckSASLog function appropriately). Otherwise, depending on your situation (e.g. a provided log check utility you can't change that does not provide a file name), you'll need to write a small wrapper script to call that check utility and do the addition of the file name to each error returned.
" Check SAS log files for errors, warnings and other problems " Assumes you are editing the file to be checked when invoking this function. " No assumption about log file name extension is made so you can check any file. function! CheckSASLog() " Go to the first line of the file :0 " Set grepformat for use with the program used by grepprg. NOTE: " quickfix needs a file name! Example grepformat when a file name is " returned by grepprg program set grepformat=%f:%l:%m " Example grepformat with no file name returned by the program used " by grepprg. "set grepformat=%l:%m " Save current grepprg setting let _grepprg=&grepprg " Define the program to use for searching the SAS log :set grepprg=~/bin/cklog " Run grepprg on the current file grep %:p " Set grepprg back to its original setting let &grepprg=_grepprg " Open the quickfix error window :cope endfunction
Menu Assignments and Key Mappings for the SAS Functions
You can relabel these, but note that you need to escape ('\') the spaces in the labels. Obviously, you can assign the functions to any function keys you aren't currently using, as well, and in an order that makes sense to you.
" Add a separator before the SAS menu items menu Tools.-Sep- : " Assign RunSASonCurrentFile function to the tools menu menu Tools.Run\ SAS\ on\ Current\ File :call RunSASonCurrentFile()
" Assign LoadSASLogLst function to the tools menu menu Tools.Load\ SAS\ Log\/List\ for\ Current\ File :call LoadSASLogLst()
" Assign ChkSASLog function to the tools menu menu Tools.Check\ SAS\ Log :call CheckSASLog()
" Map RunSASonCurrentFile to a function key map <F10> :call RunSASonCurrentFile()
" Map LoadSASLogLst to a function key map <F11> :call LoadSASLogLst()
" Map CheckSASLog to a function key map <F12> :call CheckSASLog()
A Simple SAS Log File Checking Script
This is the cklog program noted in the grepprg definition in the CheckSASLog() function. It runs on Unix. It should run okay under Cygwin on Windows. This provides the file name, line number, and the error text so it can be used with Vim's quickfix functionality. More checks could be added, but it should find most of the obvious errors and warnings and some of the unusual messages in SAS log. grep -E is the same as egrep on my system. It can also be used outside of Vim to check SAS log files from the command line. The other options are -n (give the line number), -H (give the file name), and -i (case-insensitive search).
Wrapper Script to Add Filename#!/bin/sh # Check SAS log files for warning, errors, and other possible # problems. --KDN ME=`basename $0` case $# in 0) echo -e "------------------------------------------------------------------------------" echo "$ME: check SAS log files for warnings, errors, and possible problems. " echo "Usage: $ME [FILE]" echo " For example: $ME *.log will check all log files in the current directory." echo " $ME mysas.log will just check just the mysas.log file." echo "by Kent Nassen, last update: 06JAN2009" echo "------------------------------------------------------------------------------" echo "" ;; *) for fname in $* do grep -E -H -n -i "warning:|error:|uninit| 0 obs|no obser|repeats of|not found\ |not valid|invalid|syntax error|overwritten|converted|missing values were generated\ |outside the axis range|duplicate by var|not created|contained a missing" ${fname} done ;; esac
A wrapper script to add the file name could be as simple as the following Bourne Shell script (which would become the grepprg program in CheckSASLog), where the "checksas" program operates on a file name given on the commandline and returns a list of errors and warnings from the SAS log and the sed command adds the file name ($1) to the beginning of each line ('#' is used as a delimiter in the sed substitution command because $1 is apt to contain '/' characters if it includes a file path, saving us a lot of quoting). This is not needed if your external SAS log check program output includes only the file name and no path (but it doesn't hurt either way).
#!/bin/sh /usr/local/bin/checksas $1 | /usr/bin/sed -e "s#^#$1: #"
Back to Kent's Vim Page Last-modified: Sun Aug 10 17:09:48 EDT 2025