-
2021-09-27 14:58:00
html调用cmd命令行并执行命令(亲测可用)
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>测试php+js调用cmd命令行</title> </head> <body> <a href="#" onclick="win_run(); return false;">调用cmd</a> </body> <script type="text/javascript"> function win_run(){ var cmd=new ActiveXObject("WScript.Shell"); cmd.run("cmd.exe /k ping 127.0.0.1 && cd test && activate py35 && python pr.py > result.txt");//百度搜索cmd多命令行一起写 cmd=null; window.setTimeout('window.close();',3000); } </script> </html>
参数说明:
0.不同的执行命令间需要 添加 & 或者 && 或者 || 来表明不同命令之间的依赖关系 command1 & command2 用来分隔一个命令行中的多个命令。Cmd.exe 运行第一个命令,然后运行第二个命令。 command1 && command2 只有在符号 && 前面的命令成功时,才用于运行该符号后面的命令。Cmd.exe 运行第一个命令,然后只有在第一个命令运行成功时才运行第二个命令。 command1 || command2 只有在符号 || 前面的命令失败时,才用于运行符号 || 后面的命令。Cmd.exe 运行第一个命令,然后只有在第一个命令未能运行成功(接收到大于零的错误代码)时才运行第二个命令。 1.对上边的命令行中cmd.exe /k解析: cmd /c dir 是执行完dir命令后关闭命令窗口。 cmd /k dir 是执行完dir命令后不关闭命令窗口。 2.切换路径 e: #此命令就是切换到E盘 f: #此命令就是切换到F盘 3.进入到对应文件夹(此处默认你已经进入到对应的磁盘下,例如E盘) cd dir_path 4.执行相应的命令行命令 activate py35 #有过anaconda使用经验的都懂,意在激活py35的环境 5.输出重定向到对应txt文件 command1 > result.txt #就会将command1 的输出结果重定向输出到result.txt 6.cmd窗口延时 window.setTimeout('window.close();',3000) #将cmd命令行延时3s退出 7.隐藏cmd窗口 cmd.run("cmd.exe /k ping 127.0.0.1 && cd test && activate py35 && python pr.py > result.txt",0); #这样的话,运行就不会弹出cmd窗口了。 8.特别说明 将对应的html文件在ie浏览器打开哦~
更多相关内容 -
Shell项目:命令解析器
2018-09-16 13:05:18命令解析器: 提示信息:运行自己的Shell时,首先会像系统shell一样,输出一段提示信息, 从左到右依次是:登陆时的用户名,主机信息,和当前目录。 我们定义一个函数Put Flag()来实现, 这里涉及到几个...命令解析器:
提示信息:运行自己的Shell时,首先会像系统shell一样,输出一段提示信息,
从左到右依次是:登陆时的用户名,主机信息,和当前目录。
我们定义一个函数Put Flag()来实现,
这里涉及到几个大家可能不太熟悉的函数和结构体
切割命令(识别命令):
输出了提示信息后,用户就要输入他想执行的命令了,那我们如何来识别用户输入的命令呢,又是怎样识别用户输入的是参数还是命令呢,这里我们实现一个CutCmd函数,顾名思义就是切割命令。
这里strtok函数就是用来切割命令的函数,也是该函数的核心。第一次调用时必须将要切割的字符串cmd传进去,以空格为切割标记,细心的同学可能会发现在下面while循环中strtok函数的参数传了一个NULL,这和strtok函数的实现机制有关。当strtok()在参数cmd的字符串中发现第二个参数中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok()必需给予参数cmd字符串,往后的调用则将参数cmd设置成NULL。每次调用成功则返回指向被分割出片段的指针。cmd为空值NULL,则函数保存的指针SAVE_PTR在下一次调用中将作为起始位置。
到这里我们的准备工作差不多已经完善了,接下来就到了根据用户输入的命令去执行相关可执行文件的时候了
进程替换和子进程:
当用户输入命令后,我们要根据用户输入的命令去选择相应的可执行文件来实现该命令,这里会用到一个exec函数,它实现了一个进程替换的功能,就是把当前进程的代码从调用该函数处替换成另外一个文件,而进程号不变,在系统的终端程序中,一个命令执行完之后又会回到命令解析器中,也就是说命令解析器的主程序应该是一个死循环,但是如果调用exec函数替换进程之后,这个死循环就会退出,也就是说我们这个终端每次执行一个命令就要重新启动一次,显然与系统终端有很大差别,为了消除这一差异,我们在这里使用fork函数产生一个子进程,然后把进程替换实现在子进程中,这样,每执行完一个命令,子进程结束,又会回到父进程的命令解析器的死循环中。
这里用红色下划线标注的是两个不能在子进程中实现的命令,一个是exit,退出终端的命令,执行此命令后,终端程序结束,终端退出。还有一个是cd命令,切换当前目录,如果在子进程中实现,当前目录切换之后子进程结束,又会回到当前目录,因此cd命令应该实现在父进程中。我们封装了一个函数来实现cd命令
命令解析器基本就是这些内容,接下来就是各个命令的具体实现了。
-
C语言中调用系统命令(system popen...)
2014-11-26 14:12:101、system(执行shell 命令) 相关函数 fork,execve,waitpid,popen 表头文件 #include 定义函数 int system(const char * string); 函数说明 system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来...1、system(执行shell 命令)
相关函数 fork,execve,waitpid,popen
表头文件 #include<stdlib.h>
定义函数 int system(const char * string);
函数说明 system()会调用fork()产生子进程,由子进程来调用/bin/sh-cstring来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。返回值 如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值。如果system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。附加说明 在编写具有SUID/SGID权限的程序时请勿使用system(),system()会继承环境变量,通过环境变量可能会造成系统安全的问题。范例:
#include<stdlib.h>
main()
{
system(“ls -al /etc/passwd /etc/shadow”);
}
2、popen(建立管道I/O)
相关函数 pipe,mkfifo,pclose,fork,system,fopen
表头文件 #include<stdio.h>
定义函数 FILE * popen( const char * command,const char * type);
函数说明 popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令。参数type可使用“r”代表读取,“w”代表写入。依照此type值,popen()会建立管道连到子进程的标准输出设备或标准输入设备,然后返回一个文件指针。随后进程便可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中。此外,所有使用文件指针(FILE*)操作的函数也都可以使用,除了fclose()以外。返回值 若成功则返回文件指针,否则返回NULL,错误原因存于errno中。错误代码 EINVAL参数type不合法。注意事项 在编写具SUID/SGID权限的程序时请尽量避免使用popen(),popen()会继承环境变量,通过环境变量可能会造成系统安全的问题。范例:
#include<stdio.h>
main()
{
FILE * fp;
char
buffer[80];
fp=popen(“cat /etc/passwd”,”r”);
fgets(buffer,
sizeof
(buffer),fp);
printf(“%s”,buffer);
pclose(fp);
}
执行 root :x:0 0: root: /root: /bin/bash
3、使用vfork()新建子进程,然后调用exec函数族
#include<unistd.h>
main()
{
char * argv[ ]={“ls”,”-al”,”/etc/passwd”,(char*) };
if(vfork() = =0)
{
execv(“/bin/ls”,argv);
}else{
printf(“This is the parent process\n”);
}
}转自:http://www.cnblogs.com/niocai/archive/2011/07/20/2111896.html
-
单片机串口实现字符串命令解析
2020-10-30 16:20:38下面就贴出完整的代码 命令解析头文件 cmd.h : #ifndef __CMD_H_ #define __CMD_H_ #include "iostm8s103F3.h" #define ARRAY_SIZE(x) (sizeof(x) / (sizeof((x)[0]))) //用来计算结构体数组中,数组的个数。...通常情况下串口通信用的大多数都是用十六进制数据来传输指令,比如最常见的modbus的通信,如读保持寄存器指令:01 03 00 00 00 01 84 0A,这种十六进制的指令在这里就不讨论了。想要详细了解可以看往期的文章。串口相关文章链接如下:
有时候串口也需要通过字符串命令来实现某些功能, 如果使用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/
-
python之parser.add_argument()用法——命令行选项、参数和子命令解析器
2020-06-03 13:34:37目录argparse介绍argparse使用——代码示例1、创建一个解析器——创建 ArgumentParser() 对象描述description2、添加参数——调用 add_argument() 方法添加参数add_argument() 方法定义如何解析命令行参数3、解析... -
parser.add_argument()用法——命令行选项、参数和子命令解析器
2021-06-16 16:31:52argparse是一个Python模块:命令行选项、参数和子命令解析器。通过使用这种方法,可以在使用命令行运行代码时直接给相应的变量赋值,就不需要在改python中的具体代码了。 -
Linux系统调用解析
2013-01-23 19:57:37用户可以通过系统调用命令在自己的应用程序中调用它们。从某种角度来看,系统调用和普通的函数调用非常相似。区别仅仅在于,系统调用由操作系统核心提供,运行于核心态;而普通的函数调用由函数库或用户自己提供,... -
RCE代码及命令执行漏洞
2022-02-13 17:58:29在 Web 应用中有时候程序员为了考虑灵活性、简洁性,会在代码调用 代码或命令执行函数去处理。比如当应用在调用一些能将字符串转化成代码的函数时,没有考虑用户是否能控制这个字符串,将造成代码执行漏洞。同样... -
read 系统调用剖析
2015-11-20 17:27:37Read 系统调用在用户空间中的处理过程 Linux 系统调用(SCI,system call interface)的实现机制实际上是一个多路汇聚以及分解的过程,该汇聚点就是 0x80 中断这个入口点(X86 系统结构)。也就是说,所有系统调用... -
【平头哥RVB2601开发板试用体验】AT解析器和通过w800 AT命令接入阿里云生活物联网平台
2022-03-16 17:59:21RISC-V RVB2601 Yoc 实现和测试验证阿里云平台接入的等相关功能,同时去了解YoC中网络设备和AT解析器框架。 -
Windows 最全CMD命令,带死机修复系统命令
2020-05-31 18:06:15不论大小写命令后加一个空格 notepad 记事本 ~找到CMD,并以管理员身份运行。 1、 开启管理员账户,在命令提示符中输入如下命令后回车: net user administrator /active:yes 2、 关闭管理员账户,在命令提示符中... -
08_简单的命令解析器(小钱版)[操作系统][2012-12-15]
2012-12-16 17:58:51SimpleCommandParser(简单的命令解析器) 2. 开发目的 熟悉Linux编程环境,加强对Linux命令的理解及函数的运用。 3. 开发环境 LSB Version: :core-3.0-ia32 :core-3.0-noarch :graphics-3.0-ia32 :... -
Python之深入解析Vulture如何一键找出项目中所有无效的代码
2021-10-20 19:07:10不过由于 Python 的动态特性,像 Vulture 这样的静态代码分析器很可能会遗漏一些无效代码,此外,可能会将仅被隐式调用的代码标记为未使用。 尽管如此,Vulture 对于提升代码质量来说可能是一个非常有用的工具: ... -
[Python系列-27]:命令行解析器argparse详解
2022-01-02 14:33:28作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客 ...2.1 创建一个命令行解析器对象:ArgumentParser() 2.2 为命令行添加参数: add_argument() 方法 2.3... -
[网络安全自学篇] 八十七.恶意代码检测技术详解及总结
2020-07-16 12:03:44这是作者网络安全自学教程...这篇文章将详细总结恶意代码检测技术,包括恶意代码检测的对象和策略、特征值检测技术、校验和检测技术、启发式扫描技术、虚拟机检测技术和主动防御技术。基础性文章,希望对您有所帮助~ -
使用cxf客户端生成java代码并调用
2019-05-15 19:01:36下载的首页会列出几个版本,如果要下载以前的版本,就滑到下载页面的底部----Previous releases标题下,这里官网提供了两个链接,小编英文比较菜,谷歌翻译过来是费孵化器发布以及孵化器发布,不清楚区别在哪里,... -
【漏洞复现】SOAP WSDL解析器代码注入漏洞(CVE-2017-8759)
2020-01-10 19:50:08不同的是,这次黑客在 Offcie 文档中嵌入新的 Moniker 对象,利用的是 .net库 漏洞,在Office文档中加载执行远程的恶意.NET代码,而整个漏洞的罪魁祸首竞是.NET Framework一个换行符处理失误:SOAP WSDL 解析模块 ... -
Linux常用命令,Linux常用基本命令大全
2022-03-19 07:58:25Linux常用命令大全整理前言 最近在和Linux打交道,感觉还不错。我觉得Linux相比windows比较麻烦的就是很多东西都要用命令...一、系统信息Linux常用命令大全 arch 显示机器的处理器架构 uname -m 显示机器的处理器架. -
C编译时遇到报错:fatal error LNK1120: 1 个无法解析的外部命令
2022-03-26 16:54:32fatal error LNK1120: 1 个无法解析的外部命令 ... -
编写简单的shell命令解析器
2009-04-22 21:13:00编写简单的shell命令解析器环境:RedHat 9.0 内核 2.4.20vi 文书编辑器 6.1.320gcc 3.2.2-5 实现步骤: 第0步:写一个最简单的shell命令解释器,本程序取自APUE例1-5,命令不能带参数。 需要完成的 -
JAVA上百实例源码以及开源项目源代码
2018-12-11 17:07:42Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个... -
CCP协议学习-命令解析
2018-11-17 12:00:24在本章中,将解释所有带有参数的命令和预期的返回信息,包括每个命令的示例。 12.1 连接 指令标签CONNECT CRO中的数据结构 位置 类型 描述 0 byte ... -
Flutter 安卓 Platform 与 Dart 端消息通信方式 Channel 源码解析
2021-08-08 21:22:09Flutter 官方提供三种 Platform 与 Dart 端消息通信方式,他们分别是 MethodChannel、BasicMessageChannel、EventChannel,本文会继续延续前面系列对他们进行一个深度解析,源码依赖 Flutter 2.2.3 版本,Platform ... -
[网络安全提高篇] 一一三.Powershell恶意代码检测 (1)论文总结及抽象语法树(AST)提取
2022-03-03 10:34:03这是作者网络安全自学教程系列,主要是关于安全工具和实践操作的...这篇文章将详细讲解PowerShell、Powershell恶意代码检测总结及抽象语法树(AST)提取。希望这篇文章对您有帮助,也推荐大家去阅读论文,且看且珍惜。 -
Linux命令之screen命令
2022-01-10 17:41:00Screen是一个全屏窗口管理器,它在多个进程(通常是...当调用screen时,它会创建一个包含shell(或指定命令)的窗口,然后避开您的方式,以便您可以正常使用该程序。然后,您可以随时创建包含其他程序(包括更多shell -
一篇好文之Android 调用C代码及生成调试so库
2018-11-02 15:04:53Android 编写so文件的方式主要有两种:一种是通过ndk-build(),此方法在Android Studio 3.0.1之后就不建议使用了, ...如何调用so库文件? 文章目录编译环境如何生成so文件?1. 通过ndk 编译生成so(已... -
Java安全-注入漏洞(SQL注入、命令注入、表达式注入、模板注入)
2021-11-07 16:48:06文章目录注入SQL注入JDBC拼接不当造成SQL注入框架使用不当造成SQL注入不安全的反射命令注入代码注入表达式注入Spel表达式注入OGNL表达式注入模板注入 注入 SQL注入 JDBC拼接不当造成SQL注入 JDBC有两种方法执行SQL... -
有趣的10个CMD命令
2022-02-09 11:30:51有趣的10个CMD命令 -
深入理解java虚拟机(十一) 方法调用-解析调用与分派调用
2014-09-01 15:56:08我们知道,Class 文件的编译过程中并不包括传统编译中的连接步骤,一切方法调用在 Class 文件调用里面存储的都只是符号引用,而不是方法在实际运行时的内存布局入口地址,也就是说符号引用解析成直接引用的过程。...