模拟shell小程序
接下来利用我们当前的知识,撰写一个简单的shell外壳程序。
1.shell原理
shell的原理是实际上就是运行了一个父进程,然后创建出子进程,最后使用进程替换调用,替换成其他程序。
2.shell实现
2.1.死循环
首先一个shell一旦运行起来就不会关闭(除非关闭终端窗口),因此一定是一个死循环。
int main()
{while(1){//...}return 0;
}
2.2.提示字符串
运行一个shell有出现一个类似[user@myshell]$的输入提示符,由于不能换行,因此我们还需要使用fflush()进行刷新。
#include <stdio.h>
int main()
{while(1){printf("[user@myshell]$ ");fflush(stdout);}return 0;
}
2.3.获取用户输入
#include <stdio.h>
#include <string.h>#define NUM 1024
char cmd_line[NUM];//保存完整命令字符串int main()
{while(1){//1.打印提示信息printf("[user@myshell]$ ");fflush(stdout);//2.获取 user 的输入memset(cmd_line, '\0', sizeof(cmd_line));//设定初始值if(fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)//获取字符串{//出错就直接跳出循环continue;}cmd_line[strlen(cmd_line) - 1] = '\0';//即使用户输入字符串超出范围,也能保证获取到一个完整的 C 风格字符串}return 0;
}
2.4.解析用户输入
#include <stdio.h>
#include <string.h>#define NUM 1024
char cmd_line[NUM];//保存完整命令字符串#define SIZE 32
char* g_argv[SIZE];//保存打散后的命令字符串,是一个数组#define SEP " "//分隔符int main()
{while(1){//1.打印提示信息printf("[user@myshell]$ ");fflush(stdout);//2.获取 user 的输入memset(cmd_line, '\0', sizeof(cmd_line));//设定初始值if(fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)//获取字符串{//出错就直接跳出循环continue;}cmd_line[strlen(cmd_line) - 1] = '\0';//即使用户输入字符串超出范围,也能保证获取到一个完整的 C 风格字符串//3.命令行字符解析//虽然可以自己写一个算法解析,但是我们可以使用一些现有的接口g_argv[0] = strtok(cmd_line, SEP);//第一次调用需要传入原始字符串和分隔符int index = 1;if(strcmp(g_argv[0], "ls") == 0)//如果输入的命令是 ls 就进行一些特殊处理{g_argv[index++] = "--color=auto";//增加颜色参数,让输出带有颜色}while(g_argv[index++] = strtok(NULL, SEP));//第二次还想要解析原始字符串,就需要传入 NULL}return 0;
}
2.5.创建并且替换子进程
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>#define NUM 1024
char cmd_line[NUM];//保存完整命令字符串#define SIZE 32
char* g_argv[SIZE];//保存打散后的命令字符串,是一个数组#define SEP " "//分隔符int main()
{while(1){//1.打印提示信息printf("[user@myshell]$ ");fflush(stdout);//2.获取 user 的输入memset(cmd_line, '\0', sizeof(cmd_line));//设定初始值if(fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)//获取字符串{//出错就直接跳出循环continue;}cmd_line[strlen(cmd_line) - 1] = '\0';//即使用户输入字符串超出范围,也能保证获取到一个完整的 C 风格字符串//3.命令行字符解析//虽然可以自己写一个算法解析,但是我们可以使用一些现有的接口g_argv[0] = strtok(cmd_line, SEP);//第一次调用需要传入原始字符串和分隔符int index = 1;if(strcmp(g_argv[0], "ls") == 0)//如果输入的命令是 ls 就进行一些特殊处理{g_argv[index++] = "--color=auto";//增加颜色参数,让输出带有颜色}while(g_argv[index++] = strtok(NULL, SEP));//第二次还想要解析原始字符串,就需要传入 NULL//4.fork() 创建子进程,execvp() 替换子进程pid_t id = fork();if(id == 0)//child{printf("子进程 run:\n");execvp(g_argv[0], g_argv);//自动在环境变量中搜索 g_argv[0] 的文件,执行 g_argv 数组存储的指令exit(1);//没替换成功就会异常退出}//fatherint status;pid_t ret = waitpid(id, &status, 0);//阻塞等待if(ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));}return 0;
}
2.6.debug
有一个小小的bug我们没有解决,就是使用cd命令的时候,没有办法切入到对应的目录,这是为什么呢?
3.shell拓展
3.1.用户名、主机名、目录获取
上面我们只是生硬显示了user充当用户名字,实际上我们可以通过环境变量获取执行shell的用户的名字,还可以获取在不同环境下的系统主机名字,使用这两个环境变量的内容填充到提示字符串中,目录也可以这样操作。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>#define NUM 1024
char cmd_line[NUM];//保存完整命令字符串#define SIZE 32
char* g_argv[SIZE];//保存打散后的命令字符串,是一个数组#define SEP " "//分隔符const char* GetUserName()//获取用户名字
{const char* userName = getenv("USER");if(userName) return userName;elsereturn "none";
}const char* GetHostName()//获取主机名字
{const char* hostName = getenv("HOSTNAME");if(hostName)return hostName;elsereturn "none";
}const char* GetPwd()//获取路径地址
{const char* pwd = getenv("PWD");if(pwd)return pwd;elsereturn "none";
}int main()
{while(1){//1.打印提示信息printf("[%s@%s %s]$ ", GetUserName(), GetHostName(), GetPwd());fflush(stdout);//2.获取 user 的输入memset(cmd_line, '\0', sizeof(cmd_line));//设定初始值if(fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)//获取字符串,不能使用 scanf(){//出错就直接跳出循环continue;}cmd_line[strlen(cmd_line) - 1] = '\0';//将用户输入的字符串去掉 \n//3.命令行字符解析//虽然可以自己写一个算法解析,但是我们可以使用一些现有的接口g_argv[0] = strtok(cmd_line, SEP);//第一次调用需要传入原始字符串和分隔符int index = 1;if(strcmp(g_argv[0], "ls") == 0)//如果输入的命令是 ls 就进行一些特殊处理{g_argv[index++] = "--color=auto";//增加颜色参数,让输出带有颜色}while(g_argv[index++] = strtok(NULL, SEP));//第二次还想要解析原始字符串,就需要传入 NULL//4.fork() 创建子进程,execvp() 替换子进程pid_t id = fork();if(id == 0)//child{printf("子进程 run:\n");execvp(g_argv[0], g_argv);//自动在环境变量中搜索 g_argv[0] 的文件,执行 g_argv 数组存储的指令exit(1);//没替换成功就会异常退出}//fatherint status;pid_t ret = waitpid(id, &status, 0);//阻塞等待if(ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));}return 0;
}
3.2.封装步骤
既然上面的用户名、主机名、路径都进行了函数封装,那么我也可以选择将部分的代码进行分钟,变得更有逻辑。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>#define NUM 1024
char cmd_line[NUM];//保存完整命令字符串#define SIZE 32
char* g_argv[SIZE];//保存打散后的命令字符串,是一个数组#define SEP " "//分隔符const char* GetUserName(void)//获取用户名字
{const char* userName = getenv("USER");if(userName) return userName;elsereturn "none";
}const char* GetHostName(void)//获取主机名字
{const char* hostName = getenv("HOSTNAME");if(hostName)return hostName;elsereturn "none";
}const char* GetPwd(void)//获取路径地址
{const char* pwd = getenv("PWD");if(pwd)return pwd;elsereturn "none";
}void Print(void)
{printf("[%s@%s %s]$ ", GetUserName(), GetHostName(), GetPwd());fflush(stdout);
}bool GetUserCommand()
{memset(cmd_line, '\0', sizeof(cmd_line));//设定初始值if(fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)//获取字符串,不能使用 scanf(){//出错就直接跳出循环return false;}cmd_line[strlen(cmd_line) - 1] = '\0';//将用户输入的字符串去掉 \nreturn true;
}void AnalyzeStr(void)
{//虽然可以自己写一个算法解析,但是我们可以使用一些现有的接口g_argv[0] = strtok(cmd_line, SEP);//第一次调用需要传入原始字符串和分隔符int index = 1;//处理特殊的命令情况if(strcmp(g_argv[0], "ls") == 0)//如果输入的命令是 ls 就进行一些特殊处理{g_argv[index++] = "--color=auto";//增加颜色参数,让输出带有颜色}while(g_argv[index++] = strtok(NULL, SEP));//第二次还想要解析原始字符串,就需要传入 NULL
}void RunCommand(void)
{pid_t id = fork();if(id == 0)//child{printf("子进程 run:\n");execvp(g_argv[0], g_argv);//自动在环境变量中搜索 g_argv[0] 的文件,执行 g_argv 数组存储的指令exit(1);//没替换成功就会异常退出}else{//fatherint status;pid_t ret = waitpid(id, &status, 0);//阻塞等待if(ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));}
}int main()
{while(1){//1.打印提示信息Print();//2.获取 user 的输入if(!GetUserCommand())continue;//3.命令行字符解析AnalyzeStr();//4.fork() 创建子进程,execvp() 替换子进程RunCommand();}return 0;
}
3.3.debug
如果使用了上面的shell程序,很快就会发现两个bug。
3.3.1.无法直接输入换行
在系统默认运行的shell中,允许用户不断回车,而我们的程序如果直接回车就会退出,这个问题的解决思路很简单,只需要在获取字符的时候,查看用户输入的字符长度是否为0即可,若是就让GetUserCommand()返回false,执行continue语句即可。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>#define NUM 1024
char cmd_line[NUM];//保存完整命令字符串#define SIZE 32
char* g_argv[SIZE];//保存打散后的命令字符串,是一个数组#define SEP " "//分隔符const char* GetUserName(void)//获取用户名字
{const char* userName = getenv("USER");if(userName) return userName;elsereturn "none";
}const char* GetHostName(void)//获取主机名字
{const char* hostName = getenv("HOSTNAME");if(hostName)return hostName;elsereturn "none";
}const char* GetPwd(void)//获取路径地址
{const char* pwd = getenv("PWD");if(pwd)return pwd;elsereturn "none";
}void Print(void)
{printf("[%s@%s %s]$ ", GetUserName(), GetHostName(), GetPwd());fflush(stdout);
}bool GetUserCommand()
{memset(cmd_line, '\0', sizeof(cmd_line));//设定初始值if(fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)//获取字符串,不能使用 scanf(){//出错就直接跳出循环return false;}cmd_line[strlen(cmd_line) - 1] = '\0';//将用户输入的字符串去掉 \nif(strlen(cmd_line) == 0)return false;return true;
}void AnalyzeStr(void)
{//虽然可以自己写一个算法解析,但是我们可以使用一些现有的接口g_argv[0] = strtok(cmd_line, SEP);//第一次调用需要传入原始字符串和分隔符int index = 1;//处理特殊的命令情况if(strcmp(g_argv[0], "ls") == 0)//如果输入的命令是 ls 就进行一些特殊处理{g_argv[index++] = "--color=auto";//增加颜色参数,让输出带有颜色}while(g_argv[index++] = strtok(NULL, SEP));//第二次还想要解析原始字符串,就需要传入 NULL
}
void RunCommand(void)
{pid_t id = fork();if(id == 0)//child{printf("子进程 run:\n");execvp(g_argv[0], g_argv);//自动在环境变量中搜索 g_argv[0] 的文件,执行 g_argv 数组存储的指令exit(1);//没替换成功就会异常退出}else{//fatherint status;pid_t ret = waitpid(id, &status, 0);//阻塞等待if(ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));}
}int main()
{while(1){//1.打印提示信息Print();//2.获取 user 的输入if(!GetUserCommand())continue;//3.命令行字符解析AnalyzeStr();//4.fork() 创建子进程,execvp() 替换子进程RunCommand();}return 0;
}
3.3.2.无法切换对应目录
还可以发现一个比较严重的问题,我们无法使用cd命令切换目录,这是为什么呢?原因也很简单,cd程序替换的是子进程,该目录只让子进程进行了切换命令,而我们希望的是父进程自己执行cd,修改父进程的工作路径。因此,我们需要调用一些库函数,更改父进程的工作环境。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>#define NUM 1024
char cmd_line[NUM];//保存完整命令字符串#define SIZE 32
char* g_argv[SIZE];//保存打散后的命令字符串,是一个数组#define SEP " "//分隔符const char* GetUserName(void)//获取用户名字
{const char* userName = getenv("USER");if(userName) return userName;elsereturn "none";
}const char* GetHostName(void)//获取主机名字
{const char* hostName = getenv("HOSTNAME");if(hostName)return hostName;elsereturn "none";
}const char* GetPwd(void)//获取路径地址
{const char* pwd = getenv("PWD");if(pwd)return pwd;elsereturn "none";
}void Print(void)//打印提示字符
{printf("[%s@%s %s]$ ", GetUserName(), GetHostName(), GetPwd());fflush(stdout);
}bool GetUserCommand(void)//获取用户命令字符串
{memset(cmd_line, '\0', sizeof(cmd_line));//设定初始值if(fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)//获取字符串,不能使用 scanf(){//出错就直接跳出循环return false;}cmd_line[strlen(cmd_line) - 1] = '\0';//将用户输入的字符串去掉 \nif(strlen(cmd_line) == 0)return false;return true;
}void AnalyzeStr(void)//拆分解析用户字符串
{//虽然可以自己写一个算法解析,但是我们可以使用一些现有的接口g_argv[0] = strtok(cmd_line, SEP);//第一次调用需要传入原始字符串和分隔符int index = 1;//处理特殊的命令情况if(strcmp(g_argv[0], "ls") == 0)//如果输入的命令是 ls 就进行一些特殊处理{g_argv[index++] = "--color=auto";//增加颜色参数,让输出带有颜色}while(g_argv[index++] = strtok(NULL, SEP));//第二次还想要解析原始字符串,就需要传入 NULL
}bool DoBuildin(void)//判断是否内建命令并且执行
{//内建命令if(strcmp(g_argv[0], "cd") == 0) { printf("父进程 run:\n");//更改路径的命令char* path = NULL;if(g_argv[1] == NULL){path = ".";}else{path = g_argv[1];}chdir(path);return true;}return false;
}void RunCommand(void)//运行用户命令
{pid_t id = fork();if(id == 0)//child{printf("子进程 run:\n");execvp(g_argv[0], g_argv);//自动在环境变量中搜索 g_argv[0] 的文件,执行 g_argv 数组存储的指令exit(1);//没替换成功就会异常退出}else{//fatherint status;pid_t ret = waitpid(id, &status, 0);//阻塞等待if(ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));}
}int main()
{while(1){//1.打印提示信息Print();//2.获取 user 的输入if(!GetUserCommand())continue;//3.命令行字符解析AnalyzeStr();//4.处理内建命令if(DoBuildin())continue;//5.fork() 创建子进程,execvp() 替换子进程RunCommand();}return 0;
}
这种只能由父进程来执行的命令也叫做“内建命令”,这类环境变量有很多。
3.3.3.无法更改环境变量
还有一个比较尴尬的问题,我们发现使用cd指令后,提示字符串的地址无法及时更新,也就是没有更新对应的环境变量。如果更新呢?可以使用系统提供的接口getcwd()来获取调用该接口进程所在的路径。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>#define NUM 1024
char cmd_line[NUM];//保存完整命令字符串#define SIZE 32
char* g_argv[SIZE];//保存打散后的命令字符串,是一个数组#define SEP " "//分隔符char cwd[1024];//环境变量const char* GetUserName(void)//获取用户名字
{const char* userName = getenv("USER");if(userName) return userName;elsereturn "none";
}const char* GetHostName(void)//获取主机名字
{const char* hostName = getenv("HOSTNAME");if(hostName)return hostName;elsereturn "none";
}const char* GetPwd(void)//获取路径地址
{const char* pwd = getenv("PWD");if(pwd)return pwd;elsereturn "none";
}void Print(void)
{printf("[%s@%s %s]$ ", GetUserName(), GetHostName(), GetPwd());fflush(stdout);
}bool GetUserCommand()
{memset(cmd_line, '\0', sizeof(cmd_line));//设定初始值if(fgets(cmd_line, sizeof(cmd_line), stdin) == NULL)//获取字符串,不能使用 scanf(){//出错就直接跳出循环return false;}cmd_line[strlen(cmd_line) - 1] = '\0';//将用户输入的字符串去掉 \nif(strlen(cmd_line) == 0)return false;return true;
}void AnalyzeStr(void)
{//虽然可以自己写一个算法解析,但是我们可以使用一些现有的接口g_argv[0] = strtok(cmd_line, SEP);//第一次调用需要传入原始字符串和分隔符int index = 1;//处理特殊的命令情况if(strcmp(g_argv[0], "ls") == 0)//如果输入的命令是 ls 就进行一些特殊处理{g_argv[index++] = "--color=auto";//增加颜色参数,让输出带有颜色}while(g_argv[index++] = strtok(NULL, SEP));//第二次还想要解析原始字符串,就需要传入 NULL
}bool DoBuildin()
{//内建命令if(strcmp(g_argv[0], "cd") == 0) { printf("父进程 run:\n");//更改路径的命令char* path = NULL;if(g_argv[1] == NULL){path = ".";}else{path = g_argv[1];}chdir(path);char tmp[1024];getcwd(tmp, sizeof(tmp));//获取当前进程所在的地址(工作目录)sprintf(cwd, "PWD=%s", tmp);//组合环境变量putenv(cwd);//设置环境变量return true;}return false;
}void RunCommand(void)
{pid_t id = fork();if(id == 0)//child{printf("子进程 run:\n");execvp(g_argv[0], g_argv);//自动在环境变量中搜索 g_argv[0] 的文件,执行 g_argv 数组存储的指令exit(1);//没替换成功就会异常退出}else{//fatherint status;pid_t ret = waitpid(id, &status, 0);//阻塞等待if(ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));}
}int main()
{while(1){//1.打印提示信息Print();//2.获取 user 的输入if(!GetUserCommand())continue;//3.命令行字符解析AnalyzeStr();//4.处理内建命令if(DoBuildin())continue;//5.fork() 创建子进程,execvp() 替换子进程RunCommand();}return 0;
}
相关文章:
模拟shell小程序
接下来利用我们当前的知识,撰写一个简单的shell外壳程序。 1.shell原理 shell的原理是实际上就是运行了一个父进程,然后创建出子进程,最后使用进程替换调用,替换成其他程序。 2.shell实现 2.1.死循环 首先一个shell一旦运行起…...
webpack配置全局scss
webpack配置全局scss 效果:a.vue使用index.scss中定义的$mainWidth就无需 import "xxxxxxx/index.scss"文件 src/assets/styles/index.scss $mainWidth: 1280px; $red: red src/views/a.vue .aaa {color: $red; } vue.config.js module.exports {…...
想面试前端工程师,必须掌握哪些知识和技能?【云驻共创】
在当今的数字化时代,前端工程师扮演着至关重要的角色。他们负责设计和开发用户界面,使得用户能够与应用程序或网站进行互动。为了找到最出色的前端工程师,你需要了解哪些技能和知识是必备的,同时也要掌握一些面试技巧和常见的面试…...
京东数据分析(京东数据采集):2023年10月京东平板电视行业品牌销售排行榜
鲸参谋监测的京东平台10月份平板电视市场销售数据已出炉! 根据鲸参谋电商数据分析平台的相关数据显示,10月份,京东平台上平板电视的销量将近77万,环比增长约23%,同比则下降约30%;销售额为21亿,环…...
在 Linux 中,可以使用分号 (;) 或者 运算符来执行多条命令
在 Linux 中,你可以使用分号 (;) 或者 && 运算符来执行多条命令。 使用分号 (;) 分隔多条命令: command1 ; command2 这样会依次执行 command1 和 command2,不管前面的命令是否成功。 使用 && 运算符分隔多条命令࿱…...
一些必备的 Redis 命令 | Navicat
Redis 是一种快速的内存数据结构存储系统,因其处理键值对的能力而备受推崇。在本文,我们将探索一些不可或缺的 Redis 命令(不包括之前介绍过的涉及键的命令),解锁这个强大工具的真正潜力。同时,我们也将了解…...
神经网络常用激活函数详解
🎀个人主页: https://zhangxiaoshu.blog.csdn.net 📢欢迎大家:关注🔍点赞👍评论📝收藏⭐️,如有错误敬请指正! 💕未来很长,值得我们全力奔赴更美好的生活&…...
UVA11584划分成回文串 Partitioning by Palindromes
划分成回文串 Partitioning by Palindromes 题面翻译 回文子串(palind) 问题描述: 当一个字符串正序和反序是完全相同时,我们称之为“回文串”。例如“racecar”就是一个回文串,而“fastcar”就不是。现在给一个字符串s,把它分…...
第十一章 将对象映射到 XML - 控制流属性的映射形式
文章目录 第十一章 将对象映射到 XML - 控制流属性的映射形式控制流属性的映射形式控制预计属性的可用性禁用映射%XML.Adapter 中的方法 第十一章 将对象映射到 XML - 控制流属性的映射形式 控制流属性的映射形式 对于流属性,XMLPROJECTION 的选项如下:…...
torchvision中的标准ResNet50网络结构
注:仅用以记录学习 打印出来的网络结构如下: from torchvision import models model models.resnet50(pretrainedFalse) print("model: ", model) 结构: ResNet((conv1): Conv2d(3, 64, kernel_size(7, 7), stride(2, 2), padd…...
Java 多线程之 synchronized (互拆锁/排他锁/非观锁)
文章目录 一、概述二、使用方法三、测试示例 一、概述 在Java中,synchronized 关键字用于实现线程之间的同步。提供了一种简单而强大的机制来控制多个线程之间的并发访问,确保共享资源的安全性和一致性。它解决了多线程环境中的竞态条件、数据竞争和内存…...
开源vs闭源大模型如何塑造技术的未来?开源模型的优劣势未来发展方向
开源vs闭源大模型如何塑造技术的未来?开源模型的优劣势&未来发展方向 写在最前面一、开源与闭源:定义与历史背景开源和闭源的定义开源大模型:社区驱动的创新 二、开源和闭源的优劣势比较开源大模型(瓶颈)数据&…...
如何使用无代码系统搭建软件平台?有哪些开源无代码开发平台?
无代码是什么 无代码开发,也称为零代码(Zero Code)开发,是一种技术概念。无代码开发无需代码基础,适合业务人员、IT开发及其他各类人员使用。他们通过无代码开发平台快速构建应用,并适应各种需求变化&#…...
微信怎么设置自动回复?
自动回复的用处 微信自动回复可以提高沟通效率。当你无法立即回复消息时,设置自动回复可以让对方知道你的情况,并且不会因为长时间没有回复而产生误解或不满。 微信自动回复可以节省时间和精力。如果你经常收到类似的询问或回复,通过设置自动…...
基于Vue3的低代码开发平台——JNPF
目录 一、什么是Vue.js ? 二、Jnpf-Web-Vue3 的技术栈介绍 (1)Vue3.x (2)Vue-router4.x (3)Vite4.x (4)Ant-Design-Vue3.x (5)TypeScript &#x…...
Thinkphp6 模型 指定字段自增的方法
tp6要使用Db类必须使用门面方式(think\facade\Db)调用。 use think\facade\Db; 然后,用Db::raw就可以实现指定字段自增了。...
WhatsApp开发客户攻略来袭!还有你不知道的账号解封秘籍!
别人用 WhatsApp 都是订单多到爆单,自己用 WhatsApp 却是订单、客户寥寥无几甚至账号被封?想必外贸从业者在用 WhatsApp 开发客户的时候都有这样的烦恼,今天这篇文章就和大家聊一聊怎么用 WhatsApp 高效地开发客户。 WhatsApp 开发客户的优势…...
Linux C 基于tcp多线程在线聊天室
多线程在线聊天室 概述客户端服务端 概述 客户端实现了判单用户登录结果、防止单回车字符发送、保存和显示历史聊天记录(仅自己)、退出聊天室功能。 服务端实现了验证用户是否已经存在(支持最大64用户连接)支持广播用户进入退…...
代码随想录算法训练营第23期day60|84.柱状图中最大的矩形
一、84.柱状图中最大的矩形 力扣题目链接 42接雨水 是找每个柱子左右两边第一个大于该柱子高度的柱子,而本题是找每个柱子左右两边第一个小于该柱子的柱子。 本题是要找每个柱子左右两边第一个小于该柱子的柱子,所以从栈头(元素从栈头弹出…...
vue动态获取目录结构进行配置静态路由
文章目录 前言定义项目页面格式一、vite 配置动态路由新建 /router/utils.ts引入 /router/utils.ts 二、webpack 配置动态路由总结如有启发,可点赞收藏哟~ 前言 项目中动态配置路由可以减少路由配置时间,并可减少配置路由出现的一些奇奇怪怪的问题 路由…...
飞书考勤自动化处理:基于Python与API的ETL实践指南
1. 项目概述:一个飞书考勤数据的自动化处理工具 最近在团队内部折腾自动化流程,发现考勤数据的处理是个高频且繁琐的痛点。每天手动从飞书后台导出报表,再整理、核对、计算,不仅耗时,还容易出错。直到我发现了 krist…...
JAVA短剧系统追剧神器系统源码和oss的配合使用
在JAVA短剧系统(追剧神器系统)中,阿里云OSS(对象存储服务)可扮演关键角色,主要用于存储短剧视频文件、封面图片等多媒体资源,与系统源码配合实现高效的内容管理和分发。以下从技术实现、功能配合…...
告别‘yum install’卡顿:保姆级教程优化Rocky 9的yum源配置,提速软件安装
告别‘yum install’卡顿:保姆级教程优化Rocky 9的yum源配置,提速软件安装 如果你正在使用Rocky Linux 9,可能已经体验过yum install命令那令人抓狂的等待时间。默认的网络源在高峰时段慢如蜗牛,安装一个简单的vim编辑器都可能让你…...
如何轻松批量下载B站视频?BilibiliDown终极指南免费开源
如何轻松批量下载B站视频?BilibiliDown终极指南免费开源 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors…...
基于ChatGPT与FastAPI构建YouTube视频智能摘要系统
1. 项目概述:当ChatGPT遇上YouTube,我们能做什么?最近在GitHub上看到一个挺有意思的项目,叫AIAdvantage/chatgpt-api-youtube。光看名字,你大概就能猜到它的核心玩法:把ChatGPT的智能对话能力和YouTube这个…...
从视觉到动作:基于树莓派与OpenCV的智能抓取机器人实战指南
1. 项目概述与核心价值最近在GitHub上看到一个挺有意思的项目,叫“GlassesOpenClaw”。光看名字,你可能会有点摸不着头脑,这“眼镜”和“爪子”是怎么联系到一起的?其实,这是一个典型的开源硬件与计算机视觉结合的创意…...
AgentGym-RL:基于ScalingInter-RL的LLM智能体强化学习训练框架实战
1. 项目概述:用强化学习教会大模型“走多步棋” 如果你关注过大语言模型(LLM)智能体的发展,可能会发现一个现象:很多模型在单轮问答、代码生成或数学解题上表现惊艳,但一旦把它们放到一个需要连续决策、与…...
如何快速实现VRoidStudio中文界面:面向3D创作者的完整汉化指南
如何快速实现VRoidStudio中文界面:面向3D创作者的完整汉化指南 【免费下载链接】VRoidChinese VRoidStudio汉化插件 项目地址: https://gitcode.com/gh_mirrors/vr/VRoidChinese 还在为VRoidStudio的英文界面而烦恼吗?对于国内3D角色设计师来说&a…...
第107篇:AI如何重塑知识付费?——个性化课程生成与自适应学习路径(操作教程)
文章目录 前言 环境准备:选对工具,事半功倍 分步操作:从用户画像到个性化路径 第一步:构建动态用户画像 第二步:创建模块化知识库 第三步:生成个性化课程大纲与内容 第四步:实现自适应学习路径引擎 完整代码示例:一个极简的端到端流程 踩坑提示:我趟过的雷,你避开 总…...
MCP 2026智能调度落地实录:从CPU争抢率飙升92%到资源利用率稳定在89.6%的5步闭环优化法
更多请点击: https://intelliparadigm.com 第一章:MCP 2026智能调度落地实录:从CPU争抢率飙升92%到资源利用率稳定在89.6%的5步闭环优化法 在某金融核心交易系统升级MCP 2026调度引擎后,集群CPU争抢率一度飙升至92%,导…...
