直接上各个模块的代码,注释都在文档代码中,非常详细,加上最后的Makefile文件完全可以自行运行看懂:
main函数一个文件main.c
1 /* 2 minishell实现的功能:简单命令解析、管道行解析、输入输出重定向解析、一些内置命令实现、简单的信号处理 3 未能实现的功能:语法分析、别名处理、路径扩展、通配处理、算术处理、变量处理、作业控制 4 shell_loop{ 5 read_command //读 6 parse_command //解析 7 execute_command //执行 8 } 9 */ 10 #include "parse.h" 11 #include "init.h" 12 #include "def.h" 13 14 char cmdline[MAXLINE+1];//定义全局变量存放读取的命令 15 char avline[MAXLINE+1];//保存解析出来的参数 16 char *lineptr;//初始指向cmdline数组 17 char *avptr;//初始指向avline数组 18 19 char infile[MAXNAME+1];//输入文件名,用于保存输入重定向文件名 20 char outfile[MAXNAME+1];//输出文件名 21 COMMAND cmd[PIPELINE];//参数列表 22 23 int cmd_count;//命令个数 24 int backgnd;//是否是后台操作 25 int lastpid;//这是最后一个子进程退出 26 27 int append; 28 int main() 29 { 30 31 setup();//安装信号,划分到初始化模块 32 shell_loop();//进入shell循环 33 return 0; 34 }
setup信号安装部分在初始化模块中,分为两个部分init.h和init.c
1 #ifndef _INIT_H_ 2 #define _INIT_H_ 3 void setup(void); 4 void init(void); 5 #endif
1 #include "init.h" 2 #include "externs.h" 3 #include<stdio.h> 4 #include<signal.h> 5 #include<string.h> 6 void sigint_handler(int sig) 7 { 8 printf(" [minishell]$ "); 9 fflush(stdout);//没有( ) 10 11 } 12 void setup(void) 13 { 14 signal(SIGINT,sigint_handler); 15 signal(SIGQUIT,SIG_IGN); 16 } 17 18 void init(void) 19 { 20 21 memset(cmd,0,sizeof(cmd)); 22 int i=0; 23 for(i=0;i<PIPELINE;i++) 24 { 25 cmd[i].infd=0;//初始命令的输入默认为标准输入0。 26 cmd[i].outfd=1;//初始所有输出默认标准输出1 27 } 28 memset(&cmdline,0,sizeof(cmdline)); 29 lineptr=cmdline; 30 avptr=avline; 31 memset(avline,0,sizeof(avline)); 32 memset(infile,0,sizeof(infile)); 33 memset(outfile,0,sizeof(outfile)); 34 cmd_count=0; 35 backgnd=0; 36 lastpid=0; 37 append=0; 38 printf("[minishell]$ "); 39 fflush(stdout);//无 40 }
shell_loop的主循环在parse.h和parse.c这两个命令解析模块中:
#ifndef _PARSE_H_ #define _PARSE_H_ //定义函数接口 void shell_loop(void);//shell循环 int read_command(void); int parse_command(void); int execute_command(void); int check(const char*str); #endif
#include"parse.h" #include<stdio.h> #include "def.h" #include "externs.h"//声明外部变量 #include "init.h" #include <unistd.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include "builtin.h" #include "execute.h" void get_command(int i); void getname(char *name); void print_command(); /* shell 循环 */ void shell_loop(void) { while(1) { //初始化环境 init();//每次循环前初始化cmdline和COMMOND //读取命令 if(read_command()==-1) break; //解析命令 parse_command(); //print_command();//打印命令 //执行命令 execute_command(); } printf(" exit "); } /* 读取命令,成功返回0,失败或者读到文件结束符返回-1 */ int read_command(void) { /* 按行读取命令,cmdline中包含 字符 */ if(fgets(cmdline,MAXLINE,stdin)==NULL)//利用extern来引用全局变量,将所有的extern引用声明放入头文件 return -1; return 0; } /* 解析命令:成功返回解析到的命令个数,失败返回-1 例如: cat < test.txt | grep -n public > test2.txt &这个命令 cat 先解析出来cmd[0] 以及参数cmd[0].args 然后 <输入重定向 将test.txt保存 然后 |管道 再解析命令grep到 cmd[1] 以及两个参数到cmd[1].args... */ int parse_command(void) { //开始就检测到 if (check(" ")) return 0; /*先判定是否内部命令并执行它*/ if(builtin()) return 0;//内部命令直接执行,不用解析 //cmd [< filename] [| cmd]...[or filename] [&]:方括号表示可选,省略号表示前面可以重复0次或多次 //or 可以是 > 或者 >> 输出重定向清除文件或者追加到文件尾部方式 //&是否由后台处理 //例如:cat < test.txt | grep -n public > test2.txt & if(check(" ")) return 0;//一开始回车 /*第一步:解析第一条简单语句*/ get_command(0); /*第二步:判定是否有输入重定向符*/ if(check("<")) getname(infile);//解析文件名字,check成功时,lineptr移动过所匹配的字符串 /*第三步:判定是否有管道*/ int i; for(i=1;i<PIPELINE;i++) { if(check("|")) get_command(i); else break; } /*第四步:判定是否有输出重定向符*/ if(check(">")) { //连续两个> if(check(">")) { append=1;//以追加的方式打开 } getname(outfile);//获取后面文件名,解析到全局变量outfile中 } /*第五步:判定是否有后台作业&*/ if(check("&")) backgnd=1; /*第六步:判定命令结束' '*/ if(check(" ")) { cmd_count=i;//总的命令个数 cat grep... return cmd_count; } //解析失败 else { fprintf(stderr,"Command line systax error "); return -1; } return 0; } /* 执行命令:成功返回0,失败返回-1 */ int execute_command(void) { /*执行外部命令*/ execute_disk_command(); return 0; } //例如cmd[] ls | wc -w // avline[] ls wc -w 参数列表数组 //COMMAND cmd[PIPELINE]; cmd[i]是第i条命令,cmd[i].args[j]:是第i条命令的第j个参数 //解析命令至cmd[i],提取cmdline命令参数到avline数组中,并且将COMMAND结构中的args[]中的每个指针指向avline对应参数字符串 void get_command(int i) { int j=0; int inword;//针对cat 之后有无参数。如果无参数直接遇到<,inword就不会置1.那么switch遇到<直接args[1]为NULL //cat < test.txt | grep -n public > test2.txt & while(*lineptr!='