1.3. popen和pclose函数
标准I/O库提供了两个函数popen和pclose。这两个函数实现的操作是:创建一个管道,调用fork产生一个子进程,关闭管道的不使用端,执行一个shell以运行命令,然后等待命令终止。
#include <stdio.h>
FILE * popen(const char *cmdstring, const char *type);
Int pclose(FILE * fp);
函数popen先执行fork,然后调用exec以执行cmdstring,并且返回一个标准I/O文件指针。如果type是"r",则文件指针链接到cmdstring的标准输出;如果type是"w",则文件指针连接到smdstring的标准输入。如果type是"r",则返回的文件指针链是可读的;如果type是"w", 返回的文件指针链是可写的。
函数pclose关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态。如果shell不能被执行,则pclose返回的终止状态与shell已经执行exit(127)一样。
命令cmdstring由Bourne shell以下列方式执行:
sh -c cmdstring
(其中shell的-c选项是告诉shell程序取下一个命令行参数作为命令输入,而不是从标准输入或从一个给定的文件中读命令。)
注意:popen决不能有设置用户ID或设置组ID程序调用。
1.4. 实例-分页程序
shell命令${PAGER:-more}的意思是:如果shell变量PAGER已经定义,且其值非空,则使用其值,否则使用字符串more。
#include <sys/types.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <paths.h> #include <sys/wait.h> #include <stdio.h> #define DEF_PAGER "${PAGER:-more}"; int main(int argc, char * argv[]) { char line[MAXLINE]; FILE *fpin; FILE *fpout; if(2 != argc) { printf("usage: ./a.out <pathname>/n"); return -1; } if(NULL ==(fpin = fopen(argv[1], "r"))) { printf("can't open %s/n", argv[1]); return -1; } if(NULL == (fpout = popen(PAGER,"w"))) { printf("popen error/n"); return -1; } /* 复制文件到分页程序 */ while(fgets(line, MAXLINE, fpin) != NULL) { if(fputs(line, fpout) == EOF) { printf("fputs error to pipe/n"); return -1; } } // if(ferror(fpin)) { printf("fgets error/n"); return -1; } if(pclose(fpout) == -1) { printf("pclose error/n"); return -1; } exit(0); }
1.5. 实例-自己编写的popen和pclose函数。
首先每次调用popen时,应当记住所创建的子进程的进程ID,以及其文件描述符或FILE指针。我们选择在数组child_pid中保存子进程ID,并用文件描述符作为下标。于是,当以FLlE指针作为参数调用pclose时,我们调用标准I/O函数fileno得到文件描述符,然后取得子进程ID,并用其作为参数调用waitpid。因为一个进程可能调用popen多次,所以在动态文笔child_pid数组时,其数组长度应当是最大文件描述符数。POSIX.1要求子进程关闭以前调用popen时打开且当前仍旧贷款的所以I/O流。
#include <sys/types.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <paths.h> #include <sys/wait.h> #include <errno.h> #include <fcntl.h> static pid_t *child_pid = NULL; static int maxfd; FILE (const char * cmdstring, const char * type) { int i; int pfd[2]; pid_t pid; FILE *fp; if((type[0] != 'r' && type[0] !='w') || type[1] != 0 ) { //POSIX要求 errno = EINVAL; return NULL; } if(child_pid == NULL) { maxfd = open_max(); if((child_pid = calloc(maxfd, sizeof(pid_t))) == NULL) { return NULL; } } if(pipe(pfd) < 0) { return NULL; } if((pid = fork()) < 0) { return NULL; } else if (pid == 0) { //子进程 if(*type == 'r') { close(pfd[0]); if(pfd[1] != STDOUT_FILENO) { dup2(pfd[1], STDOUT_FILENO); close(pfd[1]); } } else { close(pfd[1]); if(pfd[0] != STDIN_FILENO) { dup2(pfd[0], STDIN_FILENO); close(pfd[0]); } } for(i = 0; i < masfd; i++) { if(child_pid[i] > 0) { close(i); } } execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); _exit(127); } //父进程 if(*type == 'r') { close(pfd[1]); if((fp = fdopen(pfd[0],type) ==NULL) { return NULL; } } else { close(pfd[0]); if((fp = fdopen(pfd[1],type) ==NULL) { return NULL; } } //记录子进程的进程ID child_pid[fileno(fp)] = pid; return fp; } int pclose(FILE * fp) { int fd,stat; pid_t pid; //popen从来没有被调用 if(child_pid == NULL) { errno = EINVAL; return -1; } //fd没有被popen打开 fd = fileno(fp); if((pid = child_pid[fd]) == 0) { errno = EINVAL; return -1; } child_pid[fd] = 0; if(fclose(fp) == EOF) { return -1; } while(waitpid(pid, &stat, 0) < 0) { if(errno != EINTR) { return -1; } } //返回子进程的终止状态 return stat; }