Linux C编程实战——第七章 进程控制_项目实现_自写shell
来源:互联网 发布:湖北农村金融数据 编辑:程序博客网 时间:2024/05/17 06:24
自写shell
基本介绍:该shell命令目前实现了cd命令,tab补全,history历史,和外部命令。
基本实现原理 : 根据输入命令,解析参数,然后再fork一个进程,在进程中利用execvp运行外部命令,其cd命令根据chdir()函数实现,tab补全和历史利用readdir()函数及库实现。
这是大概思想,下面直接上代码(已经加上详细注释):
注意:在运行该程序前要安装readline对应的库,安装流程如下:
http://blog.csdn.net/chudongfang2015/article/details/52068026
myshell.c:
/*************************************************************************> File Name: meshell.c> Author:chudongfang > Mail:1149669942@qq.com > Created Time: 2016年07月28日 星期四 09时31分43秒 ************************************************************************/#include <pwd.h>#include <fcntl.h>#include <dirent.h>#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <sys/resource.h>#include <sys/wait.h>#include <stdio.h>#include <string.h>#include <math.h>#include <readline/readline.h>#include <readline/history.h>#include <stdlib.h>#define MAX_SIZE 256#define normal 0 //正常命令#define out_redirect 1 //重定向输出#define in_redirect 2 //重定向输入#define have_pipe 3 //管道命令void do_cmd(int argcout,char arglist[100][256],int his_count,char history[][256]);int find_command(char *command); void explain_input(char *buf,int *argcout,char arglist[100][256]);void get_input(char *buf,char *path,int *his_count,char history[][256]);struct passwd *pw; //用户信息结构体int main(int argc,char *argv[]){int i; char history[256][256];//存储历史 int his_count=0; //历史的数量 int argcout = 0; //命令计数器char arglist[100][256]; //储存命令char **arg = NULL; char *buf = NULL; //临时字符数组char path [MAX_SIZE]; //进程所在路径 memset(history,0,sizeof(history));buf = (char *)malloc(256); if(buf == NULL){perror("malloc failed");exit(-1);}while(1){memset(buf,0,256); memset(path,0,MAX_SIZE); if (getcwd( path, MAX_SIZE) == NULL) //得到当前路径{ printf("read path error!\n"); exit(-1);}//获取当前目录绝对路径,即去掉程序名get_input(buf,path,&his_count,history); //输出路径提示,并得到输入命令 while(strlen(buf) == 0) { get_input(buf,path,&his_count,history); //无输入,继续接受输入}if( strcmp(buf,"exit\n") == 0 || strcmp(buf,"logout\n") == 0) //exit 和 logout 退出shellbreak;for(i=0;i<100;i++) //初始化命令 {arglist[i][0]='\0'; }argcout = 0; //命令个数为0explain_input(buf,&argcout,arglist); //把输入解析成命令 do_cmd(argcout,arglist,his_count,history); //对命令进行分析,操作}if(buf != NULL){free(buf);buf = NULL;} exit(0);}/**************************************** * 函数功能: 实现提示,接受输入,tab 补全,记录输入历史 * * 函数参数: * 参数1 :输入 * 参数2 :解析后的命令 ***************************************************/void get_input(char *buf,char *path,int *his_count,char history[][256]){ int count=0,i,len;uid_t uid; char *temp; char str[500]; uid = getuid (); //得到uid pw = getpwuid (uid); //得到用户信息 if (!pw) { printf ("Couldn't find out about user %d./n", (int)uid); return ; } sprintf(str,"\033[;31m %s@\033[0m\033[;31mmyshell\033[0m\033[;32m %s\033[0m\033[;33m$\033[0m",pw -> pw_name,path); //加上颜色,合成提示语句str temp = (char* )malloc(sizeof(256)); //malloc 申请空间,用于readline 注意: readline 返回值只能用malloc申请的指针接受 memset(temp,0,sizeof(temp)); //初始化空间 temp = readline(str); //调用readline库函数 实现补全 ,调用方法在另一篇博客 strcpy(buf, temp); //把接受到的存入到buf里面 //历史记录,如果输入的全部是空格或是换行,则不存储 len = strlen(buf); for(i=0;i < len; i++) if(buf[i] == ' '||buf[i] == '\n') count++; if(count != len) { //如果输入的命令与前一个重复,则不把其记录到history里面 if(*his_count != 0){ if(strcmp(history[*his_count-1],buf) != 0){ strcpy(history[*his_count],buf); *his_count = *his_count + 1; add_history(buf); } } else { strcpy(history[*his_count],buf); *his_count = *his_count + 1; add_history(buf); } } free(temp);}/************************************************* * 函数功能 :根据输入的内容解析出命令 * 函数参数: * 1 :输入的内容 * 2 :命令个数 * 3 :存储命令数组 *************************************************/void explain_input(char *buf,int *argcout,char arglist[100][256]){int number = 0 ;char *p = buf;char *q = buf;while(1){if(p[0] == '\n' || p[0] == '\0')break;if(p[0] == ' ')p++;else{q=p;number = 0;while( (q[0] != ' ') && q[0] != '\0' &&q[0] != '\n'){number++;//代表单个命令的长度q++;}strncpy(arglist[*argcout],p,number+1); //把单个命令解析出来arglist[*argcout][number] = '\0'; //最后加上结尾*argcout = *argcout + 1; p=q;}}}/***************************************************** * 函数功能 :命令分类,并执行命令 * 参数: * 1:命令个数 * 2:存储命令数组 * **************************************************/void do_cmd(int argcout,char arglist[100][256],int his_count,char history[][256]){int flag=0;int how =0;int backgroud = 0;int status;int i;int fd;char * arg[argcout + 4];char * argnext[argcout + 4];char *file;char home[256];pid_t pid;for(i=0;i<argcout;i++)arg[i]=(char *)arglist[i]; arg[argcout] = NULL; //解析是否是后台运行for(i=0;i < argcout;i++){if(strncmp(arg[i],"&",1) == 0){if(i == argcout - 1){backgroud = 1;arg[argcout - 1] =NULL;break;}else {printf("wrong command\n");return ;}}}for(i=0 ;arg[i] != NULL; i++){//解析重定向输入 if(strcmp(arg[i],">") == 0){flag++;how = out_redirect;if (arg[i+1] == NULL)flag++;} //解析重定向输出if( strcmp(arg[i],"<") == 0){flag++;how = in_redirect;if(i == 0)flag++;} //解析管道命令if( strcmp(arg[i],"|") == 0){flag++;how = have_pipe;if(arg[i+1] == NULL)flag++;if( i == 0)flag++;}}if(flag > 1){printf("wrong command !\n");return ;}/**************解析重定向输出文件名*******************/if( how == out_redirect){for(i=0;arg[i]!=NULL;i++){if(strcmp(arg[i],">") == 0){file = arg[i+1];arg[i] = NULL;}}}/*******************解析重定向输入文件名**********************/if(how == in_redirect){for(i=0; arg[i] != NULL;i++){if(strcmp(arg[i],"<") == 0){file = arg[i+1];arg[i] = NULL;}}}/***************解析管道命令参数*******************/if(how == have_pipe){for( i=0; arg[i]!=NULL ;i++){if(strcmp(arg[i],"|") == 0){arg[i] = NULL;int j;for(j=i+1;arg[j]!=NULL;j++){argnext[j-i-1] = arg[j];}argnext[j-i-1] = arg[j];break;}}} // 如果命令为history 则输出历史。 if(strcmp(arg[0],"history") == 0&& argcout == 1){ printf("tolal num %d :\n",his_count); for(i=0;i<his_count;i++){ printf("%d\t%s\n",i+1,history[i]); } goto loop; }/*********************CD命令****************************///cd命令为内嵌命令,,因为chdir只能改变其本身进行的目录 if(strcmp(arg[0],"cd") == 0){sprintf(home,"/home/%s/",pw->pw_name); if(argcout == 1 || argcout == 2 && strcmp(arg[1],"~") == 0) //特殊情况判断{ chdir(home); goto loop;} if(chdir(arg[1]) == -1){ perror("chdir"); } goto loop;}//fork一个子进程,执行参数if( (pid = fork()) < 0){printf("fork error\n");return ;}switch(how){/***************************普通命令*************************/case 0: //子进程if(pid == 0){if( !(find_command(arg[0])) ){printf("%s :command not found !\n",arg[0]);exit(0);}execvp(arg[0],arg);exit(0);}break;/************************重定向输入命令**************************/case 1:if(pid == 0){if( !(find_command(arg[0])) ){printf("%s :command not found !\n",arg[0]);exit(0);} //打开一个文件fd = open(file,O_RDWR | O_CREAT | O_TRUNC,0644);//把屏幕输出流给文件 dup2(fd,1);execvp(arg[0],arg);//加载参数命令exit(0);}break;/**********************重定向输出命令**************************/ case 2:if(pid == 0){if( !(find_command(arg[0])) ){printf("%s :command not found !\n",arg[0]);exit(0);} //打开文件fd = open(file,O_RDONLY);//把键盘输入流给文件 dup2(fd,0);execvp(arg[0],arg); close(fd);exit(0);}break;/**********************管道命令*************************/case 3:if(pid == 0){int pid2;int status2;int fd2; //fork一个子进程,运行管道符前命令if( (pid2 = fork()) < 0){printf("fork2 error\n");return ;}else if(pid2 == 0){if(!(find_command)(arg[0])){printf("%s :command not found !\n",arg[0]);exit(0);} //打开文件,将屏幕输出流1 给 fd2,其向屏幕输出的数据就存储到文件当中 if((fd2 = open("/tmp/tempfile",O_WRONLY | O_CREAT | O_TRUNC ,0644)) == -1) printf("open /tmp/tempfile failed!");dup2(fd2,1); execvp(arg[0],arg); close(fd2); exit(0);} //等待管道符前面命令执行完 if(waitpid(pid2,&status2,0) == -1){printf("wait for child process error\n");} //管道后命令开始执行 if(!(find_command)(argnext[0])){printf("%s :command not found !\n",argnext[0]);exit(0);} if((fd2 = open("/tmp/tempfile",O_RDONLY)) == -1) { perror("Open"); }dup2(fd2,0);execvp(argnext[0],argnext);if(remove("/tmp/tempfile") == -1)//移除文件perror("remove");exit(0);}break;default :break;}/**********************杀死父进程,后端运行********************************/ if(backgroud ==1 && pid != 0){ printf("[process id %d]\n",pid); exit(0); }/************************父进程等待子进程退出******************************/ if(waitpid(pid,&status,0) == -1){ printf("wait for child process error!\n"); }loop: ;}int find_command(char *command){DIR *dp;struct dirent* dirp;char *path[]={"./","/bin","/usr/bin",NULL};//在一下路径下,找可执行程序if(strncmp(command,"./",2) == 0)command =command + 2;int i=0;while(path[i]!=NULL){if((dp = opendir(path[i])) == NULL){//打开目录printf("can not open /bin\n");}while((dirp = readdir(dp)) != NULL){ //比较是否有相同的文件if(strcmp(dirp -> d_name,command) == 0){closedir(dp);return 1;}}closedir(dp); i++;}return 0;}
Makefile:
cc = gccOBJ = myshellall :$(cc) -c ./*.c$(cc) -o $(OBJ) ./*.o -I /usr/lib/x86_64-linux-gnu/libreadline.so -lreadline -ltermcap -grm -rf *.oclean:rm -rf *.o
0 0
- Linux C编程实战——第七章 进程控制_项目实现_自写shell
- Linux C编程实战——第六章 文件操作_项目实现_自写ls命令
- 自写项目——实现tesseract-ocr功能_项目规划
- 自写项目——实现tesseract-ocr功能_初步socket实现
- 读书笔记——《UNIX环境高级编程》第七章_ 进程环境
- (Linux)Shell编程_笔记
- Linux下的C编程实战之三进程控制
- linux c 编程实战:进程控制总结(一)
- linux c 编程实战:进程控制总结(二)
- linux c 编程实战:进程控制总结(三)
- linux c 编程实战:进程控制总结(四)
- 02_第七章_视图控制对象
- linux命令行与shell脚本大全_第十五章_控制脚本
- C++primer_第七章_类_学习跟踪
- Linux下的C编程实战(三)----进程控制与进程通信编程
- Linux下的C编程实战(三)――进程控制与进程通信编程
- Linux下的C编程实战(三)――进程控制与进程通信编程
- Linux入门学习-SHELL编程基础_第四章
- C语言考试
- java之文件拷贝与BufferedXXX装饰类
- HDU 1517 A Multiplication Game(博弈)
- 在打开CAD文件的时候会出现死机的状态
- Keystone controller.py & routers.py代码解析
- Linux C编程实战——第七章 进程控制_项目实现_自写shell
- C++11:基于std::queue和std::mutex构建一个线程安全的队列
- Jump Game
- Keepalived健康检查方式配置
- hdu 2579 Dating with girls(2)
- UISwitch控件-“应用小结”
- Find a way<hdoj2612>
- 2118数据结构实验之链表三:链表的逆置
- hadoop简单实例-WordCount