popen简介:
Linux系统提供了两个标准流管道的操作函数popen和pclose。这两个函数实现的操作是:
创建一个管道,fork一个子进程,关闭管道的不使用端,通过exec函数执行一个shell命令,等待命令终止。
popen函数声明:
#include <stdio.h>
FILE *popen(const char *command,const char *type);
功能:popen打开的数据管道流,pclose关闭数据管道流。popen必须由pclose来关闭。
参数说明:
command:shell 命令字符串的指针。
type:用来表示读("r")或者写("w")的,如果type是"r",则返回的文件指针是可读的,如果type是"w",则是可写的。不能同时为读和写。
返回值:返回:若成功则为文件指针,若出错则为NULL。
popen函数使用举例:
模拟一个shell命令的实现,将命令结果保存到已有文件中。
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
/*将命令行执行的结果保存到一个已经存在的文件中 */
int main(int argc, char* argv[])
{
FILE *pipe_fp;
FILE *infile;
//pid_t pid;
char buffer[80];
if (argc != 3) {
fprintf(stderr, "usage: popen3[commond][filename]\n");
exit(1);
}
if ((infile = fopen(argv[2],"wt")) == NULL) {
perror("fopen");
exit(1);
}
if ((pipe_fp = popen(argv[1],"r")) == NULL) {
perror("popen");
exit(1);
}
while (fgets(buffer, sizeof(buffer), pipe_fp) != NULL) {
// if (tmp[strlen(tmp) - 1] == '\n') {
// tmp[strlen(tmp) - 1] = '\0'; //去除换行符
// }
fputs(buffer,infile);
}
// do {
// fgets(buffer,80,pipe_fp);
// // if (feof(infile)) {
// // break;
// // }
// fputs(buffer,infile);
// } while(!feof(infile));
fclose(infile);
pclose(pipe_fp);
return(0);
}
程序使用方法:
输入三个字符串,第一个是编译好的可执行程序;第二个是shell可以识别的命令;第三个是已经存在的文件。
运行结果:
myls ls in.txt
其中,myls就是上面的代码编译出来的可执行文件。这样,就把shell命令ls的结果保存在in.txt中了。(注意,必须先创建一个in.txt文件,这是为了简化程序)。
popen函数的源代码(来自于Android):
在Android系统中,我们可以查找到这样一个文件popen.c,文件位置和代码:
bionic/libc/unistd/popen.c:
/* $OpenBSD: popen.c,v 1.17 2005/08/08 08:05:34 espie Exp $ */ /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * *......//省略若干 */ #include <sys/param.h> #include <sys/wait.h> #include <signal.h> #include <errno.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <paths.h> static struct pid { struct pid *next; FILE *fp; pid_t pid; } *pidlist; extern char **environ; FILE * popen(const char *program, const char *type) { struct pid * volatile cur; FILE *iop; int pdes[2]; pid_t pid; char *argp[] = {"sh", "-c", NULL, NULL}; if ((*type != 'r' && *type != 'w') || type[1] != '\0') { errno = EINVAL; return (NULL); } if ((cur = malloc(sizeof(struct pid))) == NULL) return (NULL); if (pipe(pdes) < 0) { free(cur); return (NULL); } switch (pid = fork()) { case -1: /* Error. */ (void)close(pdes[0]); (void)close(pdes[1]); free(cur); return (NULL); /* NOTREACHED */ case 0: /* Child. */ { struct pid *pcur; /* * We fork()'d, we got our own copy of the list, no * contention. */ for (pcur = pidlist; pcur; pcur = pcur->next) close(fileno(pcur->fp)); if (*type == 'r') { (void) close(pdes[0]); if (pdes[1] != STDOUT_FILENO) { (void)dup2(pdes[1], STDOUT_FILENO); (void)close(pdes[1]); } } else { (void)close(pdes[1]); if (pdes[0] != STDIN_FILENO) { (void)dup2(pdes[0], STDIN_FILENO); (void)close(pdes[0]); } } argp[2] = (char *)program; execve(_PATH_BSHELL, argp, environ); _exit(127); /* NOTREACHED */ } } /* Parent; assume fdopen can't fail. */ if (*type == 'r') { iop = fdopen(pdes[0], type); (void)close(pdes[1]); } else { iop = fdopen(pdes[1], type); (void)close(pdes[0]); } /* Link into list of file descriptors. */ cur->fp = iop; cur->pid = pid; cur->next = pidlist; pidlist = cur; return (iop); } /* * pclose -- * Pclose returns -1 if stream is not associated with a `popened' command, * if already `pclosed', or waitpid returns an error. */ int pclose(FILE *iop) { struct pid *cur, *last; int pstat; pid_t pid; /* Find the appropriate file pointer. */ for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next) if (cur->fp == iop) break; if (cur == NULL) return (-1); (void)fclose(iop); do { pid = waitpid(cur->pid, &pstat, 0); } while (pid == -1 && errno == EINTR); /* Remove the entry from the linked list. */ if (last == NULL) pidlist = cur->next; else last->next = cur->next; free(cur); return (pid == -1 ? -1 : pstat); }
从代码路径可以看到,popen是在libc库中实现的,而且是通过调用pipe()函数创建的管道。