[Linux]下制作myshell

发布于:2022-11-05 ⋅ 阅读:(503) ⋅ 点赞:(0)


前言

本小节是完善我们上次完成[Linux]下制作简易myshell这篇博客。
上次因为我们的知识还不是很完善,所以留了一些小尾巴,本篇博客来将我们的myshell完成。


一、给ls命令加颜色

在这里插入图片描述
相比于系统调用接口,我们少了颜色标识。

那如何让我们自己制作的myshell也添加上颜色标识呢?

which ls

在这里插入图片描述
alias这个单词是取别名的意思。

举个栗子!
在这里插入图片描述
在这里插入图片描述
这个取别名操作在我们重启Xshell,就没有了。想要永久保存,需要我们修改配置文件。

所以在底层我们调用’ls’命令,其实是调用了’ls --color=auto’

//给ls命令添加颜色
if(strcmp(command_args[0],“ls”)==0)
command_args[index++]=(char*)“–color=auto”;

int main()
{
    //shell本质上就是一个死循环
    while(1)
    {
        //不关心获取这些属性的接口,搜索一下
        //1.显示提示符
        printf("[曾曾@我的主机名 当前目录]# ");
        fflush(stdout);
        //2.获取用户输入
        memset(command_line,'\0',sizeof(command_line)*sizeof(char));
        fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
    
        command_line[strlen(command_line)-1]='\0';
        //3.“ls -a -l”--->"ls" "-a" "-l" 截取字符串
        command_args[0]=strtok(command_line,SEP);
        
        int index=1;
        //给ls命令添加颜色
        if(strcmp(command_args[0],"ls")==0) 
            command_args[index++]=(char*)"--color=auto";
        while(command_args[index++]=strtok(NULL,SEP));
       
        //5.创建进程,执行
        pid_t id=fork();
        if(id==0)
        {
            //child
            //6.程序替换
            execvp(command_args[0],command_args);

             exit(1);//执行到这里,子进程一定替换失败了
        }
        int status=0;
        pid_t ret=waitpid(id,&status,0);
        if(ret>0)
        {
            printf("等待子进程成功:sig:%d,code:%d\n",status&0x7F,(status>>8)&0xFF);


        }//end while
    }
    return 0;
}

在这里插入图片描述
现在我们的’ls’命令也带上了颜色标识!!!

二、内建命令

2.1 cd命令

在这里插入图片描述
我们在进行’cd …'回退到上级目录的时候,我发现我当前所处的路径没有发生任何变化!

我们如果直接exec*执行cd,最多只是让子进程进行路径切换,子进程是一运行就完毕的进程的进程!我们在shell中,更希望谁的路径发生变化呢?

答:父进程,也就是shell本身!!!

如果有些行为,是必须让父进程shell执行的,不想让子进程执行,所以我们绝对不能创建子进程!只能是父进程自己实现对应的代码!

由shell自己执行的命令,我们称之为内建(内置 bind-in)命令!–>相当于shell内部的一个函数!

在这里插入图片描述

//对应上层的内建命令
int ChangeDir(const char* new_path)
{
    chdir(new_path);
    return 0;//调用成功
}
int main()
{
    //shell本质上就是一个死循环
    while(1)
    {
        //不关心获取这些属性的接口,搜索一下
        //1.显示提示符
        printf("[曾曾@我的主机名 当前目录]# ");
        fflush(stdout);
        //2.获取用户输入
        memset(command_line,'\0',sizeof(command_line)*sizeof(char));
        fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
    
        command_line[strlen(command_line)-1]='\0';
        //3.“ls -a -l”--->"ls" "-a" "-l" 截取字符串
        command_args[0]=strtok(command_line,SEP);
        
        int index=1;
        //给ls命令添加颜色
        if(strcmp(command_args[0],"ls")==0) 
            command_args[index++]=(char*)"--color=auto";
        while(command_args[index++]=strtok(NULL,SEP));
        
        //4.TODO 编写后面的逻辑,内建命令
        if(strcmp(command_args[0],"cd")==0&&command_args[1]!=NULL)
        {
            ChangeDir(command_args[1]);//让调用方进行路径切换,父进程
            continue;//结束本次循环!!!
        }
 
        //5.创建进程,执行
        pid_t id=fork();
        if(id==0)
        {
            //child
            //6.程序替换
            execvp(command_args[0],command_args);

             exit(1);//执行到这里,子进程一定替换失败了
        }
        int status=0;
        pid_t ret=waitpid(id,&status,0);
        if(ret>0)
        {
            printf("等待子进程成功:sig:%d,code:%d\n",status&0x7F,(status>>8)&0xFF);


        }//end while

    }
    return 0;
}

在这里插入图片描述
这样我们就完成了路径的切换!!!

2.2 环境变量

首先来获取环境变量!(这个在我们之前的博客讲过,忘记了可以回顾以前的内容)。

#include<stdio.h>
int main()
{
    extern char** environ;
    for(int i=0;environ[i]!=NULL;i++)
    {
        printf("[%d]:%s\n",i,environ[i]);
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述
现在我想导入一个环境变量!

export 变量名

在这里插入图片描述

在这里插入图片描述

我们使用我们自己制作的myshell来运行一下该进程。

在这里插入图片描述
我们也看到了相同的环境变量。我们发现默认会将父进程的环境变量继承到子进程的!

接下来我们想要在子进程导入环境变量,该怎么做呢?

在这里插入图片描述

我们导入一个环境变量’MYVAL’

在这里插入图片描述

导入之前系统是没有的!

在这里插入图片描述

导入后!

在这里插入图片描述

这个导入环境变量的操作也需要导入给我们的父进程,当然也就需要内建命令!!

在这里插入图片描述

程序env

#include<stdio.h>
#include<stdlib.h>
int main()
{
    printf("MYVAL=%s\n",getenv("MYVAL"));
    return 0;
}

myshell

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

#define SEP " "
#define NUM 1024
#define SIZE 128
char command_line[NUM];
char* command_args[SIZE];
char env_buffer[NUM];//for test

//对应上层的内建命令
int ChangeDir(const char* new_path)
{
    chdir(new_path);

    return 0;//调用成功
}

void PutEnvInMyShell(char* new_env)
{
    putenv(new_env);

}
int main()
{
    //shell本质上就是一个死循环
    while(1)
    {
        //不关心获取这些属性的接口,搜索一下
        //1.显示提示符
        printf("[曾曾@我的主机名 当前目录]# ");
        fflush(stdout);
        //2.获取用户输入
        memset(command_line,'\0',sizeof(command_line)*sizeof(char));
        fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
    
        command_line[strlen(command_line)-1]='\0';
        //3.“ls -a -l”--->"ls" "-a" "-l" 截取字符串
        command_args[0]=strtok(command_line,SEP);
        
        int index=1;
        //给ls命令添加颜色
        if(strcmp(command_args[0],"ls")==0) 
            command_args[index++]=(char*)"--color=auto";
        while(command_args[index++]=strtok(NULL,SEP));
        
        //4.TODO 编写后面的逻辑,内建命令
        if(strcmp(command_args[0],"cd")==0&&command_args[1]!=NULL)
        {
            ChangeDir(command_args[1]);//让调用方进行路径切换,父进程
            continue;
        }
        
        if(strcmp(command_args[0],"export")==0&&command_args[1]!=NULL)
        {
            //目前,环境变量的信息在command_line,会被清空
            //此处我们需要自己保存一下环境变量内容
            strcpy(env_buffer,command_args[1]);
            PutEnvInMyShell(command_args[1]);
            continue;
        }        
        //5.创建进程,执行
        pid_t id=fork();
        if(id==0)
        {
            //child
            //6.程序替换
            execvp(command_args[0],command_args);

             exit(1);//执行到这里,子进程一定替换失败了
        }
        int status=0;
        pid_t ret=waitpid(id,&status,0);
        if(ret>0)
        {
            printf("等待子进程成功:sig:%d,code:%d\n",status&0x7F,(status>>8)&0xFF);


        }//end while

    }
    return 0;
}

在这里插入图片描述

此时就将环境变量导入成功了!

三、重定向操作

在这里插入图片描述

此时我们发现自己完成的shell并不支持重定向操作!

首先我们需要先对输入的命令行内容进行分解!

分别找到是重定向还是追加重定向还是输出重定向!

然后再进行分解得到文件名和标记位。进行封装后利用我们上节博客学到的dup2进行重定向操作后就可以完成我们的需求!

获取文件名和标记位
在这里插入图片描述

进行重定向操作后!
在这里插入图片描述

在这里插入图片描述

至此我们就将myshell的操作完美进行了模拟实现。和Xshell的功能大差不差!!!


(本章完!)

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到