实现一个shell程序
Posted XNQC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现一个shell程序相关的知识,希望对你有一定的参考价值。
实现一个自己的shell程序,这个程序有这些功能:解释执行命令,支持输入输出重定向,支持管道,后台运行
程序。当运行该程序后,它支持以下的命令格式:
1.单个命令,如:ls。2.带l到多个参数的命令,如ls -l。3.带一个输出重定向的命令。4.带一个输入重定向的
命令。5.带一个管道的命令。6.后台运行符&可加在各个命令的最后面。7.输入exit或logout退出myshell。
错误处理:1.输入错误的命令格式报错。2.输入不存在的命令报错。
程序主函数的流程图如下:
程序的各个函数的功能及说明:
(1)void print_prompt():该函数只是简单地打印myshell的提示符,即“myshell$”。
(2)void get_input(char *buf):获得一条用户输入的待执行命令,参数buf用于存放输入的命令。如果输入的
命令过长(大于256个字符),则终止程序。输入的命令以换行符\'\\n\'作为结束标志。
(3)void explain_input(char *buf,int *argcount,char arglist[100][256]):解析buf中存放的命令,把每个
选项存放在arglist中。
(4)do_cmd(int argcount,char arglist[100][256]):执行arglist中存放的命令,arglist为待执行命令的参数
的个数。
(5)int find_command(char *command):功能是分别在当前目录下、/bin、/usr/bin目录下查找命令的可执行程
序。
下面是该程序的源代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> #include <fcntl.h> #include <sys/stat.h> #include <dirent.h> #define normal 0 //一般的命令 #define out_redirect 1 //输出重定向 #define in_redirect 2 //输入重定向 #define have_pipe 3 //命令中有管道 void print_prompt(); //打印提示符 void get_input(char*); //得到输入的命令 void explain_input(char*,int*,char a[][256]); //对输入命令进行解析 void do_cmd(int,char a[][256]); //执行命令 int find_command(char*); //查找命令中的可执行程序 int main(int argc,char *argv[]) { int i; int argcount= 0; char arglist[100][256]; char *buf= NULL; char **arg= NULL; buf= (char*)malloc(256); if(buf == NULL) { perror("malloc failed"); exit(-1); } while(1) { memset(buf,0,256); //将buf所指向的空间清零 print_prompt(); get_input(buf); //若输入的命令为exit或logout则退出本程序 if(strcmp(buf,"exit\\n")== 0||strcmp(buf,"logout\\n")== 0) break; for(i= 0;i< 100;i++) { arglist[i][0]= \'\\0\'; } argcount= 0; explain_input(buf,&argcount,arglist); do_cmd(argcount,arglist); } if(buf!= NULL) { free(buf); buf= NULL; } return 0; } void print_prompt() { printf("myshell$ "); } //获取用户输入 void get_input(char *buf) { int len= 0; int ch; ch= getchar(); while(len< 256&&ch!= \'\\n\') { buf[len++]= ch; ch= getchar(); } if(len== 256) { printf("command is too long\\n"); exit(-1); //输入的命令过长则退出程序 } buf[len]= \'\\n\'; len++; buf[len]= \'\\0\'; } /*解析buf中的命令,将结果存入arglist中,命令以回车符号\\n结束,如输入命令为 *"ls -l /tmp",则arglist[0]、arglist[1]、arglist[2]分别为ls、-l、/tmp */ void explain_input(char *buf,int *argcount,char arglist[100][256]) { char *p= buf; char *q= buf; int number= 0; while(1) { if(p[0]== \'\\n\') break; if(p[0]== \' \') p++; else { q = p; number= 0; while((q[0]!= \' \')&&(q[0]!= \'\\n\')) { number++; q++; } strncpy(arglist[*argcount],p,number+ 1); arglist[*argcount][number]= \'\\0\'; *argcount= *argcount+ 1; p = q; } } } void do_cmd(int argcount,char arglist[100][256]) { int flag= 0; int how= 0; //用于指示命令中是否含有>、<、| int background= 0; //标识命令中是否有后台运行标识符& int status; int i; int fd; char* arg[argcount+ 1]; char* argnext[argcount+ 1]; char* file; pid_t pid; //将命令取出 for(i= 0;i< argcount;i++) { arg[i]= (char*)arglist[i]; } arg[argcount]= NULL; //查看命令行是否有后台运行符 for(i= 0;i< argcount;i++) { if(strncmp(arg[i],"&",1)== 0) { if(i== argcount-1) { background= 1; arg[argcount-1]= NULL; break; } else { printf("wrong command\\n"); return; } } } for(i= 0;arg[i]!= NULL;i++) { if(strcmp(arg[i],">")== 0) { flag++; how= out_redirect; if(arg[i+ 1]== NULL) flag++; } if(strcmp(arg[i],"<")== 0) { flag++; how= in_redirect; if(i== 0) flag++; } if(strcmp(arg[i],"|")== 0) { flag++; how= have_pipe; if(arg[i+ 1]== NULL) flag++; if(i== 0) flag++; } } /* flag大于1,说明命令中含有多个>,<,|符号,本程序是不支持这样的命令的,或者命令格式不对,如"ls -l /tmp >" */ if(flag> 1) { printf("wrong command\\n"); return; } if(how== out_redirect) { //命令只含有一个输出重定向符号 for(i= 0;arg[i]!= NULL;i++) { if(strcmp(arg[i],">")== 0) { file= arg[i+ 1]; arg[i]= NULL; } } } if(how== in_redirect) { //命令中只含有一个输入重定向符号 for(i= 0;arg[i]!= NULL;i++) { if(strcmp(arg[i],"<")== 0) { file= arg[i+ 1]; arg[i]= NULL; } } } if(how== have_pipe) { /*命令只含有一个管道符号|,把管道符号后面的部分存入argnext中,管道后面的部分是一个可执行的shell命令 */ for(i= 0;arg[i]!= NULL;i++) { if(strcmp(arg[i],"|")== 0) { arg[i]= NULL; int j; for(j= i+ 1;arg[j]!= NULL;j++) { argnext[j-i-1]= arg[j]; } argnext[j-i-1]= arg[j]; break; } } } if((pid= fork())< 0) { printf("fork error\\n"); return; } switch(how) { case 0: /*pid为0说明是子进程,在子进程中执行输入的命令,输入的命令不含>、<、和| */ if(pid== 0) { if(!(find_command(arg[0]))) { printf("%s : command not found\\n",arg[0]); exit(0); } execvp(arg[0],arg); exit(0); } break; case 1: //输入的命令中含有输出重定向符> if(pid== 0) { if(!(find_command(arg[0]))) { printf("%s : command not found\\n",arg[0]); exit(0); } fd= open(file,O_RDWR|O_CREAT|O_TRUNC,0644); dup2(fd,1); execvp(arg[0],arg); exit(0); } break; case 2: //输入的命令中含有输入重定向符< if(pid== 0) { if(!(find_command(arg[0]))) { printf("%s : command not found\\n",arg[0]); exit(0); } fd= open(file,O_RDONLY); dup2(fd,0); execvp(arg[0],arg); exit(0); } break; case 3: //输入的命令中含有管道符| if(pid== 0) { int pid2; int status2; int fd2; if((pid2= fork())< 0) { printf("fork2 error\\n"); return; } else if(pid2== 0) { if(!(find_command(arg[0]))) { printf("%s : command not found\\n",arg[0]); exit(0); } fd2= open("/tmp/youdonotknowfile",O_WRONLY|O_CREAT|O_TRUNC,0644); dup2(fd2,1); execvp(arg[0],arg); exit(0); } if(waitpid(pid2,&status2,0)== -1) printf("wait for child process error\\n"); if(!(find_command(argnext[0]))) { printf("%s : command not found\\n",argnext[0]); exit(0); } fd2= open("/tmp/youdonotknowfile",O_RDONLY); dup2(fd2,0); execvp(argnext[0],argnext); if(remove("/tmp/youdonotknowfile")) printf("remove error\\n"); exit(0); } break; default: break; } //若命令中有&,表示后台执行,父进程直接返回,不等待子进程结束 if(background== 1) { printf("[process id %d]\\n",pid); return; } //父进程等待子进程结束 if(waitpid(pid,&status,0)== -1) printf("wait for child process error\\n"); } //查找命令中的可执行程序 int find_command(char *command) { DIR *dp; struct dirent *dirp; char *path[]= {"./","/bin","/usr/bin",NULL}; //使当前目录下的程序可以运行,如命令"./fork"可以被正确解释和执行 if(strncmp(command,"./",2)== 0) command= command+ 2; //分别在当前目录、/bin和/usr/bin目录查找要执行的程序 int i= 0; while(path[i]!= NULL) { if((dp= opendir(path[i]))== NULL) printf("can not open /bin \\n"); while((dirp= readdir(dp))!= NULL) { if(strcmp(dirp->d_name,command)== 0) { closedir(dp); return 1; } } closedir(dp); i++; } return 0; }
以上是关于实现一个shell程序的主要内容,如果未能解决你的问题,请参考以下文章
用于确保在任何给定时间仅运行一个 shell 脚本的 shell 片段 [重复]
ASP.net MVC 代码片段问题中的 Jqgrid 实现