操作系统 shell模拟编辑器

本文介绍了操作系统中的shell,一种命令解析器,主要用于Unix/Linux系统。内容包括shell的概念、程序示例,以及一系列关于shell命令、键盘事件监听、输入输出重定向、进程管理等的资源链接,旨在帮助读者深入理解和实现shell编辑器。

一、什么是shell
在计算机科学中,Shell俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。
——《百度百科》

shell命令编辑器更多地用于unix/linux系统之中。

二、节选程序

int main()
{
/****************************声明程序中用到的函数*****************************/
	int redirect();                                     /*重定向命令的处理函数*/
	int pipel();                                          /*管道命令的处理函数*/
	int get_line();                                           /*读取一行的函数*/
	int is_founded();                                         /*查找命令的函数*/
	void init_environ();                                /*初始化环境变量的函数*/
	void getenviron();                                  /*初始化查找路径的函数*/
	void add_history(const char *string);/*调用上下方向键实现history命令的函数*/
	void add_his_link();                               /*记录history命令的函数*/
	void history_cmd();                                /*显示history命令的函数*/
	void cd_cmd();                                          /*处理cd命令的函数*/
	void jobs_cmd();                                      /*处理jobs命令的函数*/
	void add_node();                          /*向jobs命令的链表中增加节点函数*/
	void del_node();                          /*向jobs命令的链表中删除节点函数*/
	void ctrl_z();                                /*处理用户按下ctrl_z时的函数*/
    void setflag();                                       /*将标志位置一的函数*/
	void bg_cmd();                                          /*处理bg命令的函数*/
	void fg_cmd();                                          /*处理fg命令的函数*/
	init_environ();                 /*初始化环境变量,将查找路径至于envpath[]中,
									         //初始化history,和jobs的头尾指针*/
	while (1)
	{
		char c,*arg[20];
		char hostName[60],path_string[40];
		int i=0,j=0,k=0,is_pr=0,is_bg=0,input_len=0,path,pid=0,status=0,is_addpre=0,is_exe=0,is_absolute=0;

/****************************    设置signal信号  *****************************/
		struct sigaction action;//sigaction()函数用于改变进程接收到特定信号后的行为
		action.sa_sigaction=del_node;//指向“新式”信号处理函数的指针
		sigfillset(&action.sa_mask);//sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置
		action.sa_flags=SA_SIGINFO;//sa_flags置为SA_SIGINFO(使用新式
		sigaction(SIGCHLD,&action,NULL);
		signal(SIGTSTP,ctrl_z);

/****************************      打印提示符    *****************************/
		path=get_current_dir_name();//获得当前路径名
		gethostname(hostName,sizeof(hostName));//gethostname() : 返回本地主机的标准主机名

        sprintf(path_string,"%s",path); //path路径
		strcat(hostName,"@");
		strcat(hostName,path_string);
		strcat(hostName,"#");          //近似构成linux下的shell提示符

		char * p =readline(hostName);  //使用readline实现tab补全
		if(strlen(p)==0)
			continue;//当字符串p的长度为0时,即用户只是按下回车键,并没有输入任何字符,因此不需要对后面的操作进行处理
		add_history(p);                //调用<readline/history.h>头文件中的add_history函数实现上下方向键的history命令

/****************************     获取用户输入   *****************************/
        i=0;
        while(p[i]==' ' || p[i]=='\t'|| p[i]==EOF)
            i++;                                            //跳过空格等无用信息
        for(;i<strlen(p);i++){
            if(p[i]=='\n')
                continue;                                   //输入为空时结束本次循环打印提示符
            else {
                buf[input_len++]=p[i];
            }
        }
		buf[input_len]='\0';                                       //加上串结束符
		if(buf[0]=='.'&&buf[1]=='/')                               //判断是否为./命令
			is_exe=1;
		if(buf[0]=='/')
			is_absolute=1;
		/*分配动态存储空间,将命令从缓存拷贝到input中*/
		input=(char *) malloc(sizeof(char)*(input_len+1));
		strcpy(input,buf);

		add_his_link(input);


/****************************       解析指令     *****************************/
		/******************管道和重定向命令单独处理**************/

		for (i=0,j=0,k=0;i<=input_len;i++){
			if (input[i]=='<' || input[i]=='>' ||input[i]=='|'){
				if (input[i]=='|'){
					pipel(input,input_len);/*管道命令*/
					//add_history(input);
					free(input);
				}
				else{
					//printf("is_addpre=%d,input[%d]=%c\n",is_addpre,i+1,input[i+1]);
					if (input[i]=='>' && input[i+1]=='>')
						is_addpre=1;
					redirect(input,input_len,is_addpre);/*重定向命令*/
					//add_history(input);
					free(input);
				}
				is_pr=1;
				break;
			}
		}


		/**********************   普通命令   ***********************/
       	if (is_pr==1) continue;
       	if(is_addpre==1) continue;
       	for (i=0,j=0,k=0;i<=input_len;i++){
			if (input[i]==' ' || input[i]=='\0'){
				if (j==0)       /*这个条件略去连在一起的多个空格*/
					continue;
				else{
						buf[j++]='\0';
						arg[k]=(char *) malloc(sizeof(char)*j);
						strcpy(arg[k++],buf);/*将指令或参数拷到arg中*/
					//	printf("arg[%d]=%s\n",k-1,arg[k-1]);
						j=0;                    /*准备取下一参数*/
				}
			}
			else{
					/*如果字符串最后是“&”,将后台命令标志置一*/
					if (input[i]=='&' && input[i+1]=='\0'){
						is_bg=1;
						continue;
					}
					buf[j++]=input[i];
			}
		}


        /**********************  内部命令的处理  ********************/
		/*exit命令,退出*/
		if (strcmp(arg[0],"exit")==0) {
			//add_history(input);
			printf("Shell exit.\n");
			free(input);
			break;
		}
		/*history命令,显示history数组中保存的历史命令*/
		if (strcmp(arg[0],"history")==0) {
			//add_history(input);
			history_cmd();
			free(input);
			continue;
		}
		/*cd命令,改变当前路径*/
		if (strcmp(arg[0],"cd")==0) {
			//add_history(input);
			for (i=3,j=0;i<=input_len;i++)/*获取路径*/
				buf[j++]=input[i];
			buf[j]='\0';
			arg[1]=(char *) malloc(sizeof(char)*j);
			strcpy(arg[1],buf);/*将路径保存到arg[1]中*/
			cd_cmd(arg[1]);/*cd_cmd()函数,改变路径到指定路径*/
			free(input);
			continue;
		}
		/*jobs命令,显示现有工作*/
		if (strcmp(arg[0],"jobs")==0) {
			//add_history(input);
			jobs_cmd();/*jobs_cmd()函数,遍历链表,显示所有工作*/
			free(input);
			continue;
		}
		/*bg命令,将作业放到后台执行*/
		if (strcmp(arg[0],"bg")==0) {
			//add_history(input);
			/*获取制定的作业号,作业号在%后*/
			for (i=0;i<=input_len;i++) {
				if (input[i]=='%')
				break;
			}
			i++;
			for (;i<=input_len;i++)
				buf[j++]=input[i];
			buf[j]='\0';
			arg[1]=(char *) malloc(sizeof(char)*j);
			strcpy(arg[1],buf);/*将作业号保存在arg[1]中*/
		//	printf("arg[1]=%s\n",arg[1]);
			bg_cmd(atoi(arg[1]));/*bg_cmd命令,将指定作业放到后台运行*/
			free(input);
			continue;
		}
		/*fg命令,将作业放到前台执行*/
		if (strcmp(arg[0],"fg")==0) {
			//add_history(input);
			/*获取制定的作业号,作业号在%后*/
			for (i=0;i<=input_len;i++) {
				if (input[i]=='%')
				break;
			}
			i++;
			for (;i<=input_len;i++)
				buf[j++]=input[i];
			buf[j]='\0';
			arg[1]=(char *) malloc(sizeof(char)*j);
			strcpy(arg[1],buf);/*将作业号保存在arg[1]中*/
			printf("%s\n",arg[1]);
			fg_cmd(atoi(arg[1]));/*fg_cmd命令,将指定作业放到后台运行*/
			free(input);
			continue;
		}

/****************************    寻找命令文件    *****************************/
		if (is_pr==0 ){/*非管道、重定向命令、非./、非绝对路径*/
			/*在使用exec执行命令时,最后的参数必须是NULL指针,所以将其置空*/
			arg[k]=(char *) malloc(sizeof(char));
			arg[k]=NULL;
			if(is_exe==0 && is_absolute==0) //./可执行文件名 命令的处理
			{
				if (is_founded(arg[0])==0){/*查找arg[0]中的命令是否存在*/
				printf("This command is not founded!\n");
				for (i=0;i<=k;i++)
					free (arg[i]);
				continue;}
			}
		}
		//add_history(input);

/****************************      执行命令      ******************************/
		if ((pid=fork())==0){/*子进程*/
			if (is_bg==1)/*若为后台命令,等待父进程增加节点*/
				while (sig_flag==0)/*若sig_flag==0,等待父进程完成增加节点*/
					/*等待父进程SIGUSR1信号,表示节点已加到链表中*/
					signal(SIGUSR1,setflag);/*收到信号,setflag函数将sig_flag置一
				                               ,以跳出循环*/
			sig_flag=0;/*置零,为下一命令作准备*/

			if(is_exe==1 || is_absolute==1) //./可执行文件名 以及绝对路径命令的处理
			{
				if(execv(arg[0],arg)==-1)
					perror("execv failed");
				for (i=0;i<=k;i++)
					free (arg[i]);
				continue;
			}
		    else if(execv(buf,arg)==-1)/*执行命令*/
				perror("error");
		}
		else {/*父进程*/
			pid1=pid;/*保存子进程进程号*/
			if (is_bg==1) {/*后台命令*/
				add_node(input,pid1);/*增加节点*/
				kill(pid,SIGUSR1);/*向子进程发信号,表示节点已加进链表*/
				pid1=0;/*pid1置零,为下一命令作准备*/
			}
			if (is_bg==0)
			{
				  /*前台命令*/
				  waitpid(pid,&status,0);
				//while(waitpid(pid,&status,WNOHANG)==0)
			//	{
				//	signal(SIGINT,dosig);
			//	}

			}
		}
		if (is_bg==1) sleep(1);/*等待命令(如:ls &)输出后,再打印Shell提示符*/
		for (i=0;i<k;i++)/*释放空间*/
			free (arg[i]);
		free(input);
	}

	return 0;
}

三、友情参考文献的链接
0.secret
Linux Shell脚本面试25个经典问答
https://www.cnblogs.com/gala1021/p/8519456.html

1.shell程序
1)C语言实现一个精简的shell
https://blog.youkuaiyun.com/lishichengyan/article/details/78193131
2)Linux C实现简单的shell
https://blog.youkuaiyun.com/wqx521/article/details/50894661
3)如何实现linux的Shell下按键盘的上下方向键就可以出现以前的命令?
https://bbs.youkuaiyun.com/topics/80237574
http://bbs.chinaunix.net/forum-viewthread-tid-667725.html
4)如何实现按下Tab键自动补全匹配的字符串并输出?
http://bbs.chinaunix.net/thread-656271-1-1.html
5)linux 读取键盘上下左右键小程序
https://blog.youkuaiyun.com/cean1024/article/details/72903069
6)shell命令解释器实验报告
http://www.doc88.com/p-8015468209269.html
7)linux的基本操作(shell 脚本的基础知识)
https://www.cnblogs.com/zhang-jun-jie/p/9266844.html

2.shell命令
1)Linux Shell常用shell命令
https://www.cnblogs.com/zhangziqiang/p/7478075.html
2)什么是bash shell的内建(build in)命令
https://blog.youkuaiyun.com/trochiluses/article/details/9094517
3)在linux下C语言实现对键盘事件的监听
https://blog.youkuaiyun.com/alangdangjia/article/details/27697721
4)Linux下键盘键值对应input event下的code值表
https://blog.youkuaiyun.com/cyf15238622067/article/details/78431709
5)Linux命令自己总结
https://www.cnblogs.com/200911/p/4012161.html
6)真正的Linux命令大全
https://wenku.baidu.com/view/3d63178a112de2bd960590c69ec3d5bbfd0adaed.html
7)在linux中使用getch()函数
https://www.xuebuyuan.com/3221739.html
8)linux环境下,清空history中记录的历史命令
https://www.cnblogs.com/chuanzhang053/p/9030834.html
9)linux 清空历史命令
https://www.cnblogs.com/hellojesson/p/9984518.html
10)Linux下的输入/输出重定向
https://www.cnblogs.com/fengkang1008/p/4652265.html
11)Linux中重定向
https://www.cnblogs.com/crazylqy/p/5820957.html
12)Linux的bg和fg命令简单介绍
https://www.cnblogs.com/oxspirt/p/8583307.html
13)Unix或Linux中&、jobs、fg、bg等命令的使用方法
https://blog.youkuaiyun.com/jimmy1357/article/details/46826133
14)linux:帮助命令help、man、info
https://www.cnblogs.com/kumata/p/8993914.html
15)Linux命令类型和执行顺序-type命令
https://blog.youkuaiyun.com/u010599211/article/details/85335864
16)Linux type命令的用法
https://blog.youkuaiyun.com/jiaxinhong/article/details/82826314
17)使用命令把前台和后台的进程互相转换
https://www.cnblogs.com/liu1026/p/8962440.html
18)Linux shell中运行可执行程序后加上&的作用
https://blog.youkuaiyun.com/oscarjulia/article/details/70272445
19)GNU Readline 库及编程简介
https://www.cnblogs.com/hazir/p/instruction_to_readline.html
20)readline库的简单使用
https://blog.youkuaiyun.com/xuancbm/article/details/81436681

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值