用c实现的简单linux shell
2015-06-25 20:10
483 查看
直接上代码吧!注释写的很清晰。
首先头文件:
终端提示符函数:
语法分析函数:
附加命令函数:
首先头文件:
// 头文件 #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <pwd.h> #include <signal.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/stat.h> #define MAX_PROMPT 1024//提示符最大长度 #define MAXLINE 4096 #define MAXARG 20//参数最多个数 struct parse_info; struct passwd *pwd; char *buffer; void terminal_prompt(char*);//终端提示符函数 int read_command(char **,char **,char*);//读取命令函数 int builtin_command(char *,char **);//内建命令函数 int parsing(char **,int,struct parse_info *);//语法分析函数 void execute(void);//执行函数 void child_process_handler(int sig);//处理后台运行的进程 #define STRUCT_PARSE_INFO #define BACKGROUND 1 //后台执行标志位 #define IN_REDIRECT 2 //输入重定向标志位 #define OUT_REDIRECT 4 //输出重定向标志位 #define IS_PIPED 8//管道标志位 #define TRUE 1 #define MAXPIDTABLE 1024 pid_t child_process_pid[MAXPIDTABLE];//子进程号数组 struct parse_info { int flag; //表明使用了哪些功能的标志位 char* in_file;//输入重定向的文件名 char* out_file;//输出重定向的文件名 char* command2;//命令2 char** parameters2;//命令2的参数表 };main函数:
//mysh主程序 #include "myshell.h" int main() { int i; //初始化 child_process_pid for(i=0;i<MAXPIDTABLE;i++) child_process_pid[i] = 0; //printf("mysh starting...\n"); execute(); return 0; }
终端提示符函数:
//终端提示符函数 #include "myshell.h" const int max_name_len = 256; const int max_path_len = 1024; void terminal_prompt(char *prompt) { extern struct passwd *pwd;//用户账号信息文件 char hostname[max_name_len];//主机名 char pathname[max_path_len];//路径 int length; pwd = getpwuid(getuid());//获得用户信息 getcwd(pathname,max_path_len);//获得路径 gethostname(hostname,max_name_len); sprintf(prompt,"【mysh】%s@%s:",pwd->pw_name,hostname);//将用户名和主机名添加到提示符 length = strlen(prompt); if(strlen(pathname) < strlen(pwd->pw_dir) || strncmp(pathname,pwd->pw_dir,strlen(pwd->pw_dir))!=0) sprintf(prompt+length,"%s",pathname); else sprintf(prompt+length,"~%s",pathname+strlen(pwd->pw_dir));//用户路径用~代替,当前路径在用户路径下 length = strlen(prompt); if(geteuid()==0)//root用户 sprintf(prompt+length,"# "); else//普通用户 sprintf(prompt+length,"$ "); return; }读命令函数:
//读入命令函数 //从用户输入中读取命令和参数,分别放入command[]和parameters[][]中,作为exec族函数执行。 #include "myshell.h" int read_command(char **command,char **parameters,char *prompt) { printf("%s",prompt);//输出终端提示符 if(fgets(buffer,MAXLINE,stdin) == NULL)//从键盘读入字符存入buffer { printf("\n"); exit(0); } if(buffer[0] == '\0') return -1; char *pStart,*pEnd; int count = 0; int isFinished = 0; pStart = pEnd = buffer; while(isFinished == 0) { while((*pEnd == ' ' && *pStart == ' ') || (*pEnd == '\t' && *pStart == '\t'))//忽略空格和tab字符 { pStart++; pEnd++; } if(*pEnd == '\0' || *pEnd == '\n')//到字符结尾 { if(count == 0)//读入的合法字符为0 return -1; break; } while(*pEnd != ' ' && *pEnd != '\0' && *pEnd != '\n') pEnd++; if(count == 0) { char *p = pEnd; *command = pStart;//命令存入command while(p!=pStart && *p !='/') p--; if(*p == '/')//转义字符 p++; parameters[0] = p;//参数存入parameters count += 2; } else if(count <= MAXARG) { parameters[count-1] = pStart; count++; } else { break; } if(*pEnd == '\0' || *pEnd == '\n')//到输入字符末尾 { *pEnd = '\0'; isFinished = 1; } else { *pEnd = '\0'; pEnd++; pStart = pEnd; } } parameters[count-1] = NULL; return count; }
语法分析函数:
//语法分析函数 //主要处理后台执行,重定向,管道功能实现 #include "myshell.h" int parse_info_init(struct parse_info *info) { info->flag = 0;//表明使用了哪些功能的标志位 info->in_file = NULL;//输入重定向的文件名 info->out_file = NULL;//输出重定向的文件名 info->command2 = NULL;//命令2 info->parameters2 = NULL;//命令2的参数表 return 0; } int parsing(char **parameters,int ParaNum,struct parse_info *info) { int i; parse_info_init(info); if(strcmp(parameters[ParaNum-1],"&") ==0)//后台执行 { info->flag |= BACKGROUND; parameters[ParaNum-1] = NULL; ParaNum--; } for(i=0;i<ParaNum;) { if(strcmp(parameters[i],"<")==0)//输入重定向 { info->flag |= IN_REDIRECT; info->in_file = parameters[i+1];//输入重定向的文件名 parameters[i] = NULL; i+=2; } else if(strcmp(parameters[i],">")==0)//输出重定向 { info->flag |= OUT_REDIRECT; info->out_file = parameters[i+1];//输出重定向的文件名 parameters[i] = NULL; i+=2; } else if(strcmp(parameters[i],"|")==0)//管道 { char* pCh; info->flag |= IS_PIPED; parameters[i] = NULL; info->command2 = parameters[i+1]; info->parameters2 = ¶meters[i+1]; //检查parameters2[0]中是否有转义字符 for(pCh = info->parameters2[0]+strlen(info->parameters2[0]);pCh!=&(info->parameters2[0][0]) && *pCh!='/';pCh--); if(*pCh == '/') pCh++; info->parameters2[0] = pCh; break; } else i++; } return 1; }
附加命令函数:
//内建命令函数 //实现了exit、cd命令 #include "myshell.h" int builtin_command(char *command, char **parameters) { extern struct passwd *pwd; if(strcmp(command,"exit")==0 || strcmp(command,"quit")==0){//实现exit和quit//都是退出命令 exit(0); } else if(strcmp(command,"help") == 0) { printf("copyright reserved by weiqianghu.\n"); return 1; } else if(strcmp(command,"cd")==0)//实现cd命令 { char *cd_path = NULL; if(parameters[1][0] == '~')//如果是根目录 { cd_path = malloc(strlen(pwd->pw_dir)+strlen(parameters[1]));//分配内存 if(cd_path == NULL) { printf("cd:malloc failed.\n");//内存分配失败 } else{ strcpy(cd_path,pwd->pw_dir); strncpy(cd_path+strlen(pwd->pw_dir),parameters[1]+1,strlen(parameters[1])); } } else//非根目录 { cd_path = malloc(strlen(parameters[1]+1));//分配内存 if(cd_path == NULL) { printf("cd:malloc failed.\n");//内存分配失败 } else strcpy(cd_path,parameters[1]); } if(chdir(cd_path)!= 0) printf("-myshell: cd: %s:%s\n",cd_path,strerror(errno)); else return 1; free(cd_path);//释放分配的内存空间 } return 0; }执行函数:
// mysh执行函数 #include "myshell.h" void execute(void) { int status,i; char *command = NULL;//命令 char **parameters;//参数 int ParaNum;//参数个数 char prompt[MAX_PROMPT];//提示 struct parse_info info; pid_t ChdPid,ChdPid2; parameters = malloc(sizeof(char *)*(MAXARG+2)); buffer = malloc(sizeof(char) * MAXLINE); if(parameters == NULL || buffer == NULL)//内存分配失败 { printf("mysh error:malloc failed.\n"); return; } if(signal(SIGCHLD,child_process_handler) == SIG_ERR)//父进程调用将SIGCHLD绑定到sig_handler()函数,这样fork出来的子进程在终止时会自动清理掉 perror("signal() error"); while(TRUE) { int pipe_fd[2],in_fd,out_fd; terminal_prompt(prompt);//终端提示符函数 ParaNum = read_command(&command,parameters,prompt);//读入命令 if(-1 == ParaNum) continue; ParaNum--;//命令数减一 parsing(parameters,ParaNum,&info);//对于后台执行、管道、重定向的初始化操作 if(builtin_command(command,parameters))//内建命令 continue; if(info.flag & IS_PIPED) //管道 { if(pipe(pipe_fd)<0)//pipe_fd[0]:读管道;pipe_fd[1]:写管道 { printf("mysh error:pipe failed.\n"); exit(0); } } if((ChdPid = fork())!=0) //mysh主进程 { if(info.flag & IS_PIPED) { if((ChdPid2=fork()) == 0) //要求管道进程必须为mysh进程的子进程 { close(pipe_fd[1]); close(fileno(stdin)); dup2(pipe_fd[0], fileno(stdin)); close(pipe_fd[0]); if(execvp(info.command2,info.parameters2)==-1) printf("%s\n",strerror(errno)); } else { close(pipe_fd[0]); close(pipe_fd[1]); waitpid(ChdPid2,&status,0); //wait command2 } } if(info.flag & BACKGROUND)//后台执行 { printf("后台进程pid:%u\n",ChdPid); int i; for(i=0;i<MAXPIDTABLE;i++) if(child_process_pid[i]==0){ child_process_pid[i] = ChdPid; //存储子进程号 break; } if(i==MAXPIDTABLE) printf("Too much background processes\nThere will be zombine process"); } else { waitpid(ChdPid,&status,0);//等待命令1结束 } } else //mysh的子进程 { if(info.flag & IS_PIPED) //命令2不为空 { if(!(info.flag & OUT_REDIRECT)) // 仅管道 { close(pipe_fd[0]); close(fileno(stdout)); dup2(pipe_fd[1], fileno(stdout)); close(pipe_fd[1]); } else //输出重定向和管道 { close(pipe_fd[0]); close(pipe_fd[1]);//关闭管道,发送一个EOF信号给command2 if(info.flag & OUT_REDIRECT) out_fd = open(info.out_file, O_WRONLY|O_CREAT|O_TRUNC, 0666); else out_fd = open(info.out_file, O_WRONLY|O_APPEND|O_TRUNC, 0666); close(fileno(stdout)); dup2(out_fd, fileno(stdout)); close(out_fd); } } else { if(info.flag & OUT_REDIRECT) // 仅输出重定向 { out_fd = open(info.out_file, O_WRONLY|O_CREAT|O_TRUNC, 0666); close(fileno(stdout)); dup2(out_fd, fileno(stdout)); close(out_fd); } } if(info.flag & IN_REDIRECT)//输入重定向 { in_fd = open(info.in_file, O_CREAT |O_RDONLY, 0666);//0666表示, 拥有者,组,其他人都拥有可读可写权限 close(fileno(stdin)); dup2(in_fd, fileno(stdin)); close(in_fd); } if(execvp(command,parameters)==-1) printf("%s\n",strerror(errno)); } } free(parameters);//释放parameters free(buffer);//释放buffer }后台进程处理函数:
//处理后台运行进程函数 #include "myshell.h" void child_process_handler(int sig)//用于清除后台运行的子进程,以防其变成僵尸进程 { pid_t pid; int i; for(i=0;i<MAXPIDTABLE;i++) if(child_process_pid[i] != 0) //仅处理后台执行命令 { pid = waitpid(child_process_pid[i],NULL,WNOHANG); if(pid > 0)//正常返回 { printf("\n后台进程[%d]已完成.\n",pid); child_process_pid[i] = 0; //清除 } else if(pid < 0)//出错 { if(errno != ECHILD) perror("waitpid error"); } } return; }
相关文章推荐
- xshell十大技巧
- SSH 学习记录及在SSH模式下使用XShell连接服务器
- linux学习第二节 SHELL脚本语法
- Linux Shell之表达式
- shell学习二十六天----变量与算数
- git is not in the sudoers file.This incident will be reported
- shell 脚本编译c/c++
- Shell脚本:使用rsync备份文件/目录
- Shell基于模式匹配的字符串操作
- shell脚本基础知识(上)
- Linux下创建shell脚本文件
- bash审计
- 进入adb shell后
- CentOS6.5安装Tab增强版:bash-completion
- 'The WinRM Shell client cannot process the request' 错误, Exchange 2013 PowerShell
- shell 的理解及遇到的问题
- Shell中if的基本语法和常见判断用法
- APDU指令返回码及其代表含义
- shell截取字符串的方法
- 使用DOS shell自动检测JRE环境变量