popen 的使用方法及场景

原创 2018年04月16日 22:34:53

Popen的使用方法

1.Popen的应用场景

popen应用于执行shell命令,并读取此命令的返值,或者与执行的命令进行交互。

2.Popen的实现

Popen()函数通过创建一个管道,调用fork()产生一个子进程,然后调用execl("/bin/sh", "sh", "-c”,command,0);执行shell命令。可以通过这个管道进行标准输入输出操作,下面会附上源码。

3.Pclose操作

Pclose()函数会闭标准i/0流,等待子进程结束,然后返回shell终止状态。如果不执行,则pclose()返回终止状态就是shellexit状态。

 源码附上:

/* Copyright (C) 2004       Manuel Novoa III    <mjn3@codepoet.org>
 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
 *
 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
 *
 * Dedicated to Toni.  See uClibc/DEDICATION.mjn3 for details.
 */

/* Jan 1, 2004
 *
 * Rewrite popen for SUSv3 compliance.
 *   Added a list of popen()'d to store pids and use waitpid() in pclose().
 *   Loop on waitpid() failure due to EINTR as required.
 *   Close parent's popen()'d FILEs in the {v}fork()'d child.
 *   Fix failure exit code for failed execve().
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <bits/uClibc_mutex.h>

#ifdef __UCLIBC_MJN3_ONLY__
#warning "hmm... susv3 says Pipe streams are byte-oriented."
#endif /* __UCLIBC_MJN3_ONLY__ */


/* uClinux-2.0 has vfork, but Linux 2.0 doesn't */
#include <sys/syscall.h>
#if ! defined __NR_vfork
# define vfork fork
# define VFORK_LOCK		((void) 0)
# define VFORK_UNLOCK		((void) 0)
#endif

#ifndef VFORK_LOCK
__UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER);
# define VFORK_LOCK		__UCLIBC_MUTEX_LOCK(mylock)
# define VFORK_UNLOCK		__UCLIBC_MUTEX_UNLOCK(mylock)
#endif

struct popen_list_item {
	struct popen_list_item *next;
	FILE *f;
	pid_t pid;
};

static struct popen_list_item *popen_list /* = NULL (bss initialized) */;

FILE *popen(const char *command, const char *modes)
{
	FILE *fp;
	struct popen_list_item *pi;
	struct popen_list_item *po;
	int pipe_fd[2];
	int parent_fd;
	int child_fd;
	int child_writing;			/* Doubles as the desired child fildes. */
	pid_t pid;

	child_writing = 0;			/* Assume child is writing. */
	if (modes[0] != 'w') {		/* Parent not writing... */
		++child_writing;		/* so child must be writing. */
		if (modes[0] != 'r') {	/* Oops!  Parent not reading either! */
			__set_errno(EINVAL);
			goto RET_NULL;
		}
	}

	if (!(pi = malloc(sizeof(struct popen_list_item)))) {
		goto RET_NULL;
	}
	// 打开一个pipe,管道是单向。故数据流只能单向流动。
	if (pipe(pipe_fd)) {
		goto FREE_PI;
	}

	//下面两个描述符就是管道的两端的描述,一个为读一个为写。
	child_fd = pipe_fd[child_writing]; 
	parent_fd = pipe_fd[1-child_writing]; 
	//fdopen就是打开一个描述,fd相同模式或者字集的方式打开。意思就是把一个已找打开的流与一个文件描述符相关联,且
	//这个文件描述是唯一的,这样也就可以保证这个函数接口的可重入性。如果设计的不可重入性,也就没必要再做一次fdopen了。
	if (!(fp = fdopen(parent_fd, modes))) {
		close(parent_fd);
		close(child_fd);
		goto FREE_PI;
	}

	VFORK_LOCK;
	//再这里创建一个子进程,然后执行 shell命令。这里最重的两步就是用pipe的两个描述替换标准输入或者输出。
	if ((pid = vfork()) == 0) {	/* Child of vfork... */
		close(parent_fd);
		if (child_fd != child_writing) {
			dup2(child_fd, child_writing); //用child_fd来代替标准输入或输出。
			close(child_fd);
		}

		/* SUSv3 requires that any previously popen()'d streams in the
		 * parent shall be closed in the child. */
		//关闭不必要的资源。
		for (po = popen_list ; po ; po = po->next) {
			close(po->f->__filedes);
		}

		//执行exec shell,这个时候标准输入/输出就变为pipe管道的一端了。
		//这里只能实现单向的功能。要么读要么写。
		execl("/bin/sh", "sh", "-c", command, (char *)0);

		/* SUSv3 mandates an exit code of 127 for the child if the
		 * command interpreter can not be invoked. */
		_exit(127);
	}
	VFORK_UNLOCK;

	/* We need to close the child filedes whether vfork failed or
	 * it succeeded and we're in the parent. */
	close(child_fd);
	//将当前的信息保存到全局链表。为了是pclose可以找到对的子进程与通信文件描述。
	if (pid > 0) {				/* Parent of vfork... */
		pi->pid = pid;
		pi->f = fp;
		VFORK_LOCK;
		pi->next = popen_list;
		popen_list = pi;
		VFORK_UNLOCK;

		return fp;
	}

	/* If we get here, vfork failed. */
	fclose(fp);					/* Will close parent_fd. */

 FREE_PI:
	free(pi);

 RET_NULL:
	return NULL;
}

#warning is pclose correct wrt the new mutex semantics?

int pclose(FILE *stream)
{
	struct popen_list_item *p;
	int stat;
	pid_t pid;

	/* First, find the list entry corresponding to stream and remove it
	 * from the list.  Set p to the list item (NULL if not found). */
	VFORK_LOCK;
	if ((p = popen_list) != NULL) {
		if (p->f == stream) {// 找到stream对应的popen结点。
			popen_list = p->next;
		} else {
			struct popen_list_item *t;
			do {
				t = p;
				if (!(p = t->next)) {
					__set_errno(EINVAL); /* Not required by SUSv3. */
					break;
				}
				if (p->f == stream) {
					t->next = p->next;
					break;
				}
			} while (1);
		}
	}
	VFORK_UNLOCK;

	if (p) {
		pid = p->pid;			/* Save the pid we need */
		free(p);				/* and free the list item. */

		fclose(stream);	/* The SUSv3 example code ignores the return. */

		/* SUSv3 specificly requires that pclose not return before the child
		 * terminates, in order to disallow pclose from returning on EINTR. */
		do {
			if (waitpid(pid, &stat, 0) >= 0) { //等待子进程返回。获取返回值。
				return stat;
			}
			if (errno != EINTR) {
				break;
			}
		} while (1);
	}

	return -1;
}

举例附上:

#include <stdio.h>
#include <string.h>

int main()
{
	FILE *fp = NULL;
	char buf[1024] = "";

	fp = popen("ls -al", "r");
	if(fp == NULL)
	{
		perror("popen error\n");
		return -1;
	}
	while(fgets(buf, sizeof(buf), fp) != 0)
	{
		printf("%s\n", buf);
		memset(buf, 0x0, sizeof(buf));
	}
	pclose(fp);
	return 0;
}


 

 

popen 使用方法

popen()可以执行shell命令,并读取此命令的返回值;        popen()函数通过创建一个管道,调用fork()产生一个子进程,执行一个shell以运行命令来开启一个进程。可以通过这...
  • stone8761
  • stone8761
  • 2017-08-23 09:41:21
  • 4727

system和popen的用法和区别

 转自:http://blog.csdn.net/shanzhizi/article/details/9054009 linux下使用system需要谨慎,那么代替它的方法是什么呢? ...
  • lovewubo
  • lovewubo
  • 2015-06-12 12:37:38
  • 4651

linux popen函数简单实例

Linux 中的popen机制可以在程序中执行一个shell命令,有两种操作模式,分别为读和写。在读模式中,程序中可以读取到命令的输出,其中有一个应用就是获取网络接口的参数。在写模式中,最常用的是创建...
  • szkbsgy
  • szkbsgy
  • 2016-01-30 09:58:06
  • 4498

popen的用法及与system调用的区别

首先用man查看下popen的介绍: popen(3) - Linux man page Name popen, pclose - pipe stream to or from a ...
  • judwenwen2009
  • judwenwen2009
  • 2014-05-29 00:31:58
  • 13829

Linux popen函数的使用总结

函数原型:   #include “stdio.h”   FILE *popen( const char* command, const char* mode )   参数说明:   comm...
  • u011715883
  • u011715883
  • 2014-05-14 17:38:15
  • 2066

使用popen函数执行shell命令

pclose(关闭管道I/O) 相关函数 popen 表头文件 #include 定义函数 int pclose(FILE * stream); 函数说明 pclose()用来关闭由popen所建立的...
  • zhouzhenhe2008
  • zhouzhenhe2008
  • 2016-12-27 21:42:07
  • 1063

linux popen函数

  • 2010年04月28日 20:45
  • 14KB
  • 下载

windows下利用_popen,_wpoen创建管道进行系统命令输出数据

_popen, _wpopen这是C运行库(当然 popen函数为Linux C) CreatePipe function这是API函数 system函数可以运行命令行,并不能获得显示结果。执行结果,...
  • greless
  • greless
  • 2017-05-17 15:18:41
  • 1407

Linux下system与popen函数

Linux下使用system()函数一定要谨慎 linux尽量避免使用system。 曾经的曾经,被system()函数折磨过,之所以这样,是因为对system()函数了解不够深入。只...
  • u013485792
  • u013485792
  • 2016-09-13 14:43:03
  • 2651
收藏助手
不良信息举报
您举报文章:popen 的使用方法及场景
举报原因:
原因补充:

(最多只允许输入30个字)