这里给出的shell 代码(或称为lsh、cli)都是在 GitHub上开源项目。
在大学里写一个Shell代码可能是必需的,它不仅可以提高程序能力,在实际程序应用中也非常方便,同时也可以以此为基础扩展其它功能。
shell 使用步骤:
Shell不仅要稳定可靠,还要简单易用,以下三点是必要的。
- 初始化:在这一步中, shell 将读取并执行其配置文件。这里还注册了shell 的功能函数、初始化输入输出端口等。
- 运行处理: shell 从标准输入(可以是交互式的或文件)读取命令并执行它们并输出运行结果。
- 结束退出:在不需要shell后,shell 执行关闭命令,释放内存等。
严格按照这三点实现目的是为了简单而易用。
目标已经明确,那是写一个shell,还是不要重造轮子呢?当然是后者。
下面是几个开源shell:
letter-shell :https://github.com/NevermindZZT/letter-shell
lwshell : https://github.com/MaJerle/lwshell
Shell : https://github.com/geekfactory/Shell
uC-Shell :https://github.com/weston-embedded/uC-Shell
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基本循环中的第一个为例.
我们以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库和我增加一个测试用例。
下面把项目文件打包,大家可以自行测试。