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

文章目录

  • 一、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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

字符串原地旋转

记录一下做的练习题 字符串原地旋转&#xff1a;五 三 mat [[1,2,3],[3,4,5],[4,5,6]] tag0 total 0 for i in mat:total total i[tag]tag 1 print(total) 四 X [[12,7,3],[4,5,6],[7,8,9]] Y [[5,8,1],[6,7,3],[4,5,9]] res [[0,0,0],[0,0,0],[0,0,0]] for i in rang…

阶梯排列硬币

题意&#xff1a; 你总共有 n 枚硬币&#xff0c;并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯&#xff0c;其第 i 行必须正好有 i 枚硬币。阶梯的最后一行 可能 是不完整的。 给你一个数字 n &#xff0c;计算并返回可形成 完整阶梯行 的总行数。 示例 1&#xff…

线程的创建方式

作者简介&#xff1a; zoro-1&#xff0c;目前大二&#xff0c;正在学习Java&#xff0c;数据结构&#xff0c;mysql&#xff0c;javaee等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 线程的创建方…

Spring Security 6.1.x 系列(5)—— Servlet 认证体系结构介绍

一、前言 本章主要学习Spring Security中基于Servlet 的认证体系结构&#xff0c;为后续认证执行流程源码分析打好基础。 二、身份认证机制 Spring Security提供个多种认证方式登录系统&#xff0c;包括&#xff1a; Username and Password&#xff1a;使用用户名/密码 方式…

【采坑分享】导出文件流responseType:“blob“如何提示报错信息

目录 前言&#xff1a; 采坑之路 总结&#xff1a; 前言&#xff1a; 近日&#xff0c;项目中踩了一个坑分享一下经验&#xff0c;也避免下次遇到方便解决。项目基于vue2axioselement-ui&#xff0c;业务中导出按钮需要直接下载接口中的文件流。正常是没有问题&#xff0c;但…

STM32 默认时钟更改 +debug调试

STM32时钟 文章目录 STM32时钟前言一、修改系统时钟二、DEBUG 前言 为什么我们要改STM32的时钟呢&#xff0c;打个比方在做SPI驱动的时候&#xff0c;需要16M的时钟&#xff0c;但是stm32默认是72的分频分不出来&#xff0c;这个时候我们就要改系统时钟了&#xff0c;那么怎么…

[科普] 无刷直流电机驱动控制原理图解

Title: [科普] 无刷直流电机驱动控制原理图解 文章目录 I. 引言II. 直流电机的原理1. 有刷直流电机和无刷直流电机的区别2. 有刷直流电机的运行原理3. 既是电动机又是发电机 III. 无刷直流电机的原理1. 无刷直流电机与永磁同步电机的区别2. 无刷直流电机的换向控制原理3. 无刷直…

Spring Cache(缓存框架)

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

指针的进阶

重中之重&#xff1a; 目录 1.字符指针&#xff1a; 2.指针数组 3.数组指针 4.数组参数、指针参数 5.函数指针 1.字符指针&#xff1a; 一般实现&#xff1a; int main() {char ch w;char *pc &ch;*pc w;return 0; } 二班实现&#xff1a; int main() {const c…

人工智能基础_机器学习050_对比sigmoid函数和softmax函数的区别_两种分类器算法的区别---人工智能工作笔记0090

可以看到最上面是softmax的函数对吧,但是如果当k = 2 那么这个时候softmax的函数就可以退化为sigmoid函数,也就是 逻辑斯蒂回归了对吧 我们来看一下推导过程,可以看到上面是softmax的函数 可以看到k=2 表示,只有两个类别对吧,两个类别的分类不就是sigmoid函数嘛对吧,所以说 …

3 时间序列预测入门:TCN

0 引言 TCN&#xff08;全称Temporal Convolutional Network&#xff09;&#xff0c;时序卷积网络&#xff0c;是在2018年提出的一个卷积模型&#xff0c;但是可以用来处理时间序列。 论文&#xff1a;https://arxiv.org/pdf/1803.01271.pdf 一维卷积&#xff1a;在时间步长方…

2024年天津天狮学院专升本计算机科学与技术《数据结构》考试大纲

2024年天津天狮学院计算机科学与技术专业高职升本入学考试《数据结构》考试大纲 一、考试性质 《数据结构》专业课程考试是天津天狮学院计算机科学与技术专业高职升本入学考 试的必考科目之一&#xff0c;其性质是考核学生是否达到了升入本科继续学习的要求而进行的选拔性考试…

超详细csapp-linklab之第一阶段“输出学号”实验报告

该实验的主题是“链接”。 准备工具 虚拟机&#xff0c;Ubuntu32位&#xff0c;hexedit&#xff0c;main.o&#xff0c;phase1.o&#xff0c;该实验的C代码框架如下 // main.c void (*phase)(); /*初始化为0*/int main( int argc, const char* argv[] ) {if ( phase )(*ph…

十大排序之选择排序(详解)

文章目录 &#x1f412;个人主页&#x1f3c5;算法思维框架&#x1f4d6;前言&#xff1a; &#x1f380;选择排序 时间复杂度O(n^2)&#x1f387;1. 算法步骤思想&#x1f387;2.动画实现&#x1f387; 3.代码实现 &#x1f412;个人主页 &#x1f3c5;算法思维框架 &#x1f…

三轴加速度计LIS2DW12开发(1)----轮询获取加速度数据

STM32WB55开发.6--FUS更新 概述视频教学通信模式管脚定义IIC通信模式速率生成STM32CUBEMX串口配置IIC配置CS和SA0设置串口重定向参考程序初始换管脚获取ID复位操作BDU设置设置传感器的量程配置过滤器链配置电源模式设置输出数据速率轮询获取加速度演示 概述 本文将介绍如何驱…

乘波前行的问题

1.问题&#xff1a; 考虑两个信号叠加在一起&#xff0c;比如&#xff0c;一个是工频信号50Hz&#xff0c;一个是叠加的高频信号比如有3KHz&#xff0c;简单起见&#xff0c;两个信号都是幅值固定的标准的正弦波&#xff0c;现在我们期望得到那个高频信号&#xff0c;相对工频…

Linux 网络通信

(一)套接字Socket概念 Socket 中文意思是“插座”&#xff0c;在 Linux 环境下&#xff0c;用于表示进程 x 间网络通信的特殊文件 类型。本质为内核借助缓冲区形成的伪文件。 既然是文件&#xff0c;那么理所当然的&#xff0c;我们可以使用文件描述符引用套接字。Linux 系统…

【MATLAB源码-第88期】基于matlab的灰狼优化算法(GWO)的栅格路径规划,输出做短路径图和适应度曲线

操作环境&#xff1a; MATLAB 2022a 1、算法描述 灰狼优化算法&#xff08;Grey Wolf Optimizer, GWO&#xff09;是一种模仿灰狼捕食行为的优化算法。灰狼是群居动物&#xff0c;有着严格的社会等级结构。在灰狼群体中&#xff0c;通常有三个等级&#xff1a;首领&#xff…

聊一聊索引覆盖的好处

问&#xff1a;索引覆盖啥意思&#xff1f; 答&#xff1a;若查询的字段在二级索引的叶子节点中&#xff0c;则可直接返回结果&#xff0c;无需回表。这种通过组合索引避免回表的优化技术也称为索引覆盖&#xff08;Covering Index&#xff09;。在叶子节点中的包括索引字段和主…

Python之内置函数和模块

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…