实现一个shell难不难,本人曾经参考过很多开源的,都觉得他们的代码量比较多。下面我们将实现一个史上最简单的shell。最有两个源文件,代码量也非常少,同时也是拿来就可以用 ,shell.c 中删减了部分内容,移植好就可以用。
开发环境:keil、armcc
硬件平台:ARM
头文件 通用头文件 typedef.h#ifndef __TYPEDEF_H__ #define __TYPEDEF_H__ #include头文件 command.h// 类型定义 #include // bool #include // offsetof 、typeof #define container_of(ptr, type, member) ( (type *)( (char *)(ptr) - offsetof(type,member) ) ) #define __DEF_VAR(type, var, line) type var##line #define _DEF_VAR(type,prefix,line) __DEF_VAR(type, prefix, line) #define DEFINE_TYPE(type,prefix) _DEF_VAR(type, prefix, __LINE__) #if defined(__CC_ARM) || defined(__GNUC__) #define CC_SECTION(x) __attribute__((section(x))) #define CC_UNUSED __attribute__((unused)) #define CC_USED __attribute__((used)) #define CC_ALIGN(n) __attribute__((aligned(n))) #define CC_WEAK __attribute__((weak)) #elif defined (__ICCARM__) #define CC_SECTION(x) @ x #define CC_UNUSED #define CC_USED __root #define CC_WEAK __weak #else #error "Current tool chain haven't supported yet!" #endif //check whether a number is in the range[low, high] #define IN_RANGE(x, low, high) ((low) <= (x) && (x) <= (high)) // min #define MIN(x, y) ((x) > (y) ? (y) : (x)) // max #define MAX(x, y) ((x) > (y) ? (x) : (y)) // 得到一个field在结构体(struct)中的偏移量 #define FPOS( type, field ) ( (unsigned long) &(( type *) 0)-> field ) // 得到一个结构体中field所占用的字节数 #define FSIZ( type, field ) sizeof( ((type *) 0)->field ) // 返回数组元素的个数 #define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) ) #endif
#ifndef __COMMAND_H__
#define __COMMAND_H__
#include "typedef.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CMD_MAX_LEN 64
#define CMD_MAX_ARG 16
#define CMD_MAX_NUM 32
#define SHELL_PROMPT "root #"
struct cmdobj;
typedef struct cmdops{
uint8_t (*write)(const uint8_t *buf, uint8_t len);
int (*read) (uint8_t *buf, const int len);
}CmdOps_t;
typedef struct cmdobj {
uint8_t (*write)(const uint8_t *buf, uint8_t len);
int (*read) (uint8_t *buf, const int len);
void (*print)(struct cmdobj *this, const char *fmt, ...);
char recvbuf[CMD_MAX_LEN + 1];
uint16_t recvcnt;
uint32_t enable : 1;
}CmdObj_t;
typedef struct {
char *name;
int (*handler)(CmdObj_t *o, int argc, char *argv[]);
const char *brief;
}CmdItem_t;
#define Cmd_Register(name, handler, brief)
CC_USED DEFINE_TYPE(const CmdItem_t, cmd_item_##handler)
CC_SECTION("cmd.item.1") = {name, handler, brief}
void Cmd_Init(CmdObj_t *obj, const CmdOps_t *p);
void Cmd_Enable(CmdObj_t *obj);
void Cmd_Disable(CmdObj_t *obj);
void Cmd_Exec(CmdObj_t *obj, const char *cmd);
void Cmd_Process(CmdObj_t *obj);
#ifdef __cplusplus
}
#endif
#endif
头文件 shell.h
#ifndef __SHELL_H__ #define __SHELL_H__ void Shell_Init(void); void Shell_Run(const char *cmd); void Shell_Process(void); #endif源文件 源文件 command.c
#include源文件 shell.c#include #include #include #include #include "command.h" const CC_USED CmdItem_t cmd_tbl_start CC_SECTION("cmd.item.0") = {NULL, NULL, NULL}; const CC_USED CmdItem_t cmd_tbl_end CC_SECTION("cmd.item.2") = {NULL, NULL, NULL}; static const CmdItem_t* Cmd_Find(const char *keyword) { const CmdItem_t *it; for (it = &cmd_tbl_start + 1; it < &cmd_tbl_end; it++) { if (!strcasecmp(keyword, it->name)) return it; } return NULL; } static size_t strsplit(char *s, const char *separator, char *list[], size_t len) { size_t count = 0; if (s == NULL || list == NULL || len == 0) return 0; list[count++] = s; while(*s && count < len) { if (strchr(separator, *s) != NULL) { *s = ' '; list[count++] = s + 1; } s++; } return count; } static void Cmd_Print(CmdObj_t *obj, const char *format, ...) { va_list args; int len; char buf[CMD_MAX_LEN + CMD_MAX_LEN / 2]; va_start (args, format); len = vsnprintf (buf, sizeof(buf), format, args); va_end (args); obj->write((uint8_t*)buf, len); } static void Cmd_ProcessLine(CmdObj_t *obj) { char *argv[CMD_MAX_ARG]; int argc; const CmdItem_t *it; argc = strsplit(obj->recvbuf, " ,", argv, CMD_MAX_ARG); if ((it = Cmd_Find(argv[0])) == NULL) { obj->print(obj, "Unknown command '%s' - try 'help'rn", argv[0]); return; } it->handler(obj, argc, argv); } void Cmd_Init(CmdObj_t *obj, const CmdOps_t *p) { obj->read = p->read; obj->write = p->write; obj->print = Cmd_Print; obj->enable = true; } void Cmd_Enable(CmdObj_t *obj) { obj->enable = true; } void Cmd_Disable(CmdObj_t *obj) { obj->enable = false; } void Cmd_Exec(CmdObj_t *obj, const char *cmd) { snprintf(obj->recvbuf, CMD_MAX_LEN, "%s", cmd); Cmd_ProcessLine(obj); obj->print(obj, "%s ", SHELL_PROMPT); } void Cmd_Process(CmdObj_t *obj) { int i = 0; if (!obj->read || !obj->enable) { return; } i = obj->recvcnt; obj->recvcnt += obj->read((uint8_t*)&obj->recvbuf[i], CMD_MAX_LEN - i); while (i < obj->recvcnt) { if (obj->recvbuf[i] == 'r' || obj->recvbuf[i] == 'n') { obj->recvbuf[i] = ' '; obj->print(obj, "rn"); if(obj->recvbuf[0] != ' ') { Cmd_ProcessLine(obj); } obj->print(obj, "%s ", SHELL_PROMPT); memset(&obj->recvbuf[0], 0x0, sizeof(obj->recvbuf)); obj->recvcnt = 0; } else { obj->print(obj, "%c", obj->recvbuf[i]); } i++; } if (obj->recvcnt >= CMD_MAX_LEN) { obj->recvcnt = 0; } } static int Cmd_Compare(const void *item1,const void *item2) { CmdItem_t *it1 = *((CmdItem_t **)item1); CmdItem_t *it2 = *((CmdItem_t **)item2); return strcmp(it1->name, it2->name); } static int do_help(CmdObj_t *s, int argc, char *argv[]) { int i,j, count; const CmdItem_t *item_start = &cmd_tbl_start + 1; const CmdItem_t *item_end = &cmd_tbl_end; const CmdItem_t *cmdtbl[CMD_MAX_NUM]; if (argc == 2) { if ((item_start = Cmd_Find(argv[1])) != NULL) { s->print(s, item_start->brief); s->print(s, "rn"); } return 0; } for (i = 0; i < item_end - item_start && i < CMD_MAX_NUM; i++) { cmdtbl[i] = &item_start[i]; } count = i; qsort(cmdtbl, i, sizeof(CmdItem_t*), Cmd_Compare); s->print(s, "rn"); for (i = 0; i < count; i++) { s->print(s, cmdtbl[i]->name); j = strlen(cmdtbl[i]->name); if (j < 10) { j = 10 - j; } while (j--) { s->print(s, " "); } s->print(s, "- "); s->print(s, cmdtbl[i]->brief); s->print(s, "rn"); } return 1; } Cmd_Register("help", do_help, "list all command."); Cmd_Register("?", do_help, "alias for 'help'");
#include#include #include "bsp_uart.h" #include "command.h" #include "shell.h" static CmdObj_t g_Shell_Obj; const static CmdOps_t g_cmd_ops = { USART1_Send, USART1_Recv}; int fputc(int ch, FILE *f) { if(g_Shell_Obj.write) { g_Shell_Obj.write((uint8_t *)&ch, 1); } else { USART1_Send((uint8_t *)&ch, 1); } return ch; } void Shell_Init(void) { Cmd_Init(&g_Shell_Obj, &g_cmd_ops); g_Shell_Obj.print(&g_Shell_Obj, "rn"); g_Shell_Obj.print(&g_Shell_Obj, "******************************rn"); g_Shell_Obj.print(&g_Shell_Obj, " kiwi shell. QQ: 995868145 rn"); #if (FW_TYPE_CURR == FW_TYPE_BOOT) g_Shell_Obj.print(&g_Shell_Obj, " Bootloader rn"); #else g_Shell_Obj.print(&g_Shell_Obj, " Application rn"); #endif g_Shell_Obj.print(&g_Shell_Obj, "******************************rn"); g_Shell_Obj.print(&g_Shell_Obj, "%s ", SHELL_PROMPT); } void Shell_Run(const char *cmd) { if(!cmd)return; Cmd_Exec(&g_Shell_Obj, cmd); } void Shell_Process(void) { Cmd_Process(&g_Shell_Obj); }



