shell命令实现——bc计算器

我们将程序分为两个部分
1.主程序bcmain.c

#include "headall.h"
/*
 * 实现bc程序:其主要功能是用来进行加减乘除的运算
 * 其本质是一个客户端/服务器的模型 
 *       
 *       +---------+  pipe->dc  +----------+
 * stdin 0         >============>          |
 *       |    bc   |            |   dc     |
 * stdout1         <============<          |
 *       +---------+  pipe<-dc  +----------+
 *   
 *  用户首先会连接到bc并将bc连接到dc,调用dc程序后返回结果
 *  该程序包括四个主要方面
 *  1. bc与dc之间通过管道通信:由于管道是单向的,因此要建立两个管道。
 *  2. 创建一个新的进程来运行dc
 *  3. 在新进程中,由于我们要execlp shell的dc程序,dc程序的输入是标准输入,
 *     dc程序的输出是标准输出,而我们希望重定向这两个到管道
 *     
 *  4. 在父进程中,我们会读取用户的输入,进行解析,将命令通过管道传给dc
 *     dc进行读取数据,返回结果给bc,bc显示给用户
 *
 *     3   4   5  6
 *     |   |   |  |
 *     |   |   |  |
 *     |   |   |  |
 *     +———+   +--+
 *
 */

int main(){
	
	int pid;
	// two pipe
        int todc[2],fromdc[2];
          
        // make two pipes
	if( pipe(todc) == -1 || pipe(fromdc) == -1)
		oops("pipe failed",1);
	
	//printf("todc = %d,%d\n",todc[0],todc[1]);

	//printf("todc = %d,%d\n",fromdc[0],fromdc[1]);

	pid = fork();

	switch(pid){
		
	case -1: oops("fork failed",1);break;
	
	// in child process
	case 0:
               dc_deal(todc,fromdc);
               break;
	// parent process
	default:
               bc_deal(todc,fromdc);

	}
} 
2.处理程序proclib.c
#include "headall.h"

int dc_deal(int *,int *);

int bc_deal(int *,int *);

/*
 * review 匿名管道
 * pipe(array[2]);
 * 创建一个管道,并为这个数组的两个元素创建文件描述符
 * 这里要注意: 
 * array[0] 是从管道中读取数据
 * array[1] 是向管道中写数据 
 *
 */

//父进程的处理函数
int bc_deal(int *todc,int *fromdc){

	
	char message[BUFSIZ],operation[BUFSIZ];
	int  num1,num2;

	// 管道1,写入dc的管道
	// 父子进程共享管道
        // 在父进程方面关闭 todc[0],关闭读管道
        close(todc[0]);
         
        // 管道2:读取dc的管道
	// 在父进程方面关闭 fromdc[1],关闭写管道
        close(fromdc[1]);
	
 
	FILE *fpout,*fpin;
	// 转换文件描述符为文件流
	fpout = fdopen(todc[1],"w");
	fpin  = fdopen(fromdc[0],"r");
	
	if(fpout == NULL || fpin == NULL)
		oops("error to convert the pipes to streams",1);
	
	while(printf("our bc:"),fgets(message,BUFSIZ,stdin)!=NULL){
		
		// 这里有兴趣的可以查一下sscanf的用途
 		// 此函数很强大
		// %[- +]表示操作符号,这里一定要注意
		// -号不能放在中间,否则会被当做 [A-Z]中的符号而无效的
		// 如果放中间会让减法没有用。	
		if(sscanf(message,"%d%[- + * / ^]%d",
			&num1,operation,&num2)!=3){
			
			//printf("op = %c\n",operation);
			//printf("num1 = %d,num2 = %d\n",num1,num2);
			
			printf("syntax error\n");
			continue;
		}
		
		//printf("op = %c\n",operation);			
		//printf("num1 = %d,num2 = %d\n",num1,num2);

		if(fprintf(fpout,"%d\n %d\n %c\np\n",num1,num2,
			*operation)==EOF)
			oops("error writing",1);
		
		fflush(fpout);
		if(fgets(message,BUFSIZ,fpin)==NULL)
			break;
		printf("%d %c %d = %s",num1,*operation,num2,message);
	}
	fclose(fpout);
	fclose(fpin);
}

      

// 子进程的处理函数
int dc_deal(int todc[2],int fromdc[2]){
	

	// printf("todc[2]= %d,%d\n",todc[0],todc[1]);
        // printf("fromdc[2]= %d,%d\n",fromdc[0],fromdc[1]);

	
        // 管道1,写入dc的管道
        // 父子进程共享管道
        // 在子进程方面关闭 todc[1],关闭写管道
        close(todc[1]);

        // 管道2:读取dc的管道
        // 在子进程方面关闭 fromdc[0],关闭读管道
        close(fromdc[0]);

        // printf("todc[2]= %d,%d\n",todc[0],todc[1]);
        // printf("fromdc[2]= %d,%d\n",fromdc[0],fromdc[1]);



	// 子进程方面此时拥有的是:
	// 与管道1相关的是,读管道的 todc[0]
        // 与管道2相关的是,写管道的 fromdc[1]
	// 我们需要在该子进程中执行dc,
	// dc程序的数据来源是标准输入,结果会输出到标准输出
        // 因此我们要对该程序进行重定向
	// 读管道 todc[0]作为数据来源定向到标准输入
        if(dup2(todc[0],0)==-1)
		oops("dup2 failed",1);
	// todc[0]完成使命
	close(todc[0]);

	//printf("after dup2 0,0\n");
        //printf("todc[2]= %d,%d\n",todc[0],todc[1]);
        //printf("fromdc[2]= %d,%d\n",fromdc[0],fromdc[1]);

	
	// 写管道 fromdc[1]作为数据的输出定向到标准输出
 	if(dup2(fromdc[1],1) == -1)
	        oops("dup2 failed",1);
	// fromdc[1]关闭即可
	close(fromdc[1]);

        //printf("after dup2 1,1\n");
        //printf("todc[2]= %d,%d\n",todc[0],todc[1]);
        //printf("fromdc[2]= %d,%d\n",fromdc[0],fromdc[1]);


	// 我们可以干正事了
	execlp("dc","dc",NULL);
	oops("dc failed",5);
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值