【Linux】解锁Shell脚本编写秘籍,编程高手之路等你开启

news2024/11/13 9:28:20

目录

  • 1. 打印命令行提示符
  • 2. 获取用户输入的命令行字符串
  • 3. 对命令行字符串进行解析(分割)
  • 4. 处理内建命令
    • 4.1. 内建命令</h3>
    • 4.2. 外部命令
    • 4.3. cd
    • 4.5. export
    • 4.6. echo
  • 5. 执行命令
    • 5.1. 创建子进程进行程序替换
  • 6. 重定向</h2>
  • 7. 总代码

1. 打印命令行提示符

#include<stdlib.h>

char* HostName() //获取主机名
{
    char* ret = getenv("HOSTNAME"); 
    if(ret) return ret;
    else return (char*)"None";
}
 
char* UserName() //获取用户名
{
    char* ret = getenv("USER");
    if(ret) return ret;
    else return (char*)"None";                                                         
}
    
char* CurrentWorkdir() //获取当前工作目录
{
    char* ret = getenv("PWD");
    if(ret) return ret;
    else return (char*)"None";
}

int main()
{
    //打印命令行提示符 
    //读取环境变量的内容 / 调用函数(gethostname获取主机名、getcwd获取当前工作目录)
    printf("[%s@%s %s]#", UserName(), HostName(), CurrentWorkdir());
}

2. 获取用户输入的命令行字符串

  1. 可以使用scanf读取字符串吗?
  • 答:不能,scanf用于从标准输入(键盘)读取格式化输入,读取字符串时,scanf会在遇到空白字符(空格、换行符\n、制表符\t等)时停止读取,并将读取的字符串存储到字符数组中。
  1. char* fgets(char* str, int size, FILE* stream);
  • 功能:从指定的文件流stream中读取一行(直到遇到换行符\n、文件结束符EOF、已读取了n-1个字符为止),并将读取的字符串(包括换行符,如果有的话)存储在str指向的数组中,并在数组的末尾添加’\0’来标记字符串的结束。

  • 优点:可以限制读取的字符数,防止缓冲区溢出,并且可以读取包含空格的字符串。

  • 缺点:如果读取的行比指定的长度n还要长,则多余的字符会被留在输入缓冲区中。

文件流stream是程序用于输入和输出的基本抽象,stdin为标准输入流(键盘)、stdout为标准输出流(控制台或终端)、stderr为标准错误输出流。

#include<stdlib.h>

#define SIZE 1024

int Interative(char* in)
 {
    printf("[%s@%s %s]#", UserName(), HostName(), CurrentWorkdir());                                                      
    fgets(in, SIZE, stdin);  
    in[strlen(in) - 1] = '\0';  //移除换行字符\n
    
    return strlen(in) - 1;  //处理空串""情况
}

int main()
{
    char CommandLine[SIZE]; //存储用户输入的命令行字符串

    while(1)
    {
        //打印命令行提示符+获取用户输入的命令行字符串                                      
        int n =  Interative(CommandLine);  
        if(n == 0) continue;  //空串,用户继续输入
    }

    return 0;
}

问:为什么用fegets读取一行字符串时,需要单独处理换行字符’\n’?

  • 因为我们在键盘中输入时,会多按一个回车字符’\n’,但对于命令来说,这个字符是无任何作用的,所以对于命令行参数表而言,不能存储带有换行的命令或选项。如:ls回车,实际上就是执行ls命令。

3. 对命令行字符串进行解析(分割)

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

  • 首次调用:它接受两个参数(待分解的字符串、分割符字符串),函数会在str中查找delim中的任意一个字符,一旦找到,就将该字符替换为字符串结束符’\0’,并返回指向当前令牌(分隔符之前的字符串)的指针。

  • 后续调用:在首次调用后,每次调用strtok,应将第一个参数设置为NULL,以便函数从上次分解的位置查找并分解字符串。

#include<stdlib.h>
#include<string.h>

#define SIZE 1024

char* argv[SIZE]; //相当于main函数的命令行参数表,末尾一定要以NULL结尾

void Split(char* in)
{
    int i = 0;
    argv[i++] = strtok(in, SP);  //首次调用
    while(argv[i++] = strtok(NULL, SP)); //后续调用

    if(strcmp("ls", argv[0]) == 0) 
    {
       argv[i-1] = (char*)"--color";   //加上后,ls显示内容,颜色为auto                   
       argv[i] = NULL; //argv以NULL结尾,便于子进程在进程程序替换时参数的传递
    }
}

int main()
{
    char CommandLine[SIZE]; 

    while(1)
    {
        //1.打印命令行提示符+获取用户输入的命令行字符串                                      
        int n =  Interative(CommandLine);  
        if(n == 0) continue;  //空串,用户继续输入

        //2.分割命令行字符串
        Split(CommandLine);
    }

    return 0;
}

4. 处理内建命令

4.1. 内建命令

  1. 概念:是shell内部直接提供并实现的命令,不要shell创建子进程来调用一个独立的外部程序(exe函数)来执行,而是由shell本身解释并执行。

  2. 可用性和依赖性:内建命令随shell的安装而自动提供,不需要额外的安装。

  3. 常见的内建命令:cd、echo、export、pwd、exit等。

4.2. 外部命令

  1. 概念:由用户自己编写的程序或者从外部引入的程序,它作为独立的程序存在于文件系统中,需要shell调用OS来执行一系列操作(创建子进程、子进程进行程序替换、子进程执行外部命令)。

  2. 可用性和依赖性:通常需要单独安装,并依赖于特定的OS和环境。

4.3. cd

问1:为什么会出现bash的当前工作目录,以及提示符中的工作目录,并未发生修改?

  • cd会被当作为外部命令,bash创建子进程,由子进程进行程序替换,执行cd命令,子进程执行完毕,就会退出,父进程无任何影响,所以只改变了子进程的当前工作目录。

int chdir(const char* path);

  • 功能:改变当前工作目录,但不会更新环境变量PWD中的内容。

int snprintf(char* str,size_t size,const char* format,. . .);

  • 可以将多个变量值合并为单一的、格式化的字符串。

int BuiltinCmd()
{
    int ret = 0; //用于判断是否为内建命令,不是,则为0、是,则为1
    if(strcmp("cd", argv[0]) == 0)
    {
        ret = 1; 
        char* target = argv[1]; //需要切换的路径
        if(!target) target = getenv("HOME"); //cd,表示切换到家目录中
        
        chdir(target); //改变当前工作目录,但不改变环境变量PWD的内容
        
        char tmp[SIZE]; 
        snprintf(tmp, SIZE, "PWD=%s", target);  //多变一
        putenv(tmp);   //修改或者新增环境变量                                              
 }

return ret;
}

char* getcwd(char* buf,size_t size);

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

#define SIZE 1024

char* argv[SIZE]; //相当于main函数的命令行参数表,末尾一定要以NULL结尾

int BuiltinCmd()
{
    int ret = 0; //用于判断是否为内建命令,不是,则为0、是,则为1
    if(strcmp("cd", argv[0]) == 0)
    {
        ret = 1; 
        char* target = argv[1]; //需要切换的路径
        if(!target) target = getenv("HOME"); //cd,表示切换到家目录中
        chdir(target); //更改当前工作目录
        
        char tmp[SIZE];  //存储获取到的当前工作目录的绝对路径
        char pwd[SIZE];  //存储更改后的环境变量PWD的内容
        getcwd(tmp, SIZE);  //获取当前工作目录的绝对路径
                            
        snprintf(pwd, SIZE, "PWD=%s", tmp); //多变一,拼接
    
        putenv(pwd); //修改环境变量PWD的内容
    }

    return ret;
}

int main()
{
    char CommandLine[SIZE]; 

    while(1)
    {
        //1.打印命令行提示符+获取用户输入的命令行字符串                                      
        int n =  Interative(CommandLine);  
        if(n == 0) continue;  //空串,用户继续输入

        //2.分割命令行字符串
        Split(CommandLine);

        //3.处理内建命令
        n = BuiltinCmd();
        if(n) continue;
    }

    return 0;
}

4.5. export

int BuiltinCmd()
{
    int ret = 0; 
    if(strcmp("export", argv[0]) == 0)
    {
        ret = 1;
        if(argv[1])
        {
             putenv(argv[1]);                                    
        }  
    }
    
    return ret;
}

问:为什么第一次export新增环境变量成功,但再输入了其他指令,这个新增的环境变量就不见了?

  • 因为argv为字符指针数组,执行完export后,再输入其他指令,则argv中原有的内容就会被其他指令所覆盖。
char env[SIZE]; //相当于环境变量表,存储新增的环境变量

int BuiltinCmd()
{
    if(strcmp("export", argv[0]) == 0)
    int ret = 0; 
    {
        ret = 1;
        if(argv[1])
        {
            strcpy(env, argv[1]); //将新增的环境变量的内容,拷贝到环境变量表中
            putenv(env);                                 
        }  
    }
    
    return ret;
}

4.6. echo

char env[SIZE]; //相当于环境变量表,存储新增的环境变量

int BuiltinCmd()
{
    if(strcmp("echo", argv[0]) == 0)
     {                                                       
        ret = 1;
        if(!argv[1])  //echo:打印一个空行
            printf("\n");
        else if(argv[1][0] != '$') 
            printf("%s\n", argv[1]); //echo aaaa:打印aaaa
        else 
        {
            if(argv[1][1] == '?')
            {
                printf("%d\n", exit_code); //echo $?:打印退出码
                exit_code = 0; //便于在执行echo $?时,退出码=0
            }
            else 
                printf("%s\n", getenv(argv[1] + 1)); //echo $USER:打印环境变量的内容
        }
    }

    return ret;
}

5. 执行命令

1.Shell脚本编写,bash执行命令时,通常会创建子进程,让子进程执行程序替换。

  1. 原因:a. bash是命令行解释器,需要一直执行命令,若让父进程进行替换,执行命令,则父进程就会退出;b. 进程之间具有独立性,子进程进行替换,对父进程无影响。

5.1. 创建子进程进行程序替换

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

#define SIZE 1024
#define SP " "

char* argv[SIZE];

int exit_code;  //全局变量,存储子进程的退出码,便于echo $?直接输出退出码

void Execute()
{
    pid_t id = fork(); 
       
    if(id == 0)
    {
        execvp(argv[0], argv); //程序替换,执行命令
        exit(1);  //子进程退出
    }
  
    int status = 0;
    pid_t rid = waitpid(id, &status, 0); //父进程等待,进行回收子进程的资源
    if(rid > 0)
    {
        exit_code = WEXITSTATUS(status); //正常退出,获取子进程的退出码
    }                                                                              
}


int main()
{
    char CommandLine[SIZE]; 

    while(1)
    {
        //1.打印命令行提示符+获取用户输入的命令行字符串                                      
        int n =  Interative(CommandLine);  
        if(n == 0) continue;  //空串,用户继续输入

        //2.分割命令行字符串
        Split(CommandLine);

        //4.执行命令(创建子进程,进行替换)
        Execute();
    }

    return 0;
}

6. 重定向

  1. 为什么要宏定义多个整数,定义全局变量redir_type?
  • 重定向有三种类型:输出重定向>、追加重定向>>、输出重定向。

  • 我们需要在命令行参数中,查找是否有重定向,如果有重定向,就需要获取重定向的类型,从而在根据重定向的类型执行对应的指令。

所以我们用宏定义多个整数,来表示重定向的类型; 定义一个全局变量redir_type,用来记录获取到的重定向的类型


#define NoneRedir -1
#define StdinRedir 0
#define StdoutRedir 1
#define AppenRedir 2

char* filename = NULL;
int redir_type = -1;
  1. 通常来说,重定向由三部分组成:要执行的命令 重定向符号 文件名(如:ls -l > log.txt)。
  • 我们需要获取两部分内容:将要执行的命令进行分割,获取重定向文件。
char* filename = NULL;
  1. 执行流程:首先判断是否包含重定向(checkRedir);再把我们要执行的命令进行分分割(将重定向字符设置为’\0’,这样strtok只能切割要执行的命令);最后让子进程执行重定向操作。
#define IsSpace(buf, pos) do{while(isspace(buf[pos])) pos      ++;}while(0)

while(0)目的:可以在使用宏定义的函数处,结尾加上分号(;)可以消除宏和函数之间的这部分差异,让宏看起来更像个函数。

//用宏定义多个整数,来表示重定向的类型
#define NoneRedir -1
#define StdinRedir 0
#define StdoutRedir 1
#define AppenRedir 2

char* filename = NULL; //记录文件名
int redir_type = -1;  //记录重定向类型

//去除命令行参数中的空格(连续空格)
#define IsSpace(buf, pos) do{while(isspace(buf[pos])) pos++;}while(0)

//检查是否包含重定向符号
void CheckRedir(char* in)
{
    filename = NULL;
    redir_type = -1;
    
    int pos = strlen(in) - 1; //从命令行参数字符串后面往前找
    while(pos >= 0)
    {
        if(in[pos] == '>') 
        {
            if(in[pos - 1] == '>') //追加重定向
            {
                redir_type = 2;                          
                in[pos - 1] = '\0'; //便于将要执行的命令分割开
                pos++; //当前pos位置不是空格,isspace函数为假,直接返回
                IsSpace(in, pos);  //跳过空格
                filename = in + pos; //获取文件名
                break; //记录完毕,就立即退出
             }
             else   //输出重定向
             {
                 redir_type = 1;
                 in[pos] = '\0';
                 pos++;
                 IsSpace(in, pos);             
                 filename = in + pos;
                 break;
             }
        }
        else if(in[pos] == '<') //输入重定向
        {
            redir_type = 0;
            in[pos] = '\0';
            pos++;
            IsSpace(in, pos);
            filename = in + pos;
            break;
        }
        else 
        { 
            pos--;
        }
    }
}

void Split(char* in)
{
    CheckRedir(in); //在分割之前,先判断是否包含重定向符号
    
    int i = 0;
    argv[i++] = strtok(in, SP);  //首次调用
    while(argv[i++] = strtok(NULL, SP)); //后续调用

    if(strcmp("ls", argv[0]) == 0) 
    {
       argv[i-1] = (char*)"--color";   //加上后,ls显示内容,颜色为auto                   
       argv[i] = NULL; //argv以NULL结尾,便于子进程在进程程序替换时参数的传递
    }
}

void Execute()
{
    pid_t id = fork(); 
       
    if(id == 0)  
    {
        //让子进程执行重定向
        int fd = -1;                                     
        if(redir_type == StdinRedir)
        {
            fd = open(filename, O_RDONLY);
            dup2(fd, 0);  //改变文件描述符的执行,从而实现输入、输出重定向
        }
        else if(redir_type == StdoutRedir)
        {
            fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
            dup2(fd, 1);
        }
        else if(redir_type == AppenRedir)
        {
            fd = open(filename, O_WRONLY|O_CREAT|O_APPEND      , 0666);
            dup2(fd, 1);
        }
  
        execvp(argv[0], argv); //程序替换,执行命令
        exit(1);  //子进程退出
    }
  
    int status = 0;
    pid_t rid = waitpid(id, &status, 0); //父进程等待,进行回收子进程的资源
    if(rid > 0)
    {
        exit_code = WEXITSTATUS(status); //正常退出,获取子进程的退出码
    }                                                                              
}

7. 总代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<ctype.h>
#include<sys/stat.h>
#include<fcntl.h>

#define SIZE 1024
#define SP " "

char* argv[SIZE]; 
char env[SIZE]; //相当于环境变量表,存储新增的环境变量

int exit_code;  //全局变量,存储子进程的退出码,便于echo $?直接输出退出码

//以下都和重定向有关
//用宏定义多个整数,来表示重定向的类型
#define NoneRedir -1
#define StdinRedir 0
#define StdoutRedir 1
#define AppenRedir 2

char* filename = NULL; //记录文件名
int redir_type = -1;  //记录重定向类型

//去除命令行参数中的空格(连续空格)
#define IsSpace(buf, pos) do{while(isspace(buf[pos])) pos++;}while(0)


char* HostName() //获取主机名
{
    char* ret = getenv("HOSTNAME"); 
    if(ret) return ret;
    else return (char*)"None";
}
 
char* UserName() //获取用户名
{
    char* ret = getenv("USER");
    if(ret) return ret;
    else return (char*)"None";                                                         
}
    
char* CurrentWorkdir() //获取当前工作目录
{
    char* ret = getenv("PWD");
    if(ret) return ret;
    else return (char*)"None";
}

int Interative(char* in)
 {
    printf("[%s@%s %s]#", UserName(), HostName(), CurrentWorkdir());                                                      
    fgets(in, SIZE, stdin);  
    in[strlen(in) - 1] = '\0';  //移除换行字符\n
    
    return strlen(in) - 1;  //处理空串""情况
}

//检查是否包含重定向符号
void CheckRedir(char* in)
{
    filename = NULL;
    redir_type = -1;
    
    int pos = strlen(in) - 1; //从命令行参数字符串后面往前找
    while(pos >= 0)
    {
        if(in[pos] == '>') 
        {
            if(in[pos - 1] == '>') //追加重定向
            {
                redir_type = 2;                          
                in[pos - 1] = '\0'; //便于将要执行的命令分割开
                pos++; //当前pos位置不是空格,isspace函数为假,直接返回
                IsSpace(in, pos);  //跳过空格
                filename = in + pos; //获取文件名
                break; //记录完毕,就立即退出
             }
             else   //输出重定向
             {
                 redir_type = 1;
                 in[pos] = '\0';
                 pos++;
                 IsSpace(in, pos);             
                 filename = in + pos;
                 break;
             }
        }
        else if(in[pos] == '<') //输入重定向
        {
            redir_type = 0;
            in[pos] = '\0';
            pos++;
            IsSpace(in, pos);
            filename = in + pos;
            break;
        }
        else 
        { 
            pos--;
        }
    }
}

void Split(char* in)
{
    CheckRedir(in); //在分割之前,先判断是否包含重定向符号
    
    int i = 0;
    argv[i++] = strtok(in, SP);  //首次调用
    while(argv[i++] = strtok(NULL, SP)); //后续调用

    if(strcmp("ls", argv[0]) == 0) 
    {
       argv[i-1] = (char*)"--color";   //加上后,ls显示内容,颜色为auto                   
       argv[i] = NULL; //argv以NULL结尾,便于子进程在进程程序替换时参数的传递
    }
}

int BuiltinCmd()
{
    if(strcmp("echo", argv[0]) == 0)
     {                                                       
        ret = 1;
        if(!argv[1])  //echo:打印一个空行
            printf("\n");
        else if(argv[1][0] != '$') 
            printf("%s\n", argv[1]); //echo aaaa:打印aaaa
        else 
        {
            if(argv[1][1] == '?')
            {
                printf("%d\n", exit_code); //echo $?:打印退出码
                exit_code = 0; //便于在执行echo $?时,退出码=0
            }
            else 
                printf("%s\n", getenv(argv[1] + 1)); //echo $USER:打印环境变量的内容
        }
    }
    else if(strcmp("cd", argv[0]) == 0)
    {
        ret = 1; 
        char* target = argv[1]; //需要切换的路径
        if(!target) target = getenv("HOME"); //cd,表示切换到家目录中
        
        chdir(target); //更改当前工作目录
        
        char tmp[SIZE];  //存储获取到的当前工作目录的绝对路径
        char pwd[SIZE];  //存储更改后的环境变量PWD的内容
        getcwd(tmp, SIZE);  //获取当前工作目录的绝对路径
                            
        snprintf(pwd, SIZE, "PWD=%s", tmp); //多变一,拼接
    
        putenv(pwd); //修改环境变量PWD的内容
    }
    else if(strcmp("export", argv[0]) == 0)
    int ret = 0; 
    {
        ret = 1;
        if(argv[1])
        {
            strcpy(env, argv[1]); //将新增的环境变量的内容,拷贝到环境变量表中
            putenv(env);                                 
        }  
    }

    return ret;
}

void Execute()
{
    pid_t id = fork(); 
       
    if(id == 0)  
    {
        //让子进程执行重定向
        int fd = -1;                                     
        if(redir_type == StdinRedir)
        {
            fd = open(filename, O_RDONLY);
            dup2(fd, 0);  //改变文件描述符的执行,从而实现输入、输出重定向
        }
        else if(redir_type == StdoutRedir)
        {
            fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
            dup2(fd, 1);
        }
        else if(redir_type == AppenRedir)
        {
            fd = open(filename, O_WRONLY|O_CREAT|O_APPEND      , 0666);
            dup2(fd, 1);
        }
  
        execvp(argv[0], argv); //程序替换,执行命令
        exit(1);  //子进程退出
    }
  
    int status = 0;
    pid_t rid = waitpid(id, &status, 0); //父进程等待,进行回收子进程的资源
    if(rid > 0)
    {
        exit_code = WEXITSTATUS(status); //正常退出,获取子进程的退出码
    }
}



int main()
{
    char CommandLine[SIZE]; 

    while(1)
    {
        //1.打印命令行提示符+获取用户输入的命令行字符串                                      
        int n =  Interative(CommandLine);  
        if(n == 0) continue;  //空串,用户继续输入

        //2.分割命令行字符串
        Split(CommandLine);

        //3.处理内建命令
        n = BuiltinCmd();
        if(n) continue;

        //4.执行命令(创建子进程,进行替换            
        Execute();
    }
  
    return 0;
}

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

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

相关文章

[000-01-002].第03节:Git基础命令

我的博客大纲 我的GIT学习大纲 1、Git的常用命令 2、Git操作步骤&#xff1a; 2.1.操作Git第一步&#xff1a;设置全局的用户签名 1.设置用户名&#xff1a; 格式&#xff1a;git config --global user.name 用户名命令&#xff1a;git config --global user.name root 2.设置…

【开源免费】基于SpringBoot+Vue.JS在线旅游网站(JAVA毕业设计)

本文项目编号 T 025 &#xff0c;文末自助获取源码 \color{red}{T025&#xff0c;文末自助获取源码} T025&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

【网络安全】漏洞挖掘:文件上传实现Webshell

未经许可,不得转载。 文章目录 正文正文 提交文件功能点,显示只能上传png、jpg、pdf文件 上传一个正常的图片,请求响应如下: 可以看到,该文件被上传到redacted.com,这为后面实现Webshell提供了前提。 接着,我上传webshell.php文件,文件内容为payload,拦截请求包,将…

逆变器的防孤岛测试性能评估

逆变器是太阳能发电系统中的关键设备&#xff0c;它将太阳能电池板产生的直流电转换为交流电&#xff0c;供电网或负载使用。在并网运行的太阳能发电系统中&#xff0c;如果电网出现故障&#xff0c;导致与电网断开连接&#xff0c;但逆变器仍然继续向电网供电&#xff0c;这种…

VBA CSV数据拆分

1. Range.TextToColumns函数 Option ExplicitSub txt2Col()ActiveSheet.Range("A2").CopyActiveSheet.PasteSelection.TextToColumns DataType:xlDelimited, _ConsecutiveDelimiter:True, Comma:True End Sub 2. 效果 执行前 cccccc 执行后效果​ cccc 3. 参照 更…

视频编辑SDK解决方案,代码逻辑结构清晰,接入便捷

美摄科技作为视频编辑技术领域的佼佼者&#xff0c;凭借其深厚的多媒体处理积累和创新精神&#xff0c;推出了革命性的移动端视频编辑SDK解决方案&#xff0c;彻底颠覆了传统视频编辑的方式&#xff0c;让每一份灵感都能轻松转化为引人入胜的视觉盛宴。 一站式视频创作与编辑平…

The First项目报告:BlackCardCoin让数字资产多元化

现有的区块链技术存在吞吐量瓶颈、互操作性有限和次优共识机制等问题&#xff0c;导致效率低下&#xff0c;阻碍了真正全球化金融体系的建立。因此&#xff0c;迫切需要一种创新的区块链&#xff0c;能够容纳现代金融的复杂性&#xff0c;包括即时结算、强大的安全措施&#xf…

用STM32做一个USB-TTL工具吧

某宝1分钱白嫖一个STM32C6T6的板子&#xff0c;然后手里的CH340挂了。那么自己做一个吧。 串口没有使用DMA空闲中断等等机制,有兴趣的加上了call我炒一下。 惯例CUBEMAX配置 1.usb-fs 2.usb-cdc 3.串口开一个中断&#xff0c;使用LL库 贴代码了 usart.c /* USER CODE BEGIN…

电源层 BGA 孔图案对高速信号质量的影响

电源层中的大量间隙孔会对高速信号的行为产生巨大影响。信号完整性对于设计人员来说是一个日益严重的问题&#xff0c;因为新设计需要具有越来越多引脚数的组件&#xff0c;而这些组件必须使用过孔进行连接以访问印刷电路板 (PCB) 的内层。孔和焊盘堆叠的正确设计可以产生高产量…

①MongoDB基本知识①

MongDB属于非关系型数据库一派&#xff0c;没有固定的数据格式存储&#xff0c;是一个具备高性能、高拓展的文档型数据库&#xff0c;数据以BSON(JSON的二进制)的格式存储。 特点: 基于对象模型&#xff0c;关系简单。没有外键的约束&#xff0c;也没有强连接表的关系&#x…

OpengGL教程(三)---使用VAO和VBO方式绘制三角形

本章参考官方教程&#xff1a;learnopengl-cn VertexShader.glsl #version 330 core layout(location 0) in vec3 position; layout(location 1) in vec3 color; uniform mat4 projection; // 投影矩阵 out vec4 ourColor; void main() {gl_Position projection * vec4(p…

信息安全管理工程师

信息安全管理工程师是专门从事信息安全领域的专业人员&#xff0c;主要负责维护计算机系统、网络和数据的安全&#xff0c;以抵御潜在威胁和攻击。 随着信息技术迅猛发展&#xff0c;对此类专业人才的需求持续上升。 如何能够成为一名卓越的信息安全管理工程师呢&#xff1f;…

springboot对数据库进行备份+对一个文件夹内的文件按时间排序,只保留最近的8个文件

首先&#xff0c;对数据库进行备份&#xff0c;用到的命令&#xff1a; mysqldump --opt -h 192.168.1.200 --userroot --passwordxxx --result-fileE://data//20240911141400.sql --default-character-setutf8 xxx&#xff08;数据库名&#xff09; 直接上代码 配置文件部分…

Android 语言国际化三步

1.罗列: 可以多罗列几个 不需要全部实现 res下创建这个文件:locale-config <locale-config xmlns:android"http://schemas.android.com/apk/res/android"><locale android:name"zh" /> <!--中文 --><locale android:name"e…

优化 TCP 以提高网络性能

本页面简要介绍了计算正确设置的方法&#xff0c;以缩短 Google Cloud 和混合场景中 TCP 连接的延迟时间。本页面还可帮助您了解如何缩短 Google Cloud 中流程之间的连接延迟时间。 现代微服务架构主张&#xff0c;开发者应该构建处理单一任务的小型服务。服务应根据系统的可靠…

Open CASCADE学习|通过指定点的曲线

在OpenCASCADE中&#xff0c;如果你想通过一系列指定的点来创建一条曲线&#xff0c;你可以使用Geom2dAPI_Interpolate类来实现二维曲线的插值&#xff0c;或者使用GeomAPI_Interpolate类来实现三维曲线的插值。这些类允许你定义一条B样条曲线&#xff0c;这条曲线将精确地通过…

内网安全:反弹shell

目录 一.Netcat反弹Shell 二.PowerCat反弹Shell PowerCat和nc正向连接 PowerCat和nc反向连接 PowerCat和PowerCat反向连接 三.Bash反弹shell 四.Python 反弹Shell 一.Netcat反弹Shell 在Windows容易被杀 介绍&#xff1a; Netcat简称NC,是一个简单、可靠的网络工具,被…

0V企业级别通配符证书

OV企业级别通配符证书&#xff0c;支持顶级域名下面的所有二级子域名&#xff0c;不限制二级子域名个数&#xff0c;证书显示单位实名名称。 加密算法支持2048bits&#xff1b;签名算法支持SHA256withRSA。 可提供各种服务器的证书文件安装格式。 加密支持协议&#xff1a;T…

微信如何转发群消息给其他群或其他好友?

使用微加机器人将群消息转发到其他群或好友 工具选择&#xff1a; 近期想做一个从某个微信群将消息自动转发到另一个微信群的效果&#xff08;一些课程群和线报群只有付费才能进&#xff09; 试了市面上很多免费的转发软件&#xff0c;发现免费还是很难有好东西&#xff0c;…

Nature Communications 可远程操控食欲的口服软体机器人

肥胖对人群的的影响是深远的&#xff0c;它不仅关系到个人的健康&#xff0c;还与全球公共卫生挑战密切相关。据世界卫生组织的数据&#xff0c;全球每8人中就有1人患有肥胖症。肥胖增加了患2型糖尿病、心血管疾病、某些癌症等多种健康问题的风险&#xff0c;并对社会经济产生重…