当前位置: 首页 > news >正文

Letter Shell:自定义函数参数解析 - EM

仓库:https://github.com/NevermindZZT/letter-shell

前言

在嵌入式项目中,将函数导出为 Shell 命令是一项非常实用的能力;然而,随着调试和控制需求的增加,我们往往希望调用的函数能够接受多个参数,甚至是不同类型的复杂参数,而不仅仅是简单地触发执行;

为了解决这一需求,Letter Shell 提供了“函数签名(Function Signature)”机制;通过为函数指定参数类型,Shell 能够在命令行中自动完成字符串到对应类型的转换,从而支持整型、浮点、字符串、指针等多种参数的解析;这个机制让 Shell 不再只是“命令触发器”,而是一个支持复杂交互的真正调试接口;

本节将介绍函数签名机制的使用方式、参数类型的定义方式以及注意事项,帮助你实现更强大、更灵活的 Shell 命令调用功能;

函数签名引入

从 3.2.x 版本开始,Letter Shell 引入了 函数签名(Function Signature) 的概念;以往 Shell 调用函数时,会根据输入参数“猜测”目标类型并尝试转换;这个机制在简单命令中还能应付,但在类型较复杂或参数较多的情况下,错误率明显升高,一旦类型猜错,轻则输出异常,重则直接跑飞;

于是,现在你可以选择开启一个新的宏:

SHELL_USING_FUNC_SIGNATURE

开启后,Shell 会依据你提供的“函数签名”字符串,对参数进行明确且严格的类型解析,从而彻底摆脱以往“猜类型”的粗放方式;

一旦参数类型和签名不匹配,Shell 会自动停止命令执行,这也算是多了一层保护;毕竟,错误执行一个函数远比拒绝执行带来的代价大得多;

基本类型支持

函数签名是一个字符串,用来声明函数每个参数的类型;以下是当前版本支持的基本类型签名:

类型 签名
char(字符) c
char(数字) q
short(数字) h
int(数字) i
char*(字符串) s
void*(任意指针) p

函数的返回值不参与签名声明,仅声明参数类型即可;

int func(int a, char *b, char c);

对应的函数签名是:

"isc"

使用方法

在使用 SHELL_EXPORT_CMD 宏导出命令时,只需要在最后添加 .data.cmd.signature = "..." 即可,示例代码如下:

void shellFuncSignatureTest(int a, char *b, char c)
{printf("a = %d, b = %s, c = %c\r\n", a, b, c);
}SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
funcSignatureTest, shellFuncSignatureTest, test function signature,
.data.cmd.signature = "isc");

就这么简单,你只需声明一次签名,Shell 便会在每次命令调用时,自动进行对应的参数解析与校验;

自定义参数类型解析

函数签名不仅支持基础类型,还支持 结构体等自定义类型 的解析;自定义签名格式为:

L<类型名>;

比如自定义一个结构体:

typedef struct {int a;char *b;
} TestStruct;

函数签名如下:

"iLTestStruct;s"

则代表函数的参数为:int、TestStruct* 和 char*;

注册解析器

要让 Shell 正确解析 TestStruct,我们需要写一个解析器函数,并通过宏注册:

int testStructParser(char *string, void **param)
{TestStruct *data = malloc(sizeof(TestStruct));data->b = malloc(16);if (sscanf(string, "%d %s", &(data->a), data->b) == 2){*param = (void *)data;return 0;}return -1;
}

如果解析过程中涉及动态分配内存,推荐再提供一个清理函数:

int testStructCleaner(void *param)
{TestStruct *data = (TestStruct *)param;free(data->b);free(data);return 0;
}

注册方式如下:

SHELL_EXPORT_PARAM_PARSER(0, LTestStruct;, testStructParser, testStructCleaner);

如果不需要清理(比如结构体全是值类型),可以把最后一个参数设为 NULL;

函数导出 + 自定义参数组合示例

void shellParamParserTest(int a, TestStruct *data, char *c)
{uint8_t temp[100] = {0};Shell *shell = shellGetCurrent();sprintf((char *)temp, "a = %d, data->a = %d, data->b = %s, c = %s\r\n", a, data->a, data->b, c);shellWriteString(shell, (const char *)temp);
}SHELL_EXPORT_CMD_SIGN(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
paramParserTest, shellParamParserTest, test function signature and param parser,
iLTestStruct;s);

使用示例,终端中输入命令如下:

paramParserTest 1 "12 2a" "apple"

解释如下:

  • 参数1:int → 1
  • 参数2:TestStruct* → data->a = 12, data->b = "2a"
  • 参数3:char* → "apple"

这里用双引号包住结构体参数是为了清晰分割,但实际输入中引号可选(Shell 会智能处理);

数组参数解析引入

从 Letter Shell 3.2.3 版本开始,函数签名机制进一步增强,新增了 数组参数 的支持,适用于如 int[]、char *[]、自定义结构体数组等情况;

启用方法

要启用数组参数解析功能,需要:

  1. 开启宏开关:SHELL_SUPPORT_ARRAY_PARAM
  2. 配置内存分配函数:确保宏 SHELL_MALLOC 和 SHELL_FREE 被定义,指向你项目中的内存分配函数(如使用 FreeRTOS,可指向 pvPortMalloc 和 vPortFree,裸机系统下可绑定 malloc / free 或其他实现);

数组签名语法

在原有类型签名前加 [ 表示这是一个数组类型参数;例如:

类型 签名
int[] [i
TestStruct*[] [LTestStruct;

完整签名可组合使用,如:

"i[i[LTestStruct;"

表示的类型分别为:

  • int
  • int [](整型数组)
  • TestStruct*[](结构体指针数组)

示例代码

int shellArrayTest(int a, int *b, TestStruct **datas)
{int i;printf("a = %d, b = %p, datas = %p\r\n", a, b, datas);for (i = 0; i < shellGetArrayParamSize(b); i++){printf("b[%d] = %d\r\n", i, b[i]);}for (i = 0; i < shellGetArrayParamSize(datas); i++){printf("datas[%d]->a = %d, datas[%d]->b = %s\r\n", i, datas[i]->a, i, datas[i]->b);}return 0;
}SHELL_EXPORT_CMD_SIGN(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC),
arrayTest, shellArrayTest, test array param parser,
i[i[LTestStruct;);

使用示例,终端中输入命令如下:

arrayTest 12 [65, 89, 45] ["100 hello", "56 world"]

含义解析如下:

  • 参数1:int → 12
  • 参数2:int [] →
  • 参数3:TestStruct [](结构体数组)→
    • datas[0]->a = 100, datas[0]->b = "hello"
    • datas[1]->a = 56, datas[1]->b = "world"

提示:数组用中括号括起来,元素之间用英文逗号分隔;结构体数组的每个元素用双引号包裹,格式与结构体解析规则一致;

总结

通过引入函数签名机制,Letter Shell 显著提升了命令行参数解析的可靠性与灵活性,使其不仅仅是一个简单的命令触发器,更是一个具备强交互能力的调试入口;无论是基础类型、指针类型,还是结构体乃至结构体数组,用户都可以借助清晰的签名字符串和自定义解析器,实现类型安全且表达力丰富的参数传递;

此外,函数签名机制还为 Shell 使用者提供了更明确的接口约束,有效避免了因类型错误导致的崩溃或异常;配合内存管理机制、解析器注册机制和数组支持,Letter Shell 已具备构建嵌入式调试框架所需的高级能力;

对于追求高可靠性和高可维护性的嵌入式项目来说,掌握并使用好函数签名功能,将为 Shell 接口的设计与扩展带来极大的便利和安全保障;

阅读原文:Letter Shell:自定义函数参数解析

http://www.jsqmd.com/news/752289/

相关文章:

  • 如何在GitHub上优雅显示数学公式:MathJax插件的专业解决方案
  • 3分钟轻松汉化Axure RP:告别英文界面的完整中文语言包指南
  • OpenDify全栈AI平台:从零部署私有化知识库与智能工作流
  • 如何选择降AI工具改写强度:普通模式深度模式免费试用判断标准完整操作教程
  • 终极GPU显存稳定性测试指南:memtest_vulkan完整实战教程
  • 如何专业彻底卸载Windows Defender:2025高级系统优化完整指南
  • 告别PSD分层烦恼!用3DMasterKit 10.7的深度图功能,5分钟搞定立体海报设计
  • 从用量看板分析不同业务场景的模型调用偏好与成本分布
  • ubuntu server 24.04: 如何设置默认采用 Xorg 方式登录
  • 北京金发钹祥金属材料贸易:北京不锈钢刨槽公司推荐 - LYL仔仔
  • 三步实现微信聊天记录的本地化永久保存:WeChatExporter技术解析与实践指南
  • 别只背面试题了!用这5个真实场景,带你吃透K8s核心原理
  • FPS游戏策划的平衡术:如何用‘距离衰减’和‘穿透机制’悄悄给每把枪划好‘工作岗位’
  • S32K146看门狗喂不活?手把手教你排查Autosar MCAL WDG配置的三大坑
  • SEGGER RTT:嵌入式调试的高效输出利器 - EM
  • Switch系统革命性优化指南:从基础到专业级的性能突破
  • 基于安卓的NFC标签读写与应用系统毕业设计
  • VULK MCP Server:让AI助手一键生成全栈应用
  • 5步快速掌握BookGet:古籍下载工具的完整使用教程
  • Houdini VEX实战:用Attribute Wrangle节点快速创建并控制自定义属性(从Cd到orient)
  • Dell服务器风扇控制器:5个专业技巧实现智能温控与静音管理
  • GenAI与轻量化网络在GNSS抗干扰中的创新应用
  • Legacy-iOS-Kit终极指南:如何免费降级、越狱旧版iOS设备
  • libopencm3 开发STM32体验笔记 - EM
  • 从零开始构建开源机器人手:耶鲁OpenHand完全指南
  • 解锁全平台音乐自由:用LX Music桌面版打造你的专属音乐中心 [特殊字符]
  • 3分钟快速集成:让Draw.io成为Obsidian笔记的专业图表解决方案
  • 检索式语音转换WebUI:基于VITS的高效音色克隆与实时变声解决方案
  • 告别网页版!用Python脚本实现GPT-4多轮对话机器人(附完整代码与API-Key配置避坑)
  • 在 Taotoken 平台观测不同模型的用量与成本分布