本文说明在使用zephyr shell遇到的通配符问题和处理方式。
问题
Zephyr原生支持通过UART对ESP8266和ESP32进行AT控制,由于UART的通信速度低,因此在Zephyr下添加了SPI AT,添加后想通过Zephyr的shell输入AT命令对其进行验证调试,开始执行了一些简单的命令都正常:1
2
3AT
AT+RST
AT+CWLAP
但当执行获得一些状态的AT命令时,就不会执行而直接退出1
AT+CIPMUX?
分析
1. 功能代码
shell命令函数实现如下1
2
3
4
5
6
7
8
9
10
11
12
13
14static int wifi_cmd_iface_write_cmd(const struct shell *shell, size_t argc, char **argv)
{
#ifdef ENABLE_SPI_IFACE_TEST
if(argc < 2){
return 0;
}
sprintf(at_cmd, "%s\r\n", argv[1]);
shell_print(shell, "at cmd[%d]: %s",strlen(at_cmd), at_cmd);
iface.write(&iface, at_cmd, strlen(at_cmd));
#endif
return 0;
}
SHELL_CMD(iwcmd, NULL, "iface write cmd", wifi_cmd_iface_write_cmd),
也就是说,当我执行wifi iwcmd AT+CIPMUX?
,预期argc应该等于2,argv[1]应该指向”AT+CIPUMX”, 但实际上得到的argc为1,导致直接退出。
2. 问题原因
由于只有带?的命令出问题,很自然的就想到和通配符相关。下面精简出Shell中通配符的处理流程:
shell.c shell_thread
->shell_process
->state_collect
->execute
, 最后在execute中处理shell命令,处理shell命令的时候进行通配符处理,以下只列出和通配符相关的处理流程1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45static int execute(const struct shell *shell)
{
...
//准备通配符buffer
if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) {
shell_wildcard_prepare(shell);
}
//循环处理子命令
while ((argc != 1) && (cmd_lvl < CONFIG_SHELL_ARGC_MAX){
//将cmd line解析到argvp中
quote = shell_make_argv(&argc, argvp, cmd_buf, 2);
cmd_buf = (char *)argvp[1];
if (IS_ENABLED(CONFIG_SHELL_WILDCARD) && (cmd_lvl > 0)) {
enum shell_wildcard_status status;
//解析通配符
status = shell_wildcard_process(shell, entry,
argvp[0]);
//没有匹配到就直接退出循环
if (status == SHELL_WILDCARD_CMD_NO_MATCH_FOUND) {
break;
}
//有匹配项,子命令level增加cmd_lvl
if (status != SHELL_WILDCARD_NOT_FOUND) {
++cmd_lvl;
wildcard_found = true;
continue;
}
}
}
//结束通配符解析
if (IS_ENABLED(CONFIG_SHELL_WILDCARD) && wildcard_found) {
shell_wildcard_finalize(shell);
...
}
//执行最终命令
argv[cmd_lvl] = NULL;
return exec_cmd(shell, cmd_lvl - cmd_with_handler_lvl,
&argv[cmd_with_handler_lvl], &help_entry);
}
从上面可以看到cmd_lvl是在循环处理子命令的while中进行自加,一旦shell_wildcard_process
返回SHELL_WILDCARD_CMD_NO_MATCH_FOUND
就会break提前结束子命令的解析。使得循环解析子命令提前结束,例如下面命令:1
wifi iwcmd AT+CIPMUX
解析完后cmd_lvl为3
而1
wifi iwcmd AT+CIPMUX?
由于x?没有通配的子命令或者参数会提前break, cmd_lvl就为2.
以上两种情况cmd_with_handler_lvl都为1(第一个命令iwcmd对应有handle wifi_cmd_iface_write_cmd,具体流程不分析),因此对于第二种情况传入的argc就是cmd_lvl - cmd_with_handler_lvl = 1, 导致执行命令时拿不到”AT+CIPMUX?”。
3.通配符处理流程
1 | enum shell_wildcard_status shell_wildcard_process(const struct shell *shell, |
处理
处理方式有三种:
- 配置
CONFIG_SHELL_WILDCARD=n
关掉通配符 - 将
shell_wildcard_character_exist
中?通配符移掉 - 在shell实现中为?添加转义
因为我没有使用通配符的需求,添加转义改动也不小。因此采用了方式1,最简单又没有破坏性的修改。
参考
https://docs.zephyrproject.org/latest/reference/shell/index.html#wildcards-feature