【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

这篇具有很好参考价值的文章主要介绍了【Linux初阶】进程替换的应用 - 简易命令行解释器的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🌟hello,各位读者大大们你们好呀🌟
🍭🍭系列专栏:【Linux初阶】
✒️✒️本篇内容:使用代码手段实现一个简易的命令行解释器,其中功能包括:打印输出提示符、获取用户输入、字符串切割、执行命令、ls指令下拥有颜色提示、cd、echo;
🚢🚢作者简介:计算机海洋的新进船长一枚,请多多指教( •̀֊•́ ) ̖́-



前言

本篇文章建立在学习完进程替换的基础知识之上,如果有小伙伴对进程替换的知识不清楚,可以参考我的这篇文章:【Linux初阶】进程程序替换 | 初识、原理、函数、应用 & makefile工具的多文件编译


一、回顾execvp的应用方式

在我们学习程序替换基础知识的过程中,我们曾经接触过 execvp函数:

代码示例

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

int main(int argc, char* argv[])
{
    printf("process is running...\n");
    pid_t id = fork();
    assert(id != -1);

    if (id == 0)
    {

        sleep(1);
        // execvp传递方法如下
        // ./exec ls -a -l -> "./exec" "ls" "-a" "-l"
        execvp(argv[1], &argv[1]);

        exit(1); //must failed
    }

    int status = 0;
    pid_t ret = waitpid(id, &status, 0);
    if (ret > 0) printf("wait success: exit code: %d, sig: %d\n", (status >> 8) & 0xFF, status & 0x7F);
}

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

它实现了用我们自己的程序替换shell的指令,那么如果我们可以将运行的方式进一步简化,那么不就可以变成一个简易的 shell了吗?

下面,我将会带着大家结合相关知识,去制作一个简易的命令行解释器。首先,我们创建文件 myshell.c 和 Makefile,在里面进行代码的书写。


二、打印输出提示符

  • Makefile文件编写

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

  • myshell.c文件编写
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //进程替换
#include <sys/types.h> //进程等待
#include <sys/wait.h>

int main()
{
    // 输出提示符
    printf("用户名@主机名 当前路径# ");
    fflush(stdout); //刷新输出缓冲区
    sleep(10);
}

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

完成之后可以输出提示符,在提示符后可以输入其他指令。


三、获取用户输入

#include <stdio.h>
#include <stdlib.h> //fgets
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h> //断言

#define NUM 1024

char lineCommand[NUM];

int main()
{
    // 输出提示符
    printf("用户名@主机名 当前路径# ");
    fflush(stdout);

    // 获取用户输入, 输入完成的时候,输入\n
    char* s = fgets(lineCommand, sizeof(lineCommand) - 1, stdin);
    assert(s != NULL);
    (void)s;
    printf("test : %s\n", lineCommand);
}

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

通过运行我们发现,输入指令后按回车,它把我们的回车也读取进去了,导致打印时会有一行空行,我们需要将字符串末尾处的 /n除去。

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

#define NUM 1024

char lineCommand[NUM];

int main()
{
    // 输出提示符
    printf("用户名@主机名 当前路径# ");
    fflush(stdout);

    // 获取用户输入, 输入完成的时候,输入\n
    char* s = fgets(lineCommand, sizeof(lineCommand) - 1, stdin);//#include <string.h>
    assert(s != NULL);//#include <assert.h>
    (void)s;
    // 清除最后一个\n , abcd\n
    lineCommand[strlen(lineCommand) - 1] = 0; // 将最后的\n变为0
    printf("test : %s\n", lineCommand);
}

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

我们发现,空行消失了!


四、字符串切割

在实际应用中,我们需要对完整的字符串进行切割,使它变成一个一个的子串,方便计算机读取(“ls -a -l -i” -> “ls” “-a” “-l” “-i”),我们可以创建指针数组、调用C语言中的 strtok接口完成字符串切割。

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

#define NUM 1024
#define OPT_NUM 64 //新的宏

char lineCommand[NUM];
char *myargv[OPT_NUM]; //指针数组

int main()
{
    // 输出提示符
    printf("用户名@主机名 当前路径# ");
    fflush(stdout);

    // 获取用户输入, 输入的时候,输入\n
    char* s = fgets(lineCommand, sizeof(lineCommand) - 1, stdin);
    assert(s != NULL);
    (void)s;
    // 清除最后一个\n , abcd\n
    lineCommand[strlen(lineCommand) - 1] = 0; // ?
    //printf("test : %s\n", lineCommand);

    // "ls -a -l -i" -> "ls" "-a" "-l" "-i" -> 1->n
    // 字符串切割
    myargv[0] = strtok(lineCommand, " ");//第一次分割,要传入字符串+分隔符
    int i = 1;

    // 如果没有子串了,strtok会返回NULL, myargv[end] = NULL
    while (myargv[i++] = strtok(NULL, " "));//第二次分割,要传入NULL+分隔符
    
    // 测试是否成功, 条件编译 - 可以结合下文理解,这里不影响
#ifdef DEBUG
        for(int i = 0 ; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif

}

Makefile文件修改(添加一个宏,为了实现条件编译)

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现
【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

通过测试,我们发现完成了字符串分割。但是,新的问题又出现了,我们的命令行只跑一次吗?当然不是,因此我们要添加一个死循环,保证命令行能多次输入。

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

#define NUM 1024
#define OPT_NUM 64

char lineCommand[NUM];
char *myargv[OPT_NUM]; //指针数组

int main()
{
    while(1)
    {
        // 输出提示符
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);

        // 获取用户输入, 输入的时候,输入\n
        char *s = fgets(lineCommand, sizeof(lineCommand)-1, stdin);
        assert(s != NULL);
        (void)s;
        // 清除最后一个\n , abcd\n
        lineCommand[strlen(lineCommand)-1] = 0; // ?
        //printf("test : %s\n", lineCommand);
        
        // "ls -a -l -i" -> "ls" "-a" "-l" "-i" -> 1->n
        // 字符串切割
        myargv[0] = strtok(lineCommand, " ");
        int i = 1;

        // 如果没有子串了,strtok->NULL, myargv[end] = NULL
        while(myargv[i++] = strtok(NULL, " "));


        // 测试是否成功, 条件编译
#ifdef DEBUG
        for(int i = 0 ; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif

    }
}

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

至此,我们支持了命令行的多次输入。


五、执行命令

我们选择 execvp来获取数据,因为它带有v、p,不用输入具体的路径,只需要目标文件+执行选项(数组)即可。

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

#define NUM 1024
#define OPT_NUM 64

char lineCommand[NUM];
char *myargv[OPT_NUM]; //指针数组

int main()
{
    while(1)
    {
        // 输出提示符
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);

        // 获取用户输入, 输入的时候,输入\n
        char *s = fgets(lineCommand, sizeof(lineCommand)-1, stdin);
        assert(s != NULL);
        (void)s;
        // 清除最后一个\n , abcd\n
        lineCommand[strlen(lineCommand)-1] = 0; // ?
        //printf("test : %s\n", lineCommand);
        
        // "ls -a -l -i" -> "ls" "-a" "-l" "-i" -> 1->n
        // 字符串切割
        myargv[0] = strtok(lineCommand, " ");
        int i = 1;
        if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)
        {
            myargv[i++] = (char*)"--color=auto";
        }

        // 如果没有子串了,strtok->NULL, myargv[end] = NULL
        while(myargv[i++] = strtok(NULL, " "));


        // 测试是否成功, 条件编译
#ifdef DEBUG
        for(int i = 0 ; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif
        // 执行命令
        pid_t id = fork();
        assert(id != -1);

        if(id == 0)
        {
            execvp(myargv[0], myargv);//目标文件+执行选项(数组)
            exit(1);//继续往后执行,说明替换失败,直接返回
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0); //进程等待

    }
}

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现

至此,我们已经完成了一个最简单的shell了。当然,后面还有一些细节需要补充,让我们的shell更完善。


六、ls添加颜色

输入指令不能为空;ls指令下,添加颜色标识目录或文件:

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

#define NUM 1024
#define OPT_NUM 64

char lineCommand[NUM];
char *myargv[OPT_NUM]; //指针数组

int main()
{
    while(1)
    {
        // 输出提示符
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);

        // 获取用户输入, 输入的时候,输入\n
        char *s = fgets(lineCommand, sizeof(lineCommand)-1, stdin);
        assert(s != NULL);
        (void)s;
        // 清除最后一个\n , abcd\n
        lineCommand[strlen(lineCommand)-1] = 0; // ?
        //printf("test : %s\n", lineCommand);
        
        // "ls -a -l -i" -> "ls" "-a" "-l" "-i" -> 1->n
        // 字符串切割
        myargv[0] = strtok(lineCommand, " ");
        int i = 1;
        if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)//输入指令不能为空;ls指令下,添加颜色标识目录或文件
        {
            myargv[i++] = (char*)"--color=auto"; //添加颜色在这里!!!
        }

        // 如果没有子串了,strtok->NULL, myargv[end] = NULL
        while(myargv[i++] = strtok(NULL, " "));


        // 测试是否成功, 条件编译
#ifdef DEBUG
        for(int i = 0 ; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif
        // 执行命令
        pid_t id = fork();
        assert(id != -1);

        if(id == 0)
        {
            execvp(myargv[0], myargv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
    }
}

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现


七、实现 cd

通过实验发现,我们自己制作的简易 shell输入cd.. 后路径不变,接下来我们就来实现 cd。在实现路径转换时,我们要清楚,当前路径是什么,实际上,当前路径就是进程的工作目录。

ls /proc/进程pid -al    #查看进程属性的命令

为什么我们自己的 shell不能 cd呢?因为我们在fork创建子进程后,是在子进程中进行目录更改的,子进程拥有自己的工作目录,子进程目录的更改不影响父进程的工作目录,在子进程结束后,我们用的依旧是父进程,即shell。也就是说,在我们cd之后,再输入pwd指令,路径不会改变,因为pwd又是父进程创建的另一个子进程去执行的了!

当前的工作目录可以被修改,我们可以用 chdir更改当前目录。

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

#define NUM 1024
#define OPT_NUM 64

char lineCommand[NUM];
char *myargv[OPT_NUM]; //指针数组

int main()
{
    while(1)
    {
        // 输出提示符
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);

        // 获取用户输入, 输入的时候,输入\n
        char *s = fgets(lineCommand, sizeof(lineCommand)-1, stdin);
        assert(s != NULL);
        (void)s;
        // 清除最后一个\n , abcd\n
        lineCommand[strlen(lineCommand)-1] = 0; // ?
        //printf("test : %s\n", lineCommand);
        
        // "ls -a -l -i" -> "ls" "-a" "-l" "-i" -> 1->n
        // 字符串切割
        myargv[0] = strtok(lineCommand, " ");
        int i = 1;
        if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)
        {
            myargv[i++] = (char*)"--color=auto";
        }

        // 如果没有子串了,strtok->NULL, myargv[end] = NULL
        while(myargv[i++] = strtok(NULL, " "));

        // 如果是cd命令,不需要创建子进程,让shell自己执行对应的命令,本质就是执行系统接口
        // 像这种不需要让我们的子进程来执行,而是让shell自己执行的命令 --- 内建/内置命令
        if(myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
        {
            if(myargv[1] != NULL) chdir(myargv[1]);// chdir(需要更改的路径)
            continue;
        }

        // 测试是否成功, 条件编译
#ifdef DEBUG
        for(int i = 0 ; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif
        // 内建命令 --> echo

        // 执行命令
        pid_t id = fork();
        assert(id != -1);

        if(id == 0)
        {
            execvp(myargv[0], myargv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);

    }
}

八、实现echo

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

#define NUM 1024
#define OPT_NUM 64

char lineCommand[NUM];
char *myargv[OPT_NUM]; //指针数组
int  lastCode = 0; //这里添加了变量方便输出
int  lastSig = 0;

int main()
{
    while(1)
    {
        // 输出提示符
        printf("用户名@主机名 当前路径# ");
        fflush(stdout);

        // 获取用户输入, 输入的时候,输入\n
        char *s = fgets(lineCommand, sizeof(lineCommand)-1, stdin);
        assert(s != NULL);
        (void)s;
        // 清除最后一个\n , abcd\n
        lineCommand[strlen(lineCommand)-1] = 0; // ?
        //printf("test : %s\n", lineCommand);
        
        // "ls -a -l -i" -> "ls" "-a" "-l" "-i" -> 1->n
        // 字符串切割
        myargv[0] = strtok(lineCommand, " ");
        int i = 1;
        if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)
        {
            myargv[i++] = (char*)"--color=auto";
        }

        // 如果没有子串了,strtok->NULL, myargv[end] = NULL
        while(myargv[i++] = strtok(NULL, " "));

        // 如果是cd命令,不需要创建子进程,让shell自己执行对应的命令,本质就是执行系统接口
        // 像这种不需要让我们的子进程来执行,而是让shell自己执行的命令 --- 内建/内置命令
        if(myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
        {
            if(myargv[1] != NULL) chdir(myargv[1]);
            continue;
        }
        
        //实现echo - 在这里!!!
        if(myargv[0] != NULL && myargv[1] != NULL && strcmp(myargv[0], "echo") == 0)
        {
            if(strcmp(myargv[1], "$?") == 0)
            {
                printf("%d, %d\n", lastCode, lastSig);
            }
            else
            {
                printf("%s\n", myargv[1]);
            }
            continue;
        }
        // 测试是否成功, 条件编译
#ifdef DEBUG
        for(int i = 0 ; myargv[i]; i++)
        {
            printf("myargv[%d]: %s\n", i, myargv[i]);
        }
#endif
        // 内建命令 --> echo

        // 执行命令
        pid_t id = fork();
        assert(id != -1);

        if(id == 0)
        {
            execvp(myargv[0], myargv);
            exit(1);
        }
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        assert(ret > 0);
        (void)ret;
        lastCode = ((status>>8) & 0xFF);//将status传换成数字信号
        lastSig = (status & 0x7F);
    }
}

【Linux初阶】进程替换的应用 - 简易命令行解释器的实现


结语

🌹🌹 简易命令行解释器的实现 的知识大概就讲到这里啦,博主后续会继续更新更多C++的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪文章来源地址https://www.toymoban.com/news/detail-470886.html

到了这里,关于【Linux初阶】进程替换的应用 - 简易命令行解释器的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • linux文件上传和下载、别名设置以及命令解释器

    (1) 它类似于 ftp 传输协议,属于 ssh, 但它进行加密传输,相对 FTP 来讲有更高的安全性 (2)用法 查看文件下载情况-将文件内容copy到一个file1.txt文件中 将10.0.0.3的file1,txt文件上传到当前文件的目录下面 (3) 如果不知道远程主机的目录是什么样,  ls命令 可以列出10.0.

    2024年02月03日
    浏览(64)
  • Linux之进程控制&&进程终止&&进程等待&&进程的程序替换&&替换函数&&实现简易shell

    1.1 fork的使用 我们可以使用man指令来查看一下 子进程会复制父进程的PCB,之间代码共享,数据独有,拥有各自的进程虚拟地址空间。 这里就有一个代码共享,并且子进程是拷贝了父进程的PCB,虽然他们各自拥有自己的进程虚拟地址空间,数据是拷贝过来的,通过页表映射到

    2024年04月17日
    浏览(57)
  • 【Linux】进程控制 — 进程程序替换 + 实现简易shell

    上一节我们讲了进程终止和进程等待等一系列问题,并做了相应的验证,本章将继续对进程控制进行学习,我们将学习进程程序替换,进行相关验证,运用系统进程程序替换接口,自己模拟写一个shell,该shell能够实现执行指令,等一系列命令行操作…… 概念引入: 将可执行

    2024年02月06日
    浏览(53)
  • 【Linux】进程程序替换 && 做一个简易的shell

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 文章目录 前言 进程程序替换 替换原理 先看代码和现象 替换函数 第一个execl(): 第二个execv(): 第三个execvp(): 第四个execvpe(): 环境变量 第五个execlp(): 第六个execle(): 函数解释 命名理解 在Make

    2024年04月11日
    浏览(44)
  • bin文件夹和命令行解释器cmd 的简单认识

      在许多软件安装过程中,宝宝们可能会看到一个名为 \\\"bin\\\"的文件夹 。\\\"bin\\\"是二进制(binary)的缩写,通常用于存放可执行文件(executable files)或二进制文件。它的主要作用是存储程序的实际可执行代码,以便在需要时运行。 在数据库和SQL专栏中,分享的 MySQL安装教程中

    2024年02月21日
    浏览(56)
  • 解锁Spring Boot中的设计模式—02.解释器模式:探索【解释器模式】的奥秘与应用实践!

    解释器模式(Interpreter Pattern)是一种行为设计模式,它用于定义语言的文法,并且解释语言中的表达式。在Java中,解释器模式可以用于构建解释器以解析特定的语言或表达式,如数学表达式、查询语言等。 优点: 灵活性: 解释器模式可以 灵活地添加新的表达式和规则 ,因

    2024年02月19日
    浏览(72)
  • 进程程序替换+简易版shell实现

    什么是进程程序替换? 指在一个正在运行的进程中,将原来的程序替换成新的程序的过程。 eg:如果我们想让fork创建出来的子进程执行全新的任务,此时就需要进程程序替换 为什么要进程程序替换呢? 我们一般在服务器设计的时候(linux编程的时候)往往需要子进程干两种

    2024年02月05日
    浏览(39)
  • iThinkAir代码解释器对照Code Interpreter的应用案例

    前几天OpenAI对Plus会员开放了Code Interpreter功能,有人说是王炸,有人说是核弹级更新,也有人说是继ChatGPT之后再度让人感受到震撼和颠覆的产品。 时隔几天,iThinkAir也创造了自己的\\\"代码解释器\\\"。 下面列举iThinkAir\\\"代码解释器\\\"的十几个应用案例,大家可以和Code Interpreter对照一

    2024年02月16日
    浏览(60)
  • 【探索Linux】—— 强大的命令行工具 P.10(进程的控制——创建、终止、等待、程序替换)

    前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的一些知识,也学习了一些Linux的基本操作,也了解并学习了有关Linux开发工具vim 、gcc/g++ 使用、yum工具以及git 命令行提交代码也相信大家都掌握的不错,上一篇文章我们了解了关于进程的地址空间,今

    2024年02月08日
    浏览(53)
  • 认识环境变量和进程替换,实现一个简易的shell

    首先,在百度百科中,环境变量的解释是这样的: 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将

    2024年02月08日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包