Node:Programming with pipes, Next:Low-level file routines, 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; }