用c实现的简单linux shell
来源:互联网 发布:vba 网页数据抓取 编辑:程序博客网 时间:2024/05/21 07:55
本博客不再更新,更多精彩内容请访问我的独立博客
直接上代码吧!注释写的很清晰。
首先头文件:
// 头文件#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 1024pid_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));elsereturn 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;}
0 0
- 用c实现的简单linux shell
- Linux C实现简单的shell
- linux c实现简单shell
- Linux 简单的shell实现
- Linux下C语言实现简单Shell
- 用C语言实现一个简单的Linux壳层(Shell)
- 用c语言的实现一个简单的交互式shell
- 利用linux下的c语言编程来简单的实现一个shell功能实现!
- Linux编程实现一个简单的Shell
- 【Linux】实现一个简单的shell
- Linux.实现一个简单的shell
- linux下shell的简单实现
- 【Linux】实现一个简单的shell
- Linux Shell命令的C语言实现
- 实现简单的shell
- 实现简单的shell
- Linux下 Mini-shell的实现(C/C++)
- Linux下C语言引用shell脚本的简单例子
- iOS网络篇---使用NSSession 实现获取XML
- [LeetCode] Jump Game II
- A Magic Lamp(RMQ)
- 北大百炼1004 Financial Management 题解
- Oracle表数据误删恢复
- 用c实现的简单linux shell
- JS练习:Dom节点的CRUD操作
- java中的多线程
- 贝叶斯分类(java)
- 菜鸟装机问题解决
- Ubuntu下SSH设置
- JDK自带VM分析工具jps|jstat|jmap|jconsole使用
- Android-webview加载有道词典
- Effective Java——序列化