简单汇总:C语言实现Shell命令执行的三种方法——popen技巧
最编程
2024-07-25 19:09:22
...
根据模式重定向子进程的标准输入或输出,提供控制子进程输入或输出的能力。
函数原型:
#include <stdio.h>
FILE *popen(const char *cmdstring, const char *type);
int pclose(FILE *fp);
简版实现
以下代码修改自《UNIX环境高级编程》和 glibc 中 popen 的实现。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdlib.h> /* for calloc*/
static pid_t *childpid = NULL;
static int maxfd = 1024;
FILE * popen(const char *cmd, const char * mode)
{
int do_read = 0, do_write = 0;
int parent_end, child_end;
int fds[2];
pid_t pid;
FILE *fp;
const char *type= mode;
while (*type != '\0') {
switch (*type++)
{
case 'r':
do_read = 1;
break;
case 'w':
do_write = 1;
break;
default:
return NULL;
}
}
if (do_read ^ do_write == 0)
return NULL;
if (pipe(fds) < 0)
return NULL;
if (do_read) {
parent_end = fds[0];
child_end = fds[1];
} else {
parent_end = fds[1];
child_end = fds[0];
}
if (childpid == NULL) {
if ((childpid = calloc(maxfd, sizeof(pid_t))) == NULL) {
perror("calloc error");
return NULL;
}
}
if ((pid = fork()) < 0)
return NULL;
else if (pid == 0) {
int child_std_end = do_read ? 1 : 0;
if (child_end != child_std_end)
dup2(child_end, child_std_end); /* clear close-on-exec flag of child_std_end */
close(child_end);
close(parent_end);
/*
* POSIX.1 要求 popen 关闭那些以前调用 popen 打开,
* 现在仍在子进程中打开的 I/O 流。
*/
for (int i = 0; i < maxfd; i++) {
if (childpid[i] > 0)
close(i);
}
execl("/bin/sh", "sh", "-c", cmd, (char *)0);
_exit(127); /* execl error */
}
close(child_end);
if ((fp = fdopen(parent_end, mode)) == NULL) {
perror("fail open fd");
return NULL;
}
childpid[fileno(fp)] = pid;
return fp;
}
int pclose(FILE* stream)
{
int pid, stat;
int fd;
if (childpid == NULL) {
errno = EINVAL;
return -1;
}
fd = fileno(stream);
if (fd >= maxfd) {
errno = EINVAL;
return -1;
}
if ((pid = childpid[fd]) == 0) {
errno = EINVAL;
return -1;
}
childpid[fd] = 0;
if (fclose(stream) == EOF) {
return -1;
}
while (waitpid(pid, &stat, 0) < 0) {
if (errno != EINTR) {
return -1;
}
}
return stat;
}
使用示例:
void test_popen()
{
FILE *fp;
char *data = "hello world!";
char buf[1024];
/* write example */
char *cmdstring = "wc -c"; /* 统计字符个数 */
if ((fp = popen(cmdstring, "w")) == NULL) {
perror("popen error");
return;
}
fputs(data, fp);
if (pclose(fp) < 0) {
perror("pclose error");
return;
}
/* read example */
cmdstring = "ls -al";
if ((fp = popen(cmdstring, "r")) == NULL) {
perror("popen error");
return;
}
for ( ; ; ) {
fflush(stdout);
if (fgets(buf, sizeof(buf), fp) == NULL)
break;
if (fputs(buf, stdout) == EOF) {
perror("fputs error");
return;
}
}
if (pclose(fp) < 0) {
perror("pclose error");
return;
}
}