自己動手利用簡單的C技術,實現強大的Shell

22599 人瀏覽 | 時間: 2016-06-11 22:26:28 | 作者: asd1123509133

    在開始代碼之前,先來普及一下什么是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在計算機的位置和用途

    

直接上代碼.

#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#%[email protected]%s]>", sum, hostname, buf);	
	sum++;
}

/*
 * 用戶輸入的內容處理
 * @reminder 	info 傳入的結構體
 * 	return:	
 * 		成功:EXIT_SUCCESS
 * 		失?。篍XIT_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;
}




標注
評論
站內搜
百度搜
傳送到手機
手機掃碼,識別文字,完成傳送x
加載中...
標注內容x
加載中...
添加標注x
收藏 0 點贊 0

相關閱讀

趣头条自媒体赚钱么 燕赵风采河北20选五 正常期货配资手续费标准 贵州十一选五下期预测 配资平台哪个好皆选杨方配资 胜盈配资 上海11选5专家预测 pk10技巧规律 青海快三有没有平台有 黑马股票推荐网站 福彩排列7开奖