当前位置: 首页 > news >正文

Linux系统之----模拟实现shell

在前面一个阶段的学习中,我们已经学习了环境变量、进程控制等等一系列知识,也许有人会问,学这个东西有啥用?那么,今天我就和大家一起综合运用一下这些知识,模拟实现下shell!

首先我们来看一看我们的shell都有些什么,打开一个shell:

有一个命令行提示符, 由用户名,主机名,当前目录,提示符$构成,那我们一点一点破解!

1.获取用户名,主机名

首先,用户名和主机名都可以通过getenv来查看,那么我们是不是也可以通过getenv来获取呢?于是,我们便可以像如下一样写出代码:

static std::string GetUserName()
{std::string username = getenv("USER");return username.empty() ? "None" : username;
}
static std::string GetHostName()
{std::string hostname = getenv("HOSTNAME");return hostname.empty() ? "None" : hostname;
}

那为什么要加上那个static呢?就是为了增强鲁棒性和健壮性,保证代码只能在这个文件里面使用!

 具体代码内容比较简单,就不解释了!

2.路径的设置

之后是这个路径,要求我们要随着我们操作的变化而变化:

路径也可以通过env来查看,所以我们写出如下代码:

char pwd[1024]; // 全局变量空间,保存当前shell进程的工作路径
int lastcode = 0;
static std::string GetPwd()
{// 环境变量的变化,可能会依赖于进程,pwd需要shell自己更新环境变量的值//std::string pwd = getenv("PWD");//return pwd.empty() ? "None" : pwd;char temp[1024];getcwd(temp, sizeof(temp));// 顺便更新一下shell自己的环境变量pwdsnprintf(pwd, sizeof(pwd), "PWD=%s", temp);putenv(pwd);// /// /home/whb/code/codestd::string pwd_lable = temp;const std::string pathsep = "/";auto pos = pwd_lable.rfind(pathsep);if(pos == std::string::npos){return "None";}pwd_lable = pwd_lable.substr(pos+pathsep.size());return pwd_lable.empty() ? "/" : pwd_lable;

代码逻辑:使用getcwd函数获取当前工作目录的绝对路径,并将其存储在字符数组temp中,再使用snprintf函数将temp中的路径格式化为PWD=路径的形式,并存储在pwd中。然后使用putenv函数将这个新的环境变量PWD设置到当前进程的环境变量中, 说人话就是:更新环境变量PWD,之后便是找“/”,如果没有找到/(即posstd::string::npos),则返回字符串"None"。寻找的具体操作就是先定义一个变量,并将temp赋值过去,之后找最后一个/,最后是使用substr函数提取pwd_label中最后一个/之后的部分。如果提取后的部分为空(即路径以/结尾),则返回"/";否则返回提取后的路径部分。

具体显示效果请参考centos的,我这里是Ubuntu22.04的,可能显示界面不是很一样,代码以centos为准!

 补充:这里我们用到了snprintf,其本质和printf是差不多的,Printf是根据给定的格式进行写入,而这个snprintf是像字符串中写入

3.获取家目录 

之后我们还要获取家目录,即当有人用我的shell的时候输入env的时候我们的家目录也要有显示: 

这里直接给出代码: 

static std::string GetHomePath()
{std::string home = getenv("HOME");return home.empty() ? "/" : home;
}

4.输出提示符

 但是,我们将这些都获取了就完事了吗?我们得输出啊!因此,我们还要写一个命令行输出的函数!用于输出提示符

void PrintCommandPrompt()
{std::string user = GetUserName();std::string hostname = GetHostName();std::string pwd = GetPwd();printf("[%s@%s %s]# ", user.c_str(), hostname.c_str(), pwd.c_str());
}

 上述代码没什么解释的内容,应该大家都会的~

5.输入指令

之后输出了命令行提示符就完事了吗?我们还要输入指令啊!就像下图一样

 代码如下图所示:

//获取用户的键盘输入
bool GetCommandString(char cmd_str_buff[], int len)
{if(cmd_str_buff == NULL || len <= 0)return false;char *res = fgets(cmd_str_buff, len, stdin);if(res == NULL)return false;// ls -a -l\n -> ls -a -l\0cmd_str_buff[strlen(cmd_str_buff) - 1] = 0;return strlen(cmd_str_buff) == 0 ? false : true;
}

这里我解释一下,用户输入的指令假设为ls -l -a -n,我们要是普通的scanf或者cin的话那肯定不行,我们要以回车为结尾,而不是以空格为结尾,为此我们使用fgets函数, 之后还要检查一下是否成功正确写入!但是,这样真的没问题吗?(假设没有注释下面的那句代码),试一下就知道了,不行的,会多打出来一个空行!原因就是我们表面上输入的是ls -a -l,但是我们还按了回车!!!实际上获取的确是ls -a -l\n!!!改起来也容易,我们直接把\n换为\0就Ok了~

 好了,现在我们的指令是获取完了,但是佢现在还是一坨啊,是一大串,我们的shell还是识别不了我们想干什么,所以下一步我们肯定就是将其进行分割!

6.分割指令

这里由于一条命令语句里面会有诸多条,如命令 ls -a -l 我们要将其分开,这时我们可以借助命令行参数表(实际上就是一个数组),那我们怎么分割呢?我们在之前学过strtok,可以用起来!

这里补充介绍一下strtok,它按照指定的分隔符(通常是空白字符或逗号等)将一个字符串分割成多个子字符串,用法如下:

char *strtok(char *str, const char *delim);

值得注意的是,strtok是有记忆性的,会自动记住本次切割到哪里了!所以之后的调用中,第一个参数传NULL即可 ,而且strtok 会修改原始字符串,因为它在每个标记后插入空字符 \0 来分割字符串。这意味着原始字符串在 strtok 处理后将不再保持原样。

// 命令行参数表,我故意定义成为全局
char *gargv[ARGS] = {NULL};
int gargc = 0;
bool ParseCommandString(char cmd[])
{if(cmd == NULL)return false;
#define SEP " "//3. "ls -a -l" -> "ls" "-a" "-l"gargv[gargc++] = strtok(cmd, SEP);// 整个数字,最后以NULL结尾while((bool)(gargv[gargc++] = strtok(NULL, SEP)));// 回退一次,命令行参数的格式gargc--;//#define DEBUG
#ifdef DEBUGprintf("gargc: %d\n", gargc);printf("----------------------\n");for(int i = 0; i < gargc; i++){printf("gargv[%d]: %s\n",i, gargv[i]);}printf("----------------------\n");for(int i = 0; gargv[i]; i++){printf("gargv[%d]: %s\n",i, gargv[i]);}
#endifreturn true;
}

7.初始化

 在基础变量都准备完成之后,我们可以尝试运行我们的shell了,但是在运行之前,我们要先进行初始化!

void InitGlobal()
{gargc = 0;memset(gargv, 0, sizeof(gargv));
}

8. 父子进程的创建

通过前面学习,我们知道进程在创建的时候会被fork()分为父子进程,所以我们也要模仿实现以下~

void ForkAndExec()
{pid_t id = fork();if(id < 0){//for : XXXXXperror("fork"); // errno -> errstringreturn;}else if(id == 0){//子进程execvp(gargv[0], gargv);exit(0);}else{//父进程int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){lastcode = WEXITSTATUS(status);}}
}

这里仅解释一下中间部分:如果 fork() 返回 0,表示当前是子进程。使用 execvp() 函数执行一个新的程序,该程序由全局变量 gargv 指定(gargv[0] 是程序名,gargv 是参数列表)。如果 execvp() 执行失败,将调用 exit(0) 终止子进程。

9.内建命令的构建

在原版shell中,我们输入cd或者echo,我们的shell会对其进行响应,这个就是内建命令,那我们实现下:

bool BuiltInCommandExec()
{//内建命令: 是shell自己执行的命令,如同shell执行一个自己的函数//gargv[0]std::string cmd = gargv[0];bool ret = false;if(cmd == "cd"){// buildif(gargc == 2){std::string target = gargv[1];if(target == "~"){ret = true;chdir(GetHomePath().c_str());}else{ret = true;chdir(gargv[1]);}}else if(gargc == 1){ret = true;chdir(GetHomePath().c_str());}else{//BUG}}else if(cmd == "echo"){if(gargc == 2){std::string args = gargv[1];if(args[0] == '$'){if(args[1] == '?'){printf("lastcode: %d\n", lastcode);lastcode = 0;ret = true;}else{const char *name = &args[1];printf("%s\n", getenv(name));lastcode = 0;ret = true;}}else{printf("%s\n", args.c_str());ret = true;}}}return ret;
}

如此,代码主体设计完成,下面我们写一下main.cc文件以及myshell.h文件:

由于这两个文件就是函数头文件以及各个接口函数调用,这里直接以汇总的形式给出!

这里说明一下,我们的几乎所有软件都是一个死循环!!!我们之前写的程序其实就是一个代码片段!

这是main.cc文件

#include "myshell.h"#define SIZE 1024int main()
{char commandstr[SIZE];while(true){// 0. 初始化操作InitGlobal();// 1. 输出命令行提示符PrintCommandPrompt();// 2. 获取用户输入的命令if(!GetCommandString(commandstr, SIZE))continue;// 3. "ls -a -l" -> "ls" "-a" "-l"// 对命令字符串,进行解析 -> 命令行参数表ParseCommandString(commandstr);// 4. 检测命令,内键命令,要让shell自己执行!if(BuiltInCommandExec()){continue;}// 5.执行命令, 让子进程来进行执行ForkAndExec();}return 0;
}

这是myshell.h文件

#ifndef __MYSHELL_H__
#define __MYSHELL_H__#include <stdio.h>#define ARGS 64void Debug();
void InitGlobal();
void PrintCommandPrompt();
bool GetCommandString(char cmd_str_buff[], int len);
bool ParseCommandString(char cmd[]);
void ForkAndExec();
bool BuiltInCommandExec();
#endif

 这是myshell.cc文件

#include "myshell.h"
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>// 命令行参数表,我故意定义成为全局
char *gargv[ARGS] = {NULL};
int gargc = 0;
char pwd[1024]; // 全局变量空间,保存当前shell进程的工作路径
int lastcode = 0;void Debug()
{printf("hello shell!\n");
}//void GetUserName(char name[], int len)
//{
//
//}static std::string GetUserName()
{std::string username = getenv("USER");return username.empty() ? "None" : username;
}
static std::string GetHostName()
{std::string hostname = getenv("HOSTNAME");return hostname.empty() ? "None" : hostname;
}
static std::string GetPwd()
{// 环境变量的变化,可能会依赖于进程,pwd需要shell自己更新环境变量的值//std::string pwd = getenv("PWD");//return pwd.empty() ? "None" : pwd;char temp[1024];getcwd(temp, sizeof(temp));// 顺便更新一下shell自己的环境变量pwdsnprintf(pwd, sizeof(pwd), "PWD=%s", temp);putenv(pwd);// /// /home/whb/code/codestd::string pwd_lable = temp;const std::string pathsep = "/";auto pos = pwd_lable.rfind(pathsep);if(pos == std::string::npos){return "None";}pwd_lable = pwd_lable.substr(pos+pathsep.size());return pwd_lable.empty() ? "/" : pwd_lable;
}static std::string GetHomePath()
{std::string home = getenv("HOME");return home.empty() ? "/" : home;
}// 输出提示符
void PrintCommandPrompt()
{std::string user = GetUserName();std::string hostname = GetHostName();std::string pwd = GetPwd();printf("[%s@%s %s]# ", user.c_str(), hostname.c_str(), pwd.c_str());
}//获取用户的键盘输入
bool GetCommandString(char cmd_str_buff[], int len)
{if(cmd_str_buff == NULL || len <= 0)return false;char *res = fgets(cmd_str_buff, len, stdin);if(res == NULL)return false;// ls -a -l\n -> ls -a -l\0cmd_str_buff[strlen(cmd_str_buff) - 1] = 0;return strlen(cmd_str_buff) == 0 ? false : true;
}bool ParseCommandString(char cmd[])
{if(cmd == NULL)return false;
#define SEP " "//3. "ls -a -l" -> "ls" "-a" "-l"gargv[gargc++] = strtok(cmd, SEP);// 整个数字,最后以NULL结尾while((bool)(gargv[gargc++] = strtok(NULL, SEP)));// 回退一次,命令行参数的格式gargc--;//#define DEBUG
#ifdef DEBUGprintf("gargc: %d\n", gargc);printf("----------------------\n");for(int i = 0; i < gargc; i++){printf("gargv[%d]: %s\n",i, gargv[i]);}printf("----------------------\n");for(int i = 0; gargv[i]; i++){printf("gargv[%d]: %s\n",i, gargv[i]);}
#endifreturn true;
}void InitGlobal()
{gargc = 0;memset(gargv, 0, sizeof(gargv));
}void ForkAndExec()
{pid_t id = fork();if(id < 0){//for : XXXXXperror("fork"); // errno -> errstringreturn;}else if(id == 0){//子进程execvp(gargv[0], gargv);exit(0);}else{//父进程int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){lastcode = WEXITSTATUS(status);}}
}bool BuiltInCommandExec()
{//内建命令: 是shell自己执行的命令,如同shell执行一个自己的函数//gargv[0]std::string cmd = gargv[0];bool ret = false;if(cmd == "cd"){// buildif(gargc == 2){std::string target = gargv[1];if(target == "~"){ret = true;chdir(GetHomePath().c_str());}else{ret = true;chdir(gargv[1]);}}else if(gargc == 1){ret = true;chdir(GetHomePath().c_str());}else{//BUG}}else if(cmd == "echo"){if(gargc == 2){std::string args = gargv[1];if(args[0] == '$'){if(args[1] == '?'){printf("lastcode: %d\n", lastcode);lastcode = 0;ret = true;}else{const char *name = &args[1];printf("%s\n", getenv(name));lastcode = 0;ret = true;}}else{printf("%s\n", args.c_str());ret = true;}}}return ret;
}

好了,本篇文章到此结束~ 

 

 

相关文章:

Linux系统之----模拟实现shell

在前面一个阶段的学习中&#xff0c;我们已经学习了环境变量、进程控制等等一系列知识&#xff0c;也许有人会问&#xff0c;学这个东西有啥用&#xff1f;那么&#xff0c;今天我就和大家一起综合运用一下这些知识&#xff0c;模拟实现下shell&#xff01; 首先我们来看一看我…...

Doris和Clickhouse对比

目录 一、Doris和Clickhouse对比**1. 底层架构****Doris****ClickHouse** **2. 运行原理****Doris****ClickHouse** **3. 使用场景****Doris****ClickHouse** **4. 优缺点对比****总结** 二、MPP架构和Shared-Nothing 架构对比**1. 什么是 MPP 架构&#xff1f;****定义****特点…...

思考:(linux) tmux 超级终端快速入门的宏观思维

tmux 工具集合 GitHub - rothgar/awesome-tmux: A list of awesome resources for tmux 要点&#xff1a; 习惯性思维的变换与宿主机之间的双向复制、粘贴手动备份全部窗口&#xff0c;以及还原自定义窗格提示信息TPM 插件的安装思想别名 在有些场景里&#xff0c;可能无法…...

JavaScript基础-全局作用域

在JavaScript中&#xff0c;理解不同种类的作用域是掌握这门语言的关键之一。作用域决定了变量和函数的可访问性&#xff08;即可见性和生命周期&#xff09;。其中&#xff0c;全局作用域是最基本也是最宽泛的作用域类型。本文将深入探讨全局作用域的概念、特点及其使用时需要…...

【MCAL】TC397+EB-tresos之I2c配置实战(同步、异步)

I2C总线是Philips公司在八十年代初推出的一种串行、半双工的总线&#xff0c;主要用于近距离、低速的芯片之间的通信。本篇文章首先从理论讲起&#xff0c;介绍了英飞凌TC3x系列芯片对应MCAL中对I2C驱动的定义与介绍&#xff0c;建议读者在阅读本篇文章之前对I2C有个简单的认识…...

电网拓扑分析:原理与应用

在现代电力系统中&#xff0c;电网拓扑分析是一项至关重要的技术&#xff0c;它为电力系统的安全、稳定和高效运行提供了坚实的基础。电网拓扑描述了电力系统中各元件&#xff08;如发电机、变压器、输电线路、负荷等&#xff09;之间的连接关系&#xff0c;通过拓扑分析&#…...

leetcode-hot-100(哈希)

写在前面 这部分官方标记为哈希&#xff0c;下面的代码使用的都是 C 进行实现&#xff0c;说到 C 中的哈希&#xff0c;需要了解一下 C 中的 hashtable&#xff08;std::unordered_map或std::unordered_set&#xff09;。 std::unordered_map std::unordered_map 是一个存储…...

音频类网站或者资讯总结

我爱音频网&#xff1a; 我爱音频网 - 我们只谈音频&#xff0c;丰富的TWS真无线蓝牙耳机拆解报告 (52audio.com) 其他更多资讯 音频行业全品类深度剖析&#xff0c;2024市场趋势解读汇总-EDN 电子技术设计 (ednchina.com)...

优选算法——前缀和

目录 1. 数组的中心下标 2. 除自身以外数组的乘积 3. 和为k的子数组 4. 和可被K整除的子数组 5. 连续数组 6. 矩阵区域和 1. 数组的中心下标 题目链接&#xff1a;724. 寻找数组的中心下标 - 力扣&#xff08;LeetCode&#xff09; 题目展示&#xff1a; 题目分析&am…...

VScode密钥(公钥,私钥)实现免密登录【很细,很全,附带一些没免密登录成功的一些解决方法】

一、 生成SSH密钥对 ssh-keygen 或者 ssh-keygen -t rsa -b 4096区别&#xff1a;-t rsa可以明确表示生成的是 RSA 类型的密钥-b参数将密钥长度设置为 4096 位默认&#xff1a;2048 位密钥不指定-t参数&#xff0c;ssh -keygen默认也可能生成 RSA 密钥【确保本机安装ssh&#…...

MySQL进阶篇2_SQL优化、锁

文章目录 1 SQL优化1.1插入数据优化1.2主键优化页分裂页合并主键设计原则 1.3order by设计优化1.4group by设计优化小理解 1.5limit设计优化顺序IO和随机IO小疑惑 1.6count设计优化1.7update优化关于隐式事务事务的DML操作 锁全局锁表级锁表锁元数据锁意向锁 行级锁锁的释放条件…...

Yocto项目实战经验总结:从入门到高级的全面概览

本文面向开发者和实际项目经验者&#xff0c;分享经过大量实战积累的 Yocto 项目工程经验和基础技巧。本文简明但精彩&#xff0c;应用和观察相结合&#xff0c;充分适合做为全面进阶 Yocto 项目开发的实用指南。 一、入门理解&#xff1a;Yocto 是什么&#xff1f;规划如何开始…...

关于web3

主流看法&#xff0c;集合当前网络上的大部分资料的看法&#xff1f; 基于区块链运行的交易系统&#xff1f;面向的交易市场是基于世界的&#xff0c;由于将整个世界的交易联系起来&#xff0c;所以底层区块链就类似于一个非常大的分布式系统&#xff0c;由于需要在各个地区都…...

以影像为笔,劳润智在世界舞台上书写艺术之路

在光影交织中,摄影师劳润智的镜头仿佛能穿透喧嚣,捕捉人类情感最细腻的脉动。从疫情下洛杉矶裁缝日常的温馨瞬间,到象征自由与解脱的飞鸟影像,再到探索时间与空间交错的抽象作品,每一幅作品都展现了他对艺术的深度追求与对生活的温柔洞察。 劳润智的作品为他赢得了多个国际奖项…...

2025python学习笔记

一.Python语言基础入门 第一章 01.初识Python Python的起源&#xff1a; 1989年&#xff0c;为了打发圣诞节假期&#xff0c;Gudio van Rossum吉多范罗苏姆&#xff08;龟叔&#xff09;决心开发一个新的解释程序&#xff08;Python维形&#xff09;1991年&#xff0c;第一个…...

数学相关使用笔记

1、样本标准差计算步骤整理 1. 基础数据 数据样本&#xff1a;[44.530, 44.023, 43.837, 44.213, 44.498] 样本量&#xff1a;n5 2. 计算步骤 (1) 求均值 总和 44.53044.02343.83744.21344.498 221.101 均值 221.101/5 44.2202 (2) 求平方差 ① (44.530-44.2202) 0.3…...

0.环境初始化

容器化部署 Nginx 前端文件在 html\hmdp 下&#xff0c;挂载到 /usr/share/nginx/html 下 所以要求 nginx.conf &#xff1a; root /usr/share/nginx/html; index index.html; 反向代理&#xff1a;proxy_pass http://host.docker.internal:8081; listen 80; 因为容器内端…...

数仓-范式建模、维度建模、雪花模型、星型模型对比及其适用范围

1. 范式建模 定义 范式建模是一种基于关系型数据库设计的建模方法&#xff0c;遵循数据库的范式规则&#xff08;如第一范式、第二范式、第三范式等&#xff09;&#xff0c;通过消除数据冗余、规范化字段和表结构来优化存储。数据被分解为多个表&#xff0c;通过外键关系进行…...

批量导出docker镜像

#!/bin/bash # 创建备份目录 BACKUP_DIR"docker_images_single_backup" mkdir -p "$BACKUP_DIR" # 遍历所有镜像 docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" | while read -r line; do # 提取镜像名称和ID REPO_TAG$(echo …...

棒球裁判员学习指南·棒球1号位

针对棒球裁判员的规则学习与能力提升指南&#xff0c;包含系统性学习路径和实践建议&#xff0c;帮助裁判员高效掌握规则并提升执法水平&#xff1a; 一、基础规则体系构建 1. 官方规则精读 核心文件&#xff1a;完整研读《世界棒垒球联盟&#xff08;WBSC&#xff09;官方规…...

vue注册用户使用v-model实现数据双向绑定

定义数据模型 Login.vue //定义数据模型 const registerData ref({username: ,password: ,confirmPassword: })使用 v-model 实现数据模型的key与注册表单中的元素之间的双向绑定 <!-- 注册表单 --><el-form ref"form" size"large" autocompl…...

BM14 链表的奇偶重排

20250510 题目我的解法❗️问题描述&#xff1a;为什么会报 OutOfMemoryError&#xff1f;&#x1f50d;详细分析✅如何修复&#xff1f;✅推荐修改方案&#xff08;正确版本&#xff09;✅为什么这样改有效&#xff1f;&#x1f9ea;测试示例✅总结 修改 题目 我的解法 impor…...

Client 和 Server 的关系理解

client.py 和 server.py 是基于 MCP&#xff08;Multi-Component Protocol&#xff09;协议的客户端-服务端架构&#xff0c;二者的关系如下&#xff1a; 1. 角色分工 server.py&#xff1a;服务端&#xff0c;负责注册和实现各种“工具函数”&#xff08;如新闻检索、情感分…...

Checkmk实战指南:从零构建企业级监控系统

在当今复杂的IT环境中,监控系统如同运维团队的"眼睛"和"耳朵",而Checkmk正是一款能够帮助企业实现全方位监控的开源利器。本文将带您从零开始,通过8个关键步骤构建一个功能完备的监控系统,涵盖从安装部署到高级优化的完整流程。 一、为什么选择Checkm…...

多模态大模型中的视觉分词器(Tokenizer)前沿研究介绍

文章目录 引言MAETok背景方法介绍高斯混合模型&#xff08;GMM&#xff09;分析模型架构 实验分析总结 FlexTok背景方法介绍模型架构 实验分析总结 Emu3背景方法介绍模型架构训练细节 实验分析总结 InternVL2.5背景方法介绍模型架构 实验分析总结 LLAVA-MINI背景方法介绍出发点…...

车载电子电器架构 --- 汽车网关概述

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…...

JVM对象创建内存分配

对象创建的主要流程&#xff1a; 检查加载类–》分配内存–》初始化–》设置对象头–》实例化&#xff0c;执行init方法。 在内存分配中&#xff0c;虚拟机将为新生对象内存分配 Minor GC : 新生代垃圾收集&#xff0c;特点是频繁&#xff0c;回收速度快&#xff1b; Full GC …...

project从入门到精通(五)

目录 创建资源的基本信息 在project中创建资源工作表 ​编辑信息详解 最大单位 标准费率与加班费率 每次使用成本 成本累算 基准日历 三类资源工作表的总结——不同的资源必须要设置的属性 除了资源名称是必须设置的之外&#xff0c;剩余的资源的可设置选项如下图所…...

研发效率破局之道阅读总结(5)管理文化

研发效率破局之道阅读总结(5)管理文化 Author: Once Day Date: 2025年5月10日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 程序的艺术_Once-Day…...

Java常用类概述

Java常用类概述 一、字符串三剑客1. String&#xff08;不可变字符串&#xff09;2. StringBuilder&#xff08;可变&#xff0c;线程不安全&#xff09;3. StringBuffer&#xff08;可变&#xff0c;线程安全&#xff09; 二、日期时间类&#xff08;重点掌握新版API&#xff…...

202535| Kafka架构与重要概念+幂等性+事务

好的&#xff01;以下是关于 Kafka 架构 以及其 重要概念 的详细介绍&#xff0c;结合 Mermaid 图形 和 表格&#xff0c;帮助你更好地理解各个概念的关系和作用。 Kafka 架构与重要概念 Kafka 是一个分布式消息系统&#xff0c;广泛应用于日志收集、流处理、事件驱动架构等场…...

MySQL 索引和事务

目录 一、MySQL 索引介绍 1、索引概述 2、索引作用 3、索引的分类 &#xff08;1&#xff09;普通索引 &#xff08;2&#xff09;唯一索引 &#xff08;3&#xff09;主键索引 &#xff08;4&#xff09;组合索引&#xff08;最左前缀&#xff09; &#xff08;5&…...

IPFS与去中心化存储:重塑数字世界的基石

引言 在数据爆炸式增长的数字时代&#xff0c;中心化存储的弊端日益凸显——数据垄断、隐私泄露、单点故障等问题频发。IPFS&#xff08;InterPlanetary File System&#xff09; 作为一种去中心化存储协议&#xff0c;正与区块链技术共同推动一场存储革命。本文将深入解析IPF…...

Web3 学习全流程攻略

目录 🧭 Web3 学习全流程攻略 🌱 第一阶段:打好基础(Web3 入门) 🧠 目标: 📚 学习内容: ✅ 推荐资源: 🧑‍💻 第二阶段:技术栈搭建(成为 Web3 开发者) 🧠 目标: 📚 学习内容: ✅ 推荐资源: 🌐 第三阶段:构建完整 DApp(去中心化应用)…...

AUTODL Chatglm2 langchain 部署大模型聊天助手

资源申请 注册登录 进入下面的链接 AutoDL算力云 | 弹性、好用、省钱。租GPU就上AutoDLAutoDL为您提供专业的GPU租用服务&#xff0c;秒级计费、稳定好用&#xff0c;高规格机房&#xff0c;7x24小时服务。更有算法复现社区&#xff0c;一键复现算法。https://autodl.com/ho…...

牛客练习赛138

牛客练习赛138 A.小s的签到题 思路&#xff1a;过题人数最多的就是签到题 #include <bits/stdc.h> using namespace std; typedef long long ll; typedef pair<int, char> PII;bool cmp(PII a, PII b) {return a.first > b.first; }void solve() {int n;cin …...

【脑机接口临床】脑机接口手术的风险?脑机接口手术的应用场景?脑机接口手术如何实现偏瘫康复?

脑机接口的应用 通常对脑机接口感兴趣的两类人群&#xff0c;一类是适应症患者 &#xff0c;另一类是科技爱好者。 1 意念控制外部设备 常见的外部设备有&#xff1a;外骨骼、机械手、辅助康复设备、电刺激设备、电脑光标、轮椅。 2 辅助偏瘫康复或辅助脊髓损伤患者意念控制…...

普通IT的股票交易成长史--股价起伏的真相-缺口(2)

声明&#xff1a;本文章的内容只是自己学习的总结&#xff0c;不构成投资建议。价格行为理论学习可参考简介中的几位&#xff0c;感谢他们的无私奉献。 送给自己的话&#xff1a; 仓位就是生命&#xff0c;绝对不能满仓&#xff01;&#xff01;&#xff01;&#xff01;&…...

基于NI-PXI的HIL系统开发

基于NI-PXI平台的汽车电控单元HIL系统开发全解析 引言&#xff1a;HIL系统如何成为汽车电控开发的“效率倍增器”&#xff1f; 某车企通过基于NI-PXI的HIL系统&#xff0c;将悬架控制器的测试周期从3个月压缩至2周&#xff0c;故障检出率提升65%。这背后是硬件在环技术对汽车电…...

IOC和Bean

IOC IOC将对象的创建&#xff0c;依赖关系的管理和生命周期的控制从应用程序代码中解耦出来了 IOC容器的依赖注入(DI) 在程序运行过程中动态的向某个对象中注入他所需要的其他对象 依赖注入是基于反射实现的 Spring IOC 容器使用的是Map&#xff08;concorrentMap&#xff…...

助力你的Neovim!轻松管理开发工具的魔法包管理器来了!

在现代编程环境中&#xff0c;Neovim 已经成为许多开发者的编辑器选择。而针对 Neovim 的各种插件与功能扩展&#xff0c;则是提升开发体验的重要手段。今天我们要介绍的就是一个强大而便捷的开源项目——mason.nvim&#xff0c;一个旨在简化和优化 Neovim 使用体验的便携式包管…...

AI与机器人学:从SLAM到导航的未来

AI与机器人学&#xff1a;从SLAM到导航的未来 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 AI与机器人学&#xff1a;从SLAM到导航的未来摘要引言技术路线对比1. 传感器融合架构&#xff1a;纯激光 vs 多模态2. …...

C++学习之模板初阶学习

今天我们来学习C中模板的学习。但是模板是C中比较难的部分&#xff0c;因此本节我们直接出相对比较初阶的部分。 目录 泛型编程 函数模板 函数模板格式 函数模板的原理 函数模板的特性 函数模板的实例化 模板参数的匹配原则 类模板 类模板定义格式 类模板实例化 泛型…...

专业级软件卸载工具:免费使用,彻底卸载无残留!

在数字生活节奏日益加快的今天&#xff0c;我们的电脑就像每天都在"吃进"各种软件。但您是否注意到&#xff0c;那些看似消失的程序其实悄悄留下了大量冗余文件&#xff1f;就像厨房角落里积攒的调味瓶空罐&#xff0c;日积月累就会让系统变得"消化不良"。…...

JSON|cJSON 介绍以及具体项目编写

一、JSON介绍 JSON&#xff08;JavaScript Object Notation 即JavaScript对象表示法&#xff09;是一种轻量级的数据交换格式。采用完全独立于编程语言的文本格式来存储和表示数据。 JSON是一种数据交换格式.JSON独立于编程语言(你不必学习JavaScript).JSON表达数据的方式对通…...

Cell | 大规模 单细胞图谱 揭示非小细胞肺癌抗PD-1治疗后的免疫微环境异质性

–https://doi.org/10.1016/j.cell.2025.03.018 A single-cell atlas reveals immune heterogeneity in anti-PD-1-treated non-small cell lung cancer 留意更多内容&#xff1a;组学之心 研究简介 背景与问题 非小细胞肺癌&#xff08;NSCLC&#xff09;术后复发率高。新…...

光流 | 基于深度学习的光流估计算法汇总,原理,公式,流程图,代码

基于深度学习的光流算法 一、光流估计的基本原理二、基于深度学习的光流估计算法1. **FlowNet系列**2. **FlowNet 2.0**3. **PWC-Net**4. **RAFT(Recurrent All-Pairs Field Transformers)**5. **LiteFlowNet系列**三、算法流程图示例FlowNet2.0架构PWC-Net金字塔处理流程四、…...

常见的算法介绍

算法概述线性回归(Linear Regression)线性回归是一种通过属性的线性结合来进行预测的线性模型, 其目的是找到一条直线, 一个平面或者更高维的超平面, 使预测值和真实值之间的误差最小化逻辑回归(Logistic Regression)逻辑回归是一种分类模型, 入二分类公式 P ( Y 1 ∣ X ) e …...

【基于 LangChain 的异步天气查询1】异步调用 Open-Meteo API 查询该城市当前气温

目录 一、功能概述 二、文件结构 三、城市天气实时查询&#xff08;运行代码&#xff09; weather_runnable.py main.py 运行结果 四、技术亮点 五、使用场景 一、功能概述 它实现了以下主要功能&#xff1a; 用户输入地点&#xff08;城市名&#xff09; 构造提示词…...

深入解析JavaScript变量作用域:var、let、const全攻略

在JavaScript中&#xff0c;变量作用域是一个核心概念&#xff0c;它决定了变量的可访问性和生命周期。理解变量作用域对于编写清晰、高效且无错误的代码至关重要。本文将深入探讨JavaScript中不同类型的变量声明方式&#xff08;var、let、const等&#xff09;&#xff0c;分析…...