标准I/O函数库提供了popen函数,它启动另外一个进程去执行一个shell命令行。

这里我们称调用popen的进程为父进程,由popen启动的进程称为子进程。

popen函数还创建一个管道用于父子进程间通信。父进程要么从管道读信息,要么向管道写信息,至于是读还是写取决于父进程调用popen时传递的参数。下在给出popen、pclose的定义:

#include <stdio.h>
/*
函数功能:popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令。
参数type可使用“r”代表读取,“w”代表写入。
依照此type值,popen()会建立管道连到子进程的标准输出设备或标准输入设备,然后返回一个文件指针。
随后进程便可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中
返回值:若成功则返回文件指针,否则返回NULL,错误原因存于errno中
*/
FILE * popen( const char * command,const char * type); /*
函数功能:pclose()用来关闭由popen所建立的管道及文件指针。参数stream为先前由popen()所返回的文件指针
返回值:若成功返回shell的终止状态(也即子进程的终止状态),若出错返回-1,错误原因存于errno中
*/
int pclose(FILE * stream);

下面通过例子看下popen的使用:

假如我们想取得当前目录下的文件个数,在shell下我们可以使用:

ls | wc -l
/*取得当前目录下的文件个数*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h> #define MAXLINE 1024 int main()
{
char result_buf[MAXLINE], command[MAXLINE];
int rc = 0; // 用于接收命令返回值
FILE *fp; /*将要执行的命令写入buf*/
snprintf(command, sizeof(command), "ls ./ | wc -l"); /*执行预先设定的命令,并读出该命令的标准输出*/
fp = popen(command, "r");
if(NULL == fp)
{
perror("popen执行失败!");
exit(1);
}
while(fgets(result_buf, sizeof(result_buf), fp) != NULL)
{
/*为了下面输出好看些,把命令返回的换行符去掉*/
if('\n' == result_buf[strlen(result_buf)-1])
{
result_buf[strlen(result_buf)-1] = '\0';
}
printf("命令【%s】 输出【%s】\r\n", command, result_buf);
} /*等待命令执行完毕并关闭管道及文件指针*/
rc = pclose(fp);
if(-1 == rc)
{
perror("关闭文件指针失败");
exit(1);
}
else
{
printf("命令【%s】子进程结束状态【%d】命令返回值【%d】\r\n", command, rc, WEXITSTATUS(rc));
} return 0;
}
编译并执行:

$ gcc popen.c

$ ./a.out

命令【ls ./ | wc -l】 输出【2】

命令【ls ./ | wc -l】子进程结束状态【0】命令返回值【0】

上面popen只捕获了command的标准输出,如果command执行失败,子进程会把错误信息打印到标准错误输出,父进程就无法获取。比如,command命令为“ls nofile.txt” ,事实上我们根本没有nofile.txt这个文件,这时shell会输出“ls: nofile.txt: No such file or directory”。这个输出是在标准错误输出上的。通过上面的程序并无法获取。

注:如果你把上面程序中的command设成“ls nofile.txt”,编译执行程序你会看到如下结果:

$ gcc popen.c

$ ./a.out

ls: nofile.txt: No such file or directory

命令【ls nofile.txt】子进程结束状态【256】命令返回值【1】

需要注意的是第一行输出并不是父进程的输出,而是子进程的标准错误输出。

有时子进程的错误信息是很有用的,那么父进程怎么才能获取子进程的错误信息呢?

这里我们可以重定向子进程的错误输出,让错误输出重定向到标准输出(2>&1),这样父进程就可以捕获子进程的错误信息了。例如command为“ls nofile.txt 2>&1”,输出如下:

命令【ls nofile.txt 2>&1】 输出【ls: nofile.txt: No such file or directory】

命令【ls nofile.txt 2>&1】子进程结束状态【256】命令返回值【1】

附:子进程的终止状态判断涉及到的宏,设进程终止状态为status.

WIFEXITED(status)如果子进程正常结束则为非0值。

WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。

WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真。

WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。

WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。

WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED 来判断后才使用此宏。

[转][IPC通信]基于管道的popen和pclose函数的更多相关文章

  1. 【IPC通信】基于管道的popen和pclose函数

    http://my.oschina.net/renhc/blog/35116 [IPC通信]基于管道的popen和pclose函数 恋恋美食  恋恋美食 发布时间: 2011/11/12 23:20 ...

  2. linux下代替system的基于管道的popen和pclose函数

    linux下使用system需要谨慎,那么代替它的方法是什么呢? 标准I/O函数库提供了popen函数,它启动另外一个进程去执行一个shell命令行. 这里我们称调用popen的进程为父进程,由pop ...

  3. 进程间通信之popen和pclose函数

    常见的操作是创建一个管道连接到另一个进程,然后读其输出或向其输入端发送数据,为此,标准I/O库提供了两个函数popen和pclose.这两个函数实现的操作是:创建一个管道,调用fork产生一个子进程, ...

  4. Android Binder机制详解:手写IPC通信

    想要掌握一样东西,最好的方式就是阅读理解它的源码.想要掌握Android Binder,最好的方式就是写一个AIDL文件,然后查看其生成的代码.本文的思路也是来自于此. 简介 Binder是Andro ...

  5. Binder机制,从Java到C (9. IPC通信过程)

    1.一次IPC通信過程的幾個步驟 一次通信过程简单的说有下面5个步骤,第一眼看上去,肯定不知道什么玩意,多看几遍,慢慢看,其实是能理解的. 1. Client将数据封装成Parcel. (前面已经讲过 ...

  6. IPC通信:Posix消息队列

    IPC通信:Posix消息队列 消息队列可以认为是一个链表.进程(线程)可以往里写消息,也可以从里面取出消息.一个进程可以往某个消息队列里写消息,然后终止,另一个进程随时可以从消息队列里取走这些消息. ...

  7. linux进程篇 (三) 进程间的通信3 IPC通信

    3 IPC通信 用户空间 进程A <----无法通信----> 进程B -----------------|--------------------------------------|- ...

  8. android IPC通信(上)-sharedUserId&amp;&amp;Messenger

    看了一本书,上面有一章解说了IPC(Inter-Process Communication,进程间通信)通信.决定结合曾经的一篇博客android 两个应用之间的通信与调用和自己的理解来好好整理总结一 ...

  9. python 之 并发编程(守护进程、互斥锁、IPC通信机制)

    9.5 守护进程 主进程创建守护进程 其一:守护进程会在主进程代码执行结束后就立即终止 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic process ...

随机推荐

  1. Ubuntu 16.04添加阿里云源/163源

    添加国内源有个好处,比如下载软件时直接时国内的服务器,速度有保证. 以下是操作方法: 1.备份 sudo cp /etc/apt/sources.list /etc/apt/sources.list. ...

  2. spring mvc日期转换(前端到后端,后端到前端)

    在做web开发的时候,页面传入的都是String类型,SpringMVC可以对一些基本的类型进行转换,但是对于日期类的转换可能就需要我们配置. 1.如果查询类使我们自己写,那么在属性前面加上@Date ...

  3. C++与C的那些差异

    虽说C++是向后兼容C的,但C++与C还是存在许多差异.这里举了几个例子,也是我们很容易忽略的地方.如果你还知道其他的更多的Differences,或者发现什么错误,可以评论告诉我,大家一起学习进步. ...

  4. hdu 5950 Recursive sequence

    题意:告诉你数列的递推公式为f(n+1)=f(n)+2*f(n-1)+(n+1)^4 以及前两项a,b:问第n项为多少,结果对2147493647取模. 题解:有递推公式,马上应该就能想到矩阵快速幂: ...

  5. [vs执行报错] CRT detected that the application wrote to memory after end of heap buffer

    CRT 是c/c++ run-time lib , 是程序执行时所需的核心库. 这个错误是由于以对内在操作的过程中.所写的地址超出了.所分配内在的边界 有个建议是: 1.内存申请多少释放多少,释放掉你 ...

  6. 加logo

    import cv2 import numpy as np # f0,f1='feiyong.jpg','g3logo.jpg' # # #f1,f0='feiyong.jpg','g3logo.jp ...

  7. codeforces 939F 单调队列优化dp

    F. Cutlet time limit per test 4 seconds memory limit per test 256 megabytes input standard input out ...

  8. PCB genesis方槽加内角槽孔实现方法

    一.为什么方槽孔加内角孔 如下图,客户来的方槽或Slot槽有内角尺寸要求,通常直接钻一个Slot槽孔内角是不能满足客户要求的,这时我们做CAM的需采用小钻刀进行处理.加内角孔或内角槽的方式进行处理了. ...

  9. E20170617-hm

    notation   n. 记号,标记法; implicit   adj. 不言明[含蓄]的; 无疑问的,绝对的; 成为一部份的; 内含的; selector   n. 选择者,选择器; promot ...

  10. 让谷歌浏览器(chrome)保存调试代码workspace

    方法/步骤  chrome浏览器早期版本的操作方法与我现在要讲的方法有所不同,因此操作前请注意浏览器的版本号. 示例中的版本号: 53.0.2785.116 m  任意打开一个需要调试的html文件 ...