在开始代码之前,先来普及一下什么是Shell. Shell英文又称壳层.在计算机中,是指"提供用户使用界面"的"系统"软件,通常指的是命令行界面的命令解析器.一般来说,这个词是指操作系统中,提供访问内核所提供之服务的程序.不过这个词也拿来指应用软件,或是任何在特定组件外围的"软件"...省略300字.....

    

    常用的Shell分类:

    • bash: 是GNU的Bourne Again Shell,是GNU操作系统上默认的Shell

    • Korn Shell: 是对Bourne Shell的发展,在大部分内容上与Bourne Shell(Bash)兼容

    • C Shell: 是SUN公司Shell的BSD版本

    • Z Shell: Z是最后一个字母,也就是终极Shell.它集成了Bash ksh的重要特性,同时又增加了自己独有的特性.


    来看看Shell在计算机硬件和软件中的位置

  •         wKiom1dcG0ySUTQpAADHHF1XCqk040.png-wh_50

       

    直接上代码.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <errno.h>
    
    typedef struct __Info{
    	char		buf[1024];	//用户输入信息
    	unsigned int	pipeNum;	//统计管道的个数
    }Info;
    
    //记录主机名 默认localhost
    static char hostname[1024] = "localhost";
    
    //错误提示
    int sys_err(const char* err){
    	perror(err);
    	exit(EXIT_FAILURE);
    }
    
    //消息提示
    void display(){
    	static unsigned int sum = 1;	//记录输入次数
    	char buf[1024] = { 0 };
    	//获取当前路径
    	getcwd(buf, sizeof(buf));	
    	printf("[#%d#%s@%s]>", sum, hostname, buf);	
    	sum++;
    }
    
    /*
     * 用户输入的内容处理
     * @reminder 	info 传入的结构体
     * 	return:	
     * 		成功:EXIT_SUCCESS
     * 		失败:EXIT_FAILURE
    */
    int handle(Info* info){
    	if(NULL == info){
    		printf("func %s err: [NULL == info]\n", __FUNCTION__);
    		exit(EXIT_FAILURE);
    	}
    
    	char* p = info->buf;
    	//统计管道的个数
    	while(NULL != (p = strchr(p, '|'))){
    		info->pipeNum++;
    		p++;
    	}
    	p = info->buf;
    	//去除回车
    	if(NULL != (p = strchr(p, '\n'))){
    		*p = '\0';
    	}
    
    	return EXIT_SUCCESS;
    }
    
    //字符替换
    int replate(char* str, const char src, const char des){
    	if(NULL == str){
    		printf("func %s error: [NULL == str]\n", __FUNCTION__);
    		exit(EXIT_FAILURE);
    	}
    	char* p =str;
    	while(*p){
    		if(src == *p){
    			*p = des;
    		}
    		p++;
    	}
    	return EXIT_SUCCESS;
    }
    
    //命令解析
    int resolveRun(char* ptr, char** argv){
    	if(NULL == ptr || NULL == argv){
    		printf("func %s error:[NULL == ptr || NULL == argv]\n", __FUNCTION__);
    		exit(EXIT_FAILURE);
    	}
    	int i = 0;
    	int fd;
    	char* inptr = NULL;
    	char* p = strtok_r(ptr, " ", &inptr);
    	argv[i++] = p;
    	while(NULL != (p = strtok_r(NULL, " ", &inptr))){
    		argv[i++] = p;
    	}
    	
    	//判断是否有重定向
    	i--;
    	while(i){
    		if(0 == strcmp(argv[i], ">")){
    			fd = open(argv[i+1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
    			if(0 > fd){
    				sys_err("func resolveRun() open error: ");
    			}
    			//备份输出至1023描述符
    			dup2(STDOUT_FILENO, 1023);
    			dup2(fd, STDOUT_FILENO);
    			argv[i] = NULL;
    			close(fd);
    			//找到第一个>
    			int n = 1;
    			while(n < i){
    				if(0 == strcmp(argv[n], ">")){
    					argv[n] = NULL;
    					break;
    				}
    				n++;
    			}
    			break;
    		}
    
    		if(0 == strcmp(argv[i], ">>")){
    			fd = open(argv[i+1], O_APPEND|O_CREAT|O_WRONLY, 0644);
    			if(0 > fd){
    				sys_err("func resolveRun() open error: ");
    			}
    			dup2(STDOUT_FILENO, 1023);
    			dup2(fd, STDOUT_FILENO);
    			argv[i] = NULL;
    			close(fd);
    			
    			//找到第一个>
    			int n = 1;
    			while(n < i){
    				if(0 == strcmp(argv[n], ">>")){
    					argv[n] = NULL;
    					break;
    				}
    				n++;
    			}
    			break;
    		}
    		
    		if(0 == strcmp(argv[i], "<")){
    			fd = open(argv[i+1], O_RDONLY);
    			if(0 > fd){
    				sys_err("func resolveRun() open error: ");
    			}
    			
    			char buf[1024] = { 0 };
    			int len = 0;
    			while(0 != (len = read(fd, buf, sizeof(buf)))){
    				if(0 > len){
    					sys_err("func resolveRun() read error: ");
    				}
    				write(STDIN_FILENO, buf, len);
    				bzero(buf, sizeof(buf));
    			}
    			//向末尾输出一个结束符-1
    			putc(-1, STDIN_FILENO);
    			argv[i] = NULL;
    			close(fd);
    
    			//找到第一个>
    			int n = 1;
    			while(n < i){
    				if(0 == strcmp(argv[n], "<")){
    					argv[n] = NULL;
    					break;
    				}
    				n++;
    			}	
    			break;
    		}
    		i--;
    	}
    	
    	return EXIT_SUCCESS;
    }
    
    //拆分命令
    int seve(Info* info){
    	if(NULL == info){
    		printf("func %s err: [NULL == info]\n", __FUNCTION__);
    		exit(EXIT_FAILURE);
    	}
    	//判断是否为空数据
    	if(0 == *(info->buf)){
    		return EXIT_SUCCESS;
    	}
    	
    	pid_t 		pid;
    	pid_t		wpid;
    	char 		buf[1024] 	= { 0 };
    	char* 		p 		= buf;
    	char* 		inptr 		= NULL;
    	int 		i 		= 0;
    	int 		fd[2];
    	char* 		argv[256] 	= {NULL};
    
    	//复制原有数据
    	memcpy(buf, info->buf, sizeof(buf));
    	
    	//处理tab键
    	replate(buf, '\t', ' ');
    	//处理'号
    	replate(buf, '\'', ' ');
    	//处理"号
    	replate(buf, '\"', ' ');
    
    	for(i = 0; i <= info->pipeNum; i++){
    		if(0 == i){
    			p = strtok_r(p, "|", &inptr);
    		}else{
    			p = strtok_r(NULL, "|", &inptr);
    		}		
    		//初始化
    		bzero(argv, sizeof(argv));
    		//命令解析
    		resolveRun(p, argv);
    		
    		//判断是否是内置命令
    		if(0 == i && 0 == strcmp("cd", argv[0])){
    			if(0 > chdir(argv[1])){
    				//判断错误类型,提示用户信息
    				if(ENOENT == errno){
    					printf("-sea_bash: cd: %s: 没有那个文件或目录\n", argv[1]);	
    				}
    				if(EACCES == errno){
    					printf("-sea_bash: cd: %s: 权限不够\n", argv[1]);
    				}
    				if(ENOTDIR == errno){
    					printf("-sea_bash: cd: %s: 不是目录\n", argv[1]);
    				}
    			}
    			return EXIT_SUCCESS;
    		}
    		else if(0 == i && 0 == strcmp("pwd", argv[0])){
    			char buf[1024] = { 0 };		
    			getcwd(buf, sizeof(buf));				//获取当前工作目录
    			buf[strlen(buf)] = '\n';		 		//末尾添加换行
    			write(STDOUT_FILENO, buf, strlen(buf));	//向屏幕打印当前路径
    			return EXIT_SUCCESS;					//成功结束
    		}
    		else if(0 == i && 0 == strcmp("hostname", argv[0])){
    			//清空		
    			bzero(hostname, sizeof(hostname));			//将原来的hostname清空
    			memcpy(hostname, argv[1], strlen(argv[1]));	//重新设置hostname
    			return EXIT_SUCCESS;
    		}
    		else if(0 == i && 0 == strcmp("exit", argv[0])){
    			//结束进程
    			printf("--------------------god-bey-----------------------------\n");
    			kill(getpid(), SIGINT);		//向本进程发送结束信号
    			exit(EXIT_SUCCESS);			//直接进程退出
    		}
    	
    		//创建管道
    		if(0 > pipe(fd)){
    			sys_err("func seve() pipe error: ");
    		}
    		pid = fork();
    		if(0 > pid){
    			sys_err("func seve() fork error:");
    		}else if(0 == pid){
    			close(fd[0]);		//子进程关闭读端
    			dup2(1022, fd[0]);	//将上一个管道的读端重定向到fd[0]
    			close(1022);		//关闭1022上一个信号的读端,避免多个读端存在
    			break;				//跳出
    		}
    		
    		//还原输出描述符
    		dup2(1023, STDOUT_FILENO);	
    		//保存读端给下一个进程使用
    		dup2(fd[0], 1022);
    		close(fd[1]);
    		close(fd[0]);
    	}
    
    	//子进程处理
    	if(i != info->pipeNum+1){
    		//没有管道命令
    		if(0 != info->pipeNum){
    			if(i == info->pipeNum){
    				close(fd[1]);
    				dup2(fd[0], STDIN_FILENO);
    			}
    			if(0 == i){
    				dup2(fd[1], STDOUT_FILENO);
    			}
    			else{
    				dup2(fd[0], STDIN_FILENO);
    				dup2(fd[1], STDOUT_FILENO);
    			}
    		}
    		execvp(argv[0], argv);
    		printf("-sea_bash: %s: command not found\n", argv[0]);
    		exit(EXIT_FAILURE);
    	}
    	
    	//父进程等待子进程结束
    	if(i == info->pipeNum+1){
    		do{
    			wpid = waitpid(-1, NULL, WNOHANG);
    			if(0 < wpid){
    				i--;
    			}
    		}while(0 < i);
    	}
    	return EXIT_SUCCESS;
    }
    
    int main(int argc, char* argv[]){
    	Info info = {{0}, 0};
    	for(;;){
    		display();
    		//获取用户输入 
    		fgets(info.buf, sizeof(info.buf), stdin);
    		//信息处理
    		handle(&info);
    		//拆分命令
    		seve(&info);
    		//清空初始化
    		bzero(&info, sizeof(info));
    	}
    	return EXIT_SUCCESS;
    }