操作系統 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.csdn.net/lishichengyan/article/details/78193131
2)Linux C實現簡單的shell
https://blog.csdn.net/wqx521/article/details/50894661
3)如何實現linux的Shell下按鍵盤的上下方向鍵就可以出現以前的命令?
https://bbs.csdn.net/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.csdn.net/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.csdn.net/trochiluses/article/details/9094517
3)在linux下C語言實現對鍵盤事件的監聽
https://blog.csdn.net/alangdangjia/article/details/27697721
4)Linux下鍵盤鍵值對應input event下的code值表
https://blog.csdn.net/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.csdn.net/jimmy1357/article/details/46826133
14)linux:幫助命令help、man、info
https://www.cnblogs.com/kumata/p/8993914.html
15)Linux命令類型和執行順序-type命令
https://blog.csdn.net/u010599211/article/details/85335864
16)Linux type命令的用法
https://blog.csdn.net/jiaxinhong/article/details/82826314
17)使用命令把前臺和後臺的進程互相轉換
https://www.cnblogs.com/liu1026/p/8962440.html
18)Linux shell中運行可執行程序後加上&的作用
https://blog.csdn.net/oscarjulia/article/details/70272445
19)GNU Readline 庫及編程簡介
https://www.cnblogs.com/hazir/p/instruction_to_readline.html
20)readline庫的簡單使用
https://blog.csdn.net/xuancbm/article/details/81436681

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章