暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

封装一个可靠的POPEN

二进制人生 2019-12-12
820

微信公众号:二进制人生
专注于嵌入式linux开发。问题或建议,请发邮件至hjhvictory@163.com。
更新日期:2019/12/12

内容目录

函数原型基本原理简单实例项目级封装

函数原型

#include <stdio.h>

FILE *popen(const char *cmdstring, const char *type);

ret = 成功返回文件指针,失败返回NULL

int pclose(FILE *fp);

ret = cmdstring的终止状态,失败返回-1

基本原理

(1)popen()会调用fork()产生子进程,然后子进程里会调用execl函数来执行/bin/sh -c cmdstring。
父进程和子进程间通过管道pipe通信,如果type是“r”,则子进程会将管道的写端连接到标准输出,而父进程返回管道的读端fp,如下图:

我们对fp进行读,也就是在读取子进程执行命令的输出结果。
如果type是“w”,则子进程会将管道的读端连接到标准输入,而父进程返回管道的写端fp,如下图:

我们对fp进行写,也就是在向子进程的标准输入写数据。

(2)可以使用所有操作文件指针FILE*的函数,但fclose()除外。在调用完popen之后,务必调用pclose函数关闭返回的函数指针,实际上调用fclose也不会发生什么重大错误,pclose最终还是调用了fclose,只不过它会调用waitpid或者wait4函数等待子进程执行完毕再close掉fp。

(3)返回值:若成功则返回文件指针,否则返回NULL,错误原因存于errno中。
返回NULL的原因有很多:

  1. 参数非法,错误值errno = EINVAL;

  2. 分配内存失败;

  3. 调用pipe失败;

  4. 调用fork失败;

(4)popen函数不会阻塞,但是pclose函数会调用waitpid或者wait4函数等待子进程的执行结束,所以在调用pclose之后会阻塞,这一点是需要切记的。pclose函数的返回值是调用waitpid函数获取到的子进程的退出状态status。

注意:popen()会继承环境变量,通过环境变量可能会造成系统安全的问题。

简单实例

#include<stdio.h>
#include<stdlib.h>
int main()
{
    FILE *fp;
    char buffer[1024];
    fp = popen("cat /etc/passwd""r");
    fgets(buffer, sizeof(buffer), fp);
    printf("%s", buffer);
    pclose(fp);
    return 0;
}

项目级封装

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

int POPEN_READ(char *cmd,char *res,int len)
{
    len--;//预留一个位来保存'\0'
    if(cmd == NULL || res == NULL)
        return -1;

    FILE *fp;
    char buf[1024];
    int l,nread = 0;
    fp = popen(cmd,"r");
    if(fp != NULL){
        //之所以这么复杂,是因为我要处理传入的buf的len<输出的结果的情况。
        while(fgets(buf,1024,fp) != NULL)   
        {   
            l = strlen(buf);
            if(l >= len - nread)
            {
                memcpy(res + nread,buf,len-nread);
                res[len+1] = '\0';
                return len;
            }
            else
            {
                memcpy(res + nread,buf,l+1);
            }
            nread += l;  
        }   
        pclose(fp);
        return nread;
    }
    else{
        printf("popen % failed!",cmd);
        return -1;
    }

}


int main()
{
    char buf[1024];
    memset(buf,0,sizeof(buf));
    POPEN_READ("ls /",buf,sizeof(buf));
    printf("buf=%s\n",buf);
    return 0;
}


图注:二进制人生公众号



文章转载自二进制人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论