这里给出的shell 代码(或称为lsh、cli)都是在 GitHub上开源项目。
在大学里写一个Shell代码可能是必需的,它不仅可以提高程序能力,在实际程序应用中也非常方便,同时也可以以此为基础扩展其它功能。
shell 使用步骤:
Shell不仅要稳定可靠,还要简单易用,以下三点是必要的。
初始化:在这一步中, shell 将读取并执行其配置文件。这里还注册了shell 的功能函数、初始化输入输出端口等。 运行处理: shell 从标准输入(可以是交互式的或文件)读取命令并执行它们并输出运行结果。 结束退出:在不需要shell后,shell 执行关闭命令,释放内存等。 严格按照这三点实现目的是为了简单而易用。
目标已经明确,那是写一个shell,还是不要重造轮子呢?当然是后者。
下面是几个开源shell:
letter-shell :github.com/NevermindZZ…
lwshell : github.com/MaJerle/lws…
Shell : github.com/geekfactory…
uC-Shell :github.com/weston-embe…
letter-shell :
letter-shell其功能强大,并且一直在更新演化,其资料也较多请搜索网络。
lwshell :
lwshell是MaJerle开源,他不仅开源有lwshell, 还有lwrb,lwprintf等等。其官网还有详细说明资料。其开源项目无其它依懒,直接可移植使用。
Shell:
跟lwshell相差不大,真是简单到漏水,但也满足基本需求。
uC-Shell:
这个就是著名的uc-os的开源附件。其功能强大,设计精巧,其程序说明还和原理方法都有详细指引。经典设计和久经沙场不得不让人佩服。唯一不足是依懒uc库,这些都不是问题,有源码可以随意DIY。
前面讲到基本使用步骤,下面列出shell使用方法
shell循环方式:
在裸机中以while非阻塞式循环。 在裸机中以1mS定时非阻塞式循环。 在OS系统中以1ms间隔周期非阻塞式循环。 在OS系统中以消息、事件、信号等任务同步的方式阻塞式循环。 第一种最为简单,其余依次递增。
对于这四种循环方式,其shell 在运行过程中做了什么,分为以下四个工作内容。
Shell循环工作内容:
读取:从标准输入读取命令和数据串。 解析:将命令字符串分成程序和参数。 执行:运行解析的命令。 输出: 输出执行结果 以上为Shell的整体结构,图示如下所示
下面以lwshell和shell的代码为示范:
我们以shell基本循环中的第一个为例. 以祼机while形式循环处理。
我们以STM32cubeMx生成的任意一个工程的Main函数并在while循环中加入shell函数,注意在这我项目中我们将lwshell和shell同时进行运行。
为了调试方便我们用SEGGER_RTT作为输出。
int main(void) { HAL_Init(); SystemClock_Config();
rtt_init();
shell_shell_init();
shell_shell_register();
lwshell_shell_init();
lwshell_register();
while (1) {
read_port();
shell_task();
lwshell_task();
}
} 在最后没有作退出处理,而是让shell一直工作。
下面我们来看项目工程:
下面为函数代码:
void shell_shell_init(void) { // 初始化注册读写功能函数 shell_init(shell_read, shell_shell_write, 0); xprintf("Shell_shell initialization complete\r\n"); }
// Add commands to the shell void shell_shell_register(void) { shell_register(command_mycommand, ("shellcmd")); shell_register(command_othercommand, ("shell")); shell_register(Simultaneous_processing_command, ("cmd")); }
void lwshell_shell_init(void) { lwshell_init(); lwshell_set_output_fn(lwshell_out); xprintf("lwshell_shell initialization complete\r\n"); }
// Add commands to the shell
void lwshell_register(void)
{
/* Define shell commands */
lwshell_register_cmd("lwshell", lwshell_command, "lwshell running command");
lwshell_register_cmd("lw", lwshell_othercommand, "lwshell other command");
lwshell_register_cmd("cmd", lwshell_othercommand, "lwshell other command");
}
void lwshell_task(void) { uint8_t input; if(RingBuf_get(&lwshellRB, &input) == true) { lwshell_input((const void *)&input, sizeof(input)); } } 我们注册了五个命令调用函数,分别为Shell库和lwshell库分别单独操作各两个函数,另一个为两个库同时调用一个函数,共用一个命令“cmd”。如下:
int command_mycommand(int argc, char** argv)
int command_othercommand(int argc, char** argv)
int32_t lwshell_command(int32_t argc, char** argv)
int32_t lwshell_othercommand(int32_t argc, char** argv)
int32_t Simultaneous_processing_command(int32_t argc, char** argv)
{ xprintf("Shell_shell Running "mycommand" now argv: %d\r\n",argc); for(int32_t i = 0; i < argc; ++i) { xprintf("ARG[%d]: %s\r\n", (int)i, argv[i]); } xprintf("Exit..."); return SHELL_RET_SUCCESS; } 其函数执行的功能都是一样。这里只列出一个。
Shell库的执行命令为:shellcmd; shell; cmd
Lwshell库的执行命令为:lwshell; lw; cmd
Cmd为两个共用命令。
测试效果如下:
项目中包含一个SEGGER_RTT文件,一个环形队列库,一个xprintf打印库,还有两个shell库和我增加一个测试用例。