嵌入式系统开发,经常需要用到命令行功能,比如uboot命令行。
命令行可以增加程序运行的灵活性,方便我们调试程序,大幅提高我们开发效率。
本文通过过一个简单的例子,来教大家如何实现一个简单的命令行模块,
该模块可以很方便的移植到自己的c项目中。
完整源码获取,见文末。
一、设计思想
二、实现
1、结构体
模块支持的命令相关信息,我们用结构体 struct _cmdnum来维护。
typedef void (* CMDFUNC)(char [][256],int argc);
typedef struct _cmdnum{
char *cmd;
int index;
int paramcount;
CMDFUNC callback;
char *info;
}CMDNUM,*PCMDNUM;
| 成员 | 含义 |
|---|---|
| char *cmd | 命令名字 |
| int index | 命令下标 |
| int paramcount | 命令参数个数 |
| CMDFUNC callback | 命令对应回调函数 |
2、支持的命令
本问例子中加了4个命令:
| 功能 | 参数个数 | 回调函数 | |
|---|---|---|---|
| help | 显示所有命令信息 | 0 | showhelp() |
| exit | 退出当前进程 | 0 | quit() |
| test | 打印所有参数信息,测试用 | 3 | test() |
| send | 将输入的16进制字符串,转换成16进制整形数并存入数组中 | MAX_PARAM_NUM | send() |
定义如下:
CMDNUM cmdlist[] =
{
{"help",1,0,showhelp},
{"exit",2,0,quit},
{"test",3,3,test},
{"send",4,MAX_PARAM_NUM,send},
};
3、相关函数
1. getline()
#include <stdio.h>
ssize_t getline(char **lineptr, size_t *n, FILE *stream);
从文件流stream读取一行数据,结果保存到*lineptr中,结尾包括空字符和换行符。
2. parsecmd()
void parsecmd(char *s)
解析命令行内容,主要通过函数stripcmd/getcmdindex实现。
- stripcmd
int stripcmd(char *s,char cmds[][256])
借助库函数strtok,通过空格将命令行s中各个参数分隔开,提取出参数,然后存储到二维数组**char cmds[256]**中,
- getcmdindex
int getcmdindex(char *name)
通过命令名字name,遍历数组cmdlist[],并得到数组下标index。
4、核心代码
CMDNUM cmdlist[] =
{
{"help",1,0,showhelp,"show all cmd info"},
{"exit",2,0,quit,"quit application"},
{"test",3,3,test,"[param...]print params"},
{"send",3,MAX_PARAM_NUM,senddata,"[xx...]converts data from sting to hex and store to buf"},
};
void parsecmd(char *s)
{
char cmd[MAX_PARAM_NUM][256];
int argc = 0;
int index = -1;
argc = stripcmd(s,cmd);
//printf("argc=%dn",argc);
index=getcmdindex(cmd[0]);
if(index == -1)
{
if(argc>0)
{
cprintf(RED,"tNo such commond n");
}
return;
}
elseif(argc>0)
{
cmdlist[index].callback(cmd,argc);
}
}
UINT8 hex2char(UINT8 ch)
{
if ((ch >= '0') && (ch <= '9')) {return ch - '0';}
if ((ch >= 'a') && (ch <= 'f')) {return ch - 'a' + 10;}
if ((ch >= 'A') && (ch <= 'F')) {return ch - 'A' + 10;}
return (UINT8)0xff;
}
UCHAR stringToByte(char* str)
{
UCHAR bytes = 0;
if(strlen(str)==1)
{
bytes= 0 << 4 | hex2char(str[0]);
}else
{
bytes= hex2char(str[0]) << 4 | hex2char(str[1]);
}
return bytes;
}
//7e 01 02 01 00 07 07 01 00 00 00 c6 51 2a 7e
void senddata(char argv[][256],int argc)
{
int i = 0;
int len = 0;
UCHAR buf[256] = {0};
if(argc < 2)
{
return;
}
for(i=1;i<argc;i++)
{
buf[i-1] = stringToByte(argv[i]);
}
cprintf(GREEN,"n buf:");
for(i=0;i<argc-1;i++)
{
cprintf(GREEN,"%02x ",buf[i]);
}
putchar('n');
}
void test(char argv[][256],int argc)
{
int i = 0;
for(i=0;i<argc;i++)
{
cprintf(YEL,"targv[%d]:%sn",i,argv[i]);
}
return;
}
void showsysinfo(void)
{
cprintf(D_GREEN_H,"---------------yikoulinux cmdline demo-----------n");
}
void quit(char cmd[][256],int argc)
{
cprintf(RED_H,"exit to system n");
exit(0);
}
void showhelp(char cmd[][256],int argc)
{
int i = 0;
for(i=0;i<sizeof(cmdlist)/sizeof(CMDNUM);i++)
{
cprintf(GREEN_H,"t%s",cmdlist[i].cmd);
cprintf(YEL_H,"t%sn",cmdlist[i].info);
}
return ;
}
int main(int argc, char* argv[])
{
char *line;
int ret = 0;
size_t len_line = 0;
showsysinfo();
while(1)
{
fflush( stdin );
cprintf(D_GREEN_H,DEV_NAME"# ");
ret = getline(&line, &len_line, stdin);
if(ret == -1)
{
break;
}
parsecmd(line);
free(line);
line = NULL;
}
return0;
}
三、测试
1. help
2. test
3. send
4. exit
四、源码获取
公众号【一口Linux】后台回复:cmd
或者加一口君好友 此外:一口君原创得所有c语言基础知识、c语言实例代码,均整理成pdf文档【定期更新】
204