【Linux】第二十站:模拟实现shell

news2024/11/27 15:49:03

文章目录

  • 一、shell的实现细节
    • 1.shell的一些细节
    • 2.用户名、主机名、工作目录
    • 2.输入命令
    • 3.改为循环
    • 4.切割字符串
    • 5.普通命令的执行
    • 6.内建命令的处理
    • 7.子进程的退出码
    • 8.总结
  • 二、模式实现shell完整代码

一、shell的实现细节

1.shell的一些细节

shell操作系统的一个外壳程序。

shell/bash也是一个进程,执行指令的时候,本质就是自己创建子进程执行的!

2.用户名、主机名、工作目录

当我们进入shell的时候,我们知道会出现一个这样的东西

image-20231123160801267

我们先来实现一下它

其实关于这些信息,在我们的环境变量里面刚好就有

image-20231123160923739

所以这里,我们就不用系统调用了,而是通过环境变量来完成

#include<stdio.h>
#include<stdlib.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"



const char* getusername()
{
    return getenv("USER");
}
const char* gethostname()
{
    return getenv("HOSTNAME");
}
const char* getpwd()
{
    return getenv("PWD");
}
int main()
{
    printf(LEFT"%s@%s %s"RIGHT LABLE"\n",getusername(),gethostname(),getpwd());
    return 0;
}

如上代码所示,我们现在就可以简单的实现打开shell时候要输入命令行的界面

运行结果为

image-20231123163010271

不过由于我们后序要输入一个命令,由于输入结束后本身带有换行,所以我们这里可以去掉这个换行

2.输入命令

我们可以先直接加上一个scanf,这样的话它就会去接收命令了

image-20231123163448241

image-20231123163433680

但是这样对吗,我们可以加上一个打印来看看结果

image-20231123163733195

运行结果如下,我们可以看到其实打印结果并非我们的预期

image-20231123163812684

这其实是因为scanf它本身读取到空格就结束了,所以我们需要一次读取一行

在c语言中有一个函数叫做fgets

image-20231123164043048

char *fgets(char *s, int size, FILE *stream);

这个函数的意思是,从stream这个流中,读取size个字符,放入s中。(s是放在哪里,size是可以访问多少)

如下所示,在这里我们需要注意的是,这里对于27行,我们可以不用对这个大小减一。都是可以的

image-20231123165553240

运行结果为

image-20231123165745667

那么在这里我们有一个问题,这里的s有可能为空吗,即有可能输入失败吗?

在不考虑设备出现问题的情况下,其实是不可能的,即便我们什么都不输入,它也会由于我们按了\n而将这个字符给输入进去。

而且我们发现我们在打印的时候,多了一个空行,这个空行其实就是因为我们输入了这个\n字符所导致的,所以我们可以进行一次调整

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"

#define LINE_SIZE 1024
char commandline[LINE_SIZE];


const char* getusername()
{
    return getenv("USER");
}
const char* gethostname()
{
    return getenv("HOSTNAME");
}
const char* getpwd()
{
    return getenv("PWD");
}
int main()
{
    printf(LEFT"%s@%s %s"RIGHT LABLE" ",getusername(),gethostname(),getpwd());
    char* s = fgets(commandline,sizeof(commandline),stdin);
    assert(s);
    commandline[strlen(commandline) - 1] = '\0';

    if(s)
        printf("echo : %s",commandline);
    return 0;
}

运行结果为

image-20231123182157106

为了方便,我们可以将对应的代码给写为一个函数

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"

#define LINE_SIZE 1024
char commandline[LINE_SIZE];


const char* getusername()
{
    return getenv("USER");
}
const char* gethostname()
{
    return getenv("HOSTNAME");
}
const char* getpwd()
{
    return getenv("PWD");
}

void Interact(char* cline,int size)
{
    printf(LEFT"%s@%s %s"RIGHT LABLE" ",getusername(),gethostname(),getpwd());
    char* s = fgets(cline,size,stdin);
    (void)s;
    assert(s);
    commandline[strlen(cline) - 1] = '\0';
}
int main()
{
    Interact(commandline,sizeof(commandline));
    printf("echo : %s",commandline);
    return 0;
}

然后运行结果为

image-20231123183907749

3.改为循环

我们的前面的代码还有的缺陷就是只能用一次,所以我们不妨直接将其改为一个循环结构

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"

#define LINE_SIZE 1024
char commandline[LINE_SIZE];


const char* getusername()
{
    return getenv("USER");
}
const char* gethostname()
{
    return getenv("HOSTNAME");
}
const char* getpwd()
{
    return getenv("PWD");
}

void Interact(char* cline,int size)
{
    printf(LEFT"%s@%s %s"RIGHT LABLE" ",getusername(),gethostname(),getpwd());
    char* s = fgets(cline,size,stdin);
    (void)s;
    assert(s);
    commandline[strlen(cline) - 1] = '\0';
}
int main()
{
    int quit = 0;
    while(!quit)
    {
        Interact(commandline,sizeof(commandline));
        printf("echo : %s\n",commandline);
    }
    return 0;
}

这样的话,看起来就已经像回事了

image-20231123184505350

4.切割字符串

为了分析这个指令,我们首先要做的就是先将字符串给切割出来。放到一个数组里面去

于是我们可以利用这个函数

 char *strtok(char *str, const char *delim);

第一个参数是待分割的字符串,第二个参数是分割的字符

这个函数每调用一次,便会截取一次字符串。具体操作方法,可见下文

字符串函数详解

最终代码如下所示

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32


char commandline[LINE_SIZE];


const char* getusername()
{
    return getenv("USER");
}
const char* gethostname()
{
    return getenv("HOSTNAME");
}
const char* getpwd()
{
    return getenv("PWD");
}

void Interact(char* cline,int size)
{
    printf(LEFT"%s@%s %s"RIGHT LABLE" ",getusername(),gethostname(),getpwd());
    char* s = fgets(cline,size,stdin);
    (void)s;
    assert(s);
    commandline[strlen(cline) - 1] = '\0';
}
int splitstring(char* argv[])
{
    int i = 0;
    argv[i++] = strtok(commandline,DELIM);
    while(argv[i++] = strtok(NULL,DELIM));
    return i - 1;
}
int main()
{
    char* argv[ARGC_SIZE] = {NULL};
    int quit = 0;
    while(!quit)
    {
        Interact(commandline,sizeof(commandline));
        int argc = splitstring(argv);
        if(argc == 0) continue;
		//测试分割
        for(int i = 0; argv[i]; i++)
        {
            printf("[%d] : %s\n",i,argv[i]);
        }
    }
    return 0;
}

运行结果为

image-20231123193949608

5.普通命令的执行

由于我们的命令分为内建命令和普通命令。这里我们先考虑普通命令

对于普通命令,我们的基本思路是创建一个子进程,然后通过程序替换来实现。

这里的程序替换根据我们已有的条件最好选择时候execvp/execvpe。环境变量可带可不带,但是v和p一定要带上,因为我们需要从PATH环境变量中去找指令且我们已经有了一个数组了,所以使用v形式更加方便

如下所示,我们就可以实现一个简单的普通命令的执行了

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.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

char commandline[LINE_SIZE];


const char* getusername()
{
    return getenv("USER");
}
const char* gethostname()
{
    return getenv("HOSTNAME");
}
const char* getpwd()
{
    return getenv("PWD");
}

void Interact(char* cline,int size)
{
    printf(LEFT"%s@%s %s"RIGHT LABLE" ",getusername(),gethostname(),getpwd());
    char* s = fgets(cline,size,stdin);
    (void)s;
    assert(s);
    commandline[strlen(cline) - 1] = '\0';
}
int splitstring(char* argv[])
{
    int i = 0;
    argv[i++] = strtok(commandline,DELIM);
    while(argv[i++] = strtok(NULL,DELIM));
    return i - 1;
}
int main()
{
    extern char** environ;
    char* argv[ARGC_SIZE] = {NULL};
    int quit = 0;
    while(!quit)
    {
        //2.交互问题,解决命令行
        Interact(commandline,sizeof(commandline));
        //3.子串分割问题,解析命令行
        int argc = splitstring(argv);
        if(argc == 0) continue;

      //  for(int i = 0; argv[i]; i++)
      //  {
      //      printf("[%d] : %s\n",i,argv[i]);
      //  }
        
        //4.指令的判断(内建命令和普通命令)
        
        //5.普通命令的执行
        pid_t id = fork();
        if(id < 0)
        {
            perror("fork");
        }
        else if (id == 0)
        {
            //子进程执行命令
            execvpe(argv[0],argv,environ);
            exit(EXIT_CODE);
        }
        else
        {
            int status = 0;
            pid_t rid = waitpid(id,&status,0);
            if(rid == id)
            {
                
            }
        }
    }
    return 0;
}

我们可以简单的使用一下

image-20231123202524230

我们发现这些命令都可以正常的使用。

不过相比原本的shell还是有一些区别的,比如说,我们的shell中像目录文件,可执行程序没有颜色。ll指令还没有办法解析(因为没有支持改名)等问题。

而且还有当我们想要使用内建命令的时候,是没有任何效果的

image-20231123202902572

因为这是子进程在跑这个命令,最后变化的都是子进程的目录等,最后子进程退出了。所以最终就没有任何变化。

我们现在可以将普通指令的执行给封装为一个函数,如下所示

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.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

char commandline[LINE_SIZE];
char* argv[ARGC_SIZE] = {NULL};
extern char** environ;
int last_code = 0;
int quit = 0;

const char* getusername()
{
    return getenv("USER");
}
const char* gethostname()
{
    return getenv("HOSTNAME");
}
const char* getpwd()
{
    return getenv("PWD");
}

void Interact(char* cline,int size)
{
    printf(LEFT"%s@%s %s"RIGHT LABLE" ",getusername(),gethostname(),getpwd());
    char* s = fgets(cline,size,stdin);
    (void)s;
    assert(s);
    commandline[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);
        exit(EXIT_CODE);
    }
    else
    {
        int status = 0;
        pid_t rid = waitpid(id,&status,0);
        if(rid == id)
        {
           last_code = WEXITSTATUS(status);
        }
    }
}
int main()
{
    while(!quit)
    {
        //2.交互问题,解决命令行
        Interact(commandline,sizeof(commandline));
        //3.子串分割问题,解析命令行
        int argc = splitstring(commandline,argv);
        if(argc == 0) continue;
        //4.指令的判断(内建命令和普通命令)
        
        //5.普通命令的执行
        NormalExcute(argv);
    }
    return 0;
}

6.内建命令的处理

对于内建命令,我们则是直接判断即可

比如下面

image-20231124142115463

运行结果最终为,我们可以发现确实更改当前目录了

image-20231124142232901

不过我们会发现,这个前面字符串的显示还是存在一些问题的

这是因为我们当前所使用的是环境变量的方式,我们可以去sprintf函数去修改环境变量。不过这样有很多比较麻烦的事情

最简单的方式是,我们不用环境变量来获取当前工作目录了,我们可以直接用getcwd这个函数来使用,它可以获得当前的工作目录。放入一个数组中。

image-20231124143809245

如下代码所示,完成了cd的内建命令,并且我们实现了ls在显示时候的颜色输出。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.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

char commandline[LINE_SIZE];
char* argv[ARGC_SIZE] = {NULL};
extern char** environ;
int last_code = 0;
int quit = 0;
char pwd[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);
    char* s = fgets(cline,size,stdin);
    (void)s;
    assert(s);
    commandline[strlen(cline) - 1] = '\0';
}
int splitstring(char cline[],char* _argv[])
{
    if(strcmp(cline,"") == 0) return 0;
    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);
        exit(EXIT_CODE);
    }
    else
    {
        int status = 0;
        pid_t rid = waitpid(id,&status,0);
        if(rid == id)
        {
           last_code = 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;
    }

    if(_argc > 0 && strcmp(_argv[0],"ls") == 0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }
      return 0;
}
int main()
{
    while(!quit)
    {
        //2.交互问题,解决命令行
        Interact(commandline,sizeof(commandline));
        //3.子串分割问题,解析命令行
        int argc = splitstring(commandline,argv);
        if(argc == 0) continue;        
        //4.指令的判断(内建命令和普通命令)
        int n = BuildCommand(argv,argc);
        //5.普通命令的执行
        if(!n) NormalExcute(argv);
    }
    return 0;
}

我们再来实现一下其他的内建命令,比如echo命令,如果我们直接使用的话,相当于是调用了子进程中的echo命令,它会出现这样的问题,无论我们输入什么,最终也依然输出什么

image-20231124164608751

而且如果我们导入环境变量的时候,直接使用export也是不可以的

image-20231124170820416

因为这是创建了子进程才导入的,我们不希望创建子进程,所以就需要内建命令

直接在这里putenv即可

image-20231124171038176

这样的话,就有了这个环境变量了

image-20231124171258629

不过上面的其实还存在很多问题

因为如果我们再次使用一下export的话,我们会发现原来加入的环境变量消失了

image-20231124173731157

这其实是因为我们前面的代码是将_argv中的内容直接导入到了环境变量中。这里的导入不是说拷贝一份。而是将这个地址写入到了环境变量所对应的空间中。而我们后面再次导入的时候已经将这块的内容修改了。所以说原来的就不见了。总之环境变量表保存的不是字符串,而是地址。

所以我们需要专门开辟一块空间用来存储环境变量

image-20231124180738933

然后我们最后将echo给实现一下即可。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.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

char commandline[LINE_SIZE];
char* argv[ARGC_SIZE] = {NULL};
extern char** environ;
int last_code = 0;
int quit = 0;
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);
    char* s = fgets(cline,size,stdin);
    (void)s;
    assert(s);
    commandline[strlen(cline) - 1] = '\0';
}
int splitstring(char cline[],char* _argv[])
{
    if(strcmp(cline,"") == 0) return 0;
    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);
        exit(EXIT_CODE);
    }
    else
    {
        int status = 0;
        pid_t rid = waitpid(id,&status,0);
        if(rid == id)
        {
           last_code = 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(*_argv[1] == '$')
        {
            char* val = getenv(_argv[1] + 1);
            if(val) printf("%s\n",val);
        }
        else 
        {
            printf("%s\n",_argv[1]);
        }
        return 1;
    }


    if(_argc > 0 && strcmp(_argv[0],"ls") == 0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }
      return 0;
}
int main()
{
    while(!quit)
    {
        //2.交互问题,解决命令行
        Interact(commandline,sizeof(commandline));
        //3.子串分割问题,解析命令行
        int argc = splitstring(commandline,argv);
        if(argc == 0) continue;

        //4.指令的判断(内建命令和普通命令)
        int n = BuildCommand(argv,argc);
        //5.普通命令的执行
        if(!n) NormalExcute(argv);
    }
    return 0;
}

7.子进程的退出码

当子进程退出的时候,它会有一个退出码。

所以我们可以对于echo的内建命令在加上一个打印退出码的操作

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.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

char commandline[LINE_SIZE];
char* argv[ARGC_SIZE] = {NULL};
extern char** environ;
int last_code = 0;
int quit = 0;
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);
    char* s = fgets(cline,size,stdin);
    (void)s;
    assert(s);
    commandline[strlen(cline) - 1] = '\0';
}
int splitstring(char cline[],char* _argv[])
{
    if(strcmp(cline,"") == 0) return 0;
    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);
        exit(EXIT_CODE);
    }
    else
    {
        int status = 0;
        pid_t rid = waitpid(id,&status,0);
        if(rid == id)
        {
           last_code = 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",last_code);
            last_code = 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;
    }


    if(_argc > 0 && strcmp(_argv[0],"ls") == 0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }
    return 0;
}
int main()
{
    while(!quit)
    {
        //2.交互问题,解决命令行
        Interact(commandline,sizeof(commandline));
        //3.子串分割问题,解析命令行
        int argc = splitstring(commandline,argv);
        if(argc == 0) continue;
        //4.指令的判断(内建命令和普通命令)
        int n = BuildCommand(argv,argc);
        //5.普通命令的执行
        if(!n) NormalExcute(argv);
    }
    return 0;
}

运行效果如下

image-20231124182832088

8.总结

所以,当我们进行登录的时候,系统就要启动一个shell进程,那么我们shell本身的环境变量是从哪里来的???

其实在我们用户的目录下,就一个bash_profile文件

image-20231124183111243

它里面的内容是这样的,这里面就会帮助我们导入环境变量

image-20231124183138019

类似的,还有.bashrc文件等

所以说

当用户登录的时候,shell会读取用户目录下的.bashprofile文件,里面保存了导入环境变量的方式!

二、模式实现shell完整代码

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.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

char commandline[LINE_SIZE];
char* argv[ARGC_SIZE] = {NULL};
extern char** environ;
int last_code = 0;
int quit = 0;
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);
    char* s = fgets(cline,size,stdin);
    (void)s;
    assert(s);
    commandline[strlen(cline) - 1] = '\0';
}
int splitstring(char cline[],char* _argv[])
{
    if(strcmp(cline,"") == 0) return 0;
    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);
        exit(EXIT_CODE);
    }
    else
    {
        int status = 0;
        pid_t rid = waitpid(id,&status,0);
        if(rid == id)
        {
           last_code = 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",last_code);
            last_code = 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;
    }


    if(_argc > 0 && strcmp(_argv[0],"ls") == 0)
    {
        _argv[_argc++] = "--color";
        _argv[_argc] = NULL;
    }
    return 0;
}
int main()
{
    while(!quit)
    {
        //2.交互问题,解决命令行
        Interact(commandline,sizeof(commandline));
        //3.子串分割问题,解析命令行
        int argc = splitstring(commandline,argv);
        if(argc == 0) continue;
        //4.指令的判断(内建命令和普通命令)
        int n = BuildCommand(argv,argc);
        //5.普通命令的执行
        if(!n) NormalExcute(argv);
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1254654.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaEE初阶】浅谈进程

✏️✏️✏️今天正式进入JavaEE初阶的学习&#xff0c;给大家分享一下关于进程的一些基础知识。了解这部分内容&#xff0c;只是为后续多线程编程打好基础&#xff0c;因此进程部分的知识&#xff0c;不需要了解更加细节的内容。 清风的CSDN博客 &#x1f61b;&#x1f61b;&a…

Unsupervised Skill Discovery via Recurrent Skill Training论文笔记

Zheyuan Jiang, Jingyue Gao, Jianyu Chen (2022). Unsupervised Skill Discovery via Recurrent Skill Training. In Conference on Neural Information Processing Systems (NeurIPS), 2022. 通过循环技能训练发现无监督技能 1、Motivation 以往的无监督技能发现方法主要使…

Spring面向切面编程(AOP);Spring控制反转(IOC);解释一下Spring AOP里面的几个名词;Spring 的 IoC支持哪些功能

文章目录 Spring面向切面编程(AOP)什么是AOPSpring AOP and AspectJ AOP 的区别&#xff1f;Spring AOP中的动态代理如何理解 Spring 中的代理&#xff1f;解释一下Spring AOP里面的几个名词Spring在运行时通知对象Spring切面可以应用5种类型的通知&#xff1a;什么是切面 Aspe…

【开源】基于Vue+SpringBoot的食品生产管理系统

项目编号&#xff1a; S 044 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S044&#xff0c;文末获取源码。} 项目编号&#xff1a;S044&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 加工厂管理模块2.2 客户管理模块2.3…

Typescript基础面试题 | 05.精选 ts 面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

代码随想录算法训练营第四十九天|123. 买卖股票的最佳时机III 、188. 买卖股票的最佳时机 IV

LeetCode 123. 买卖股票的最佳时机 III 题目链接&#xff1a;123. 买卖股票的最佳时机 III - 力扣&#xff08;LeetCode&#xff09; 这个道题和121. 买卖股票的最佳时机 I、122. 买卖股票的最佳时机 II很像&#xff0c;是两题的结合。 我们就定义两个数组来实现。 代码&…

【Apache Doris】Manager极致丝滑地运维管理

【Apache Doris】Manager极致丝滑地运维管理 1.标准VS可视化运维管理2. 环境信息2.1.硬件信息2.2.软件信息 3.前置准备3.1.安装包准备3.2.文档手册准备 4.集群初始化4.1.系统参数预设4.2.Manager部署4.3.新集群部署4.4 监控告警4.4.1 监控4.4.2 告警 5. 集群升级5.1 新包准备5.…

C#,《小白学程序》第二十一课:大数的减法(BigInteger Subtract)

1 文本格式 using System; using System.Linq; using System.Text; using System.Collections.Generic; /// <summary> /// 大数的&#xff08;加减乘除&#xff09;四则运算、阶乘运算 /// 乘法计算包括小学生算法、Karatsuba和Toom-Cook3算法 /// </summary> p…

第二十一章 解读XML与JSON文件格式(工具)

XML 带分隔符的文件仅有两维的数据&#xff1a;行 & 列。如果我们想在程序之间交换数据结构&#xff0c;需要一种方法把层次结构&#xff0c;序列&#xff0c;集合和其它的数据结构编码成文本。 今天要说的 XML 是最突出的处理上述这种转换的标记格式&#xff0c;它使用标…

01、copilot+pycharm

之——free for student 目录 之——free for student 杂谈 正文 1.for student 2.pycharm 3.使用 杂谈 copilot是github推出的AI程序员&#xff0c;将chatgpt搬到了私人终端且无token限制&#xff0c;下面是使用方法。 GitHub Copilot 是由 GitHub 与 OpenAI 合作开发的…

智能AI名片-Pushmall推贴SCRM数字名片的发展趋势

智能AI名片-Pushmall推贴SCRM数字名片的发展趋势 基于相识靠铺人脉相互引荐&#xff0c;共享人脉资源&#xff0c;众筹共创赋能交友、商务实现大众创业&#xff0c;灵活创收的智能AI名片平台。帮助企业实现成员管理与客户资源管理。功能说明 1、搜索查询&#xff1a;个人信息与…

Leetcode—828.统计子串中的唯一字符【困难】

2023每日刷题&#xff08;四十一&#xff09; Leetcode—828.统计子串中的唯一字符 算法思想 枚举所有种类字母在s中出现的位置&#xff0c;分别统计只包含这个字母不包含该类字母中其他字母的子串个数 实现代码 int uniqueLetterString(char* s) {int len strlen(s);cha…

Quartz定时任务基础

springBoot有一个定时执行某个方法的 注解&#xff1a; Scheduled 可以满足挺多的需求&#xff0c;但是到了一些场景&#xff0c;就显得比较麻烦&#xff0c;比如&#xff1a; 机器待机五分钟后执行切换待机状态。如果是按照使用Scheduled注解&#xff0c;就得持久化一个表&…

【5G PHY】5G SS/PBCH块介绍(四)

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

利用ngrok实现内网穿透(全网最详细教程)

准备工具&#xff1a; 1、phpstudy 用于在本地搭建网站 2、ngrok 用于将自己的本地端口暴露到公网上&#xff0c;从而实现内网穿透 文章开始前给大家分享一个学习人工智能的网站&#xff0c;通俗易懂&#xff0c;风趣幽默 人工智能https://www.captainbed.cn/myon/ ~~~~~…

C#文件基本操作(判断文件是否存在、创建文件、复制或移动文件、删除文件以及获取文件基本信息)

目录 一、判断文件是否存在 1.File类的Exists()方法 2.FileInfo类的Exists属性 二、创建文件 1.File类的Create()方法 2.FileInfo类的Create()方法 三、复制或移动文件 1.File类的Copy()方法 2.File类的Move()方法 3.FileInfo类的CopyTo()方法 四、删除文件 1.File…

大数据数据仓库,Sqoop--学习笔记

数据仓库介绍 1. 数据仓库概念 数据仓库概念创始人在《建立数据仓库》一书中对数据仓库的定义是&#xff1a;数据仓库&#xff08;Data Warehouse&#xff09;是一个面向主题的&#xff08;Subject Oriented&#xff09;、数据集成的&#xff08;Integrated&#xff09;、相对…

【AUTOSAR】【通信栈】ComXf

AUTOSAR专栏——总目录_嵌入式知行合一的博客-CSDN博客文章浏览阅读292次。本文主要汇总该专栏文章,以方便各位读者阅读。https://xianfan.blog.csdn.net/article/details/132072415 目录 一、概述 二、限制说明

NX二次开发UF_CURVE_ask_curve_turn_angle 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_curve_turn_angle Defined in: uf_curve.h int UF_CURVE_ask_curve_turn_angle(tag_t curve, double orientation [ 3 ] , double * angle ) overview 概述 Returns …

Python大数据考题

Python大数据考题&#xff1a; 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle&#xff0c;尤其sql要…