在《system() 执行 shell 命令》中,我们介绍了 system 执行 shell 命令的方法,system 返回值比较混乱,难以理解,而且 popen 在处理子进程标准输出上会很方便。
注意:管道只能处理标准输出,不能处理标准错误输出。
popen 和 pclose 的实现与 system 类似,多了一步创建管道的操作。
popen 成功返回 FILE 句柄,失败返回 NULL, 失败的原因可能为 fork 或 pipe 失败,也可能为分配内存失败;
pclose 失败返回 -1, 成功则返回 exit status, 同 system 类似,需要用WIFEXITED,WEXITSTATUS 等获取命令返回值。
此外,同 system 类似, pclose 会等待进程退出,如果等不到进程退出(例如已经被 wait 回收),则 error 设置为ECHILD.
注意:
1. pclose 仅仅是为了回收子进程,避免僵尸进程的产生;
2. 和 system 一样,SIGCHLD 依然会影响 popen,见示例程序。
[cpp]view plaincopy
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
typedefvoid(*sighandler_t)(int);
intmain(intargc,char*argv[])
{
charcmd[1024];
charline[1024];
FILE*pipe;
intrv;
if(argc!=2)
{
printf("Usage:%s<path>\n",argv[0]);
return-1;
}
//pclosefail:Nochildprocesses
signal(SIGCHLD,SIG_IGN);
snprintf(cmd,sizeof(cmd),"ls-l%s2>/dev/null",argv[1]);
//sighandler_told_sighandler=signal(SIGCHLD,SIG_DFL);
pipe=popen(cmd,"r");
if(NULL==pipe)
{
printf("popen()failed:%s\n",cmd);
return-1;
}
while(fgets(line,sizeof(line),pipe)!=NULL)
{
printf("%s",line);
}
rv=pclose(pipe);
//signal(SIGCHLD,old_sighandler);
if(-1==rv)
{
printf("pclose()failed:%s\n",strerror(errno));
return-1;
}
if(WIFEXITED(rv))
{
printf("subprocessexited,exitcode:%d\n",WEXITSTATUS(rv));
if(0==WEXITSTATUS(rv))
{
//ifcommandreturning0meanssucceed
printf("commandsucceed\n");
}
else
{
if(127==WEXITSTATUS(rv))
{
printf("commandnotfound\n");
returnWEXITSTATUS(rv);
}
else
{
printf("commandfailed:%s\n",strerror(WEXITSTATUS(rv)));
returnWEXITSTATUS(rv);
}
}
}
else
{
printf("subprocessexitfailed\n");
return-1;
}
return0;
}
说明:
1. 将 SIGCHLD 忽略之后,pclose 不能回收子进程退出信息,errno 为ECHILD, 错误信息为 "No child processes";
2. 由于管道不能捕获 stderr, 因此命令将 stderr 重定向至 /dev/null.