C语言编写Linux的Shell外壳

目录

一、输出命令行

1.1 了解环境变量

1.2 获取用户名、主机名、当前路径

1.3 缓冲区改进MakeCommandLine

二、获取用户命令

2.1 读取函数的选择

2.2 细节优化

2.3 返回值

三、指令和选项分割

3.1 strtok 函数

3.2 分割实现 

四、执行命令

4.1 fork 方法

4.2 进程等待

4.3 进程替换

4.4 程序编写

五、程序优化

5.1 执行次数

5.2 检测命令是否为内建命令

5.3 子进程执行失败

5.4 命令行路径更改

六、完整代码


在学习之前我们要认识到,Shell外壳中的命令行以及我们输入的指令都是字符串!

首先我们要创建两个文件:MyShell.cmakefile ,一个存储我们的 Shell 外壳,一个方便操作

其次,我们看一下 MyShell.c 中需要包含的头文件:

  1 #include <stdio.h>2 #include <unistd.h>     //进程创建接口3 #include <sys/types.h>  //进程等待4 #include <sys/wait.h>   //进程等待     

一、输出命令行


首先我们先来认识一下命令行,其中, Flash 是当前用户名、@ 后紧跟的是当前主机名、主机名空格后紧跟的是当前路径。所以由此我们知道,如果想打印出我们的命令行,我们至少要知道三个信息:1.用户名 2.主机名 3.当前路径

1.1 了解环境变量

这三个信息如何拿去呢?我们的操作系统中有自带的环境变量,在环境变量中存放着我们需要的三个信息。我们如何查我们的环境变量呢?

有三种方法:

1.命令行参数 -> [env] ,当我们在命令行输入指令,系统就会显示出一大串信息(这里有省略),其中就有我们需要的 [USER] [PWD] [HOSTNAME]

2.ENVIRON 等第三方提供的接口,这里不做详细介绍

3.使用C语言提供的接口函数,如 getenv() ,我们这里使用该方式,下面会详细介绍。

1.2 获取用户名、主机名、当前路径

getenv 函数 —— stdlib.h C语言标准库

其中,传参 [const char* name] 表示的是我们需要从环境变量中拿取的变量名称,我们这里需要拿到的就是上述使用命令行参数 [env] 读取到的 [USER] [HOSTNAME] [PWD],它的返回值就是我们需要的一个字符串。

现在我们就可以来编写我们的函数了。

char* GetUserName()
{char* User = getenv("USER");if (!User) return "None";return User;
}
char* GetHostName()
{char* Host = getenv("HOSTNAME");if (!Host) return "None";return Host;
}
char* GetCwd()
{char* PWD = getenv("PWD");if (!PWD) return "None";return PWD;
}
void MakeCommandLine()
{char* UserName = GetUserName();char* HostName = GetHostName();char* Cwd = GetCwd();printf("[%s@%s %s]>\n", UserName, HostName, Cwd);
}
int main()
{MakeCommandLine();return 0;
}

我们还可以继续改进一下,使用缓冲区的概念

1.3 缓冲区改进MakeCommandLine

首先我们先宏定义一个缓冲区的大小,这里我设置为 256 。

#define SIZE 256
char Line[SIZE];//自定义缓冲区

其次我们再来学习一个函数 snprintf

按照参数的顺序,依次是 s : 需要写入的目的地 n : 写入字符的数量 format : 标准格式

使用 snprintf 就可以像我们自定义的缓冲区里写啦!

snprintf(Line, sizeof(Line), "[%s@%s %s]>", UserName, HostName, Cwd);
printf("%s\n", Line);

成品: 

#define SIZE 256
//只有MakeCommandLine函数变化,只展示该函数
void MakeCommandLine()
{char Line[SIZE];char* UserName = GetUserName();char* HostName = GetHostName();char* Cwd = GetCwd();snprintf(Line, sizeof(Line), "[%s@%s %s]>", UserName, HostName, Cwd);printf("%s\n", Line);
}

但是还有一个细节,当我们想让程序慢一点结束时,使用 sleep 函数,就会发生神奇的一幕:

为什么没有立刻打印呢?原因是 stdout 的缓冲问题。printf 是行缓冲的,通常情况下,当遇到换行符('\n')时,缓冲区的内容会被送往 stdout 进行输出。然而在某些情况下,如果在调用 sleep 之前没有刷新缓冲区,那么输出可能会延迟直到缓冲区被刷新。

我们的解决方法是使用 fflush 函数!这将刷新(即清空并发送)包含 printf 输出的缓冲区,从而确保立即看到输出:

snprintf(Line, sizeof(Line), "[%s@%s %s]>", UserName, HostName, Cwd);
printf("%s\n", Line);
fflush(stdout);
sleep(5);

二、获取用户命令

首先,我们在输出命令行时,为了方便阅读,用 printf 输出 Line 时加了 '\n' ,我们现在要保证命令行和命令在同一行,所以现在我们要删除掉 '\n' 。

2.1 读取函数的选择

我们可以继续使用 scanf 函数来读取命令吗?按照我们输入的命令,如 [ls -a -l] ,他们都是以空格为分隔符,显然与我们的 sacnf 发生了冲突,而且无法控制每次输入命令的空格数量,所以我们不能使用 scanf 读取输入。

我们的命令都是用行读取,C语言也提供了这样的函数 fgets 。

其中,返回指向读取到字符开头的指针,传参依次为读取字符后存放的位置,读取字符的长度,读取字符的位置。 

 int GetUserCommand(char Command[], size_t n){                                               char* s = fgets(Command, n, stdin);if (!s) return -1;                                                    }     int main()                                                         {                                                                  //输出命令行                                                     MakeCommandLine();                                               //读取用户命令                                                   char UserCommand[SIZE];                                          GetUserCommand(UserCommand, sizeof(UserCommand));  printf("echo : %s\n", UserCommand); //打印验证一下是否被读取                                            return 0;}

2.2 细节优化

现在又来了一个细节问题:

fgets 按行读取,如果我们想执行就必须按 [回车] ,假设我们输入的是"Hello World",那么其读取到的就是"Hello World\n",再加上我们自己写的 printf 中的 '\n' 就变成了两行,下面我们进一步优化一下,只需要把命令的最后一个字符改成 '\0' 即可:

 int GetUserCommand(char Command[], size_t n){                                               char* s = fgets(Command, n, stdin);if (!s) return -1;Command[strlen(Command) - 1] = '\0';                                                    } 

2.3 返回值

关于函数的返回值,为了与 [return -1] 区分开,也为了更好地执行命令,我们可以返回一下读取到的命令长度,这样当命令 [> 0] 时,我们再继续执行,否则直接退出:

 int GetUserCommand(char Command[], size_t n){                                               char* s = fgets(Command, n, stdin);if (!s) return -1;Command[strlen(Command) - 1] = '\0';    return strlen(Command);                                                } int main()                                                         {                                                                  //输出命令行                                                     MakeCommandLine();                                               //读取用户命令                                                   char UserCommand[SIZE];                                          int n = GetUserCommand(UserCommand, sizeof(UserCommand));  (void)n;//暂时不搞,先强转一下,防止警告                                         return 0;}

三、指令和选项分割

像我们上面使用的 []ls -a -l] ,它们是由指令和选项构成,所以我们如果要执行,肯定也要把读取用户传入的字符串分割成下面的形式, [ls] [-a] [-l] 。

3.1 strtok 函数

那么如何分割呢?这又要使用到C语言中的字符串函数 strtok 。

其中,返回值是分割后的小字符串,传入参数 str 是要分割的字符串, delimiters 是分隔符

如果首次分割是已经传入参数 str ,那么再次调用时把 str 设置为 NULL ,strtok就会默认继续分割上次的字符串。

一说到分隔符,我们这里的分隔符显而易见的就是空格啦!由于我们的分割是把一个字符串分割成若干小字符串,所以我们就可以直接定义一个全局的数组,用来挨个存放这些小字符串:

但是要注意, strtok 函数使用的分隔符都是字符串,我们的空格不能设置为 ' ' ,而应该为 " "

 #define SEP " "    

3.2 分割实现 

void SplitCommand(char Command[], size_t n)
{(void)n;argv[0] = strtok(Command, SEP);int index = 1;while ((argv[index++] = strtok(NULL, SEP)));//strtok如果无法分割,则返回NULL,此时argv最后一个元组直接被赋值为NULL且while循环结束
}

我们还可以通过打印来验证是否正确:

 int i = 0;                                                               for (; argv[i]; i++){printf("%s ", argv[i]);}printf("\n");

四、执行命令

我们之前讲过 Bash 会创建子进程,为了保证安全,都是子进程在执行我们的命令,而我们今天要实现的也是使用子进程来帮助我们执行命令。

4.1 fork 方法

fork 函数并不是我们的C语言函数,而是操作系统提供的,所以我们可以用 man 2 fork 查询

这里不做过多说明,实在不知道可以移步到Linux进程概念(1)-CSDN博客

如果执行的是子进程,那么返回值为0,如果是父进程,会返回子进程的 pid

4.2 进程等待

我们之前说过,如果父进程先于子进程退出,子进程就会变成僵尸进程,为了避免这种影响,父进程可以通过进程等待的方式,回收子进程资源,获取子进程退出信息。

下面我们来介绍几个进程等待的方法:

wait 方法:

    返回值:等待成功返回被等待子进程的 pid ,失败返回 -1

    参数:输出型参数,获取子进程退出状态,不关心则可以设置成为NULL

waitpid 方法:

    返回值:

       当正常返回的时候 waitpid 返回收集到的子进程的进程ID;如果设置了选项 WNOHANG,而调用中 waitpid 发现没有已退出的子进程可收集,则返回 0;如果调用中出错,则返回 -1,这时 errno 会被设置成相应的值以指示错误所在;

    参数:

    pid:
       pid=-1,等待任一个子进程。与wait等效。
       pid>0.等待其进程ID与pid相等的子进程。
    status:
       WIFEXITED: 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
       WEXITSTATUS: 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
    options:
       WNOHANG: 若 pid 指定的子进程没有结束,则 waitpid() 函数返回0,不予以等待。

       若正常结束,则返回该子进程的ID。

4.3 进程替换

我们用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

有六种以 exec 开头的函数,统称为 exec 函数:

#include <unistd.h>

int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

这些函数都可以用 man execl 查询:

4.4 程序编写

void ExecuteCommand(char Command[], size_t n)
{pid_t id = fork();if (id == 0)//子进程,执行命令{execvp(argv[0],argv);}else if (id > 0)//父进程{int status = 0;waitpid(id, &status, 0);}else//fork创建失败,直接退出{exit(1);}
}

下面我们来看一下完整代码以及效果图:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <unistd.h>     //进程创建接口
#include <sys/types.h>  //进程等待
#include <sys/wait.h>   //进程等待#define SIZE 256
#define NUM 16
#define SEP " "char* argv[NUM];char* GetUserName()
{char* User = getenv("USER");if (!User) return "None";return User;
}
char* GetHostName()
{char* Host = getenv("HOSTNAME");if (!Host) return "None";return Host;
}
char* GetCwd()
{char* PWD = getenv("PWD");if (!PWD) return "None";return PWD;
}
void MakeCommandLine()
{char Line[SIZE];char* UserName = GetUserName();char* HostName = GetHostName();char* Cwd = GetCwd();snprintf(Line, sizeof(Line), "[%s@%s %s]>", UserName, HostName, Cwd);printf("%s", Line);fflush(stdout);
}
int GetUserCommand(char Command[], size_t n)
{char* s = fgets(Command, n, stdin);if (!s) return -1;Command[strlen(Command) - 1] = '\0';return strlen(Command);
}
void SplitCommand(char Command[], size_t n)
{(void)n;argv[0] = strtok(Command, SEP);int index = 1;while ((argv[index++] = strtok(NULL, SEP)));//strtok如果无法分割,则返回NULL,此时argv最后一个元组直接被赋值为NULL且while循环结束
}
void ExecuteCommand(char Command[], size_t n)
{pid_t id = fork();if (id == 0)//子进程,执行命令{execvp(argv[0],argv);}else if (id > 0)//父进程{int status = 0;waitpid(id, &status, 0);}else//fork创建失败,直接退出{exit(1);}}
int main()
{//输出命令行MakeCommandLine();//读取用户命令char UserCommand[SIZE];int n = GetUserCommand(UserCommand, sizeof(UserCommand));(void)n;printf("echo : %s\n", UserCommand);//指令和选项分割SplitCommand(UserCommand, sizeof(UserCommand));//执行命令ExecuteCommand(UserCommand, sizeof(UserCommand));return 0;
}

五、程序优化

5.1 执行次数

虽然我们的 Shell 已经完成的有一点雏形了,但是怎么这个外壳只能使用一次呀?我们是不是要让他多执行几次呢?所以我们就要把这几个步骤都放到一个 while 循环中。

int main()
{while(1){//输出命令行MakeCommandLine();//读取用户命令char UserCommand[SIZE];int n = GetUserCommand(UserCommand, sizeof(UserCommand));(void)n;//指令和选项分割SplitCommand(UserCommand, sizeof(UserCommand));//执行命令ExecuteCommand(UserCommand, sizeof(UserCommand));}return 0;
}

5.2 检测命令是否为内建命令

我们在我们的 Shell 中使用 cd 命令,但是我们的命令行无法进入某目录,这是为什么呢?
因为我们上面创建了子进程,我们的 cd 命令是让子进程执行的,和我们真正的 bash 没有关系,我们正确的做法是让父进程执行!

什么是内建命令呢?

下面我们来看看如何检测是否为内建命令。

虽然我们的 [cd] 已经可以使用,但是我们的命令行路径怎么不回退呢?
因为我们还要更改我们的环境变量!

更改环境变量要用到系统调用,需要获取当前路径,并写入环境变量中。

获取当前路径:

man 2 getcwd

写入环境变量:

man snprintf

man putenv

所以我们此时在对我们的 cd 函数做修改:

void ExecuteCd()
{const char* path = argv[1];if (path == NULL) path = GetHome();else chdir(path);char tmp[SIZE];getcwd(tmp, sizeof(tmp));snprintf(cwd, sizeof(cwd), "PWD=%s", tmp);putenv(cwd);
}

当然还有其他内建命令,方法都诸如此类。

5.3 子进程执行失败

我们直接在全局定义一个退出码 int lastcode ,然后在父进程中左对应的修改:

void ExecuteCommand(char Command[], size_t n)
{pid_t id = fork();if (id == 0)//子进程,执行命令{execvp(argv[0],argv);}else if (id > 0)//父进程{int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);if (lastcode != 0) printf("%s:%s:%d\n", argv[0], strerror(lastcode), lastcode);}}else//fork创建失败,直接退出{exit(1);}
}

5.4 命令行路径更改

在我们的 XShell 中提供的 Shell 外壳,其命令行的路径都是相对路径,我们的 Shell 也可以改成这样,如下:

我们这里采用了宏函数,也可以使用正常函数,在使用宏函数是时,若程序是代码块,建议放在 [do while(0)] 中,如下:

#define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; }while(0)

同时在输出命令行中也调用该函数,如下:

void MakeCommandLine()
{char Line[SIZE];char* UserName = GetUserName();char* HostName = GetHostName();char* Cwd = GetCwd();SkipPath(Cwd);snprintf(Line, sizeof(Line), "[%s@%s %s]>", UserName, HostName, Cwd);printf("%s", Line);fflush(stdout);
}

六、完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <unistd.h>     //进程创建接口
#include <sys/types.h>  //进程等待
#include <sys/wait.h>   //进程等待#define SIZE 256
#define NUM 16
#define SEP " "
#define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; }while(0)char* argv[NUM];
char cwd[SIZE];
int lastcode = 0;char* GetUserName()
{char* User = getenv("USER");if (!User) return "None";return User;
}
char* GetHostName()
{char* Host = getenv("HOSTNAME");if (!Host) return "None";return Host;
}
char* GetCwd()
{char* PWD = getenv("PWD");if (!PWD) return "None";return PWD;
}
void MakeCommandLine()
{char Line[SIZE];char* UserName = GetUserName();char* HostName = GetHostName();char* Cwd = GetCwd();SkipPath(Cwd);snprintf(Line, sizeof(Line), "[%s@%s %s]>", UserName, HostName, Cwd);printf("%s", Line);fflush(stdout);
}
int GetUserCommand(char Command[], size_t n)
{char* s = fgets(Command, n, stdin);if (!s) return -1;Command[strlen(Command) - 1] = '\0';return strlen(Command);
}
void SplitCommand(char Command[], size_t n)
{(void)n;argv[0] = strtok(Command, SEP);int index = 1;while ((argv[index++] = strtok(NULL, SEP)));//strtok如果无法分割,则返回NULL,此时argv最后一个元组直接被赋值为NULL且while循环结束
}
const char* GetHome()
{const char* home = getenv("HOME");if (home == NULL) return "/";return home;
}
void ExecuteCd()
{const char* path = argv[1];if (path == NULL) path = GetHome();else chdir(path);char tmp[SIZE];getcwd(tmp, sizeof(tmp));snprintf(cwd, sizeof(cwd), "PWD=%s", tmp);putenv(cwd);
}
int CheckBuiltin()
{int ch = 0;//默认非内建命令const char* command = argv[0];if (strcmp(command, "cd") == 0){ch = 1;ExecuteCd();}return ch;
}
void ExecuteCommand(char Command[], size_t n)
{pid_t id = fork();if (id == 0)//子进程,执行命令{execvp(argv[0],argv);}else if (id > 0)//父进程{int status = 0;pid_t rid = waitpid(id, &status, 0);if (rid > 0){lastcode = WEXITSTATUS(status);if (lastcode != 0) printf("%s:%s:%d\n", argv[0], strerror(lastcode), lastcode);}}else//fork创建失败,直接退出{exit(1);}}int main()
{while(1){//输出命令行MakeCommandLine();//读取用户命令char UserCommand[SIZE];int n = GetUserCommand(UserCommand, sizeof(UserCommand));(void)n;//指令和选项分割SplitCommand(UserCommand, sizeof(UserCommand));//检查是否为内建命令int ch = CheckBuiltin();if (ch) continue;//执行命令ExecuteCommand(UserCommand, sizeof(UserCommand));}return 0;
}

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

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

相关文章

0.17元的4位数码管驱动芯片AiP650,支持键盘,还是无锡国家集成电路设计中心某公司的

推荐原因&#xff1a;便宜的4位数码管驱动芯片 只要0.17元&#xff0c;香吗&#xff1f;X背景的哦。 2 线串口共阴极 8 段 4 位 LED 驱动控制/7*4 位键盘扫描专用电路 AIP650参考电路图 AIP650引脚定义

scratch小动物的晚会 2024年3月中国电子学会图形化编程 少儿编程 scratch编程等级考试一级真题和答案解析

目录 scratch小动物的晚会 一、题目要求 1、准备工作 2、功能实现 二、案例分析

51单片机入门_江协科技_20.1_Proteus串口仿真

1.为了解决51单片机学习过程中在Proteus中的串口仿真的问题&#xff0c;需要在Proteus中建立串口仿真的环境&#xff08;目前Proteus安装在Win7x64虚拟机环境中&#xff1b; 2. 在CSDN中找到VSPD下载地址&#xff0c;在虚拟机中进行VSPD的安装&#xff0c;具体链接地址如下&am…

HTML块级元素和内联元素(头部和布局)

目录 1.HTML块级和内联标签&#xff1a; 1.块级元素&#xff1a; 2.内联元素: 3.元素嵌套&#xff1a; 4.元素转换&#xff1a; 示例如下: 2.内联框架&#xff1a; 前言&#xff1a; 示例如下: 3.布局&#xff1a; 4.头部标签&#xff1a; 前言&#xff1a; 说明&…

【蓝桥杯-Even Parity】

蓝桥杯-Even Parity 洛谷 UVA11464 Even Parity 暴力思路&#xff1a; 去遍历每个元素&#xff0c;如果不符合要求则翻转 时间复杂度大概在O&#xff08;2^&#xff08;nn&#xff09; nn&#xff09; 改进思路&#xff1a; 先去枚举确定第一行&#xff08;第一行得合法&…

反截屏控制技术对于防止数据外泄都有哪些具体作用?

反截屏控制技术在防止数据外泄方面具有以下具体作用&#xff1a; 智能反截屏技术&#xff1a; 当用户启动截屏操作时&#xff0c;并只有非涉密内容被截屏&#xff0c;所有涉密窗口自动隐藏&#xff0c;防止涉密内容被截屏。这一技术普遍支持目前市面上的各种截屏操作&#xff0…

Apache ECharts-数据统计(详解、入门案例)

简介&#xff1a;Apache ECharts 是一款基于 Javascript 的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表。 1、介绍 图 1.1 Apache ECharts 功能、运行环境 功能&#xff1a; ECharts&#xff…

微信小程序使用icon图标

原因&#xff1a; 微信小程序使用fontawesome库使用icon图标&#xff0c;网上有很多教程&#xff0c;按照网上说法制作&#xff0c;引入到微信小程序中&#xff0c;但是验证成功&#xff0c;只能使用部分图标&#xff0c;结果不尽如人意。后面使用阿里巴巴开源iconfont来使用ic…

【opencv】教程代码 —Histograms_Matching(2)计算直方图、直方图比较、直方图均衡、模板匹配...

计算直方图直方图比较图像进行直方图均衡化处理模板匹配 1. calcHist_Demo.cpp 计算直方图 这段代码的功能是加载图像&#xff0c;分离图像的三个颜色通道&#xff0c;然后分别计算这三个通道的直方图&#xff0c;绘制出来并显示结果。直方图是图像中像素值分布的图形表示&…

【Django学习笔记(四)】JavaScript 语言介绍

JavaScript 语言介绍 前言正文1、JavaScript 小案例2、代码位置2.1 在当前 HTML 文件中2.2 在其他 js 文件中 3、代码注释3.1 HTML的注释3.2 CSS的注释3.3 Javascript的注释 4、变量 & 输出4.1 字符串4.2 数组4.3 对象(python里的字典) 5、条件语句6、函数7、DOM7.1 根据 I…

目标检测——图像中提取文字

一、重要性及意义 图像提取文本&#xff0c;即光学字符识别&#xff08;OCR&#xff09;技术&#xff0c;在现代社会中的重要性和意义日益凸显。以下是关于图像提取文本的重要性和意义的几个关键方面&#xff1a; 信息获取的效率提升 快速处理大量文档&#xff1a;OCR技术可…

报错 | 2023新版IDEA/PyCharm连接远程服务器的Docker需使用密钥认证

文章目录 01 问题情景02 需求场景及工作原理03 解决步骤3.1 在本地生成密钥对3.2 将公钥保存至服务器3.3 本地连接时选择私钥文件 网上有很多文章讲怎么解决&#xff0c;但都要么写得很复杂&#xff0c;要么没有写明白原理或操作详情&#xff0c;造成我一头雾水。 01 问题情景…

指针变量的类型

1.为什么指针变量要要求类型 当指针变量的类型跟存放的地址变量的类型不匹配时&#xff0c;当值变化时&#xff0c;地址增量也不同 #include <stdio.h> int main() {int a 0x1234;int *p&a;char *c&a;printf("p %p\n",p);printf("c %p\n"…

Jenkins--任务详解

一、任务类型 Jenkins的主要功能的实现是由执行任务去完成的&#xff0c;常用的任务类型主要有以下三种&#xff1a; 自由风格任务(Free Style Project): 这是Jenkins中最常用的任务类型&#xff0c;允许你自定义各种构建步骤和配置选项&#xff0c;如源码管理、构建触发器、…

redis的常用基本命令与持久化

文章目录 redis的基本命令1.授权密码2.增加、覆盖、查询、删除、切换库名、移动、清空数据库 Redis持久化RDB模式主动备份自动备份RDB备份过程 AOF备份模式开启AOF备份模式执行流程 总结 redis的基本命令 1.授权密码 config set requirepass 密码设置完密码需要认证密码以后才…

云计算存在的安全隐患

目录 一、概述 二、ENISA云安全漏洞分析 三、云计算相关系统漏洞 3.1 概述 3.2 漏洞分析 3.2.1 Hypervisor漏洞 3.2.1.1 CVE-2018-16882 3.2.1.2 CVE-2017-17563 3.2.1.3 CVE-2010-1225 3.2.2 虚拟机漏洞 3.2.2.1 CVE-2019-14835 3.2.2.2 CVE-2019-5514 3.2.2.3 CV…

备战蓝桥杯,用JAVA刷洛谷算法题单:【算法2-2】常见优化技巧

参考 【算法2-2】常见优化技巧 - 题单 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) P1102 A-B 数对P1638 逛画展P1115 最大子段和P7072 [CSP-J2020] 直播获奖P2671 [NOIP2015 普及组] 求和P4147 玉蟾宫P2866 [USACO06NOV] Bad Hair Day SP1950 长方形P2032 扫描P2216 [HAOI…

ES9学习 -- 对象的剩余参数与扩展运算符 / 正则扩展 / Promise.finally / 异步迭代

文章目录 1. 对象的剩余参数与扩展运算符1.1 对象的剩余参数1.2 扩展运算符 2. 正则扩展3. Promise.finally4. 异步迭代4.1 同步遍历器的问题4.2 异步遍历器使用 1. 对象的剩余参数与扩展运算符 1.1 对象的剩余参数 let obj { name:"kerwin", age:100, location:&…

推理端框架简介 高通SNPE 神经网络处理引擎 阿里巴巴 MNN 腾讯 FeatherCNN 端侧推理引擎 卷积计算优化 卷积计算优化

高性能计算(High performance computing, 缩写HPC) 指通常使用很多处理器(作为单个机器的一部分) 或者某一集群中组织的几台计算机(作为单个计 算资源操作)的计算系统和环境。 有许多类型的HPC 系统,其范围从标准计算机的大型集群,到高度专用的硬件。 大多数基于集群的H…

MVCC详细总结

简介 MVCC&#xff08;Multi-Version Concurrency Control&#xff09;是一种多版本并发控制机制&#xff0c;主要用于数据库管理系统中&#xff0c;实现对数据库的并发访问。在编程语言中&#xff0c;MVCC可以实现事务内存。 MVCC的特点是读不加锁&#xff0c;读写不冲突。MVC…