Node:Programming with pipes, Next:, Previous:Single-character input and output, Up:Input and output



Programming with pipes

There may be times when you will wish to manipulate other programs on a GNU system from within your C program. One good way to do so is the facility called a pipe. Using pipes, you can read from or write to any program on a GNU system that writes to standard output and reads from standard input. (In the ancestors of modern GNU systems, pipes were frequently files on disk; now they are usually streams or something similar. They are called "pipes" because people usually visualise data going in at one end and coming out at the other.)

For example, you might wish to send output from your program to a printer. As mentioned in the introduction to this chapter, each printer on your system is assigned a device name such as /dev/lp0. Pipes provide a better way to send output to the printer than writing directly to the device, however.

Pipes are useful for many things, not just sending output to the printer. Suppose you wish to list all programs and processes running on your computer that contain the string init in their names. To do so at the GNU/Linux command line, you would type something like the following command:

ps -A | grep init

This command line takes the output of the ps -A command, which lists all running processes, and pipes it with the pipe symbol (|) to the grep init command, which returns all lines that were passed to it that contain the string init. The output of this whole process will probably look something like this on your system:

    1 ?        00:00:11 init
 4884 tty6     00:00:00 xinit

The pipe symbol | is very handy for command-line pipes and pipes within shell scripts, but it is also possible to set up and use pipes within C programs. The two main C functions to remember in this regard are popen and pclose.

The popen function accepts as its first argument a string containing a shell command, such as lpr. Its second argument is a string containing either the mode argument r or w. If you specify r, the pipe will be open for reading; if you specify w, it will be open for writing. The return value is a stream open for reading or writing, as the case may be; if there is an error, popen returns a null pointer.

The pclose function closes a pipe opened by popen. It accepts a single argument, the stream to close. It waits for the stream to close, and returns the status code returned by the program that was called by popen.

If you open the pipe for reading or writing, in between the popen and pclose calls, it is possible to read from or write to the pipe in the same way that you might read from or write to any other stream, with high-level input/output calls such as getdelim, fprintf and so on.

The following program example shows how to pipe the output of the ps -A command to the grep init command, exactly as in the GNU/Linux command line example above. The output of this program should be almost exactly the same as sample output shown above.

#include <stdio.h>
#include <stdlib.h>

int
main ()
{
  FILE *ps_pipe;
  FILE *grep_pipe;

  int bytes_read;
  int nbytes = 100;
  char *my_string;

  /* Open our two pipes */
  ps_pipe = popen ("ps -A", "r");
  grep_pipe = popen ("grep init", "w");

  /* Check that pipes are non-null, therefore open */
  if ((!ps_pipe) || (!grep_pipe))
    {
      fprintf (stderr,
               "One or both pipes failed.\n");
      return EXIT_FAILURE;
    }

  /* Read from ps_pipe until two newlines */
  my_string = (char *) malloc (nbytes + 1);
  bytes_read = getdelim (&my_string, &nbytes, "\n\n", ps_pipe);

  /* Close ps_pipe, checking for errors */
  if (pclose (ps_pipe) != 0)
    {
      fprintf (stderr,
               "Could not run 'ps', or other error.\n");
    }

  /* Send output of 'ps -A' to 'grep init', with two newlines */
  fprintf (grep_pipe, "%s\n\n", my_string);

  /* Close grep_pipe, cehcking for errors */
  if (pclose (grep_pipe) != 0)
    {
      fprintf (stderr,
               "Could not run 'grep', or other error.\n");
    }

  /* Exit! */
  return 0;
}