自己动手实现linux-->Shell

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

常用的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在计算机的哪里和用途

此次自定义Shell使用了多进程方式,进程间通信使用pipe(管道).

直接上代码.

#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
 * 		失败: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(getppid(), 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);        //备份管道的读端到1022,供下一个管道的使用
		close(fd[1]);             //关闭写端
		close(fd[0]);             //因为已经备份,可直接关闭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;
}

//entrance
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;
}
时间: 2024-10-08 13:34:50

自己动手实现linux-->Shell的相关文章

Linux Shell sort 指定排序第几列

ip.txt 里存储着ip信息 统计排序后取前10条 awk '{cnt[$1]++} END{for (ip in cnt) print ip":"cnt[ip]}' ip.txt | sort -k 2 -rn -t":" | head -n 10 awk '{cnt[$1]++} END{for (ip in cnt) print cnt[ip],ip}' ip.txt | sort -rn | head -n 10 sort -k  根据第几列排序  -n

linux shell脚本执行错误:bad substitution

脚本test.sh内容: #!/bin/bash read pressKey indexes=0 c=${pressKey:indexes:1} 使用调试方式执行:sh -x test.sh第3行总出现bad substitution提示信息. 百思不得其解: 于是百度,查到一条有用信息,这与linux shell使用的是/bin/sh,还是/bin/bash有关系.我的脚本中指定使用的是/bin/bash shell,但是我在调试的时候使用的是sh shell,因此调试时导致错误提示信息. 解

linux shell 数组建立及使用技巧

转自linux shell 数组建立及使用技巧 linux shell在编程方面比windows 批处理强大太多,无论是在循环.运算.已经数据类型方面都是不能比较的. 下面是个人在使用时候,对它在数组方面一些操作进行的总结. 1.数组定义 [[email protected] ~]$ a=(1 2 3 4 5)[[email protected] ~]$ echo $a1 一对括号表示是数组,数组元素用“空格”符号分割开. 2.数组读取与赋值 得到长度: [[email protected] ~

Linux shell脚本-基础学习笔记

Linux脚本能力不是太强,最近再补习下,毕竟linux shell在日常工作中还是很普遍的, 用起来更方便.省时省力. 以下是学习笔记,偏理论,后面有几个例子,供参考. shell脚本组成元素系统命令.文本处理工具(grep\sed等).变量.条件判断.循环结构和函数 -------------------------------------------- 三剑客:grep,sed,awk,还有wc,sort,head等 ------------------------------------

Linux Shell脚本攻略(1.10)

1.10 获取.设置日期和延时 很多应用程序需要以不同的格式打印日期.设置日期和时间.根据日期和时间执行某项操作.延时通常用于在程序执行过程中提供一段等待时间(比如1秒).同样的,我们也能够一多种格式打印日期,或者在命令行中设置日期.在类Unix系统中,日期被存储为一个整数,其大小为自世界标准时间起所流逝的秒数.这种计时方式称为纪元时或Unix时间. 1.10.1 获取.设置时间 以下的程序给出了多种用法: #!/bin/bash start=$(date +%s) #获取纪元时间 date #

Linux Shell脚本攻略(1.8)

1.8 使用别名 linux中的别名就相当于windows中的快捷方式,使用别名可以省去用户输入一长串命令序列的麻烦. 1.8.1 创建临时别名(快捷方式) alias new_command='command sequence' #格式说明 alias install='sudo apt-get install' #实例说明 在声明 install='sudo apt-get install'之后,就可以用install代替'sudo apt-get install'了.使用这种方式声明的别名

Linux shell脚本基础学习详细介绍(完整版)一

Linux shell脚本基础学习这里我们先来第一讲,介绍shell的语法基础,开头.注释.变量和 环境变量,向大家做一个基础的介绍,虽然不涉及具体东西,但是打好基础是以后学习轻松地前提.1. Linux 脚本编写基础◆1.1 语法基本介绍 1.1.1 开头 程序必须以下面的行开始(必须方在文件的第一行): #!/bin/sh 符号#!用来告诉系统它后面的参数是用来执行该文件的程序.在这个例子中我们使用/bin/sh来执行程序. 当编辑好脚本时,如果要执行该脚本,还必须使其可执行. 要使脚本可执

Linux shell脚本基础学习详细介绍(完整版)二

详细介绍Linux shell脚本基础学习(五) Linux shell脚本基础前面我们在介绍Linux shell脚本的控制流程时,还有一部分内容没讲就是有关here document的内容这里继续. Linux shell脚本基础已经被分成好几个部分了,这里对控制流程的内容也就马上讲完了,这是最后一部分关于here document,这里举例稍微有点复杂,我们慢慢来分析这个复杂Linux shell脚本. 6. Here documents 当要将几行文字传递给一个命令时,here docu

“Linux Shell编程”视频学习笔记

一.Linux Shell基础编程 视频1 1.1.查看你系统shell信息 $ cat /etc/shell 命令可以获取Linux系统里面有多少种shell程序 $ echo $SHELL 命令可以查看当前你所使用的shell是哪一个 1.2.查看文件信息,ls $ ls -l 查看文件信息:文件类型.文件权限.文件硬链接数.文件所属用户.文件所属组.文件大小.文件最近修改时间.文件名 1.3.改变文件权限,chmod 只有root用户或者文件的所有者能改变文件的权限 例子:$ chmod

37条常用Linux Shell命令组合

序号 任务 命令组合 1 删除0字节文件 find . -type f -size 0 -exec rm -rf {} \; find . type f -size 0 -delete 2 查看进程,按内存从大到小排列 ps -e -o “%C : %p : %z : %a”|sort -k5 -nr 3 按cpu利用率从大到小排列 ps -e -o “%C : %p : %z : %a”|sort -nr 4 打印说cache里的URL grep -r -a jpg /data/cache/*