zoukankan      html  css  js  c++  java
  • c语言popen介绍

    1、函数定义

    #include <stdio.h>
    FILE * popen(const char *command , const char *type );
    int pclose(FILE *stream);

    2、函数说明

      popen()函数通过创建一个管道,调用fork()产生一个子进程,执行一个shell以运行命令来开启一个进程。这个管道必须由pclose()函数关闭,而不是fclose()函数。pclose()函数关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态。如果shell不能被执行,则pclose()返回的终止状态与shell已执行exit一样。

      type参数只能是读或者写中的一种,得到的返回值(标准I/O流)也具有和type相应的只读或只写类型。如果type是"r"则文件指针连接到command的标准输出;如果type是"w"则文件指针连接到command的标准输入。

      command参数是一个指向以NULL结束的shell命令字符串的指针。这行命令将被传到bin/sh并使用-c标志,shell将执行这个命令。

      popen()的返回值是个标准I/O流,必须由pclose来终止。前面提到这个流是单向的(只能用于读或写)。向这个流写内容相当于写入该命令的标准输入,命令的标准输出和调用popen()的进程相同;与之相反的,从流中读数据相当于读取命令的标准输出,命令的标准输入和调用popen()的进程相同。

    3、返回值

      如果调用fork()或pipe()失败,或者不能分配内存将返回NULL,否则返回标准I/O流。popen()没有为内存分配失败设置errno值。如果调用fork()或pipe()时出现错误,errno被设为相应的错误类型。如果type参数不合法,errno将返回EINVAL。

    4、参数说明:

      command: 是一个指向以 NULL 结束的 shell 命令字符串的指针。这行命令将被传到 bin/sh 并使用 -c 标志,shell 将执行这个命令。

      mode: 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。

    5、作用:

        popen函数允许一个程序将另外一个程序作为新进程来启动,并可以传递数据或者通过它接受数据。

        其内部实现为调用 fork 产生一个子进程,执行一个 shell, 以运行命令来开启一个进程,这个进程必须由 pclose() 函数关闭。

    6、缺点:

        使用popen的不好影响是,针对每个popen调用,不仅要启动一个被请求的程序,还要启动一个shell,即每个popen调用将多启动两个进程。

    例子1:  
     #include<stdio.h> #include<unistd.h> #include<string.h> int main() { FILE *fp=NULL; FILE *fh=NULL; char buff[128]={0}; memset(buff,0,sizeof(buff)); fp=popen("ls -l","r");//将命令ls-l 同过管道读到fp fh=fopen("shell.c","w+");// 创建一个可写的文件 fread(buff,1,127,fp);//将fp的数据流读到buff中 fwrite(buff,1,127,fh);//将buff的数据写入fh指向的文件中 pclose(fp); fclose(fh); return 0; }
    [lol@localhost practice]$ ls
    popen popen.c shell.c
    [lol@localhost practice]$ cat shell.c
    total 12
    -rwxrwxr-x. 1 lol lol 5478 May 24 15:39 popen
    -rw-rw-r--. 1 lol lol 473 May 24 15:39 popen.c
    -rw-rw-r--. 1 lol lol  [lol@localhost practice]$ vim popen.c
    [lol@localhost practice]$ 
    例子2:
    //
    execute shell command//执行一个shell命令,输出结果逐行存储在resvec中,并返回行数 int32_t myexec(const char *cmd, vector<string> &resvec) { resvec.clear(); FILE *pp = popen(cmd, "r"); //建立管道 if (!pp) { return -1; } char tmp[1024]; //设置一个合适的长度,以存储每一行输出 while (fgets(tmp, sizeof(tmp), pp) != NULL) { if (tmp[strlen(tmp) - 1] == ' ') { tmp[strlen(tmp) - 1] = ''; //去除换行符 } resvec.push_back(tmp); } pclose(pp); //关闭管道 return resvec.size(); }
    例子3:
    int main()
    {
        FILE *fpr = NULL, *fpw = NULL;
        char buf[256];
        int ret;
    
        fpr = popen("cat /etc/group", "r");  //执行完这行代码,标准输出就装满,这里这个标准输出标记为out1,管道指向out1,fpr指向管道的读端
        fpw = popen("grep root", "w");       //执行这句代码,会一直去读取标准输出,若标准输出为空,则会阻塞,直到标准输出不为空,执行命令后又
                                             //会去指读取标准输出继续执行。这里这个标准输入标记为in2。
                                             //管道指向int2,fpw指向管道的写端
    
        while ((ret = fread(buf, 1, sizeof(buf), fpr)) > 0)  //从out1中读取256个字节数据,存放在buf
        {
            fwrite(buf, 1, ret, fpw);                        //将buf的数据写到int2(此时gerp命令一直在获取int2,直到进行退出)
        }
    
        pclose(fpr);
        pclose(fpw);
    }

    6、文件描述符

    当某个进程打开文件时,操作系统返回相应的文件描述符,进程为了处理该文件必须引用此描述符。

    所谓的文件描述符是一个低级的正整数。最前面的三个文件描述符(0,1,2)分别与标准输入(stdin),标准输出(stdout)和标准错误(stderr)对应

    因此,函数 scanf() 使用 stdin,而函数 printf() 使用 stdout。你可以用不同的文件描述符改写默认的设置并重定向进程的 I/O 到不同的文件。

    popen函数的理解 

    (1) popen(comm, type)函数会创建一个管道,再fork一个子进程,在子进程中执行execX函数来执行comm命令(因为execX执行新程序后新程序的进程空间会覆盖原进程的进程空间,所以开一个子进程来执行execX家族函数),然后想要返回stdout或者stdin的文件指针(取决于type); 

    (2) 因为comm命令是通过子进程的执行的,那么stdin或者stdout文件指针也是子进程的进程片空间的,要将其返回给父进程,这就需要管道了; 

    (3) stdin是供程序写数据的,stdout是供程序读数据的。这里设计的巧妙之处在于,管道的读端跟stdout绑定,管道的写端跟stdin绑定; 

    (4) 读写管道操作的无非就是管道(文件)的fd(文件描述符),这里将fd封装到文件流指针fp中; 

    (5) popen返回的是stdout,那么type为”r”,表示创建一个管道且该管道文件的读端赋给fpr; 

    (6) popen返回的是stdin,那么type为”w”,表示创建一个管道且该管道文件的写端赋给fpw; 

    (7) 这样子,读fpr(管道的读端)等于读子进程的stdout,写fpw(管道的写端)等于写子进程的stdin。

    参考:

    https://www.jb51.net/article/140783.htm

    https://blog.csdn.net/lihfqq/article/details/84687669

    https://blog.csdn.net/hhhlizhao/article/details/71552588

    https://blog.csdn.net/yellowston/article/details/90727424

    https://blog.csdn.net/qq_29344757/article/details/70925123

  • 相关阅读:
    R vs Python:构建data.frame、读取csv与统计描述
    R语言学习笔记:使用reshape2包实现整合与重构
    Python学习笔记:lambda表达式
    Python学习笔记:startswith & endswith 判断开头结尾是否为指定字符串
    Python学习笔记:一手漂亮的Python函数
    电信行业数据挖掘分析
    Oracle学习笔记:实现select top N的方法
    Oracle学习笔记:ORA-22992 cannot use LOB locators selected from remote tables
    Linux学习笔记:ls和ll命令
    vb常用命名空间
  • 原文地址:https://www.cnblogs.com/ggzhangxiaochao/p/15022755.html
Copyright © 2011-2022 走看看