標準流管道popen、pclose函數說明:
#include <stdio.h>
FILE *popen(const char *command, const char *type)
返回值:若成功,返回文件流指針;若出錯,返回-1
參數說明:
- Command:指向的是一個以 null 結束符結尾的字符串,這個字符串包含一個 shell 命令,並被送到/bin/sh 以-c 參數執行,即由 shell 來執行
- type:
- ”r”: 文件指針連接到 command 的標準輸出
- “w” :文件指針連接到 command 的標準輸入
#include <stdio.h>
int pclose(FILE *stream)
返回值:若成功,返回 popen 中執行命令的終止狀態;若出錯,返回-1
參數說明:
- stream:要關閉的文件流
popen函數其實是對管道操作的一些包裝,所完成的工作有以下幾步:
- 創建一個管道。
- fork 一個子進程。
- 在父子進程中關閉不需要的文件描述符。
- 執行 exec 函數族調用。
- 執行函數中所指定的命令。
可以對照着Richard Stevens 實現的源碼加深理解。
linux popen源碼:
/*
* popen.c Written by W. Richard Stevens
*/
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include "ourhdr.h"
static pid_t *childpid = NULL;
/* ptr to array allocated at run-time */
static int maxfd; /* from our open_max(), {Prog openmax} */
#define SHELL "/bin/sh"
FILE *
popen(const char *cmdstring, const char *type)
{
int i, 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.2 */
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(SHELL, "sh", "-c", cmdstring, (char *) 0);
_exit(127);
}
/* parent */
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)
return(-1); /* popen() has never been called */
fd = fileno(fp);
if ( (pid = childpid[fd]) == 0)
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 */
}
接下來看一個簡單的示例程序:
/* popen.c*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#define BUFSIZE 1000
int main()
{
FILE *fp;
char *cmd = "ls -al";
char buf[BUFSIZE];
if((fp=popen(cmd,"r"))==NULL) //調用 popen 函數執行命令ls -al
perror("popen");
fread( buf, sizeof(char), sizeof(buf),fp); //將剛剛fp指向的文件流讀取到buf中
printf("%s",buf);
pclose(fp);
exit(0);
}
實驗結果:
ubuntu:~/test/process_test$ gcc popen.c -o popen
ubuntu:~/test/process_test$ ./popen
total 92
drwxr-xr-x 2 4096 Aug 15 16:56 .
drwxr-xr-x 19 4096 Aug 15 10:12 ..
-rwxr-xr-x 1 12 Aug 15 11:16 file_fork
-rwxr-xr-x 1 8784 Aug 15 10:46 fork
-rwxr-xr-x 1 8785 Aug 15 10:45 fork2
-rwxr--r-- 1 571 Aug 15 11:25 fork2.c
-rwxr--r-- 1 469 Aug 15 10:42 fork.c
-rwxr-xr-x 1 8890 Aug 15 11:16 open_fork
-rwxr--r-- 1 1114 Aug 15 11:25 open_fork.c
-rwxr-xr-x 1 8884 Aug 15 16:16 pipe
-rwxr--r-- 1 1677 Aug 15 16:16 pipe.c
-rwxr-xr-x 1 8683 Aug 15 16:56 popen
-rwxr--r-- 1 409 Aug 15 16:56 popen.c
在終端執行shell命令”ls -al”
ubuntu:~/test/process_test$ ls -al
total 92
drwxr-xr-x 2 4096 Aug 15 16:56 .
drwxr-xr-x 19 4096 Aug 15 10:12 ..
-rwxr-xr-x 1 12 Aug 15 11:16 file_fork
-rwxr-xr-x 1 8784 Aug 15 10:46 fork
-rwxr-xr-x 1 8785 Aug 15 10:45 fork2
-rwxr--r-- 1 571 Aug 15 11:25 fork2.c
-rwxr--r-- 1 469 Aug 15 10:42 fork.c
-rwxr-xr-x 1 8890 Aug 15 11:16 open_fork
-rwxr--r-- 1 1114 Aug 15 11:25 open_fork.c
-rwxr-xr-x 1 8884 Aug 15 16:16 pipe
-rwxr--r-- 1 1677 Aug 15 16:16 pipe.c
-rwxr-xr-x 1 8683 Aug 15 16:56 popen
-rwxr--r-- 1 409 Aug 15 16:56 popen.c
實驗結果跟在終端執行shell命令是一樣的。理解源碼之後,對popen理解也更加深刻,在不同場景中運用也更加自如。