0.准备工作

在这里插入图片描述

1.大体框架

#include <stdio.h>
#include <stdlib.h&gt;
#include <string.h&gt;
#include <assert.h&gt;
#include <unistd.h&gt;
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

#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;
}

一、获取命令行

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

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中
    // "abcdn"
    //又因为fgets会读入回车键,所以我们手动把回车位置改为''
    cline[strlen(cline)-1] = '';
}

二、解析命令行

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.普通命令

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, &amp;status, 0);
        if(rid == id) 
        {
        //等待进程成功,更改退出
            lastcode = WEXITSTATUS(status);
        }
    }
}

2.内建命令

在这里插入图片描述

int buildCommand(char *_argv[], int _argc)
{
    if(_argc == 2 &amp;&amp; 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 &amp;&amp; strcmp(_argv[0], "export") == 0){
    	//自己维护环境变量空间
        strcpy(myenv, _argv[1]);//将环境变量放入自己的myenv
        putenv(myenv);
        return 1;
    }
    else if(_argc == 2 &amp;&amp; strcmp(_argv[0], "echo") == 0){
        if(strcmp(_argv[1], "$?") == 0)
        {
            printf("%dn", lastcode);
            lastcode=0;
        }
        else if(*_argv[1] == '$'){
        //_argv[1]+1为$后面的值例如$PATH
        //那么最后就获取PATH的值
            char *val = getenv(_argv[1]+1);
            if(val) printf("%sn", val);
        }
        else{
            printf("%sn", _argv[1]);
        }

        return 1;
    }

    // 特殊处理一下ls
    //因为ls中如果是可执行文件,其会显示特殊颜色
    if(strcmp(_argv[0], "ls") == 0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }
    return 0;
}

四、完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

#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);
    
    // "abcdn"
    cline[strlen(cline)-1] = '';
}

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("%dn", lastcode);
            lastcode=0;
        }
        else if(*_argv[1] == '$'){
            char *val = getenv(_argv[1]+1);
            if(val) printf("%sn", val);
        }
        else{
            printf("%sn", _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;
}

原文地址:https://blog.csdn.net/m0_74774759/article/details/134255146

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_17041.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注