文章目錄
- 一、核心設計思想
- 二、命令系統實現詳解(含完整注釋)
- 1. 示例命令函數實現
- 2. 初始化命令系統
- 3. 命令注冊函數
- 4. 命令查找函數
- 5. 命令執行函數
- 三、命令結構體(cmd\_t)
- 四、運行效果示例
- 五、小結
在嵌入式系統的命令行控制臺(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 和 argvreturn cmd->func(argc, argv);
}
三、命令結構體(cmd_t)
// cmd.h 中結構定義示例
#define CMD_NAME_LEN 16
#define CMD_MAX_ARGS 8typedef 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()
; - 通用接口:命令參數解析和傳遞簡潔統一;
- 結構清晰:注冊、查找、執行職責分離。
適用于嵌入式項目中需要人機交互或調試接口的場景,例如串口控制、調試參數設置、模塊測試等。