管道文件的使用:读取命令的输出

stdlib 库有个很多人用过的函数:system.

如果希望这段代码能够跨平台,而希望让程序能等个1秒,一个很好的方法就是,system(“sleep 1″);这样,无论是windows还是linux,我们都可以很好的执行”等待1秒”,而不必多写好多参数什么的.

但是,有时候,我们希望能够得到这个命令的输出,而不是只是”执行它”.那么,我们需要点新的东西了.

管道 即是一个由标准输入输出链接起来的进程集合，所以每一个进程的输出（stdout）被直接作为下一个进程的输入（stdin）。 每一个链接都由未命名管道实现。过滤程序经常被用于这种设置。 [1]

我们可以通过一些系统提供的管道函数,获得这个输出.

这不是一个标准库函数,而是系统相关的.

POSIX 下一个比较方便的函数是 popen.

popen的函数原型是:

FILE *popen(const char *command, const char *type);

manual 下的描述是

the popen() function opens a process by creating a pipe, forking, and invoking the shell. Since a pipe is by definition unidirectional, the type argument may specify only reading or writing, not both; the resulting stream is correspondingly read-only or write-only.

大意是fork出一个线程,执行然后得到结果,存储到管道文件中,返回文件只读或者只写句柄.或者失败,返回NULL.

popen的方法很简单,和fopen一样,传入两个字符串,得到一个文件句柄.只不过fopen第一个参数是文件名,而popen是命令.然后其他用法 — 都已经得到了 FILE * 了还有什么不会的么?

windows下也有个很相似的函数,不过是函数名是_popen,我们可以简单的通过宏搞定跨平台.

扯淡完毕,代码本身其实可以简单的说明一切

#include
#include
#include

#ifndef WIN32 // headers for linux
#include
#include
#endif // WIN32

#ifdef WIN32 // windows
#define _exec(cmd) _popen(cmd, "r")
#else // linux
#define _exec(cmd) popen(cmd, "r")
#endif // WIN32

int main(int argc,char *argv[])
{
FILE *stream;
char buf[1024];
memset(buf,'\0',sizeof(buf));//初始化buf,以免后面写如乱码到文件中
stream = _exec("echo \"hello, world !\""); //将参数内容命令的输出,得到管道文件句柄
if(stream != NULL)
{
//将刚刚FILE* stream的数据流读取到buf中
fread( buf, sizeof(char), sizeof(buf), stream);
fwrite( buf, 1, sizeof(buf), stdout );//将buf中的数据写到stdout
//关闭流
pclose(stream);
}else
{
//执行失败
printf("error!\n");
}
return 0;
}

代码是使用管道执行

echo "hello, world!"

这个命令. 然后读取执行结果,输出.

一个需要注意的问题是,因为原理是fork一个线程,所以环境变量是直接复制的,权限问题什么的要注意一下.