1. popen的应用场景

popen应用于执行shell命令,并读取此命令的返值,或者与执行的命令进行交互。

2. popen的实现

popen()函数通过创建一个管道,调用fork()产生一个子进程,执行一个shell以运行命令来开启一个进程。可以通过这个管道执行标准输入输出操作。这个管道必须由pclose()函数关闭,必须由pclose()函数关闭,必须由pclose()函数关闭,而不是fclose()函数(若使用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()的进程相同。

返回值

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

FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

3. pclose操作

pclose()函数会闭标准I/0流,等待子进程结束,然后返回shell终止状态。如果不执行,则pclose()返回终止状态就是shell的exit状态。

源码附上:

/* Copyright (C) 2004       Manuel Novoa III    <mjn3@codepoet.org>* Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>** Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.** Dedicated to Toni.  See uClibc/DEDICATION.mjn3 for details.*//* Jan 1, 2004** Rewrite popen for SUSv3 compliance.*   Added a list of popen()'d to store pids and use waitpid() in pclose().*   Loop on waitpid() failure due to EINTR as required.*   Close parent's popen()'d FILEs in the {v}fork()'d child.*   Fix failure exit code for failed execve().*/#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <bits/uClibc_mutex.h>#ifdef __UCLIBC_MJN3_ONLY__
#warning "hmm... susv3 says Pipe streams are byte-oriented."
#endif /* __UCLIBC_MJN3_ONLY__ *//* uClinux-2.0 has vfork, but Linux 2.0 doesn't */
#include <sys/syscall.h>
#if ! defined __NR_vfork
# define vfork fork
# define VFORK_LOCK     ((void) 0)
# define VFORK_UNLOCK       ((void) 0)
#endif#ifndef VFORK_LOCK
__UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER);
# define VFORK_LOCK     __UCLIBC_MUTEX_LOCK(mylock)
# define VFORK_UNLOCK       __UCLIBC_MUTEX_UNLOCK(mylock)
#endifstruct popen_list_item {struct popen_list_item *next;FILE *f;pid_t pid;
};static struct popen_list_item *popen_list /* = NULL (bss initialized) */;FILE *popen(const char *command, const char *modes)
{FILE *fp;struct popen_list_item *pi;struct popen_list_item *po;int pipe_fd[2];int parent_fd;int child_fd;int child_writing;            /* Doubles as the desired child fildes. */pid_t pid;child_writing = 0;         /* Assume child is writing. */if (modes[0] != 'w') {     /* Parent not writing... */++child_writing;       /* so child must be writing. */if (modes[0] != 'r') {    /* Oops!  Parent not reading either! */__set_errno(EINVAL);goto RET_NULL;}}if (!(pi = malloc(sizeof(struct popen_list_item)))) {goto RET_NULL;}// 打开一个pipe,管道是单向。故数据流只能单向流动。if (pipe(pipe_fd)) {goto FREE_PI;}//下面两个描述符就是管道的两端的描述,一个为读一个为写。child_fd = pipe_fd[child_writing]; parent_fd = pipe_fd[1-child_writing]; //fdopen就是打开一个描述,fd相同模式或者字集的方式打开。意思就是把一个已找打开的流与一个文件描述符相关联,且//这个文件描述是唯一的,这样也就可以保证这个函数接口的可重入性。如果设计的不可重入性,也就没必要再做一次fdopen了。if (!(fp = fdopen(parent_fd, modes))) {close(parent_fd);close(child_fd);goto FREE_PI;}VFORK_LOCK;//再这里创建一个子进程,然后执行 shell命令。这里最重的两步就是用pipe的两个描述替换标准输入或者输出。if ((pid = vfork()) == 0) {    /* Child of vfork... */close(parent_fd);if (child_fd != child_writing) {dup2(child_fd, child_writing); //用child_fd来代替标准输入或输出。close(child_fd);}/* SUSv3 requires that any previously popen()'d streams in the* parent shall be closed in the child. *///关闭不必要的资源。for (po = popen_list ; po ; po = po->next) {close(po->f->__filedes);}//执行exec shell,这个时候标准输入/输出就变为pipe管道的一端了。//这里只能实现单向的功能。要么读要么写。execl("/bin/sh", "sh", "-c", command, (char *)0);/* SUSv3 mandates an exit code of 127 for the child if the* command interpreter can not be invoked. */_exit(127);}VFORK_UNLOCK;/* We need to close the child filedes whether vfork failed or* it succeeded and we're in the parent. */close(child_fd);//将当前的信息保存到全局链表。为了是pclose可以找到对的子进程与通信文件描述。if (pid > 0) {                /* Parent of vfork... */pi->pid = pid;pi->f = fp;VFORK_LOCK;pi->next = popen_list;popen_list = pi;VFORK_UNLOCK;return fp;}/* If we get here, vfork failed. */fclose(fp);                   /* Will close parent_fd. */FREE_PI:free(pi);RET_NULL:return NULL;
}#warning is pclose correct wrt the new mutex semantics?int pclose(FILE *stream)
{struct popen_list_item *p;int stat;pid_t pid;/* First, find the list entry corresponding to stream and remove it* from the list.  Set p to the list item (NULL if not found). */VFORK_LOCK;if ((p = popen_list) != NULL) {if (p->f == stream) {// 找到stream对应的popen结点。popen_list = p->next;} else {struct popen_list_item *t;do {t = p;if (!(p = t->next)) {__set_errno(EINVAL); /* Not required by SUSv3. */break;}if (p->f == stream) {t->next = p->next;break;}} while (1);}}VFORK_UNLOCK;if (p) {pid = p->pid;          /* Save the pid we need */free(p);              /* and free the list item. */fclose(stream);    /* The SUSv3 example code ignores the return. *//* SUSv3 specificly requires that pclose not return before the child* terminates, in order to disallow pclose from returning on EINTR. */do {if (waitpid(pid, &stat, 0) >= 0) { //等待子进程返回。获取返回值。return stat;}if (errno != EINTR) {break;}} while (1);}return -1;
}

举例附上:

#include <stdio.h>
#include <string.h>int main()
{FILE *fp = NULL;char buf[1024] = "";fp = popen("ls -al", "r");if(fp == NULL){perror("popen error\n");return -1;}while(fgets(buf, sizeof(buf), fp) != 0){printf("%s\n", buf);memset(buf, 0x0, sizeof(buf));}pclose(fp);return 0;
}

popen使用方法及场景相关推荐

  1. python私有方法应用场景_Python 私有属性和私有方法应用场景分析

    类的私有属性和方法 Python是个开放的语言,默认情况下所有的属性和方法都是公开的 或者叫公有方法,不像C++和 Java中有明确的public,private 关键字来区分私有公有. Python ...

  2. dnf命令参数详细说明、bclinux8或centos8以上系统使用dnf命令离线安装本地rpm包方法及场景和原因、使用dnf命令提示正在等待 pid 为422620的进程退出。的解决方法

    文章目录 dnf命令 dnf说明 安装 DNF 包管理器 dnf [选项] 命令 [dnf使用说明] dnf安装本地rpm包 全部参数 bclinux8或centos8以上系统使用dnf命令安装rpm ...

  3. 【经验总结】C#常用线程同“.NET研究”步方法应用场景和实现原理

    简单描述volatile,Interlocked,lock,Mutex,Semaphore,Spin lock,AutoResetEvent,ManualResetEvent,ReaderWriter ...

  4. python私有方法应用场景_Python私有属性私有方法应用实例解析

    01. 应用场景及定义方式 应用场景 在实际开发中,对象 的 某些属性或方法 可能只希望 在对象的内部被使用,而 不希望在外部被访问到 私有属性 就是 对象 不希望公开的 属性 私有方法 就是 对象 ...

  5. Java私有方法运用场景_java6-3 封装和private关键字

    1.  private: 是一个权限修饰符 可以修饰成员变量和成员方法 被其修饰的成员只能在本类中被访问 定义一个学生类: 成员变量:name,age 成员方法:show()方法 2.我们在使用这个案 ...

  6. Java中注解与反射的使用方法及场景,强行解释一波!

    作者:BudingCode blog.csdn.net/m0_55221239/article/details/115025182 注解 注解定义 Java 注解(Annotation)又称 Java ...

  7. 软件测试—软件测试基础知识—测试用例设计的方法之场景法、正交试验法和错误推断法

    场景法 尽可能真实全部的模拟用户操作–订单,发货,商品状态变化 场景法主要基于: 1.业务(需求)层面 :对所测软件的重要功能.业务逻辑(系统要干什么,怎么去实现,这个过程).行业背景深入理解. 2. ...

  8. 商业模式新生代_商业模式设计方法之场景(化)——《商业模式新生代》之十二...

    今天我们来看#商业模式#设计方法的最后一种方法--场景,其实这里的场景不知道大家和我一样,看起来比较别扭,难以理解.因为这本书是舶来品,英译过来的,所以我只好用万能的百度来查了查,书里场景对应的单词是 ...

  9. SVN patch的使用方法及场景

    写目录 1. 客户端 1.1 相关功能菜单 1.1.1 `create patch` 1.1.2 `apply patch` 1.2 本次commit生成patch 1.3 多条commit合成pat ...

最新文章

  1. JAVA学习笔记系列4-Eclipse版本选择
  2. 深入理解PHP Opcode缓存原理
  3. python 创建json_使用Django和Python创建Json response的方法
  4. mysql linux centos7_MySQL在Linux centos7环境下安装教程详解(图)
  5. openstack一键安装脚本(转载)
  6. mysql 8.0认证失败_解决mysql8.0因密码认证插件导致的链接不上
  7. 字节跳动专家会_年薪30万60万!字节跳动招这个语系的语言专家!
  8. 第006讲 多媒体页面 标签汇总
  9. 微博营销普遍遇到的误区
  10. win2012服务器 注册表,第十一章 Windows Server 2012 R2 注册表域注册表编辑器 ---学习笔记...
  11. 对话OpenCV创始人Gary:“亚洲社会都太重视学历和证书”
  12. See Conf 悠鹤《蚂蚁庄园背后的技术与思考》笔记
  13. 增加matlab内存大小,matlab扩大内存的方法
  14. 忽如一夜春风来,千树万树梨花开
  15. airflow调度方案
  16. Win10双网卡不双待攻略
  17. ip iq 谐波检测matlab仿真,ip-iq谐波检测法的仿真及实验研究ip-iq谐波检测法的仿真及实验研究.pdf...
  18. 语音识别(1)四行代码,完成文本转换语音
  19. 软件项目管理与素质拓展-2.3项目管理是残缺的美
  20. 如何将一组列表(三个以上,数值类型不一)保存为txt文件

热门文章

  1. 递推最小二乘辨识平面双机械臂Matlab代码
  2. spellchecker.php,将Spellchecker整合到Tinymce中不起作用
  3. iOS项目中引入pod管理工具
  4. java隐藏jframe_通过setVisible隐藏/显示的JFrame的窗口事件?
  5. 商标网上申请注册指南(支持下载)
  6. iMeta | 中国农大杨栋组揭示膳食纤维化学结构对肠道微生物的调控
  7. 8位、16位和32位的MCU,更多的位是更好的吗?
  8. 小技能分享——如何将JPG/PNG等格式的图片集转化为mp4/avi等格式的视频
  9. 安卓环境搭建及虚拟机genymotion使用
  10. 计算机广告制作学几年能学好,计算机广告制作(高级)专业介绍