• Print  |
  • Feedback  |

Knowledge Base


TS-353

Using the SAS System with other Programming Languages in the UNIX Environment

OUTLINE
  1. Abstract

  2. Introduction

  3. Calling Standalone Programs from the SAS System
    • 3.1 Return Codes
    • 3.2 The X Statement
    • 3.3 The X Command
    • 3.4 The %SYSEXEC Macro
    • 3.5 The SYSTEM Call routine
    • 3.6 The PIPE Device Type

  4. Calling SAS from C
    • 4.1 Discussion of fork and exec
    • 4.2 Pipes
    • 4.3 Using Pipes with the SAS System
    • 4.4 The -stdio Option in the SAS System
    • 4.5 Pre-defined File Descriptors
    • 4.6 The fcntl System Call
    • 4.7 Sample C Program Using Pipes With The SAS System

  5. Named Pipes

  6. Calling SAS from a shell script
    • 6.1 Other Ways To Interpret Results

  7. Miscellaneous Features
    • 7.1 SYSJOBID
    • 7.2 %SYSGET and SYSGET
    • 7.3 The -SYSPARM Option

  8. SAS/TOOLKIT

  9. Conclusion


1. Abstract

        Several features exist in the SAS System that facilitate the
        development of powerful and flexible applications in the UNIX
        environment. These features allow you to run standalone programs
        from the SAS System and invoke the SAS System from standalone
        programs. Passing data to and from the SAS System in both scenarios
        is explained with detailed examples.

2. Introduction

        Customers often call Technical Support and want to access a
        standalone program they have written in a language other than SAS.
        This paper discusses several options for accessing other programs
        from the SAS System in the UNIX environment. Methods for passing
        data to and from these programs is explained.  Customers may also
        want to invoke a SAS application from their standalone program (for
        example, a C program).  This paper discusses options for accessing
        the SAS System from C programs and shell scripts. Methods for
        passing data to and from the SAS System are explained.

3. Calling Standalone Programs from the SAS System

        If you have a standalone program that you want to execute from
        within the SAS System, you have several options. The method you
        choose depends on what you want to accomplish. Five methods will be
        discussed:

           - X statement

           - X command

           - %SYSEXEC macro

           - SYSTEM function in the DATA step or in SCL

           - PIPE device type on the FILENAME statement

        An explanation will be given later.  Before these methods are
        discussed, an explanation of return codes is necessary.

3.1 Return Codes

        When you execute a UNIX program from within the SAS System, the
        return code of the UNIX command is available to the SAS session
        unless the command is executed using the PIPE device type.  The SAS
        System accesses the return code with the wait system call.

        The return code is returned in two bytes. The first byte (low order
        8 bits) contains information about any UNIX signal that was
        received while the command was executing. This byte is usually 0.
        The second byte contains the actual return code. For example, if
        you executed a command from within the SAS System that exited with
        a return code of 1, SAS would receive this information in two
        bytes:

          00000001  00000000
          |      |  |      |
          --------  --------
              |        |
              |        | first byte contains signal information
              |
              | second byte contains actual return code
        The return code that is available to SAS in this case is 256, not
        1, because the return code is shifted to the left 8 bits. If the
        command you are executing is not terminated with a signal, the
        return code is always n*256 where n is the actual return code (or
        exit status) of the program. The following table illustrates return
        code values.

              exit    return
              status  code      two bytes
              -------+--------+------------------
              0      |    0   | 00000000 00000000
              1      |  256   | 00000001 00000000
              2      |  512   | 00000010 00000000
              3      |  768   | 00000011 00000000
              4      | 1024   | 00000010 00000000

                        .
                        .
                        .
        You should always divide the return code by 256 to get the actual
        exit status of your programs unless your program was terminated
        with a signal.

        A program is terminated with a signal if its return code is greater
        than 0 and less than 256. The return code is equivalent to the
        signal that was sent to the program. For example, the following SAS
        statements submit the sleep command for 35 seconds, then print the
        return code to the SAS log:

          x 'sleep 35';
          %put &SYSRC;
        From another window, ascertain the process id of the sleep command
        and specify it in the following UNIX command:

          kill -SIGINT <process id>
        The return code will be the integer equivalent to the interrupt
        signal (SIGINT).

        Signals and their corresponding integer values are defined in the
        following header file:

          /usr/include/sys/signal.h

3.2 The X Statement

        The X statement is a convenient way to unconditionally execute a
        UNIX command from within a SAS program or SCL program. For example,
        if you wanted to remove the directory and all files in the
        directory /tmp/sasjob, you could put the following statement at the
        beginning of your SAS program:

          x 'rm -r /tmp/sasjob';
          %put "The return code is &SYSRC";
        The X statement is not itself a conditional statement.  In the
        following example, the X statement will always execute.

          data _null_;
            sasvar=0;
            if sasvar=1 then do;
              x 'echo "this statement will always execute"';
              end;
          run;
        You can make the X statement "conditional" by embedding your SAS
        program in a macro. For example,

          /*
           * macro definition
           */
          %macro cond(makedir);
            %if &makedir = true %then %do;

              x 'rm -r /tmp/sasjob';

              %let rc = %eval(&SYSRC/256);
              %if &rc = 0 %then %do;
                %put rm -r /tmp/sasjob successful;
              %end;
              %else %do;
                %put rm -r /tmp/sasjob failed with rc=&rc;
              %end;
            %end;
          %mend;

          /*
           * macro invocation
           */
          %cond(true);
        The advantage to this method is that you don't need to have the
        statement embedded within the step boundary of a DATA step. If the
        only task you want to accomplish is to clean up the directory
        /tmp/sasjob, you can avoid the overhead of loading the DATA step.

        As you can see from the example, the return code is available in
        the &SYSRC macro variable.

        Before the SAS System starts the shell to execute any UNIX command,
        it checks to see whether the command is cd, pwd, or setenv. These
        three commands are built into the SAS System because they relate to
        the environment of the SAS session. Refer to "SAS Companion for
        UNIX Environments: Language, Version 6 First Edition" for
        information on how the SAS System processes these commands.

3.3 The X Command

        You can also use the X command to execute UNIX commands in Display
        Manager.  The X command is equivalent to the X statement with two
        exceptions. First, the X command is issued from the command line.
        Second, the X command does not put the return code in &SYSRC.

3.4 The %SYSEXEC Macro

        The %SYSEXEC macro provides the same functionality as the X
        statement.  The %SYSEXEC macro can be used in open code. The return
        code is available in the &SYSRC macro variable, as in the following
        example:

          %SYSEXEC /bin/sh -c "exit 1";
          %put "The return code is &SYSRC";

Note in the above example, the value of &SYSRC will be 256, not 1.

3.5 The SYSTEM Call routine

        Sometimes, you may want to conditionally execute a system command
        within the DATA step or your SCL program. This can be accomplished
        using the SYSTEM call routine. The syntax is:

          rc=system('unix_command');
          rc=rc/256
        The numeric variable rc is assigned the return code of
        unix_command.  Remember that you need to divide the return code by
        256.  Your program can conditionally execute other code based on
        the return code you get from unix_command.

        For example, the following program will print the message "Elvis is
        alive" if elvis is a userid in the /etc/passwd file:

          data _null_;
            rc=system('grep "^elvis:" /etc/passwd > /dev/null');
            if rc = 0 then
              put 'elvis is alive';
          run;

This program will also run in an SCL program:

          INIT:
            rc=system('grep "^elvis:" /etc/passwd > /dev/null');
            if rc = 0 then
              put 'elvis is alive';
          RETURN;
        The advantage of the system call is that it is a conditional
        statement within a DATA step or SCL program. The statement can be
        executed based on one or more conditions.  The X statement and
        %SYSEXEC macro cannot be conditionally executed without macro
        statements.

        The disadvantage of the SYSTEM call is that it must be run in
        either a DATA step or SCL program; and you may have the overhead of
        loading the DATA step or AF application.

3.6 The PIPE Device Type

        If you want to read data generated from a standalone program, an
        effective and efficient facility to do this is the PIPE device
        type. The syntax for associating a fileref with a UNIX command
        using the PIPE device type is:

          FILENAME fileref PIPE 'unix_command';
        When the fileref is accessed in the DATA step, unix_command is
        executed.  Data written to standard output (stdout) by unix_command
        is buffered and available to subsequent input statements in the
        DATA step. For example, the following program creates a dataset
        with one variable called UID. The dataset contains an observation
        for everyone who uses the korn shell (/bin/ksh).

          filename korn pipe 'grep "ksh$" /etc/passwd | sed "s/:.*$//"';
          data kshusers;
            infile korn lrecl=20 pad;
            input uid $20.;
          run;

Here's the same program written in SCL:

          pipe:
            /*
             * create the dataset work.kshusers with the uid variable
             */
            dsid=open('kshusers', 'n');
            rc=newvar(dsid, 'uid', 'c', 20);
            rc=close(dsid);

            /*
             * open the dataset in update mode so we can use the append function
             */
            dsid=open('kshusers', 'u');
            call set(dsid);

            /*
             * assign our UNIX command to a fileref
             */
            command='grep "ksh$" /etc/passwd | sed "s/:.*$//"';
            rc=filename('comfref', command, 'PIPE');
            fid=fopen('comfref', 'S');

            /*
             * read the output from the pipe
             */
            do while(fread(fid) ^= -1);
              rc=fget(fid,uid);
              rc=append(dsid);
              end;

            /*
             * clean up
             */
            rc=close(dsid);
            rc=fclose(fid);
            rc=filename('comfref', ' ');
          return;
        Information written to the pipe by your program is blocked until
        the SAS System reads from the pipe. You don't have to worry about
        running out of virtual memory due to large amounts of data passed
        through the pipe since the data is transferred in pieces (or
        blocks). You can however run out of memory if the actual command
        you are executing consumes all available memory on your machine.

        One limitation of the PIPE device type is the inability to read
        binary data in variable length records using RECFM=N. For example,
        to have a program that generates floating point numbers and pipe
        the numbers directly into SAS, you need to use the RECFM=F argument
        on the INFILE statement. The RECFM=F option indicates to the SAS
        System that the records are fixed.  Another way around this
        limitation is to redirect the output of the program to a temporary
        file, and then read the disk file.

        When the SAS System opens a binary file using the RECFM=N option,
        no buffering is performed. The SAS System must therefore be able to
        seek backwards in the file. The ability to seek backwards does not
        exist in a pipe since the data is gone once it is read. Therefore,
        the RECFM=F option allows you to read binary data since i/o is
        buffered.

        You can also write to filerefs assigned with the PIPE device type.
        For example, you can associate a fileref with your print command
        and have your output directed to the printer:

          filename printer pipe 'pstext | lp -dchpljr24';
          data _null_;
            file printer;
            put 'This text has been sent through pstext';
            put 'before being printed using the network';
            put 'line printer (lp) utility';
          run;
        In the above example, text written to the PRINTER fileref is first
        piped through the pstext utility. The pstext utility is shareware
        that converts plain text to PostScript. The output from pstext is
        sent to the lp command.

        It is important to realize that 'unix_command' can be any valid
        UNIX command or commands. UNIX has a rich set of filter programs
        for reading, massaging, and writing data. These utilities can be
        used with SAS to give you flexibity in designing an application.

4. Calling SAS from C

        You may want to invoke the SAS System from a C program.  You may
        also want to pass data between your C program and the SAS process.
        This can be accomplished with the fork and exec system calls. Data
        can be passed back and forth using pipes. First, I will discuss a
        simple example that runs a SAS program from a C program. Then I
        will discuss pipes and how to pass data back and forth between a
        parent and a child "SAS" process.

4.1 Discussion of fork and exec

        An in-depth discussion of the fork and exec system calls is beyond
        the scope of this paper. A brief overview is provided here.  For
        more information, you can refer to the UNIX man pages on fork and
        exec.

        In UNIX, programs are started with the exec system calls (there are
        six different exec calls). A UNIX program is a file that contains
        instructions and data that are used to initialize the instruction
        and user-data segments of a process. A process is an instance of a
        running program that contains instruction, user-data, and system-
        data segments.

        The exec system calls initialize a process from a specified
        program; exec is the only way programs can be started under UNIX.
        The fork system call creates a new (child) process that is a clone
        of an existing process; fork is the only way new processes can be
        created.

        The six exec system calls are execl, execv, execle, execve, execlp,
        and execvp. Refer to the UNIX man page on exec for a discussion of
        exec system calls.

        The system calls fork and exec are usually used together; exec by
        itself is of little use; fork by itself is of no practical use.
        Here is a C program that uses fork and exec to run a SAS program:

          #include <stdio.h>
          main()
          {
            int childpid,  /* child process id */
                rc;        /* return code      */

            if ((childpid = fork()) == -1
            {
              fprintf(stderr, "Can't fork0);
              exit(1);
            }
            else if (childpid == 0)
            {
              /*
               * this is the child process; execute the SAS program
               */
              execlp("sas", "sas", "program.sas", NULL);
            }
            else
            {
              /*
               * this is the parent process
               */
              printf("waiting for sas process %d to complete0, childpid);
              wait(&rc);
              rc=rc/256;
              printf("sas process completed with rc=%d0, rc);
            }
          }
        One important thing to note about fork and exec is that a copy of
        the process is made in virtual memory when the fork statement is
        executed. This is especially important when you use the SYSTEM
        function, the X statement, or the %SYSEXEC macro because these
        statements are implemented using fork and exec.  For example, if
        you have an interactive SAS program with the following SCL
        statement, a copy of the process is made in memory to execute the
        mkdir program:

          rc=system('mkdir /tmp/foo');
        This is particularly important if your application takes up a lot
        of memory. For example, if your SAS process is 32MB, another 32MB
        of virtual memory will be needed to execute the mkdir command
        without regard for the memsize setting. If you have memsize set to
        32MB, and your SAS process is already 32MB, SAS will attempt to
        fork and exec the command specified by the system function. So, you
        essentially need n MB of virtual memory free before executing the
        command, where n is the current size of your SAS process. This is a
        lot of overhead for executing trivial UNIX commands like mkdir in
        your SAS application.

        When designing your SAS application, you should execute as many
        UNIX commands as possible early before your application gets large.
        Different UNIX platforms implement fork differently. Some systems
        also support the vfork system call. vfork forks a process but the
        instruction and data segments are shared. This not only requires
        less memory, but also speeds the cloning process.  The SAS System
        uses vfork for the PIPE device type on the filename statement. It
        is more efficient in terms of memory overhead and speed to execute
        your UNIX commands using the PIPE device type instead of using the
        X statement, X command, system function, or %SYSEXEC macro. The
        following DATA step illustrates this:

          filename unixcmd PIPE 'mkdir /tmp/foodir';
          data _null_;
            infile unixcmd;
          run;
        Note that you do not actually have to use the input statement to
        read the data.

        The fork system call is used on systems that do not support vfork.
        The vfork system call is provided on HP-UX systems. On systems that
        do not support vfork, fork may be implemented similar to the vfork
        system call. Refer to the UNIX man page on fork and vfork for more
        information on how your system implements forking.

        You can also use the PIPE device type in SCL. Here is an SCL method
        that can be called from SCL programs:
        UNIXCMD:
          method command $;
            rc=filename('comfref', command, 'PIPE');
            fid=fopen('comfref', 'S');
            fid=fclose(fid);
            rc=filename('comfref',' ');
          endmethod;
        RETURN:
        The method can be called from an SCL program with the following
        syntax:

          call method('methods.scl', 'unixcmd', 'mkdir /tmp/foodir');
        Note that methods.scl is the catalog entry that contains the SCL
        source for the method.  Refer to documentation on methods in SAS
        Technical Report P-216, SAS/AF Software, SAS/FSP Software, and SAS
        Screen Control Language: Changes and Enhancements, Release 6.07.

        There are two disadvantages to using the PIPE device type in the
        DATA step. First, the DATA step must be loaded into memory. Second,
        you have no automatic way of checking the return code of a UNIX
        program executed using the PIPE device type.

        There are other ways to get the return code using the PIPE device
        type. You can save the return code to a flat file, then read the
        return code from the file. You can also echo the return code to
        stdout, then read the return code in using an input statement. The
        following DATA step illustrates this:

          filename unixcmd PIPE 'mkdir /tmp/foodir >/dev/null 2>&1; echo $?';
          data _null_;
            infile unixcmd;
            input rc;
            put rc=;
          run;

          Notes from the above example:

            >/dev/null - direct stdout of mkdir command to /dev/null
            2>&1       - direct stderr of mkdir command to /dev/null
            echo $1    - echo the return code to stdout (available in the
                         bourne shell and korn shell using the special
                         shell veriable $?) - the C-shell variable is $status
        Note in the above example that you do not need to divide the return
        code by 256. This is because the return code is being accessed from
        the shell (special variable $?).

4.2 Pipes

In the UNIX environment, pipes are a means for processes to communicate with each other. Pipes are created with the pipe system call int file_desc[2]; ... pipe(file_desc); ...

        Pipes are unidirectional. The pipe system call returns two file
        descriptors:

          file_desc[0] - used for reading
          file_desc[1] - used for writing
        Pipes are typically used when a parent process wants to write data
        to a child process:

          1.  the process creates a pipe

          2.  the process closes the read end of the pipe (file_desc[0])

          3.  the process uses the fork system call (or vfork) to make a
              copy of itself

          4.  the child process closes the write end of the pipe
              (file_desc[1])

        This provides a one-way flow of data between the two processes as
        shown in the following diagram:

<diagram of pipes - (diagram PIPES)>

4.3 Using Pipes with the SAS System

        Before using fork, exec, and pipe to develop an application that
        communicates with a SAS process, you must first be familiar with
        the following:

           - the -stdio option in the SAS System

           - Pre-defined File Descriptors in the SAS System

           - the C function fcntl

4.4 The -stdio Option in the SAS System

        UNIX processes have three file descriptors that are opened
        automatically:

           - stdin (file descriptor 0) is used for reading

           - stdout (file descriptor 1) is used for writing

           - stderr (file descriptor 2) is used for writing errors or
             warnings.

        File descriptors are described in more detail in "SAS Companion for
        UNIX Environments: Language, Version 6 First Edition".  Three SAS
        filerefs are automatically assigned to these three file descriptors
        in interactive SAS programs:

           - STDIN - standard input

           - STDOUT - standard output

           - STDERR - standard error

        These three filerefs are not assigned if the -stdio option is used.
        The -stdio option instructs the SAS System to receive input from
        stdin, to write the log to stderr, and to write output to stdout.
        The -stdio option is designed to run the SAS System in
        noninteractive mode, from a C program, or from a shell script.

        For example, in the following SAS command, file sas_source is used
        as the source program, sas_output is used for procedure output, and
        sas_log is used for the SAS log.

          /bin/sh
          /bin/ksh      sas -stdio < sas_source > sas_output 2> sas_log
          /bin/csh      (sas -stdio < sas_source > sas_output) >& sas_log

4.5 Pre-defined File Descriptors

        The SAS System assigns filerefs of the following form to files
        having a file descriptor larger than 2 and less than 10:

          FILDESnumber

          where number is a two-digit representation of the file descriptor.
        For example, if you invoke the SAS System using the following
        command, UNIX opens the file stats and assigns it to file
        descriptor number 5:

          sas run_stats 5< stats
        The SAS System assigns the fileref FILDES05 to the file and
        executes the program run_stats. When you read from FILDES05, you
        read the file 'stats'.  Not only does this allow you to communicate
        with SAS using pipes, you can also use the same program to process
        data from different files without changing the program to refer to
        each file. You simply specify the input file when you invoke the
        SAS System.

4.6 The fcntl System Call

        The following discussion of the fcntl system call is necessary
        before showing the next example.  The fcntl system call changes
        properties of an open file descriptor.  The F_DUPFD command
        duplicates the file descriptor.  Here is an example of fcntl as it
        would appear in your program, with a description of the arguments.

          #include <fcntl.h>
          ...
          int ret_fd;
          ...
          ret_fd=fcntl(fd, F_DUPFD, arg);
          ...

          where  ret_fd - the new descriptor (int)
                     fd - existing file descriptor (int)
                F_DUPFD - command that requests a duplicate of the
                          existing file descritor fd (int)
                    arg - the lowest number that the new file descriptor is
                          to assume (int)
Refer to the UNIX man pages or other UNIX documentation for a more detailed discussion of fcntl.

4.7 Sample C Program Using Pipes With The SAS System

        #include <stdio.h>
        #include <fcntl.h>

        /*
         * The following array (i.e. SAS program) could be generated dynamically
         */
        char sasprogram[] = { "
        libname output '.';
        data output.file;
          infile fildes05;
          input var1 var2;
        run;
        proc print data=output.file;
        run;"
        };

        int     src[2],    /* file descriptors for the SAS source code */
                data[2];   /* file descriptors for the SAS data        */
        int     saspid;    /* Process ID of the child (SAS process)    */

        void CloseSAS();

        main(argc,argv)
        int     argc;
        char    **argv;
        {
          char  buf[512];  /* Generic data buffer                      */
          int  counter;    /* for loop counter                         */

          /*
           * Invoke the SAS System, and give it the SAS program to run
           */
          if (OpenSAS(sasprogram) == -1)
          {
            fprintf(stderr, "Can't execute The SAS System!!0);
            exit(1);
          }

          /*
           * Generate two columns of integers for SAS input data.
           */
          for(counter=0;counter<5;counter++)
          {
            sprintf(buf,"%d %d0,counter, counter);
            DataToSAS( buf, strlen(buf) );
          }

          /*
           * Terminate the SAS Process, then exit
           */
          CloseSAS();
          return(0);
        }

        int OpenSAS(sasprogram)
        char    *sasprogram;
        {
          int fd;       /* file descriptor returned from fcntl       */

          pipe(src);    /* The pipe for the SAS source statements.   */
          pipe(data);   /* The pipe for the input data stream.       */

          if (!(saspid = fork()))
          {
            /*
             * We're in the child. Reroute the pipes
             */
            close(0);                  /* close stdin of the SAS process */

            fcntl(src[0], F_DUPFD, 0); /* associate stdin with read end  */
                                       /* of the source pipe (src[0])    */

            close(src[0]);             /* close both ends of source pipe */
            close(src[1]);

            close(5);                  /* close just in case it is open  */

            fd=fcntl(data[0], F_DUPFD, 5);  /* associate read end of the */
            printf("fd from fcntl=%d0,fd);/* data pipe with file       */
                                            /* descriptor 5              */

            close(data[0]);            /* close both ends of the data    */
            close(data[1]);            /* pipe                           */

            /*
             * run the SAS program
             */
            execlp("sas", "sas", "-stdio", "-nodms", NULL);
          }

          /*
           * we are in the parent process
           */

          /*
           * close the unwanted ends of the pipes; we are closing the read
           * end of the pipe since both source and data will be "written to"
           * the SAS process
           */
          close(src[0]);
          close(data[0]);

          /*
           * If invoking the SAS System failed
           */
          if (saspid == -1)
          {
            close( src[1] );    /* close write end of both pipes        */
            close( data[1] );

            return(-1);         /* return with -1                       */
          }

          /*
           * Send the SAS System the SAS source to run and close the
           * source pipe
           */
          write(src[1], sasprogram, strlen(sasprogram));
          close(src[1]);

          /*
           * Now any data written to the pipe "data[1]" will be sent
           * to filedescriptor #5 of the SAS process.
           */
          return(saspid);
        }

        /*
         *  Send data to the running SAS process
         */
        int     DataToSAS(buf, len)
        char    *buf;
        int     len;
        {
          if (write(data[1], buf, len) != len)
            return(-1);
          else
            return(0);
        }

        /*
         *  Terminate the SAS process, wait for it, and print exit status
         */
        void CloseSAS()
        {
          int rc;

          close(data[1]);

          while (wait(&rc) != saspid);
          printf("the SAS process ended with rc= %d0, rc / 256);
        }

5. Named Pipes

        A named pipe, sometimes called a FIFO file, is a combination of a
        file and a pipe. Like a file, a named pipe can be opened, written
        to, and read from. When it is opened, a named pipe behaves more
        like a pipe than a file. Written data is read in first-in-first-out
        order. Once data has been read, it cannot be read again.

        The following diagram illustrates how a named pipe works:

<named pipe diagram (diagram NAMED_PIPE)>

        The UNIX command mknod is used with the p option to create a named
        pipe. For example, to create a named pipe called named_pipe, issue
        the following UNIX command:

          mknod named_pipe p
        You can use named pipes if you want a SAS program to wait for input
        before running. For example, the following SAS program will wait
        until data is written to named_pipe before completing.

          data pipe;
            infile 'named_pipe';
            input dream1 dream2;
          run;
        When SAS executes the infile statement, the SAS process is blocked
        (or put in a wait state) until data is written to the file
        named_pipe. SAS will continue to read from the FIFO until the end-
        of-file is encountered.

        If the above SAS statements are in a file called npipe.sas and
        submitted using the following UNIX command, the program waits for
        data to be written to named_pipe:

          sas npipe.sas &
        Once data is written to named_pipe, the SAS System continues
        processing until the process that was writing to the named pipe
        closes the file (that is, until an EOF is encountered). To get the
        SAS process to finish reading from named_pipe, we could use the
        following UNIX command to write data to 'named_pipe':

          echo "1 2" > named_pipe
        The dataset pipe that is created would only have one observation.
        You can write as many records to named_pipe as you want.

6. Calling SAS from a shell script

        Sometimes it is useful to invoke the SAS System from a shell
        script. There is nothing you can do in a shell script that can't be
        done in a C program but shell scripts are usually more convenient
        and easier to maintain.  You can invoke the SAS System in
        interactive or batch mode. Invoking the SAS System from a shell
        script allows you to complete some tasks before the SAS System is
        invoked. Such tasks include:

           - AFS authentication: useful if you have data stored under AFS
             where authentication is required before data is accessed

           - invocation options: you may want to add invocation options
             that your script parses before invoking the SAS System.

           - move and copy files: you may want to make copies of data or
             create unique libraries for the SAS process

           - privileges: you may want to verify that a user has appropriate
             priviledges, such as write access to a directory, before
             invoking an application

           - logging: you may want to log information such as: the host
             that SAS is being run on or time it takes the process to run.

           - control options: you may want to control certain options that
             are used, for example, MEMSIZE, AUTOEXEC, and SYSPARM

           - control SAS processes: you may want to limit the number of SAS
             processes that are running concurrently by checking the number
             of active SAS processes before submitting.

           - set environment variables - the SAS System can access these
             variables using the %SYSGET function

        The following shell script runs npipe.sas in the background. After
        running npipe.sas, the script sends data to named_pipe.

          #!/bin/sh

          #
          # make the named pipe
          #
          mknod named_pipe p

          #
          # run the sas program in the background; this program will be
          # blocked (that is, wait), until all data is written to the
          # named_pipe (that is, until an EOF is received)
          #
          sas npipe.sas &

          #
          # use a "here text" with the cat command to send data to the
          # named pipe
          #
          cat - > named_pipe << ----EOT----
          1 2
          3 4
          5 6
          7 8
          ----EOT----

          #
          # remove the named pipe
          #
          rm named_pipe

          #
          # end of script
          #
        After your SAS program completes, you can check the SAS return code
        or scan the log for certain messages. Here are the return codes
        generated by Release 6.09 of the SAS System and what they indicate:
		  Condition                                  Return Code
          all steps terminated normally              0
		  
          SAS System issued warning(s)               1

          SAS System issued error(s)                 2
		  
          User issued ABORT statement                3

          User issued ABORT RETURN statement         4

          User issued ABORT ABEND statement          5

          User issued ABORT RETURN n statement       n

          User issued ABORT ABEND n statement        n

6.1 Other Ways To Interpret Results

        You can also use other UNIX tools to scan the SAS log for certain
        text. For example, grep could be used to search for specific ERRORS
        or WARNINGS in the SAS log.

7. Miscellaneous Features The following features can also be useful in SAS applications: SYSJOBID, %SYSGET, SYSGET, and the -SYSPARM option

7.1 SYSJOBID SYSJOBID is an automatic macro variable that contains the process id of the SAS process that is running.

7.2 %SYSGET and SYSGET The %SYSGET macro function returns the character string that is the value of the environment variable passed as the argument. A warning message is printed if the environment variable does not exist. The form of the %SYSGET function is:

%SYSGET(environment_variable);
     For example, to print the value of the HOME environment variable in
     the SAS log, you could use the following SAS statements:

       %LET HOMEVAR=%SYSGET(HOME);
       %PUT &HOMEVAR;
     The SYSGET function must be run in a DATA step or an SCL program. The
     form of the SYSGET function is:

       SYSGET('environment_variable');
     For example, to print the value of the HOME environment variable in
     the SAS log, you could use the following DATA step:

       DATA _NULL_;
         HOMEVAR=SYSGET('HOME');
         PUT HOMEVAR;
       RUN;

7.3 The -SYSPARM Option

        The -SYSPARM option allows you to pass information to a SAS process
        from the command line. The information is available to the SAS
        System in the automatic macro variable &SYSPARM. For example, the
        following SAS invocation passes an input file name to the SAS
        System:

          sas -sysparm "/my/input/file";
        Then, you can use the following infile statement in your SAS
        programs:

          INFILE "&SYSPARM";
        You must enclose the SYSPARM argument in quotes if your argument
        has embedded blanks.

8. SAS/TOOLKIT

        SAS/TOOLKIT software, an integrated component of the SAS System,
        enables you to write your own customized SAS procedures (including
        graphics procedures), informats, formats, functions (including IML
        and DATA step functions), CALL routines, and database engines in
        several languages, including C and Fortran.

        There are several advantages that SAS/TOOLKIT has over integrating
        standalone programs with the SAS System. Among the advantages are:

           - extensibility: you can directly extend the capabilities of the
             SAS System to meet the specialized needs of your users

           - convenience: a component (for example, a SAS procedure)
             incorporated into the SAS System allows you to put all your
             programming tools in one place

           - integration: the data used by user written procedures are
             stored as SAS datasets and can thus be easily sorted, printed,
             and analyzed using other SAS procedures during a single job

           - efficiency: the components you develop run in the same address
             space as your SAS process. Therefore, you do not have the
             cloning overhead associated with fork and exec.

        SAS/TOOLKIT is a powerful extension to the SAS System. Once you
        compile and link the component(s) you are working on, you no longer
        need SAS/TOOLKIT because you don't need SAS/TOOLKIT to use the
        components you develop.

9. Conclusion

        Your SAS applications can be integrated and used with other
        programs and applications. The capabilities discussed in this paper
        provide you with the flexibility and power to design applications
        to meet your needs.