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

news2025/1/19 20:39:48

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


文章目录

  • 前言
  • 一、回顾execvp的应用方式
  • 二、打印输出提示符
  • 三、获取用户输入
  • 四、字符串切割
  • 五、执行命令
  • 六、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);
}

在这里插入图片描述

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

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


二、打印输出提示符

  • Makefile文件编写

在这里插入图片描述

  • 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);
}

在这里插入图片描述

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


三、获取用户输入

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

在这里插入图片描述

通过运行我们发现,输入指令后按回车,它把我们的回车也读取进去了,导致打印时会有一行空行,我们需要将字符串末尾处的 /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);
}

在这里插入图片描述

我们发现,空行消失了!


四、字符串切割

在实际应用中,我们需要对完整的字符串进行切割,使它变成一个一个的子串,方便计算机读取(“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文件修改(添加一个宏,为了实现条件编译)

在这里插入图片描述
在这里插入图片描述

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

#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

    }
}

在这里插入图片描述

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


五、执行命令

我们选择 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); //进程等待

    }
}

在这里插入图片描述

至此,我们已经完成了一个最简单的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);
    }
}

在这里插入图片描述


七、实现 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);
    }
}

在这里插入图片描述


结语

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

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

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

相关文章

实验11 人工神经网络(2)

1. 实验目的 ①掌握梯度下降法的优化算法&#xff1b; ②能够使用tf.keras构建Sequential模型&#xff0c;完成多分类任务。 2. 实验内容 ①下载MNIST数据集&#xff0c;建立神经网络模型&#xff0c;实现对MNIST手写数字数据集的识别&#xff0c;调整超参数和训练参数&…

wps js宏编辑器案例3-工作簿和工作表操作

本文通过一个连锁门店每天营业额按门店名称进行拆分的案例&#xff0c;讲述wps js宏编辑器中工作簿、工作表和Range的相关操作&#xff0c;比如&#xff1a;工作表的复制&#xff0c;工作簿另存&关闭&#xff0c;Range.findNext使用注意实现等&#xff0c;案例已上传到gite…

Android学习专题】java基本概念及日常问题处理(学习记录)

java程序的入口函数 java入口函数只有一种&#xff0c;而且必须以main命名&#xff0c;且以public static进行修饰。 入口(Entry of Program)&#xff1a;指程序运行的起点。 只有main方法可以作为程序的入口。 main方法结构&#xff1a;public static void main(String[] arg…

Meaning of life: The patch of life

这世界上每一个人都不是完美的&#xff0c;生活也是如此&#xff0c;一场电影、一次旅行、一把游戏、一次畅谈都是生命的"补丁"&#xff0c; 生活总有不顺心的时候&#xff0c;但我们也要继续前行。 The patch of life Not long ago, My daughters frosting shoes w…

chatgpt赋能Python-python_bold

Python Bold&#xff1a;强劲的SEO工具 在当今数字化的时代&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;基本上是每个企业成功的重要因素之一。然而SEO并不是一项易于处理的任务。面对越来越激烈的竞争&#xff0c;只有使用最好的工具才能真正成功。而Python Bold就是…

即时通讯(IM)开源项目OpenIM对WebAssembly支持,提升web端体验

WebAssembly 是什么&#xff1f; 2019 年 12 月&#xff0c;W3C 标准批准了第四种官方语言&#xff1a;WebAssembly (Wasm)。这种语言在结构、使用和功能方面与以前的语言有很大不同。 WebAssembly 是一种新的编码方式&#xff0c;可以在现代的Web浏览器中运行 &#xff0d; …

[CTF/网络安全] 攻防世界 Web_php_include 解题详析(php伪协议、data伪协议、file伪协议)

[CTF/网络安全] 攻防世界 Web_php_include 解题详析 代码审计PHP_include文件包含漏洞及伪协议PayloadPHP伪协议恶意 PHP 伪协议data伪协议恶意 data 伪协议file伪协议恶意 file 伪协议 知识点及姿势读取目录路径dirname(FILE)读取目录路径实例读取目录文件glob读取目录文件实例…

chatgpt赋能Python-python_check_module

Python Check Module简介 Python是一个非常流行的编程语言&#xff0c;具有易学、易用的特点&#xff0c;因此在算法、数据分析等领域广泛使用。Python Check Module是Python的一个模块&#xff0c;可以用于对Python代码进行静态分析&#xff0c;检测代码中的潜在问题&#xf…

chatgpt赋能Python-python_canal

Python Canal: 让你的代码流畅无阻 Python Canal 是一个简单易用的Python 并行工具&#xff0c;可以帮助你提高应用程序的性能。它使用了现代操作系统的资源管理能力来有效地管理你的多进程和多线程应用程序&#xff0c;这可以使你的应用程序更轻松地处理大规模数据和异步任务…

Python集合:让你的数据去重变得更简单!

集合set是一个无序的、不可重复的元素集合。 如果你想学习自动化测试&#xff0c;我这边给你推荐一套视频&#xff0c;这个视频可以说是B站百万播放全网第一的自动化测试教程&#xff0c;同时在线人数到达1000人&#xff0c;并且还有笔记可以领取 B站讲的最详细的Python接口自…

使用Win-flex bison生成一个计算器(VS2022)

本文参考网页“Flex和Bison简介和Windows下使用入门”&#xff08; Flex和Bison简介和Windows下使用入门 | 码农家园 &#xff09;&#xff0c;使用Win-flex bison生成生成一个计算器&#xff08;VS2022&#xff09;。 1. 使用VS2022创建空项目 使用VS2022创建名为MyProject的…

【html】制作一个能生成图片的markdown在线编辑器

markdown是什么&#xff0c;为什么大家都会用它呢&#xff0c;如果你想写文章&#xff0c;写文档&#xff0c;写作&#xff0c;用markdown是最好的选择&#xff0c;它是一个很好用的文档排版工具&#xff0c;觉得好用&#xff0c;何不尝试把它弄下来做成自己的呢&#xff0c;接…

面试题:如何测试登录功能

最近在做一个创新项目&#xff0c;这个项目有二个平台&#xff0c;每个平台都有前后端&#xff0c;故有四个系统&#xff0c;每个系统都有登录功能&#xff0c;而且不同系统代码设计方式都有所差异&#xff0c;所以就这个登录功能而言就要测试四次&#xff0c;看似一个简单的登…

面试官:你的技术看起来像是初级

最近看了很多简历&#xff0c;很多候选人年限不小&#xff0c;但是做的都是一些非常传统的项目&#xff0c;想着也不能通过简历就直接否定一个人&#xff0c;何况现在大环境越来 越难&#xff0c;大家找工作也不容易&#xff0c;于是就打算见一见。 在沟通中发现&#xff0c;由…

信号完整性分析基础知识之传输线和反射(六):传输线中拐角、过孔等容性负载的反射

测试焊盘、过孔、封装引线&#xff0c;甚至连在传输线中间的小短截线都可以充当集总电容器。下图显示了在走线中间添加电容器时的反射电压和传输电压。由于电容器最初具有低阻抗&#xff0c;因此反射回源的信号将有轻微的负下降。如果在走线的前端附近连接了一个接收器&#xf…

用pip安装cartopy(windows平台),解决GEOS库的问题

对象&#xff1a;cartopy Cartopy官方网站介绍&#xff1a; Cartopy is a Python package designed for geospatial data processing in order to produce maps and other geospatial data analyses. Cartopy是一个Python包&#xff0c;旨在进行地理空间数据处理&#xff0c;以…

Metersphere+jar+beanshell+连接linux

Meterspherejarbeanshell连接linux java编写连接linux代码 使用jsch连接linux&#xff0c;下载jsch包或者使用maven <dependencies><dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55<…

小航助学GESP_C++二级模式测试试卷(含题库答题软件账号)

GESP在线模拟训练系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 答案:A 第1题人们在使用计算机时所提到的 Windows 通常指的是&#xff08;&#xff09;。 A、操作系统B、多…

就业内推 | 国企中电福富专场!多地有岗,CCIE、HCIE认证优先

01 中电福富信息科技有限公司 &#x1f537;招聘岗位&#xff1a;网络工程师&#xff08;北京&#xff09; &#x1f537;职责描述&#xff1a; 1、负责云平台、数据中心网络架构的调整和性能优化&#xff0c;确保网络的性能、稳定和安全性&#xff1b; 2、对网络突发事件、网…

力扣LCP 33. 蓄水

LCP 33. 蓄水 给定 N 个无限容量且初始均空的水缸&#xff0c;每个水缸配有一个水桶用来打水&#xff0c;第 i 个水缸配备的水桶容量记作 bucket[i]。有以下两种操作&#xff1a; 升级水桶&#xff1a;选择任意一个水桶&#xff0c;使其容量增加为 bucket[i]1 蓄水&#xff1…