精华内容
下载资源
问答
  • eval会对后面的cmdLine进行两遍扫描,如果第一遍扫描后,cmdLine是个普通命令,则执行此命令;如果cmdLine中含有变量的间接引用,则保证间接引用的语义。 举例如下: set 11 22 33 44 如果要输出最近一个参数,...

    eval会对后面的cmdLine进行两遍扫描,如果第一遍扫描后,cmdLine是个普通命令,则执行此命令;如果cmdLine中含有变量的间接引用,则保证间接引用的语义。

    举例如下:

    set 11 22 33 44

    如果要输出最近一个参数,即44,可以使用如下命令,

    echo $4

    但是如果我们不知道有几个参数的时候,要输出最后一个参数,大家可能会想到使用$#来输出最后一个参数,

    如果使用命令:

    echo "\$$#"

    则得到的结果是 $4,而不是我们想要的44。这里涉及到一个变量间接引用的问题,我们的本意是输出 $4,默认情况下,命令后忽略变量间接引用的情况。

    这时候,就可以使用eval命令。

    eval echo "\$$#"

    得到的结果为44

     

    1.eval命令将会首先扫描命令行进行所有的替换,憨厚再执行命令。该命令使用于那些一次扫描无法实现其功能的变量。该命令对变量进行两次扫描。这些需要进行两次扫描的变量有时候被称为复杂变量。

     

    2.eval也可以用于回显简单变量,不一定时复杂变量。

    NAME=ZONE

    eval echo $NAME等价于echo $NAME

     

    3.两次扫描

    test.txt内容:hello shell world!

    myfile="cat test.txt"

    echo $myfile  #result:cat test.txt
    eval echo $myfile  #result:hello shell world!

    从(2)可以知道第一次扫描进行了变量替换,第二次扫描执行了该字符串中所包含的命令

     

    4.获得最后一个参数

    echo "Last argument is $(eval echo \$$#)"

     

     

    展开全文
  • 批处理命令字符串替换

    千次阅读 2018-10-20 07:40:03
    字符串aa赋值 C:\Users\Administrator.ZHANGHAO-PC>set aa='my ver is 3.2.0' 打印字符串aa C:\Users\Administrator.ZHANGHAO-PC>echo %aa% 'my ver is 3.2.0' 替换字符串aa中的3.2.0为3.0.7 C...

    给字符串aa赋值

    C:\Users\Administrator.ZHANGHAO-PC>set aa='my ver is 3.2.0'

    打印字符串aa

    C:\Users\Administrator.ZHANGHAO-PC>echo %aa%
    'my ver is 3.2.0'

    替换字符串aa中的3.2.0为3.0.7

    C:\Users\Administrator.ZHANGHAO-PC>set aa=%aa:3.2.0=3.0.7%

    C:\Users\Administrator.ZHANGHAO-PC>echo %aa%
    'my ver is 3.0.7'
     

    展开全文
  • shell字符串替换命令

    千次阅读 2020-03-25 21:11:51
    替换文件中的字符串 sed -i "s/oldstring/newstring/g" `grep oldstring -rl /tmp/mydir` shell脚本中替换字符串 #!/bin/bash str='hello oldstring!' dist="${str/oldstring/newstring}" echo $dist ...
    替换文件中的字符串
    sed -i "s/oldstring/newstring/g" `grep oldstring -rl /tmp/mydir`

    shell脚本中替换字符串

    #!/bin/bash
    
    str='hello oldstring!'
    dist="${str/oldstring/newstring}"
    
    echo $dist

     

    参考:https://blog.csdn.net/q322625/article/details/88888305

    展开全文
  • 单片机串口实现字符命令解析

    千次阅读 2020-10-30 16:20:38
    i++ ) // 遍历命令列表 { if ( strcmp( command_list.cmdNames[i].cmd_name, cmdStr ) == 0 ) //比较接收到的命令字符串 和 列表中存储的命令字符串是否相等,如果相等就调用命令字符串对应的函数。 { command_list...

            通常情况下串口通信用的大多数都是用十六进制数据来传输指令,比如最常见的modbus的通信,如读保持寄存器指令:01 03 00 00 00 01 84 0A,这种十六进制的指令在这里就不讨论了。想要详细了解可以看往期的文章。串口相关文章链接如下:

    STM32F103单片机modbus通信示例

    STM32单片机串口空闲中断+DMA接收不定长数据

    STM32单片机串口空闲中断接收不定长数据

    STM8学习笔记---串口通信中如何自己定义通信协议

    STM8学习笔记---Modbus通信协议简单移植

    STM8学习笔记---串口printf函数的实现

           有时候串口也需要通过字符串命令来实现某些功能, 如果使用AT指令的话,通信就是以字符串格式进行。

           有时候自己做产品的时候,需要通过指令来控制设备,使用字符串的话更方便。比如发送一条开LED灯的指令"led on",关灯指令"led off"。这样通过字符串命令来控制设备,比直接通过16进制数字控制设备看起来更方便直观。比如今天要实现的功能。

            那么如何解析字符串命令呢?通常第一个想法就是,将读取到的字符串和程序中存储的字符串进行比较,如果字符串相等的话,就调用某个函数去执行相应的功能。这种办法是最简单也就是最容易实现的。通常用一个switch语句就可以搞定,switch的每个分支代表一个指令,然后去调用相关的函数执行。相关代码可以参考 C语言中字符串比较 这篇文章。

            还有没有其他方法呢?如何了解数据结构的话就可以想到哈希表。可以使用类似于哈希表的原理来实现。在一个表中每一个键就对应一个值。通过键就可以找到值。就好像学生的学号和姓名一样,将学号和姓名连接起来,放在一起,通过学号就可以找到姓名。将字符串和要执行的函数对应起来,放在哈希表中。在表中找到字符串后,就可以直接找到对应执行的函数。增加或者删除串口命令时,只需要操作这张表就行了。就不用每次指令或者函数名发生变化时,都要去switch语句中修改。
           在单片机中没有类似哈希表的这种数据结构,那要怎么实现呢?于是想到了用结构体去实现,在一个结构体里面有两个元素,一个是字符串,一个是需要执行的函数。这样字符串和函数就被绑定在了一起。在初始化命令的时候,将字符串和对应的函数,都写在一个结构体中。那么只有找到了这个字符串,就自然会找到对应的执行函数。这样就实现了类似哈希表的功能。 

            首先定义一个结构体,用来存储字符串命令和对应功能的函数     

    typedef void ( *functions )( void );                   // 函数指针类型
    //命令结构体
    typedef struct
    {
        char cmd_name[MAX_CMD_LENGTH + 1];                //字符数组存储字符串命令
        functions cmd_functions;                          //通过指针传递函数名
    }CMD_Name_Func;

           在结构体里面有两个元素,一个字符数组用来存储字符串命令。一个指针用来存储函数的入口地址,该函数没有返回值,没有参数。

           如果每个命令都对应一个这样的结构体,命令比较多的时候,如何能方便快速去找到这些结构体呢?最简单的就是将这些结构体存储在数组中,这样数组的每一个元素就是一个结构体,通过数组的下标就能很方便的访问到每一个结构体。

    // 命令列表结构体类型
    typedef struct
    {
        CMD_Name_Func cmdNames[MAX_CMDS_COUNT];                     //结构体数组字符串命令 和对应函数
        int num;	                                                //统计结构体个数
    }CMD_LIST;

           在另一个结构体中用一个数组来存储命令结构体,每个结构体的数组元素都代表一个字符串命令和对应的函数,同时用一个计数器来统计,共存储了多少条命令。当串口接收到一个字符串后,就遍历一次结构体数组,对比里面是否有相同的字符串,如果有,就执行该字符串对应的函数。通过这种方式来处理字符串和命令的话,只需要在初始化的时候将字符串命令添加到这个列表中就可以了,而程序的其他地方就不需要修改了。

         要实现上面所说的功能,还需要再实现两个函数,一个函数实现将命令添加到结构体,一个函数实现遍历结构体数组,寻找匹配的字符串并执行相应的函数。

    static CMD_LIST command_list = {NULL, 0};  // 全局命令列表,保存已注册命令集合
    //注册命令
    void register_cmds( CMD_Name_Func reg_cmds[], int length )
    {
        int i;
    
        if ( length > MAX_CMDS_COUNT )
        {
            return;
        }
    
        for ( i = 0; i < length; i++ )
        {
            if ( command_list.num < MAX_CMDS_COUNT ) // 命令列表未满
            {
                strcpy( command_list.cmdNames[command_list.num].cmd_name, reg_cmds[i].cmd_name );       //将字符串命令拷贝到列表中
                command_list.cmdNames[command_list.num].cmd_functions = reg_cmds[i].cmd_functions;          //将命令对应的函数存储在列表中
                command_list.num++;                                                                     // 数量值默认为0,每添加一个命令,数量加1.             
            }
        }
    }
    

             这个命令注册函数实现将命令结构体添加到命令列表中。用户新增加一条指令,就调用一次注册函数,将字符串命令添加到命令列表字符串中,同时将字符串命令对应的函数也添加到列表函数中。如果有新增加的子模块,只需要在子模块中调用一次注册命令,就完成了字符串命令的增加。其他代码不需要修改。

    比如现在led模块需要添加命令

    //注册led命令
    void led_register( void )
    {
        //初始化 字符串命令和对应函数
        CMD_Name_Func led_cmds[] =
        {
            {"led1 on", led1_on},                       // 添加字符串命令 和 对应的函数
            {"led1 off", led1_off},                     
            {"led2 on", led2_on},
            {"led2 off", led2_off},
            {"led3 on", led3_on},
            {"led3 off", led3_off}
        };
        //将命令添加到列表中
        register_cmds( led_cmds, ARRAY_SIZE( led_cmds ) );	// ARRAY_SIZE 用来计算结构体数组中,数组的个数。个数=结构体总长度/单个数组长度
    }

            在led模块中创建命令结构体,并将创建的结构体添加到命令列表中。通过代码可以看到增加了6条关于led的字符串命令,每个字符串命令都对应一个需要执行的函数。

           假如现在还需要添加一个蜂鸣器的子模块,那么就可以直接在蜂鸣器的子文件内直接注册命令。

    //注册 beep命令
    void beep_register( void )
    {
        //初始化 字符串命令和对应函数
        CMD_Name_Func beep_cmds[] =
        {
            {"beep on", beep_on},                       
            {"beep off", beep_off} 
        };
        //将命令添加到列表中
        register_cmds( beep_cmds, ARRAY_SIZE( beep_cmds ) );	// ARRAY_SIZE 用来计算结构体数组中,数组的个数。个数=结构体总长度/单个数组长度
    }

    在蜂鸣器的模块中添加了两条命令,然后通过注册函数将蜂鸣器相关的命令就添加到了命令列表中。

    通过一个注册命令就实现了命令的添加,而不需要修改其他的代码,实现了代码的"高内聚低耦合"

    上面实现了命令的注册,还需要实现一个命令的解析。

    void match_cmd( char *cmdStr )
    {
        int i;
        if ( strlen( cmdStr ) > MAX_CMD_LENGTH )
        {
            return;
        }
        for ( i = 0; i < command_list.num; i++ )     // 遍历命令列表
        {
            if ( strcmp( command_list.cmdNames[i].cmd_name, cmdStr ) == 0 )   //比较接收到的命令字符串 和 列表中存储的命令字符串是否相等,如果相等就调用命令字符串对应的函数。
            {
                command_list.cmdNames[i].cmd_functions();
            }
        }
    }

            每次注册命令的时候,会有个计数器统计注册命令的数量。在命令解析函数中就循环去判断接收到的命令是否在命令列表中,如果命令列表中存在相等的字符串,就去执行对应的函数。而命令解析函数是不关心接收到的具体字符串命令是什么,需要执行的相应函数是什么。所以每次命令添加或者删除的时候,对命令解析和函数没有任何的影响。

           这个命令解析函数比较类似于设计模式中的"工厂模式",所谓的工厂模式百度百科解释如下:

            如果不了解面向对象编程的话,可能上面的这个解释看的不太明白。举个简单的例子就是,工厂生产东西的时候不关心具体生产的是什么东西,客户将需要生产东西的大小尺寸颜色特征告诉工厂,工厂就按照要求去执行。比如客户要求做一个直径5cm的玻璃透明圆柱体,圆柱体只需要底面,不需要顶面。工厂就按照客户的要求去生产这样一个东西,虽然这个东西按照一般经验来看就是一个透明的玻璃杯。但是工厂不用关心这个东西的名称和用途,只需要按照客户的要求去实现。

           而上面的命令解析函数,实际上也就是一个工厂,客户将一个字符串和一个函数送来。工厂就按照指定的字符串去执行指定函数。而工厂本身不去关心这个字符串具体是什么?函数具体是什么?这样的话,只要客户在命令列表中注册了字符串命令和相应的执行动作。命令解析函数就可以实现想要的功能。

           通过这种模式去解析字符串命令的话,就可以移植到到任何需要命令解析的单片机上,而不用去关心单片机的IO、中断、寄存器等等其他东西。

    下面就贴出完整的代码

    命令解析头文件 cmd.h :

    #ifndef __CMD_H_
    #define __CMD_H_
    #include "iostm8s103F3.h"
    #define ARRAY_SIZE(x)	(sizeof(x) / (sizeof((x)[0])))        //用来计算结构体数组中,数组的个数。个数=结构体总长度/单个数组长度
    #define		MAX_CMD_LENGTH		15	                // 最大命令名长度
    #define		MAX_CMDS_COUNT		20	                // 最大命令数
    typedef void ( *functions )( void );  	                        // 命令操作函数指针类型
    //命令结构体类型 用于存储字符串命令和对应函数
    typedef struct
    {
        char cmd_name[MAX_CMD_LENGTH + 1];                          // 命令名 字符串末尾系统会自动添加结束符'/0'       sizeof("name")大小为 10
        functions cmd_functions;			  	        // 命令操作函数     sizeof(func) 大小为 2
    }CMD_Name_Func;
    // 命令列表结构体类型  用于存储字符串命令数组
    typedef struct
    {
        CMD_Name_Func cmdNames[MAX_CMDS_COUNT];                     // 存储字符串命令 和对应函数
        int num;	                                                // 命令数组个数
    }CMD_LIST;
    
    
    void register_cmds( CMD_Name_Func reg_cmds[], int num );
    void match_cmd( char *str );
    
    #endif
    
    
    

    命令解析代码cmd.c

    #include <string.h>
    #include "cmd.h"
    #include "uart.h"
    static CMD_LIST command_list = {NULL, 0};  // 全局命令列表,保存已注册命令集合
    /*
    * 函数介绍: 命令注册函数 每新添加一个命令,就添加到命令列表中
    * 输入参数: reg_cmds 待注册命令结构体数组
    *            length   数组个数
    * 输出参数: 无
    * 返回值 :  无
    * 备    注: length 不得超过 MAX_CMDS_COUNT  
    */
    void register_cmds( CMD_Name_Func reg_cmds[], int length )
    {
        int i;
    
        if ( length > MAX_CMDS_COUNT )
        {
            return;
        }
    
        for ( i = 0; i < length; i++ )
        {
            if ( command_list.num < MAX_CMDS_COUNT ) // 命令列表未满
            {
                strcpy( command_list.cmdNames[command_list.num].cmd_name, reg_cmds[i].cmd_name );       //将字符串命令拷贝到列表中
                command_list.cmdNames[command_list.num].cmd_functions = reg_cmds[i].cmd_functions;          //将命令对应的函数存储在列表中
                command_list.num++;                                                                     // 数量值默认为0,每添加一个命令,数量加1.             
            }
        }
    }
    
    /*
    * 函数介绍: 命令匹配执行函数
    * 输入参数: cmdStr 待匹配命令字符串
    * 输出参数: 无
    * 返回值 :  无
    * 备    注: cmdStr 长度不得超过 MAX_CMD_NAME_LENGTH
    */
    void match_cmd( char *cmdStr )
    {
        int i;
    
        if ( strlen( cmdStr ) > MAX_CMD_LENGTH )
        {
            return;
        }
    
        for ( i = 0; i < command_list.num; i++ )	                                                    // 遍历命令列表
        {
            if ( strcmp( command_list.cmdNames[i].cmd_name, cmdStr ) == 0 )                             //比较接收到的命令字符串 和 列表中存储的命令字符串是否相等,如果相等就调用命令字符串对应的函数。
            {
                command_list.cmdNames[i].cmd_functions();
            }
        }
    }

    led头文件led.h

    #ifndef __LED_H
    #define __LED_H
    #include "iostm8s103F3.h"
    
    #define  LED1  PD_ODR_ODR4                       //蓝
    #define  LED2  PA_ODR_ODR1                       //绿
    #define  LED3  PA_ODR_ODR2                       //红
    #define BLUE    {LED1=1;LED2=0;LED3=0;}
    #define GREEN   {LED1=0;LED2=1;LED3=0;}
    #define RED     {LED1=0;LED2=0;LED3=1;}
    #define CYAN    {LED1=1;LED2=1;LED3=0;}          //青
    #define PURPLE  {LED1=1;LED2=0;LED3=1;}        //紫
    #define YELLOW  {LED1=0;LED2=1;LED3=1;}         //黄
    #define ONALL   {LED2=1;LED3=1;LED1=1;}
    #define OFFALL  {LED1=0;LED2=0;LED3=0;}
    void LED_GPIO_Init( void );
    void led1_on(void);
    void led1_off(void);
    void led2_on(void);
    void led2_off(void);
    void led3_on(void);
    void led3_off(void);
    void led_register(void);
    #endif
    

    led.c

    #include "led.h"
    #include "cmd.h"
    
    //3色LED
    void LED_GPIO_Init( void )
    {
        PD_DDR |= ( 1 << 4 );        //PD4 输出 led
        PD_CR1 |= ( 1 << 4 );        //PD4 推挽输出
        PD_CR2 |= ( 1 << 4 );
    
        PA_DDR |= ( 1 << 1 );        //PA1 输出 led
        PA_CR1 |= ( 1 << 1 );        //PA1 推挽输出
        PA_CR2 |= ( 1 << 1 );
    
        PA_DDR |= ( 1 << 2 );        //PA2 输出 led
        PA_CR1 |= ( 1 << 2 );        //PA2 推挽输出
        PA_CR2 |= ( 1 << 2 );
    }
    
    void led1_on( void )
    {
        LED1 = 1;
    }
    
    void led1_off( void )
    {
        LED1 = 0;
    }
    void led2_on( void )
    {
        LED2 = 1;
    }
    
    void led2_off( void )
    {
        LED2 = 0;
    }
    void led3_on( void )
    {
        LED3 = 1;
    }
    
    void led3_off( void )
    {
        LED3 = 0;
    }
    
    //注册led命令
    void led_register( void )
    {
        //初始化 字符串命令和对应函数
        CMD_Name_Func led_cmds[] =
        {
            {"led1 on", led1_on},                       // 一个结构体变量大小为 12 (字符串大小10 + 函数名大小2)
            {"led1 off", led1_off},                     // 一个结构体变量大小为 12
            {"led2 on", led2_on},
            {"led2 off", led2_off},
            {"led3 on", led3_on},
            {"led3 off", led3_off}
        };
    
        //将命令添加到列表中
        register_cmds( led_cmds, ARRAY_SIZE( led_cmds ) );	// ARRAY_SIZE 用来计算结构体数组中,数组的个数。个数=结构体总长度/单个数组长度
    }
    

    beep.h

    #ifndef __BEEP_H
    #define __BEEP_H
    #include "iostm8s103F3.h"
    #define  BEEP  PB_ODR_ODR4                   
    void BEEP_GPIO_Init( void );
    void beep_register( void );
    #endif

    beep.c

    #include "beep.h"
    #include "cmd.h"
    
    void BEEP_GPIO_Init( void )
    {
        PB_DDR |= ( 1 << 4 );        //PB4 
        PB_CR1 |= ( 1 << 4 );        //PB4 推挽输出
        PB_CR2 |= ( 1 << 4 );
    }
    void beep_on( void )
    {
        BEEP = 1;
    }
    void beep_off( void )
    {
        BEEP = 0;
    }
    //注册 beep命令
    void beep_register( void )
    {
        //初始化 字符串命令和对应函数
        CMD_Name_Func beep_cmds[] =
        {
            {"beep on", beep_on},                       // 一个结构体变量大小为 12 (字符串大小10 + 函数名大小2)
            {"beep off", beep_off}                     // 一个结构体变量大小为 12
           
        };
        //将命令添加到列表中
        register_cmds( beep_cmds, ARRAY_SIZE( beep_cmds ) );	// ARRAY_SIZE 用来计算结构体数组中,数组的个数。个数=结构体总长度/单个数组长度
    }
    

    uart.h

    #ifndef __UART_H
    #define __UART_H
    #include "iostm8s103F3.h"
    
    extern char uartRecStr[20];             //串口接收字符串存储
    extern unsigned char uartRecCnt;                   //接收数据个数
    extern _Bool rec_ok;
    
    void Uart1_IO_Init( void );
    void Uart1_Init( unsigned int baudrate );
    void SendChar( unsigned char dat );
    void SendString( unsigned char* s );
    
    #endif

    uart.c

    #include "uart.h"
    #include "main.h"
    
    char uartRecStr[20] = {0};             //串口接收字符串存储
    unsigned char uartRecCnt = 0;                   //接收数据个数
    _Bool rec_ok = 0;                                //接收完成标志位
    
    //在Library Options中将Printf formatter改成Large
    //重新定向putchar函数,使支持printf函数
    int putchar( int ch )
    {
        while( !( UART1_SR & 0X80 ) );              //循环发送,直到发送完毕
        UART1_DR = ( u8 ) ch;
        return ch;
    }
    //串口只用发送口,不用接收口
    void Uart1_IO_Init( void )
    {
        PD_DDR |= ( 1 << 5 );                       //输出模式 TXD
        PD_CR1 |= ( 1 << 5 );                       //推挽输出
    
        PD_DDR &= ~( 1 << 6 );                      //输入模式 RXD
        PD_CR1 &= ~( 1 << 6 );                      //浮空输入
    }
    
    //波特率最大可以设置为38400
    void Uart1_Init( unsigned int baudrate )
    {
        unsigned int baud;
        baud = 16000000 / baudrate;
        Uart1_IO_Init();
        UART1_CR1 = 0;      //禁止发送和接收
        UART1_CR2 = 0;      //8 bit
        UART1_CR3 = 0;      //1 stop
        UART1_BRR2 = ( unsigned char )( ( baud & 0xf000 ) >> 8 ) | ( ( unsigned char )( baud & 0x000f ) );
        UART1_BRR1 = ( ( unsigned char )( ( baud & 0x0ff0 ) >> 4 ) );
        UART1_CR2_bit.REN = 1;                      //接收使能
        UART1_CR2_bit.TEN = 1;                      //发送使能
        UART1_CR2_bit.RIEN = 1;                     //接收中断使能
    }
    
    //阻塞式发送函数
    void SendChar( unsigned char dat )
    {
        while( ( UART1_SR & 0x80 ) == 0x00 );       //发送数据寄存器空
        UART1_DR = dat;
    }
    //发送字符串
    void SendString( unsigned char* s )
    {
        while( 0 != *s )
        {
            SendChar( *s );
            s++;
        }
    }
    
    //接收中断函数 中断号18
    #pragma vector = 20                             // IAR中的中断号,要在STVD中的中断号上加2
    __interrupt void UART1_Handle( void )
    {
        unsigned char res = 0;
    
        res = UART1_DR;
        UART1_SR &= ~( 1 << 5 );                    //RXNE 清零
        //SendChar(res);                            //test
        if( ( res != '\r' ) && ( res != '\n' ) )    //字符串以回车换行符结束
        {
            uartRecStr[uartRecCnt++] = res;
        }
        else
        {
            rec_ok = 1;                             //置接收完成标志
        }
    }

    主程序main.c

    /*
    *函数功能,实现串口字符串命令解析
    */
    #include "iostm8s103F3.h"
    #include "main.h"
    #include "stdio.h"
    #include "delay.h"
    #include "stdlib.h"
    #include "uart.h"
    #include "string.h"
    #include "cmd.h"
    #include "led.h"
    #include "beep.h"
    
    void SysClkInit( void )
    {
        CLK_SWR = 0xe1;                             //HSI为主时钟源  16MHz CPU时钟频率
        CLK_CKDIVR = 0x00;                          //CPU时钟0分频,系统时钟0分频
    }
    
    void main( void )
    {
        __asm( "sim" );                             //禁止中断
        SysClkInit();
        delay_init( 16 );
        LED_GPIO_Init();
        BEEP_GPIO_Init();
        Uart1_Init( 9600 );
        __asm( "rim" );                             //开启中断
        
        //注册命令
        led_register();
        beep_register();
    
        while( 1 )
        {
            if( rec_ok )
            {
                rec_ok = 0;
                uartRecCnt = 0;
                SendString( uartRecStr );
                SendString( "\r\n" );
                match_cmd( uartRecStr );
                memset( uartRecStr, 0, sizeof( uartRecStr ) );		//清空备份数组 需要添加头文件 string.h
            }
        }
    }
    

            在主函数中检测串口是否接收到了字符串,串口接收字符串以回车换行结束。若串口接收到了字符串,将接收到的字符串通过串口发送出去,并检查一次接收到的字符串是否和命令列表中的字符串匹配?如果接收到的字符串和命令列表中的字符串匹配,就中执行一次相关的函数。最后将串口缓冲区清空,继续等待下一次命令。

    测试效果如下

            这样通过字符串命令就可以直接控制LED灯和蜂鸣器了,如果下次需要增加一个继电器控制模块,就只需要编写继电器模块的c代码,在进入main函数时,注册继电器命令。继电器的模块就会被添加进来了。而不需要修改其他的模块和串口任何代码。

            通过上面的例子可以看到这种模式是相当的好用,难道这种方法就没有一点缺点吗?如果在单片机上用的话,这种模式有一个致命的缺点,那就是太占内存了。

    首先看一张图

            这个是新建了4个led命令结构体,可以看出来每个命令的字符串数组长度都是16,函数指针默认为int型,占两个字节。一个结构体总共占18个字节。 4个led命令占4*18=72个字节的空间。虽然led命令最长的字符串只占8个字节,但是系统依然会分配16个字节空间。

           这个是命令列表,默认的最多命令数是20个,系统初始化的时候就一次性将这20个命令的空间分配好了。虽然代码中只用了4个命令,但是系统空间的占用却一点也没有少。

            这样的话对于空间比较小的单片机来说,虽然这种方法好用,但是太浪费空间。如果指令比较多,或者指令名比较长的话,可能光是指令列表就会把单片机的内存占满。这样的话还不如直接在switch语句中去比较字符串,直接比较字符串的话,只需要开辟一个字符串的存储空间就可以满足需求了。

            所以根据不同的情况选择合适的方法,适合自己的方法就是好方法。

    完整工程代码下载地址:https://download.csdn.net/download/qq_20222919/13077567

    本文代码参考资料地址:https://www.shaoguoji.cn/2017/11/18/c-object-oriented-command-parser/

    展开全文
  • Windows CMD中 find命令字符串查找)

    千次阅读 2019-10-09 19:35:56
    Windows CMD中 find命令字符串查找) 2014-07-08 16:59:11icanlove阅读数 79214文章标签:windowscmd更多 分类专栏:cmd 在cmd窗口中敲下find /? 这条命令,然后重重地按下回车键。 find /? 的执行结果很快,...
  • windows bat命令判断是否包含某字符

    千次阅读 2020-12-08 11:03:17
    判断 BRANCH_NAME 是否包含 hong echo %BRANCH_NAME%| findstr "hong" >nul && ( echo %BRANCH_NAME% has %b% ) || ( echo %a% not %b% ) if "%OTA_BUILD_ENABLE%"=="1" ( echo "hello" ...
  • Linux对字符串常用操作命令

    千次阅读 2018-02-28 18:56:21
    以空格分割字符串 awk ‘{print $1}’ 以特定字符分割字符串 str=${str//,/ } ——————–//后面是分割字符串的标志符号,最后一个/后面还有一个空格 剪切字符串 cut -b|-c|-f 3 ———————–b代表字节,...
  • CMD字符命令用途

    千次阅读 2016-04-28 20:53:31
    CMD字符命令用途 @ \\隐藏命令的回显。 ~ \\在for中表示使用增强的变量扩展; 在set中表示使用扩展环境变量指定位置的字符串; 在set/a中表示按位取反。 % \\使用两个%包含一个字符串表示引用环境变量。比如一个...
  • vi vim 查找和替换字符命令

    万次阅读 2018-11-14 09:49:00
    在vi里可没有菜单-〉查找,不过没关系,你在命令模式下敲斜杆(/)这时在状态栏(也就是屏幕左下脚)就出现了 “/”然后输入你要查找的关键字敲回车就可以了。如果你要继续查找此关键字,敲字符n就可以继续查...
  • shell字符串截取之cut命令

    千次阅读 2017-07-08 14:33:45
    第二,字符(characters),用选项-c 第三,域(fields),用选项-f 一、cut -b 如上图所示,-b后边可以设定截取哪个字节 -b支持形如3-5的写法,而且多个定位之间用逗号隔开就可以: 需要注意的是-b选项...
  • vim中如何对文件进行查找和替换字符串的查找字符串的替换 字符串的查找 1. vim 中用 / 和 ? 来查找字符串,两者的区别是: /string 会高亮显示光标后匹配的第一个字符串,回车后光标移到该字符串的第一个字母; ...
  • 正则表达式正则表达式用于字符串的模式分割,...3. []:匹配中括号内任意一个字符一般操作字符串的命令支持正则,操作文件的命令只支持通配符基础正则表达式字符截取命令cut命令 默认是以tab键为分隔符df命令的结果是
  • shell中字符连接  第一种方法 str="abc" str="$str efg" #实现了追加赋值 echo $str #显示 abc efg 第二种 dir=/var/folders/rd/_4857kzd5cb5hpgd30b01rgr0000gn/T; # 1 tmp=test.txt;
  • BAT批处理有着具有非常强大的字符串处理能力,其功能虽没有C、Python等高级编程语言丰富,但是常见的字符串截取、替换、连接、查找等功能也是应有尽有,本文逐一详细讲解。
  • linux 命令行分割字符串的几种方法

    千次阅读 2018-09-13 09:25:38
    使用xargs echo '11@22@33' | xargs -d '@' -n 1 echo | sed '$d' 使用awk echo '11@22@33' | awk -F '@' '{for(i=1;i&lt;=NF;i++){print $i;}}'
  • @echo off  for /f "tokens=16" %%i in ('ipconfig /all ^| find /i "IPv4 地址"') do set ip=%%i for /f "tokens=15" %%i in ('ipconfig /all ^| find /i "子网掩码"') do set mask=%%i ...
  • 我们有时候 经常需要把把很多命令的部分 定义成 字符串,然后拼接起来,然后一起执行: 那么 拼接起来的字符命令 如何在shell执行呢? 简单到令人发指:但是网上的各种复杂的方法,反而都无法执行...
  • 用Sed命令可以把一个字符串中的一些字符删除,比如删除日期 #Echo “2006-11-21 22:16:30” | sed ‘s/-//g’ | sed ‘s/ //g’ | sed ‘s/://g’ 得到的结果就是:20061121221630
  • linux命令中的特殊字符

    千次阅读 2017-02-02 15:22:28
    Linux命令中的特殊字符 1、字符串引用符双引号和单引号 双引号和单引号的主要作用是引用字符串 1.1、双引号  通常用来表示引用一个字符串,有时能屏蔽一些标点等特殊字符命令中使用双引号时,一般表示引用...
  • 获取字符串的MD5值:字符串“hello”的MD5:$ echo -n 'hello'|md5sum|cut -d ' ' -f1 得到的MD5值:5d41402abc4b2a76b9719d911017c592 命令解释: md5sum: 显示或检查 MD5(128-bit) 校验和,若没有文件选项,或者...
  • 入门学习Linux常用必会60个命令实例详解doc/txt

    千次下载 热门讨论 2011-06-09 00:08:45
    umount 命令是mount命令的逆操作,它的参数和使用方法和mount命令是一样的。Linux挂装CD-ROM后,会锁定CD—ROM,这样就不能用CD- ROM面板上的Eject按钮弹出它。但是,当不再需要光盘时,如果已将/cdrom作为符号链接...
  • 然后就找了找能不能有类似于Linux下的find或者grep命令,可以准确查找出文件的位置。windows下有这个一个命令,为findstr,其用法如下: findstr [/b] [/e] [/l] [/r] [/s] [/i] [/x] [/v] [/n]
  • mysql命令:为mysql命令指定字符

    千次阅读 2019-08-02 17:17:53
    mysql命令指定字符集:方法1 mysql -hlocalhost -P3306 -uroot --default-character-set=gbk mysql命令指定字符集:方法2 name gbk 验证字符集是否设置 SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\...
  • 图形界面转字符界面 切换至字符界面,重启后生效: sudo systemctl set-default multi-user.target 切换至图形界面: sudo systemctlset-default graphical.target 打开图形界面,输入后即进入图形界面 sudo init ...
  • LCD1602命令

    千次阅读 2020-04-21 17:49:05
    *注:这里列举的DDRAM的地址准确来说应该是DDRAM地址+80H之后的值,因为在向数据总线写数据的时候,命令字的最高位总是为1。 1602三条控制线 EN、RW、RS。 其中EN起到类似片选和时钟线的作用,RW和RS指示了读、写的...
  • javac、java命令指定字符

    千次阅读 2012-02-29 16:45:29
    javac -encoding GBK -cp mail.jar MailSender.java java -Dfile.encoding=GBK -cp "mail.jar:" MailSender
  • F1:按F1一次,命令提示符向后切换到已经执行过的命令字符。如果已经是最后的一条的命令,则不进行任何切换操作。 例子:之前输入“dir”,按F1一次后自动输入d,按两次自动输入i,三次自动输入r。 F2:按下此键后,...
  • sed 字符串替换

    万次阅读 2018-08-09 10:08:53
    sed 's/原字符串/替换字符串/' 单引号里面,s表示替换,三根斜线中间是替换的样式,特殊字符需要使用反斜线”\”进行转义。 2. 单引号” ‘ ’”是没有办法用反斜线”\”转义的,这时候只要把命令中的单引号改为双引号就...
  • a=l b=s eval ${a}${b} 最后形成ls命令
  • 当然理论上是可以的,但是仔细想想让计算机根据某种特定的需求进行编程,需要让字符串变成可以执行的命令。这样我们才能使计算机按着某种逻辑进行设计语句了。当然本文不介绍如何实现计算机自编程。单纯介绍两个核心...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,316,771
精华内容 526,708
关键字:

命令字符