文章目录
在嵌入式系统的命令行控制台(Shell)中,命令解析模块扮演着关键角色。它负责:
- 接收字符串命令;
- 拆分命令参数;
- 查找匹配的命令函数;
- 调用命令对应的处理函数。
本文基于 cmd.c
实现讲解一个简单而高效的命令注册与执行框架。
一、核心设计思想
命令系统基于以下数据结构和接口实现:
- 命令表(cmd_table):保存所有注册命令;
- 命令函数指针(cmd_func_t):指向具体执行逻辑;
cmd_execute()
:接收命令字符串,拆分参数并调用对应命令函数;cmd_register()
:注册命令;cmd_find()
:通过命令名查找。
二、命令系统实现详解(含完整注释)
#include "cmd.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define CMD_TABLE_MAX 32 // 最多支持 32 个命令
// 命令表:用于保存所有注册的命令
static cmd_t cmd_table[CMD_TABLE_MAX];
static int cmd_count = 0; // 当前注册命令数
1. 示例命令函数实现
// help 命令:打印帮助信息
int cmd_help(int argc, char *argv[])
{
printf("help: Show this message\r\n");
// 可扩展:遍历 cmd_table 打印所有命令和说明
return 0;
}
// echo 命令:回显输入参数
int cmd_echo(int argc, char *argv[])
{
for (int i = 1; i < argc; i++)
{
printf("%s ", argv[i]);
}
printf("\r\n");
return 0;
}
2. 初始化命令系统
// 初始化命令系统:注册内置命令
void cmd_init(void)
{
cmd_register("help", cmd_help, "Show help");
cmd_register("echo", cmd_echo, "Echo args");
}
3. 命令注册函数
// 注册命令:添加命令名、函数指针和帮助信息到命令表
int cmd_register(const char *name, cmd_func_t func, const char *help)
{
if (cmd_count >= CMD_TABLE_MAX)
return -1; // 命令表满了,注册失败
// 复制命令名到表项(限制最大长度)
strncpy(cmd_table[cmd_count].name, name, CMD_NAME_LEN - 1);
cmd_table[cmd_count].name[CMD_NAME_LEN - 1] = '\0';
// 设置函数指针和帮助信息
cmd_table[cmd_count].func = func;
cmd_table[cmd_count].help = help;
cmd_count++; // 更新命令数量
return 0;
}
4. 命令查找函数
// 查找命令:通过命令名在命令表中查找
cmd_t *cmd_find(const char *name)
{
for (int i = 0; i < cmd_count; i++)
{
if (strcmp(cmd_table[i].name, name) == 0)
return &cmd_table[i]; // 找到并返回指针
}
return NULL; // 未找到
}
5. 命令执行函数
// 执行命令行字符串:拆分参数并调用命令函数
int cmd_execute(const char *cmdline)
{
if (cmdline == NULL || *cmdline == '\0')
return -1; // 空命令行,忽略
// 使用 buf 保存一份可修改的命令行
char buf[128];
strncpy(buf, cmdline, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
char *argv[CMD_MAX_ARGS]; // 参数数组
int argc = 0;
// 使用 strtok 拆分参数
char *token = strtok(buf, " ");
while (token && argc < CMD_MAX_ARGS)
{
argv[argc++] = token;
token = strtok(NULL, " ");
}
if (argc == 0)
return -1; // 没有有效参数
// 查找对应命令
cmd_t *cmd = cmd_find(argv[0]);
if (!cmd)
{
printf("Unknown command: %s\r\n", argv[0]);
return -1; // 未知命令
}
// 调用命令函数,传递 argc 和 argv
return cmd->func(argc, argv);
}
三、命令结构体(cmd_t)
// cmd.h 中结构定义示例
#define CMD_NAME_LEN 16
#define CMD_MAX_ARGS 8
typedef int (*cmd_func_t)(int argc, char *argv[]); // 命令处理函数类型
typedef struct {
char name[CMD_NAME_LEN]; // 命令名称
cmd_func_t func; // 命令处理函数
const char *help; // 帮助字符串
} cmd_t;
四、运行效果示例
假设输入如下命令:
echo Hello STM32
Shell 处理流程如下:
输入字符拼接成字符串;
回车后传入
cmd_execute()
;strtok
拆分为argv = {"echo", "Hello", "STM32"}
;查表找到
cmd_echo
;调用
cmd_echo(argc=3, argv)
;控制台输出:
Hello STM32
五、小结
这个命令系统具备以下优点:
- 轻量级:适合裸机或RTOS;
- 易扩展:添加命令只需实现函数并调用
cmd_register()
; - 通用接口:命令参数解析和传递简洁统一;
- 结构清晰:注册、查找、执行职责分离。
适用于嵌入式项目中需要人机交互或调试接口的场景,例如串口控制、调试参数设置、模块测试等。