函数原型:
#include<stdio.h>
FILE* popen(const char* command, const char* type);
void pclose(FILE* stream);
man文本英语描述:
DESCRIPTION:
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.
The command argument is a pointer to a null-terminated string contain‐
ing a shell command line. This command is passed to /bin/sh using the
-c flag; interpretation, if any, is performed by the shell. The type
argument is a pointer to a null-terminated string which must contain
either the letter 'r' for reading or the letter 'w' for writing. Since
glibc 2.9, this argument can additionally include the letter 'e', which
causes the close-on-exec flag (FD_CLOEXEC) to be set on the underlying
file descriptor; see the description of the O_CLOEXEC flag in open(2)
for reasons why this may be useful.
RETURN VALUE:
The popen() function returns NULL if the fork(2) or pipe(2) calls fail,
or if it cannot allocate memory.
The pclose() function returns -1 if wait4(2) returns an error, or some
other error is detected. In the event of an error, these functions set
errno to indicate the cause of the error.
ERRORS:
The popen() function does not set errno if memory allocation fails. If
the underlying fork(2) or pipe(2) fails, errno is set appropriately.
If the type argument is invalid, and this condition is detected, errno
is set to EINVAL.
If pclose() cannot obtain the child status, errno is set to ECHILD.
中文总结:
-
popen函数是通过创建一个管道的方式打开一个进程,并调用shell。因为管道是被定义为单向的,所以type的值只能是r或w其中的一个,结果流也是如此。
-
command(命令行/命令的意思)参数:是一个以NULL结尾的字符串,这个字符串应是一个shell命令。这个命令会被送到 /bin/sh中,并以 -c执行即被shell执行。
-
type参数:只能是r或w中的一个,r或w是相当command管道而言的。r表示从command管道中读,w表示通过command管道输出到stdout中(输出流为全缓冲)。
-
返回值:如果fork或者pipe失败,或者不能分配到内存都会返回NULL,成功则返回标志I/O流。popen没有为申请内存失败设置errno值,但fork和pipe都有其相应的errno值。如果type参数不合法,errno会别设置为EINVAL.
注意:只能用pclose函数进行关闭操作,不能用fclose。
相关代码:
type为 r时:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<string.h>
const int BUF_SIZE = 1024;
int main()
{
FILE* stream;
FILE* wstream;
char buf[BUF_SIZE];
stream = popen("ls -l","r");
wstream = fopen("test_popen.txt","w+");
memset(buf, '\0', sizeof(buf));
fread(buf, sizeof(char), sizeof(buf), stream);//把返回标准I/O流内的内容读到buf中
fwrite(buf,sizeof(char), strlen(buf), wstream);
fclose(wstream);//一定要记得关闭
pclose(stream);//只能用pclose不能用fclose
return 0;
}
执行结果:
当type为 w 时:(此时popen函数会通过FIFO管道直接向stdout中写数据)
#include<stdio.h>
int main()
{
FILE* stream;
stream = popen("ls -l","w");
if(stream == NULL)
{
perror("popen");
return -1;
}
pclose(stream);
return 0;
}
注意:一个执行的命令是 cat test_popen.txt 一个是./a.out
以下是在网上找到的popen函数和pclose函数实现的代码:
Figure 15.12. The popen and pclose functions
#include "apue.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
/*
* Pointer to array allocated at run-time.
*/
static pid_t *childpid = NULL;
/*
* From our open_max(), Figure 2.16.
*/
static int maxfd;
FILE *
popen(const char *cmdstring, const char *type)
{
int i;
int pfd[2];
pid_t pid;
FILE *fp;
/* only allow "r" or "w" */
if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {
errno = EINVAL; /* required by POSIX */
return(NULL);
}
if (childpid == NULL) { /* first time through */
/* allocate zeroed out array for child pids */
maxfd = open_max();
if ((childpid = calloc(maxfd, sizeof(pid_t))) == NULL)
return(NULL);
}
if (pipe(pfd) < 0)
return(NULL); /* errno set by pipe() */
if ((pid = fork()) < 0) {
return(NULL); /* errno set by fork() */
} else if (pid == 0) { /* child */
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]);
}
}
/* close all descriptors in childpid[] */
for (i = 0; i < maxfd; i++)
if (childpid[i] > 0)
close(i);
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127);
}
/* parent continues... */
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);
}
childpid[fileno(fp)] = pid; /* remember child pid for this fd */
return(fp);
}
int
pclose(FILE *fp)
{
int fd, stat;
pid_t pid;
if (childpid == NULL) {
errno = EINVAL;
return(-1); /* popen() has never been called */
}
fd = fileno(fp);
if ((pid = childpid[fd]) == 0) {
errno = EINVAL;
return(-1); /* fp wasn't opened by popen() */
}
childpid[fd] = 0;
if (fclose(fp) == EOF)
return(-1);
while (waitpid(pid, &stat, 0) < 0)
if (errno != EINTR)
return(-1); /* error other than EINTR from waitpid() */
return(stat); /* return child's termination status */
}