LINUX SHELL 设计实现
flaty
13年前
没啥可说的,直接贴原码:
LINUX的作业。嘿。
设计并实现一个Linux下的的交互式shell命令解释器:MyShell;以模拟unix/linux系统下的完善的bash shell的功能。不需要具备非常完备的shell功能。主要有如下功能:
<!--[if !supportLists]-->l <!--[endif]-->初始化环境,主要是默认搜索路径、history循环数组、作业列表
<!--[if !supportLists]-->l <!--[endif]-->打印命令提示符;
<!--[if !supportLists]-->l <!--[endif]-->接受、识别和分析命令提示符后输入的命令行,并创建子程序,在子程序中将其转换为相对的系统调用以调用内核功能实现任务;
<!--[if !supportLists]-->l <!--[endif]-->能实现内部命令,包括:exit(退出shell)、history、cd、jobs、bg、fg。
<!--[if !supportLists]-->l <!--[endif]-->能够执行外部命令,并到搜索路径中去查找外部命令对应的执行文件;命令均可带参数;
<!--[if !supportLists]-->l <!--[endif]-->支持&、|、<、>四个特殊字符及其所代表的功能;
<!--[if !supportLists]-->l <!--[endif]-->支持前、后台作业控制,包括fg/bg/jobs以及ctrl+c/ctrl+z(挂起、中止和继续运行)。
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> #include <string.h> #include <signal.h> #include <sys/types.h> #include<unistd.h> #include<stdio.h> #include<stdlib.h> #include<fcntl.h> #include<string.h> #include <errno.h> #define MAXLEN 128 //命令最大长度 #define HIS_LEN 10 //历史记录最大长度 typedef struct history //历史记录结点 { int i; int start; int end; char cmd[HIS_LEN][MAXLEN]; } history; typedef struct node{ //作业链表结点 pid_t pid; char cmd[100]; char state[10]; struct node *next; }node; /********************************声明全局变量*********************************/ node *head,*end; //jobs 链表 history his; //history 队列 char buf[MAXLEN]; //保存输入命令 char *path[10]; //保存多个环境变量.--pATH char path_cmd[50]; //保存输入命令的绝对路 int path_sum; //PATH环境变量个数。 char work_path[100]; //保存当前工作路径 int jobs_count=0; //当前工作数量 pid_t pid; //子进程PID char *argv[10]; //根据空格分解命令的字符指针数组 int cmd_count=0; //历史记录个数 int stop_flag=0; //-------------------------------------声明函数---------------------------------------- void print_work_path(); // 打印SHELL 提示符 void print_string(char *); // 打印字符串 int read_commands(char *); // 读取命令 void init_envi(); // 初始化环境 void analyse_cmd(char**); // 分析命令,并根据空格分解之 int cmd_addpath(char *); // 补全命令 void my_cd(char* ); // CD 命令 void my_history(char *); // HISTORY 命令 void save_history(); // 保存输入命令历史 void my_jobs(); // JOBS查看 void my_fg(char *ch,sigset_t *son_set); // FG命令 void my_bg(char *); // BG命令 void add_node(int,char*,char *); // 增加作业结点命令 void father_sig_hander(int ); // 父进程信号处理函数 void reDirect(char *buf); // 管道重定向 char *instru_divide(char *ch); void aru_divide(char *instru, char **ch); //-----------------main----------------- main (){ int is_bg; //后台命令标记,后台=0,否则为1。 init_envi(); //初始化环境 //-------------信号处理--------------- sigset_t son_set; //设置父进程信号处理函数 struct sigaction father_pact; father_pact.sa_handler=father_sig_hander; sigemptyset(&son_set); //设置子进程屏蔽信号集 sigaddset(&son_set,SIGTSTP); sigaddset(&son_set,SIGINT); //-------------接收命令--------------- while(1){ sigprocmask(SIG_BLOCK,&son_set,NULL); //pid=0; stop_flag=0; waitpid(-1,NULL,WNOHANG); //回收僵尸进程 signal(SIGINT,father_sig_hander); //为父进程安装信号处理函数 sigaction(SIGTSTP,&father_pact,NULL); is_bg=0; print_work_path(); //打印命令提示符 if (!read_commands(buf)) //不处理空格等无用命令, 并去除命令前后空格! continue; save_history(his); //保存命令历史 int flag=cmd_match(buf); if(flag==1){ //----------管道重定向相关--------- reDirect(buf); continue; } else if(flag==2){ is_bg=1; } analyse_cmd(argv);//解析命令,根据空格分开。 //----------------------------执行内部命令------------------------ if(!strcmp("exit",argv[0])&&argv[1]==NULL) { puts("goodbye!");exit(0);} else if(!strcmp("cd",argv[0])){ //------cd my_cd(argv[1]); continue; } else if(!strcmp("history",argv[0])){ //-------history my_history(argv[1]); continue; } else if(!strcmp("jobs",argv[0])){ //--------jobs my_jobs(); continue; } else if(!strcmp("fg",argv[0])){ //--------fg my_fg(argv[1],&son_set); continue; } else if(!strcmp("bg",argv[0])){ //--------bg my_bg(argv[1]); continue; } //------------根据pATH补全外部命令,包括当前路径--------- if(cmd_addpath(argv[0])==0){ strcpy(path_cmd,argv[0]); //如果在PATH没找到,把命令视为绝对路径,直接执行! // } //------------------执行外部命令------------------------ if((pid=fork())<0) perror("execute fail!!"); //-------------子进程-------------- else if(pid==0) { if(execv(path_cmd,argv)==-1) puts("cmd not found!!"); } //--------------父进程 ---------------- else { sigprocmask(SIG_UNBLOCK,&son_set,NULL); if(is_bg==1) add_node(pid,argv[0],"running"); else{ waitpid(pid,NULL,0); } } }//while } //-------------main 结束-------------------------- //--------------初始化环境----------------------- void init_envi(){ FILE *fp; char ch,temp[100],*p; int i=0; his.end=his.start=0; jobs_count=0; stop_flag=0; head=end=NULL; if((fp=fopen("./test_profile","r"))==0) { perror("init path failure!! exit!!"); exit(1); } while((ch=fgetc(fp))!=EOF) temp[i++]=ch; temp[i]=0; i=0; while(temp[i]&&temp[i]!='=') ++i; strcpy(temp,temp+i+1); char *delim = ":"; p=strtok(temp, delim); path[0]=(char *)malloc(strlen(p)+1); strcpy(path[0],p); i=1; while((p=strtok( NULL, delim))!=NULL) { path[i]=(char *)malloc(strlen(p)+1); strcpy(path[i],p); i++; } path_sum=i-1; } //------------命令匹配-------------------------------------------------------- int cmd_match(char *buf){ char *ch; if(strchr( buf, '|')!=NULL||strchr( buf, '<')!=NULL||strchr( buf, '>')!=NULL) return 1; else if((ch=strchr( buf, '&'))!=NULL) { *ch=0 ; return 2;} } //------------打印命令提示-------------------------------- void print_work_path() { char * user = getlogin(); getcwd(work_path,sizeof(work_path)); printf("<"); print_string(user); printf("@ "); print_string(work_path); printf(">$"); } //------------读取输入命令------------------------ int read_commands(char * buf) { int i=0;char ch; //printf("%s",buf); //gets(buf); ch=getchar(); if(ch=='\n') return 0; buf[i++]=ch; while((ch=getchar())!='\n') buf[i++]=ch; buf[i]=0; //puts(buf); i=0; while(buf[i]==' '||buf[i]=='\t'&&buf[i]) ++i; if(buf[i]==0) return 0; else { i=0; while(buf[i]==' '||buf[i]=='\t') ++i;//去除前面空格 strcpy(buf,buf+i); // int j=strlen(buf)-1; // while(buf[j]==' '||buf[j]=='\t') j--; // buf[j+1]=0; //puts(buf); //printf("%d",strlen(buf)); return 1; } } //----------------------解析命令-------------------- void analyse_cmd(char **argv){ int i=1; char *delim = "' ','\t'"; argv[0]=strtok(buf, delim); //puts(argv[0]); while((argv[i]=strtok( NULL, delim))!=NULL) i++; argv[i]=NULL; } //---------------------补全命令------------------- int cmd_addpath(char * argv){ int i=0; //char //puts("!!!!"); //printf("%d\n",path_sum); strcpy(path_cmd,work_path);//查找当前路径并补全命令 strcat(path_cmd,"/"); strcat(path_cmd,argv); //puts(path_cmd); if(!access(path_cmd,F_OK)) return 1; while(i<path_sum){ //查找pATH并补全命令 strcpy(path_cmd,path[i]); strcat(path_cmd,"/"); strcat(path_cmd,argv); //puts(path_cmd); if(access(path_cmd,F_OK)==0) return 1; i++; } return 0; } //---------------常用函数-------------- void print_string(char *p){ printf("%s",p); } //---------------内部命令-------------------------------------------- //------------save history------------------ void save_history(){ cmd_count++; his.start%=HIS_LEN; his.end%=HIS_LEN; strcpy(his.cmd[his.end++],buf); if((his.end)%HIS_LEN==his.start) his.start++; } //---------------------cd------------------------- void my_cd(char * para){ if(para==NULL) chdir("/root"); else if(para!=NULL&&chdir(para)==-1) perror("dir not exist!!n"); } //----------------------history---------------------------- void my_history(char * argv) { int i=0,len,j; if(cmd_count<HIS_LEN) len=cmd_count; else len=HIS_LEN; j=len; if(argv==NULL){ for(i=his.start;i<his.start+len;i++) printf("%d \t%s\n",j--,his.cmd[i%HIS_LEN]); } } //--------------------作业控制相关------------------------------- void my_jobs() { int i=1; if(head==NULL) {printf("no jobs!!\n");return;} node *p=head; while(p->next!=NULL) { printf("[%d]- %d %s \t%s\n",i++,p->pid,p->state,p->cmd); p=p->next; } printf("[%d]+ %d %s \t%s\n",i,p->pid,p->state,p->cmd); } //-------------------------fg----------------------- void my_fg(char *ch,sigset_t *son_set){ sigprocmask(SIG_UNBLOCK,son_set,NULL); if(head==NULL){ //无作业 printf("no job!!\n"); return; } if(ch==NULL){ // puts("para missing!"); } else { //作业放到前台运行 int i=atoi(ch),j=1; node *p=head,*q; if(i>2){ while(p->next!=NULL&&j<i-1) { p=p->next; j++;} if(p->next==NULL) { printf("no such job!!\n"); return; } }else { if(i==2&&p->next==NULL) {printf("no such job!!\n");return;} } if(i==1){ if(head==end){ //printf("only one!\n"); p=head; pid=p->pid;printf("%d\t\t%s\n",pid,p->cmd); free(p); head=end=NULL; }else{ // printf("i==1!\n"); q=p->next; pid=head->pid;printf("%d\t\t%s\n",pid,head->cmd); free(head); head=q;} } else{ //printf("other\n"); q=p->next; pid=q->pid; p->next=q->next; if(q->next==NULL) end=p;//如果是最后一个结点 printf("%d\t\t%s\n",pid,q->cmd); free(q); } jobs_count--; kill(pid,SIGCONT); waitpid(pid,NULL,0); } } //-------------------------bg--------------------- void my_bg(char *ch){ if(head==NULL){ //无作业 printf("no job!!\n"); return; } else if(ch==NULL){ //把作业放到后台 puts("para missing!"); } else{ //根据作业号放至后台运行。 int i=atoi(ch),j=1; node *p=head; while(p->next!=NULL&&j++<i) p=p->next; if(p==NULL) { printf("no such job!!\n"); return; } pid=p->pid; kill (pid,SIGCONT); strcpy(p->state,"running"); } } //--------------------往作业链表增加结点------------------ void add_node(int pid,char *argv,char *state) { node *p; jobs_count++; p=(node *)malloc(sizeof(node)); p->pid=pid; if (stop_flag==0) strcat(argv," &"); strcpy(p->cmd,argv); strcpy(p->state,state); p->next=NULL; printf("[%d] %d %s %s\n",jobs_count,p->pid,p->state,p->cmd); if(head==NULL){ head=p;end=p; }else { end->next=p; end=end->next; } } //-------------信号处理函数------------------------------------------- void father_sig_hander(int p){ if(p==SIGINT) { kill(pid,SIGTERM); } else if(p==SIGTSTP) { stop_flag=1; //printf("%d\n",pid); kill(pid,SIGSTOP); add_node(pid,strrchr(path_cmd,'/')+1,"stop"); } } //--------------------管道重定向----------------------------// //-------------找出管道符位置并返回*-------------------------/ char *instru_divide(char *ch){ char *argu1, *argu2; char *token[] = {"<",">","|"}; int i = 0, len; len = strlen(ch); for(;i<3;i++){ argu1 = strtok(ch,token[i]); if(strlen(argu1)<len){ break; } else{ continue; } } return token[i]; } //---------------求取execvp函数第二个参数-------------/ void aru_divide(char *instru, char **ch){ int i = 0; ch[i] = strtok(instru," "); while(ch[i]&&i<8){ i++; ch[i] = strtok(NULL," "); } ch[i] = (char *)0;//最后一个参数后面指针赋值NULL } void reDirect(char *str){ char *instru1 , *instru2 , *token , ch[50]; int pipe_fd[2]; int fid; strcpy(ch,str); token = instru_divide(str);//token 为管道符 |、 >、或 < instru1 = strtok(str,token);//处理前管道符前一个命令 instru2 = strstr(ch,token)+1;//处理前管道符后一个命令 char *prog1[8],*prog2[8];//定义指针数组存储execvp函数第二个参数 aru_divide(instru1,prog1); aru_divide(instru2,prog2); pid_t pid[2]; if(strcmp(token,"|")==0){ if(pipe(pipe_fd) < 0){ perror ("pipe failed"); exit (errno); } pid[0] = fork(); if(pid[0]<0){ perror("fork error"); exit(1); } else if(pid[0]==0){ close(pipe_fd[0]); dup2(pipe_fd[1], 1); close(pipe_fd[1]); execvp(prog1[0], prog1); } else{ pid[1] = fork(); if(pid[1]<0){ perror("fork error"); exit(1); } else if(pid[1]==0){ close(pipe_fd[1]); dup2(pipe_fd[0],0); close(pipe_fd[0]); if(execvp(prog2[0], prog2)){ close(pipe_fd[0]); close(pipe_fd[1]); return; } } close(pipe_fd[0]); close(pipe_fd[1]); waitpid(pid[0],NULL,0); waitpid(pid[1],NULL,0); } return; } if(strcmp(token,">")==0){ if((fid = open(prog2[0],O_CREAT|O_RDWR|O_TRUNC))==-1){ perror("open error"); exit(1); } pid[0] = fork(); if(pid[0]<0){ perror("fork error"); exit(1); } else if(pid[0]==0){ dup2(fid,1); close(fid); execvp(prog1[0], prog1); } else{ waitpid(pid[0],NULL,0); } return; } if(strcmp(token,"<")==0){ if(access(prog2[0],F_OK)){ perror("file not found"); exit(1); } pid[0] = fork(); if(pid[0]<0){ perror("fork error"); exit(1); } else if(pid[0]==0){ fid = open(prog2[0],O_RDWR); dup2(fid,0); close(fid); if(execvp(prog1[0], prog1)){ puts("rwewrw"); } } else{ waitpid(pid[0],NULL,0); } return; } }