next up previous contents
下一主题: 6.2.4 使用 进行原子操作 向上: 6.2 半双工 UNIX 管道 前一主题: 6.2.2 在 中创建管道

6.2.3 管道的简易方法!

如果以上所有的絮叨听起来像是创建和使用管道的一种非常迂回的方式,那么还有一种替代方案。


  LIBRARY FUNCTION: popen();                                                    

  PROTOTYPE: FILE *popen ( char *command, char *type);                          
    RETURNS: new file stream on success                                         
             NULL on unsuccessful fork() or pipe() call                         

  NOTES: creates a pipe, and performs fork/exec operations using "command"

这个标准库函数通过在内部调用 pipe() 来创建一个半双工管道。然后它 fork 一个子进程,exec Bourne shell,并在 shell 中执行 "command" 参数。数据流的方向由第二个参数 "type" 决定。它可以是 "r" 或 "w",分别代表 "read" 或 "write"。它不能同时是两者!在 Linux 下,管道将以 "type" 参数的第一个字符指定的模式打开。因此,如果您尝试传递 "rw",它将仅以 "read" 模式打开。

虽然这个库函数为您执行了相当多的繁琐工作,但也存在着重大的权衡。您失去了曾经通过使用 pipe() 系统调用和自行处理 fork/exec 所拥有的精细控制。然而,由于直接使用了 Bourne shell,shell 元字符扩展(包括通配符)在 "command" 参数中是允许的。

使用 popen() 创建的管道必须使用 pclose() 关闭。到现在为止,您可能已经意识到 popen/pclose 与标准文件流 I/O 函数 fopen() 和 fclose() 惊人地相似。


  LIBRARY FUNCTION: pclose();                                                   

  PROTOTYPE: int pclose( FILE *stream );                                        
    RETURNS: exit status of wait4() call                                        
             -1 if "stream" is not valid, or if wait4() fails                   

  NOTES: waits on the pipe process to terminate, then closes the stream.

pclose() 函数对 popen() fork 的进程执行 wait4()。当它返回时,它会销毁管道和文件流。再一次,它与基于流的普通文件 I/O 的 fclose() 函数同义。

考虑这个例子,它打开一个到 sort 命令的管道,并继续对一个字符串数组进行排序

/*****************************************************************************
 Excerpt from "Linux Programmer's Guide - Chapter 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULE: popen1.c
 *****************************************************************************/
  
#include <stdio.h>

#define MAXSTRS 5

int main(void)
{
        int  cntr;
        FILE *pipe_fp;
        char *strings[MAXSTRS] = { "echo", "bravo", "alpha",
                                  "charlie", "delta"};

        /* Create one way pipe line with call to popen() */
        if (( pipe_fp = popen("sort", "w")) == NULL)
        {
                perror("popen");
                exit(1);
        }

        /* Processing loop */
        for(cntr=0; cntr<MAXSTRS; cntr++) {
                fputs(strings[cntr], pipe_fp);
                fputc('\n', pipe_fp);
        }

        /* Close the pipe */
        pclose(pipe_fp);
        
        return(0);
}

因为popen()使用 shell 来执行其指令,所有 shell 扩展字符和元字符都可供使用!此外,更高级的技术,例如重定向,甚至输出管道,都可以与 一起使用popen(). 考虑以下示例调用

        popen("ls ~scottb", "r");
        popen("sort > /tmp/foo", "w");
        popen("sort | uniq | more", "w");

作为 popen() 的另一个例子,考虑这个小程序,它打开两个管道(一个到 ls 命令,另一个到 sort 命令)

/*****************************************************************************
 Excerpt from "Linux Programmer's Guide - Chapter 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULE: popen2.c
 *****************************************************************************/

#include <stdio.h>

int main(void)
{
        FILE *pipein_fp, *pipeout_fp;
        char readbuf[80];

        /* Create one way pipe line with call to popen() */
        if (( pipein_fp = popen("ls", "r")) == NULL)
        {
                perror("popen");
                exit(1);
        }

        /* Create one way pipe line with call to popen() */
        if (( pipeout_fp = popen("sort", "w")) == NULL)
        {
                perror("popen");
                exit(1);
        }

        /* Processing loop */
        while(fgets(readbuf, 80, pipein_fp))
                fputs(readbuf, pipeout_fp);

        /* Close the pipes */
        pclose(pipein_fp);
        pclose(pipeout_fp);

        return(0);
}

对于我们 popen() 的最后演示,让我们创建一个通用程序,该程序在传递的命令和文件名之间打开一个管道

/*****************************************************************************
 Excerpt from "Linux Programmer's Guide - Chapter 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULE: popen3.c
 *****************************************************************************/

#include <stdio.h>

int main(int argc, char *argv[])
{
        FILE *pipe_fp, *infile;
        char readbuf[80];

        if( argc != 3) {
                fprintf(stderr, "USAGE:  popen3 [command] [filename]\n");       
                exit(1);
        }

        /* Open up input file */
        if (( infile = fopen(argv[2], "rt")) == NULL)
        {
                perror("fopen");
                exit(1);        
        }

        /* Create one way pipe line with call to popen() */
        if (( pipe_fp = popen(argv[1], "w")) == NULL)
        {
                perror("popen");
                exit(1);
        }

        /* Processing loop */
        do { 
                fgets(readbuf, 80, infile);
                if(feof(infile)) break;

                fputs(readbuf, pipe_fp);
        } while(!feof(infile));

        fclose(infile); 
        pclose(pipe_fp);

        return(0);
}

尝试运行这个程序,使用以下调用

        popen3 sort popen3.c
        popen3 cat popen3.c
        popen3 more popen3.c
        popen3 cat popen3.c | grep main


next up previous contents
下一主题: 6.2.4 使用 进行原子操作 向上: 6.2 半双工 UNIX 管道 前一主题: 6.2.2 在 中创建管道

转换于
1996年3月29日 星期五 14:43:04 EST