命令系统
XWSH 的命令系统:内置命令详解、外部命令添加方法和重名检查机制。
Categories:
4 分钟阅读
命令结构体
所有 XWSH 命令都使用 struct xwsh_cmd 结构体定义(xwmd/cli/xwsh/mi.h:21):
struct xwsh_cmd {
char * name; /**< 命令的名称 */
xwer_t (*func)(xwsz_t argc, char ** argv); /**< 命令的函数 */
char * desc; /**< 命令的描述 */
};
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
name |
char * |
命令名称字符串(大小写敏感) |
func |
函数指针 | 命令处理函数,接受参数个数和数组 |
desc |
char * |
命令描述,显示在帮助信息中 |
命令处理函数签名
xwer_t cmd_handler(xwsz_t argc, char ** argv);
argc:参数个数(包含命令名本身,argv[0])argv:参数指针数组,每个元素指向以\0结尾的字符串- 返回值:
XWOK(0)表示成功,负值表示错误码
内置命令
XWSH 内置了四个基础命令,定义在 xwmd/cli/xwsh/cmd.c 中。
1. help - 显示帮助信息
功能:显示所有可用命令(内置 + 外部)的名称和描述。
用法:
help
示例输出:
--------------------------------------------
help show this help
clear clear screen
rd read memory
wr write memory
mycmd my custom command
实现(xwsh_cmd_help()):
xwer_t xwsh_cmd_help(xwsz_t argc, char ** argv)
{
XWOS_UNUSED(argc);
XWOS_UNUSED(argv);
xwsh_show_all_cmds(); // 调用核心函数显示命令列表
return XWOK;
}
2. clear - 清屏
功能:使用 ANSI 转义序列清除终端屏幕,并将光标移动到左上角。
用法:
clear
ANSI 转义序列:
\e[2J:清除整个屏幕\e[0;0H:移动光标到第 0 行第 0 列
实现(xwsh_cmd_clear()):
xwer_t xwsh_cmd_clear(xwsz_t argc, char ** argv)
{
XWOS_UNUSED(argc);
XWOS_UNUSED(argv);
printf("\e[2J"); // 清屏
printf("\e[0;0H"); // 光标归位
return XWOK;
}
3. rd - 读取内存
功能:以十六进制和 ASCII 格式显示指定内存区域的内容。
用法:
rd <地址十六进制> [大小十进制]
参数:
| 参数 | 格式 | 默认值 | 说明 |
|---|---|---|---|
| 地址 | 十六进制 | 无 | 要读取的内存起始地址 |
| 大小 | 十进制 | 1 | 要读取的字节数 |
示例:
rd 0x90000000 # 读取 0x90000000 处的 1 个字节
rd 0x90000000 16 # 读取 0x90000000 处的 16 个字节
rd 0x20000000 256 # 读取 0x20000000 处的 256 个字节
输出格式:
origin address: 0x90000000
00000000: 12 34 56 78 9a bc de f0 11 22 33 44 55 66 77 88 |.4Vx....."3DUfw.|
00000010: aa bb cc dd ee ff 00 11 22 33 44 55 66 77 88 99 |........"3DUfw..|
格式说明:
- 每行显示 16 字节
- 左侧:偏移量(十六进制)
- 中间:十六进制值(每 8 字节后加空格分隔)
- 右侧:ASCII 表示(不可打印字符显示为
.)
4. wr - 写入内存
功能:向指定内存地址写入数据。
用法:
wr <地址十六进制> <值十六进制> [b|w|l]
参数:
| 参数 | 格式 | 默认值 | 说明 |
|---|---|---|---|
| 地址 | 十六进制 | 无 | 要写入的内存地址 |
| 值 | 十六进制 | 无 | 要写入的数值 |
| 大小 | 字符 | l |
写入大小:b(字节)、w(字)、l(长字) |
示例:
wr 0x90000000 0x1234 w # 向 0x90000000 写入 0x1234(2 字节)
wr 0x90000000 0x55 b # 向 0x90000000 写入 0x55(1 字节)
wr 0x90000000 0xdeadbeef # 向 0x90000000 写入 0xdeadbeef(4 字节)
大小说明:
| 选项 | 大小 | 对应类型 | 地址对齐 |
|---|---|---|---|
b |
1 字节 | xwu8_t |
任意 |
w |
2 字节 | xwu16_t |
2 字节对齐 |
l |
4 字节 | xwu32_t |
4 字节对齐 |
外部命令添加
用户可以通过 xwsh_set_ext_cmd_table() 函数添加自定义命令。
1. 定义命令结构体数组
#include "xwmd/cli/xwsh/mi.h"
xwer_t my_test_cmd(xwsz_t argc, char ** argv)
{
printf("My test command executed!\r\n");
printf("argc: %d\r\n", argc);
for (xwsz_t i = 0; i < argc; i++) {
printf("argv[%d]: %s\r\n", i, argv[i]);
}
return XWOK;
}
xwer_t my_calc_cmd(xwsz_t argc, char ** argv)
{
if (argc != 3) {
printf("Usage: calc <num1> <num2>\r\n");
return -EINVAL;
}
xws32_t a, b;
sscanf(argv[1], "%d", &a);
sscanf(argv[2], "%d", &b);
printf("%d + %d = %d\r\n", a, b, a + b);
return XWOK;
}
const struct xwsh_cmd my_cmds[] = {
{ .name = "test", .func = my_test_cmd, .desc = "my test command" },
{ .name = "calc", .func = my_calc_cmd, .desc = "simple calculator" },
};
2. 注册命令表
时机:必须在 XWSH 启动前调用(xwsh_start() 或 xwsh_init() 之前)。
xwer_t rc = xwsh_set_ext_cmd_table(my_cmds,
sizeof(my_cmds) / sizeof(my_cmds[0]));
if (rc < 0) {
printf("Failed to set command table: %d\r\n", rc);
}
3. 清空命令表
要移除所有外部命令,可传递 NULL 和 0:
xwsh_set_ext_cmd_table(NULL, 0); // 清空外部命令表
重名检查机制
xwsh_set_ext_cmd_table() 函数执行严格的重名检查,确保命令名称唯一性。
检查规则
-
外部命令 vs 内部命令
for (j = 0; j < xwsh_cmd_table_size; j++) { if (!strcmp(cmd[i].name, xwsh_cmd_table[j].name)) { rc = -EEXIST; // 与内置命令重名 goto err_duplicate; } } -
外部命令之间
for (j = 0; j < i; j++) { if (!strcmp(cmd[i].name, cmd[j].name)) { rc = -EEXIST; // 外部命令之间重名 goto err_duplicate; } }
错误处理
| 错误码 | 含义 | 处理建议 |
|---|---|---|
-EEXIST |
命令名称已存在 | 修改命令名称或移除冲突命令 |
-EINVAL |
参数无效(cmd 为 NULL 但 num 不为 0) |
检查参数逻辑 |
命名建议
- 使用小写字母:保持与内置命令一致
- 避免特殊字符:只使用字母、数字、下划线
- 简洁明了:命令名应反映功能
- 长度适中:建议 2-10 个字符
命令执行流程
1. 命令查找(xwsh_find_cmd())
const struct xwsh_cmd * xwsh_find_cmd(char * name)
{
// 1. 检查内置命令表
for (i = 0; i < xwsh_cmd_table_size; i++) {
if (!strcmp(name, xwsh_cmd_table[i].name)) {
return &xwsh_cmd_table[i];
}
}
// 2. 检查外部命令表
if ((NULL != xwsh_ext_cmd_table) && (0 != xwsh_ext_cmd_table_size)) {
for (i = 0; i < xwsh_ext_cmd_table_size; i++) {
if (!strcmp(name, xwsh_ext_cmd_table[i].name)) {
return &xwsh_ext_cmd_table[i];
}
}
}
return NULL; // 命令未找到
}
2. 命令执行(xwsh_run_cmdline())
xwer_t xwsh_run_cmdline(char * line)
{
// 1. 分割命令行
rc = xwsh_split_cmdline(line, &argc, argv, XWSH_MAX_PARAM_NUM);
// 2. 查找命令
cmd = xwsh_find_cmd(argv[0]);
// 3. 执行命令
if (cmd) {
if (cmd->func) {
rc = cmd->func(argc, argv); // 调用命令处理函数
} else {
printf("cmd %s not implemented.\r\n", cmd->name);
}
} else if (argv[0]) {
printf("\r\ncan't find cmd %s.\r\n", argv[0]);
}
return rc;
}
最佳实践
1. 命令设计原则
- 单一职责:每个命令只完成一个明确的功能
- 清晰的帮助:命令描述应简洁说明功能
- 参数验证:在命令处理函数中验证参数合法性
- 错误反馈:提供有意义的错误信息
2. 内存安全
xwer_t safe_memory_cmd(xwsz_t argc, char ** argv)
{
xwptr_t addr;
// 参数数量检查
if (argc != 2) {
printf("Usage: %s <address>\r\n", argv[0]);
return -EINVAL;
}
// 地址格式验证
if (sscanf(argv[1], "%lx", &addr) != 1) {
printf("Invalid address format\n");
return -EINVAL;
}
// 地址范围检查(可选)
if (addr < 0x20000000 || addr >= 0x24000000) {
printf("Address out of valid range\n");
return -EACCES;
}
// 执行操作
// ...
return XWOK;
}
3. 性能考虑
- 避免长时间阻塞:命令执行时间应尽量短
- 减少内存分配:使用栈变量而非堆分配
- 输出优化:避免大量字符串操作和格式化
调试技巧
1. 命令调试输出
xwer_t debug_cmd(xwsz_t argc, char ** argv)
{
#ifdef XWSH_DBG
xwsz_t i;
xwlogf(D, "XWSH.CMD", "argc: %d\r\n", argc);
for (i = 0; i < argc; i++) {
xwlogf(D, "XWSH.CMD", "argv[%d]: %s\r\n", i, argv[i]);
}
#endif
// 命令逻辑
return XWOK;
}
2. 启用调试模式
在 core.c 和 readline.c 中取消注释 #define XWSH_DBG 可启用详细调试输出。