您的位置:首页 > 运维架构 > Shell

shell命令实现——bc计算器

2011-12-03 22:02 316 查看
我们将程序分为两个部分

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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: