linux下使用system需要谨慎,那么代替它的方法是什么呢?

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

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

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

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

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

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

1 ls wc -l

我们可以在程序中这样写:

01 /*取得当前目录下的文件个数*/
02 #include <stdio.h>
03 #include <stdlib.h>
04 #include <errno.h>
05 #include <sys/wait.h>
06  
07 #define MAXLINE 1024
08  
09 int main()
10 {
11     char result_buf[MAXLINE], command[MAXLINE];
12     int rc = 0; // 用于接收命令返回值
13     FILE *fp;
14  
15     /*将要执行的命令写入buf*/
16     snprintf(command, sizeof(command), "ls ./ | wc -l");
17  
18     /*执行预先设定的命令,并读出该命令的标准输出*/
19     fp = popen(command, "r");
20     if(NULL == fp)
21     {
22         perror("popen执行失败!");
23         exit(1);
24     }
25     while(fgets(result_buf, sizeof(result_buf), fp) != NULL)
26     {
27         /*为了下面输出好看些,把命令返回的换行符去掉*/
28         if('\n' == result_buf[strlen(result_buf)-1])
29         {
30             result_buf[strlen(result_buf)-1] = '\0';
31         }
32         printf("命令【%s】 输出【%s】\r\n", command, result_buf);
33     }
34  
35     /*等待命令执行完毕并关闭管道及文件指针*/
36     rc = pclose(fp);
37     if(-1 == rc)
38     {
39         perror("关闭文件指针失败");
40         exit(1);
41     }
42     else
43     {
44         printf("命令【%s】子进程结束状态【%d】命令返回值【%d】\r\n", command, rc, WEXITSTATUS(rc));
45     }
46  
47     return 0;
48 }
编译并执行:

$ 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 来判断后才使用此宏。

更多内容请参考:shanzhizi专栏

linux下代替system的基于管道的popen和pclose函数的更多相关文章

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

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

  2. [转][IPC通信]基于管道的popen和pclose函数

    标准I/O函数库提供了popen函数,它启动另外一个进程去执行一个shell命令行. 这里我们称调用popen的进程为父进程,由popen启动的进程称为子进程. popen函数还创建一个管道用于父子进 ...

  3. 【C/C++】Linux下使用system()函数一定要谨慎

    [C/C++]Linux下使用system()函数一定要谨慎 http://my.oschina.net/renhc/blog/53580 曾经的曾经,被system()函数折磨过,之所以这样,是因为 ...

  4. Linux进程间通信(三):匿名管道 popen()、pclose()、pipe()、close()、dup()、dup2()

    在前面,介绍了一种进程间的通信方式:使用信号,我们创建通知事件,并通过它引起响应,但传递的信息只是一个信号值.这里将介绍另一种进程间通信的方式——匿名管道,通过它进程间可以交换更多有用的数据. 一.什 ...

  5. [编译] 9、在Linux下搭建 nordic 最新基于 zephyr 的开发烧写环境

    目录 前言 1.概述 2.安装工具 3.获取 nRF Connect SDK 源码 4.安装 Python modules 5.安装 toolchain 6.下载 nRF Command Line T ...

  6. linux下安装svn(基于编码的方式)

    svn是什么,相信能看到这里的同学应该不会有这个问题了,费话不多说,开始: 1.创建目录 mkdir /home/svn/ 2.获取安装svn所需源文件(svn的官方网址是http://subvers ...

  7. [转载]Linux下关于system调用

    曾经的曾经,被system()函数折磨过,之所以这样,是因为对system()函数了解不够深入.只是简单的知道用这个函数执行一个系统命令,这远远不够,它的返回值.它所执行命令的返回值以及命令执行失败原 ...

  8. Linux下使用system()函数一定要谨慎

    转载自:http://my.oschina.net/renhc/blog/53580   linux尽量避免使用system. 曾经的曾经,被system()函数折磨过,之所以这样,是因为对syste ...

  9. Linux下的文件操作——基于文件描述符的文件操作(2)

    文件描述符的复制 MMAP文件映射 ftruncate修改文件大小 文件描述符的复制 ​ 系统调用函数dup和dup2可以实现文件描述符的复制,经常用来重定向进程的stdin(0), stdout(1 ...

随机推荐

  1. git 最基本的使用方法

    1. git init    ----初始化git仓库 2.git add .   ----把代码添加到仓库 3.git commit -m '注释'  ---commit:提交 -m:全部提交  ‘ ...

  2. 【BZOJ-3653】谈笑风生 DFS序 + 可持久化线段树

    3653: 谈笑风生 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 628  Solved: 245[Submit][Status][Discuss] ...

  3. Linux File System Change Monitoring Technology、Notifier Technology

    catalog . 为什么要监控文件系统 : hotplug . udev . fanotify(fscking all notification system) . inotify . code e ...

  4. codevs 1432 总数统计

    1432 总数统计 时间限制: 1 s空间限制: 128000 KB题目等级 : 钻石 Diamond   题目描述 Description 给出n个数,统计两两之和小于k的方案数之和. 输入描述 I ...

  5. STL之deque用法详解

    C++ Deque(双向队列): Deque是一种优化了的.对序列两端元素进行添加和删除操作的基本序列容器.它允许较为快速地随机访问,但它不像vector 把所有的对象保存在一块连续的内存块,而是采用 ...

  6. Autofac的高级使用——Autofac.2.6.3.862

    Autofac的高级使用——Autofac.2.6.3.862 目录(?)[-] 使用代码方式进行组件注册依赖服务类和组件类 使用配置文件进行组件注册不需要依赖 定义配置文件 读取config配置文件 ...

  7. document.execCommand 常用的方法

    execCommand方法是执行一个对当前文档,当前选择或者给出范围的命令.处理Html数据时常用如下格式: document.execCommand(sCommand[,交互方式, 动态参数]) , ...

  8. Linux安全之——Ubuntu的iptable命令使用

    设置开机自动启动iptables # sysv-rc-conf --level 2345 iptables on 列出当前iptables的策略和规则 # iptables -L -n 允许已经建立的 ...

  9. C#获取C++中修改过的float数组(指针),dll

    C++中 struct rankPoint{ float sim; }; ]){ ; i < ; i++) prank[i].sim = ; ; i < ; i++) prank[i].s ...

  10. c#多态之抽象类与接口的一点收获~~

    多态之抽象类与接口的相似点及不同点,刚学习的一点收获,或许不是很完整,借鉴看视频及一些被人写的文章自己写的下的一些心得!以便之久复习使用! 一.抽象类 (1) 抽象方法只作声明,而不包含实现,可以看成 ...