fork()+exec*()与popen()执行另一程序


前言

fork()创建个子进程都是让子进程继续执行父进程的文本段,但更多的情况下是让该进程去执行另外一个程序,本文分别介绍fork()+exec*()和popen()去执行另一程序的方法。


一、exec*()执行另一程序

exec*()一系列函数的原型如下:

int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
int execle(const char *path, const char *arg, …, char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

  • l 表示以列表(list)的形式传递要执行程序的命令行参数
  • v表示以数组(vector)的形式传递要执行程序的命令行参数
  • e表示给该命令传递环境变量(environment)。

代码示例

执行ifconfig命令可以获取IP地址,显示如下:
在这里插入图片描述
以下为fork()+execl()执行ifconfig命令获取IP地址代码示例:

#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<ctype.h>

#define TMP_FILE "/tmp/.ifconfig.log"	// 标准输出重定向的文件, /tmp路径是在Linux系统在内存里做的一个文件系统

int main(int argc, char **argv)
{
	pid_t	pid;
	int		fd;
	char	buf[1024];
	int		rv;
	char	*ptr;
	FILE	*fp;
	char	*ip_start;
	char	*ip_end;
	char	ipaddr[16];

	if( (fd=open(TMP_FILE,O_RDWR|O_CREAT|O_TRUNC,0644)) < 0 )	//打开/tmp/.ifconfig.log文件
	{
		printf("Redirect standard output to file failure:%s\n",strerror(errno));
		return -1;
	}

	pid = fork();	//创建子进程
	if( pid < 0 )	//创建子进程失败
	{
		printf("fork() create process failure:%s\n",strerror(errno));
		return -1;
	}
	else if( 0 == pid )		//子进程正在运行
	{
		printf("Child process start excute ifconfig program\n");
		
		dup2(fd,STDOUT_FILENO);		//输出重定向
		
		execl("/sbin/ifconfig","ifconfig","eth0",NULL);		//调用execl()函数,/sbin/ifconfig为要执行文件的路径,ifconfig和eth0为执行文件时要传递的参数,最后一个参数以NULL结尾
		
		printf("Child process excute another program,will not return here.Return here means execl() error\n");
		
		return -1;
	}
	else	//父进程正在运行
	{
		sleep(3);	//让子进程先运行
	}
	//子进程调用了execl(), 会丢掉父进程的文本段,不会执行到这里,只有父进程会执行后面的代码
	memset(buf,0,sizeof(buf));		//清空buf[]

	rv=read(fd,buf,sizeof(buf));	//把前面打开的文件写入buf中
	printf("Read %d bytes data dierectly read after child process write\n",rv);	//此时读取的字节数应为0,因为子进程往文件里写内容时已经将文件偏移量修改到文件尾

	memset(buf,0,sizeof(buf));	//清空buf[]
	lseek(fd,0,SEEK_SET);		//把光标置于文件首
	rv=read(fd,buf,sizeof(buf));	//把fd内容读到buf[]中
	printf("Read %d bytes data after lseek:\n%s",rv,buf);	//此时读取的字节数应为全部字节
	// 使用read()一次性读N多个字节进buf[],但有时我们希望一行一行地读取文件内容,这时可以使用fdopen()将文件描述符fd转成文件流fp
	fp = fdopen(fd,"r");

	fseek(fp,0,SEEK_SET);	//重新把光标放于文件首
	while(fgets(buf,sizeof(buf),fp))	//fgets()一次读一行,读到文件尾则返回NULL
	{
		if( strstr(buf,"netmask") )		//寻找含有字符串netmask的一行
		{
			ptr = strstr(buf,"inet");	//查找inet关键字
			if( !ptr )
			{
				break;
			}
			ptr += strlen("inet");

			while( isblank(*ptr) )	//判断是否为空白符
				ptr ++;

			ip_start = ptr;		//空白符后为IP地址的起始地址

			while( !isblank(*ptr) )
				ptr ++;

			ip_end =ptr;	//IP后第一个空白符为IP地址的末尾地址

			memset(ipaddr,0,sizeof(ipaddr));
			memcpy(ipaddr,ip_start,ip_end-ip_start);	//将IP地址拷贝到ipaddr中,ip_end-ip_start是IP地址的长度,ip_start是IP地址的起始置

			break;
		}
	}

	printf("Parser and get IP address:%s\n",ipaddr);

	fclose(fp);
	unlink(TMP_FILE);

	return 0;
}

程序运行结果:
在这里插入图片描述

二、popen()执行另一程序

函数popen()可以执行一条命令,并返回一个基于管道(pipe)的文件流,我们可以从该文件流中一行样解析所需要的东西。

代码示例

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<ctype.h>

int get_ipaddr(char *interface,char *ipaddr,int ipaddr_size);	//函数声明

int main(int argc,char **argv)
{
	char	ipaddr[16];
	char	*interface="eth0";

	memset(ipaddr,0,sizeof(ipaddr));	//清空ipaddr

	if( get_ipaddr(interface,ipaddr,sizeof(ipaddr)) < 0)	//调用get_ipaddr函数
	{
		printf("ERROR:get IP address failure\n");
		return -1;
	}

	printf("get network interface %s IP address [%s]\n",interface,ipaddr);
	return 0;
}

int get_ipaddr(char *interface,char *ipaddr,int ipaddr_size)
{
	char	buf[1024];
	char	*ptr;
	char	*ip_start;
	char	*ip_end;
	FILE	*fp;
	int		len;
	int		rv;

	if( !interface || !ipaddr || ipaddr_size<16 )
	{
		printf("Invalid input arguments\n");
		return -1;
	}

	memset(buf,0,sizeof(buf));
	snprintf(buf,sizeof(buf),"ifconfig %s",interface);
	if( NULL == (fp=popen(buf,"r")) )
	{
		printf("popen() to excute command \"%s\" failure:%s\n",buf,strerror(errno));
		return -2;
	}

	rv = -3;

	while( fgets(buf,sizeof(buf),fp) )
	{
		if( strstr(buf,"netmask") )
		{
			ptr=strstr(buf,"inet");
			if( !ptr )
			{
				break;
			}
			ptr += strlen("inet");

			while( isblank(*ptr) )
				ptr++;
			
			ip_start=ptr;

			while( !isblank(*ptr) )
				ptr++;

			ip_end=ptr;
			memset(ipaddr,0,sizeof(ipaddr));

			len = ip_end - ip_start;
			len = len>ipaddr_size ? ipaddr_size :len;
			memcpy(ipaddr,ip_start,len);

			rv = 0;

			break;
		}
	}

	return rv;
}

程序运行结果:
在这里插入图片描述

总结

使用fork()+exec*()或popen()都可以让进程去执行另外一条命令,但两者相比,使用函数popen()更为简单一点。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值