用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