linux下使用system须要谨慎,那么代替它的方法是什么呢?linux
标准I/O函数库提供了popen函数,它启动另一个进程去执行一个shell命令行。shell
这里咱们称调用popen的进程为父进程,由popen启动的进程称为子进程。函数
popen函数还建立一个管道用于父子进程间通讯。父进程要么从管道读信息,要么向管道写信息,至因而读仍是写取决于父进程调用popen时传递的参数。下在给出popen、pclose的定义:spa
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的使用:.net
假如咱们想取得当前目录下的文件个数,在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.ccode
$ ./a.outblog
命令【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专栏。