• 【Linux】Shell命令行的简易实现(C语言实现)内键命令,普通命令



    0.准备工作

    在这里插入图片描述

    1.大体框架

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define LEFT "["
    #define RIGHT "]"
    #define LABLE "#"
    #define DELIM " \t"
    //用于修饰命令行
    //类似:[hh@VM-4-10-centos ~]$ 
    
    #define LINE_SIZE 1024//输入命令最大长度
    #define ARGC_SIZE 32//命令行参数表的大小
    #define EXIT_CODE 44//退出码
    
    int lastcode = 0;//上一次的退出码
    int quit = 0
     
    char commandline[LINE_SIZE];//输入的命令
    char *argv[ARGC_SIZE];//解析后保存的命令
    char pwd[LINE_SIZE];//保存当前所在路径
    
    // 自定义环境变量表
    char myenv[LINE_SIZE];
     //因为环境变量表里面保存的不是
     //变量本身,而是其地址,所以我们为了防止
     //自己导入的环境变量被覆盖,需要自己维护一段空间
     //这里myenv只能维护一个环境变量,因为只有一个地址
    
    const char *getusername()
    {//获取用户名
        return getenv("USER");
    }
    
    const char *gethostname()
    {//获取主机名
        return getenv("HOSTNAME");
    }
    
    void getpwd()
    {
    //将当前路径保存到pwd中
        getcwd(pwd, sizeof(pwd));
    }
    
    void interact(char *cline, int size){ }//获取命令行
    
    int splitstring(char cline[], char *_argv[]){}//解析命令行
    
    void NormalExcute(char *_argv[]){}//执行普通命令
    
    int buildCommand(char *_argv[], int _argc){}//执行内键命令
    
    int main()
    {
        while(!quit){
            // 1.
            // 2. 交互问题,获取命令行 
            interact(commandline, sizeof(commandline));
            // 3. 子串分割的问题,解析命令行
            int argc = splitstring(commandline, argv);
            if(argc == 0) continue;
    
            // 4. 指令的判断 
            //内键命令,本质就是一个shell内部的一个函数
            int n = buildCommand(argv, argc);
    
            // 5. 普通命令的执行
            if(!n) NormalExcute(argv);
        }
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    一、获取命令行

    在获取命令之前我们需要先建立一个命令行
    类似这种效果:在这里插入图片描述

    void interact(char *cline, int size)
    {
        getpwd();//将当前路径写入到pwd中
        printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(), pwd);
          fgets(cline, size, stdin);
         //这里不用scanf的原因是其遇到空格与回车不会读取
         //所以我们选择用fgets,将命令写入cline中
        // "abcd\n\0"
        //又因为fgets会读入回车键,所以我们要手动把回车位置改为'\0'
        cline[strlen(cline)-1] = '\0';
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    二、解析命令行

    int splitstring(char cline[], char *_argv[])
    {
        int i = 0;
        //strtok用于分割字符串,上面我们宏定义了只有要DELIM中的字符
        //就会发生分割,将分割后的字符串写入argv中
        argv[i++] = strtok(cline, DELIM);
        while(_argv[i++] = strtok(NULL, DELIM)); // 故意写的=
        return i - 1;
        //返回argv中存的字符串个数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    三、进程执行

    1.普通命令

    void NormalExcute(char *_argv[])
    {
        pid_t id = fork();//创建子进程
        if(id < 0){
            perror("fork");
            return;
        }
        else if(id == 0){
            //让子进程执行命令
     		//execvp相当于一个加载器
     		//该进程的用户空间代码和数据完全被新程序替换,从新程序的启动
           例程开始执行
           //会从环境变量中的路径中找到我们的可执行程序
            execvp(_argv[0], _argv);
            exit(EXIT_CODE);
        }
        else{
            int status = 0;
            pid_t rid = waitpid(id, &status, 0);
            if(rid == id) 
            {
            //等待子进程成功,更改退出码
                lastcode = WEXITSTATUS(status);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2.内建命令

    在这里插入图片描述

    int buildCommand(char *_argv[], int _argc)
    {
        if(_argc == 2 && strcmp(_argv[0], "cd") == 0){
        //chdir改变当前进程路径
        //假如我们让子进程执行cd命令,子进程确实路径改变了
        //但子进程执行完就被父进程回收了,没屁用
        //因为进程的独立性我父进程路径不受影响。
        //所以我们要手动修改路径
            chdir(argv[1]);
            getpwd();
            //将新的路径写入pwd
            //改变环境变量PWD,用pwd对其进行写入
            sprintf(getenv("PWD"), "%s", pwd);
            return 1;
        }
        else if(_argc == 2 && strcmp(_argv[0], "export") == 0){
        	//自己维护环境变量空间
            strcpy(myenv, _argv[1]);//将环境变量放入自己的myenv
            putenv(myenv);
            return 1;
        }
        else if(_argc == 2 && strcmp(_argv[0], "echo") == 0){
            if(strcmp(_argv[1], "$?") == 0)
            {
                printf("%d\n", lastcode);
                lastcode=0;
            }
            else if(*_argv[1] == '$'){
            //_argv[1]+1为$后面的值例如$PATH
            //那么最后就获取PATH的值
                char *val = getenv(_argv[1]+1);
                if(val) printf("%s\n", val);
            }
            else{
                printf("%s\n", _argv[1]);
            }
    
            return 1;
        }
    
        // 特殊处理一下ls
        //因为ls中如果是可执行文件,其会显示为特殊颜色
        if(strcmp(_argv[0], "ls") == 0)
        {
            _argv[_argc++] = "--color";
            _argv[_argc] = NULL;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    四、完整代码:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define LEFT "["
    #define RIGHT "]"
    #define LABLE "#"
    #define DELIM " \t"
    #define LINE_SIZE 1024
    #define ARGC_SIZE 32
    #define EXIT_CODE 44
    
    int lastcode = 0;
    int quit = 0;
    extern char **environ;
    char commandline[LINE_SIZE];
    char *argv[ARGC_SIZE];
    char pwd[LINE_SIZE];
    
    // 自定义环境变量表
    char myenv[LINE_SIZE];
     
    
    
    const char *getusername()
    {
        return getenv("USER");
    }
    
    const char *gethostname()
    {
        return getenv("HOSTNAME");
    }
    
    void getpwd()
    {
        getcwd(pwd, sizeof(pwd));
    }
    
    void interact(char *cline, int size)
    {
        getpwd();
        printf(LEFT"%s@%s %s"RIGHT""LABLE" ", getusername(), gethostname(), pwd);
         fgets(cline, size, stdin);
        
        // "abcd\n\0"
        cline[strlen(cline)-1] = '\0';
    }
    
    int splitstring(char cline[], char *_argv[])
    {
        int i = 0;
        argv[i++] = strtok(cline, DELIM);
        while(_argv[i++] = strtok(NULL, DELIM));  
        return i - 1;
    }
    
    void NormalExcute(char *_argv[])
    {
        pid_t id = fork();
        if(id < 0){
            perror("fork");
            return;
        }
        else if(id == 0){
            //让子进程执行命令
            //execvpe(_argv[0], _argv, environ);
            execvp(_argv[0], _argv);
            exit(EXIT_CODE);
        }
        else{
            int status = 0;
            pid_t rid = waitpid(id, &status, 0);
            if(rid == id) 
            {
                lastcode = WEXITSTATUS(status);
            }
        }
    }
    
    int buildCommand(char *_argv[], int _argc)
    {
        if(_argc == 2 && strcmp(_argv[0], "cd") == 0){
            chdir(argv[1]);
            getpwd();
            sprintf(getenv("PWD"), "%s", pwd);
            return 1;
        }
        else if(_argc == 2 && strcmp(_argv[0], "export") == 0){
            strcpy(myenv, _argv[1]);
            putenv(myenv);
            return 1;
        }
        else if(_argc == 2 && strcmp(_argv[0], "echo") == 0){
            if(strcmp(_argv[1], "$?") == 0)
            {
                printf("%d\n", lastcode);
                lastcode=0;
            }
            else if(*_argv[1] == '$'){
                char *val = getenv(_argv[1]+1);
                if(val) printf("%s\n", val);
            }
            else{
                printf("%s\n", _argv[1]);
            }
    
            return 1;
        }
    
        // 特殊处理一下ls
        if(strcmp(_argv[0], "ls") == 0)
        {
            _argv[_argc++] = "--color";
            _argv[_argc] = NULL;
        }
        return 0;
    }
    
    int main()
    {
        while(!quit){
            // 1.
            // 2. 交互问题,获取命令行  
            interact(commandline, sizeof(commandline));
    
            
            // 3. 子串分割的问题,解析命令行
            int argc = splitstring(commandline, argv);
            if(argc == 0) continue;
    
            // 4. 指令的判断 
            
            //内键命令,本质就是一个shell内部的一个函数
            int n = buildCommand(argv, argc);
    
            // 5. 普通命令的执行
            if(!n) NormalExcute(argv);
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
  • 相关阅读:
    毕业季-个人总结
    【花式】Matlab实现填充柱形图
    聊聊 Linux iowait
    大数据随记 —— Spark Core 与 RDD 简介
    Sketch在mac运行时崩溃,什么是安全模式以及如何启用它?
    window下redis安装
    Halcon中涉及的数字图像十大理论知识
    java图形验证码到底是怎么生成的?
    fastadmin框架如何开启事务
    ch55xduino
  • 原文地址:https://blog.csdn.net/m0_74774759/article/details/134255146