“命令终端”的实现3-命令的执行
前面已经能获取到输入的字符了,接着就是解析这些字符,判断,符合要求的,执行对应的函数。而对应的函数,就是需要实现的命令。本文从具体的命令实现逐步倒推,最后对接上一文章。
一、命令函数
Linux 中,基本上每条命令都有参数及帮助信息,仿照这些功能,给出实现函数指针及结构体:
/** Monitor Command Table*/
typedef int (*cmd_func)(int argc, char * const argv[]);struct cmd_tbl_s {char *name; /* Command Name */int maxargs; /* maximum number of arguments */cmd_func cmd; /* Implementation function */char *usage; /* Usage message (short) */
#ifdef CONFIG_SYS_LONGHELPchar *help; /* Help message (long) */
#endif
#ifdef CONFIG_AUTO_COMPLETE/* do auto completion on the arguments */int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};typedef struct cmd_tbl_s cmd_tbl_t;/* someone should implement the table */
extern cmd_tbl_t cmd_table[];
为保持命令的独立性,设置cmd_func
函数指针。命令结构体为全局变量,下面给出其定义及对应命令的实现:
/* just a foolish command table */
cmd_tbl_t cmd_table[] =
{{"help", CONFIG_SYS_MAXARGS, do_help, "print help info."},{"print", 1, do_print, "print the env."},{"exit", 1, do_exit, "exit..."},{NULL, 0, NULL, NULL},
};int do_help(int argc, char * const argv[])
{_do_help(cmd_table, sizeof(cmd_table)/sizeof(cmd_tbl_t), argc, argv);return 0;
}int do_print(int argc, char * const argv[])
{myprintf("in %s\n", __FUNCTION__);return 0;
}int do_exit(int argc, char * const argv[])
{exit(0);return 0;
}
以上就是具体命令的实现过程,可以将每条命令都理解为一个单独的可带参数的程序。
二、查找命令
查找命令名称前,需要先对输入的字符串进行解析,实现函数如下:
int parse_line (char *line, char *argv[])
{int nargs = 0;#ifdef DEBUG_PARSERcmd_printf ("parse_line: \"%s\"\n", line);
#endifwhile (nargs < CONFIG_SYS_MAXARGS){/* skip any white space */while ((*line == ' ') || (*line == '\t')){++line;}if (*line == '\0'){ /* end of line, no more args */argv[nargs] = NULL;
#ifdef DEBUG_PARSERcmd_printf ("parse_line: nargs=%d\n", nargs);
#endifreturn (nargs);}argv[nargs++] = line; /* begin of argument string *//* find end of string */while (*line && (*line != ' ') && (*line != '\t')){++line;}if (*line == '\0'){ /* end of line, no more args */argv[nargs] = NULL;
#ifdef DEBUG_PARSERcmd_printf ("parse_line: nargs=%d\n", nargs);
#endifreturn (nargs);}*line++ = '\0'; /* terminate current arg */}cmd_printf ("** Too many args (max. %d) **\n", CONFIG_SYS_MAXARGS);#ifdef DEBUG_PARSERcmd_printf ("parse_line: nargs=%d\n", nargs);
#endifreturn (nargs);
}
输出参数为argv
数组,此即为命令参数列表,其中argv[0]
即命令名称,返回值为命令参数个数,即argc
。这就是我们熟悉的C语言 main 函数的参数。
查找命令实际是搜索结构体数组cmd_table
中的命令名称。如果找到返回名称,反之为空。
cmd_tbl_t* find_cmd_tbl3(const char* cmd, cmd_tbl_t *table)
{const char *p;char *q;cmd_tbl_t *c, *found;int nmatches, longest;longest = 0;nmatches = 0;found = 0;for (c = table; (p = c->name) != NULL; c++){for (q = cmd; *q == *p++; q++)if (*q == 0) /* exact match? */return (c);if (!*q){return NULL;}}return NULL;
}cmd_tbl_t *find_cmd (const char* cmd)
{//int len = sizeof(cmd_table) / sizeof(cmd_tbl_t);//return find_cmd_tbl(cmd, cmd_table, len);//return find_cmd_tbl2(cmd, cmd_table);return find_cmd_tbl3(cmd, cmd_table);
}
注意,如果命令数量过多,不宜使用从头遍历的方法查找。本文工程仅为示例,暂不研究效率。
三、汇总运行
运行命令函数实现如下,
int run_command (const char *cmd)
{cmd_tbl_t *cmdtp;char cmdbuf[CB_SIZE]; /* working copy of cmd */char *token; /* start of token in cmdbuf */char *sep; /* end of token (separator) in cmdbuf *///char finaltoken[CB_SIZE];char *str = cmdbuf;char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */int argc, inquotes;int repeatable = 1;int rc = 0;#ifdef DEBUG_PARSERcmd_printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);cmd_puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */cmd_puts ("\"\n");
#endif//clear_ctrlc(); /* forget any previous Control C */if (!cmd || !*cmd){return -1; /* empty command */}if (strlen(cmd) >= CB_SIZE){cmd_puts ("## Command too long!\n");return -1;}strcpy (cmdbuf, cmd);/* Process separators and check for invalid* repeatable commands*/#ifdef DEBUG_PARSERcmd_printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endifwhile (*str) {/** Find separator, or string end* Allow simple escape of ';' by writing "\;"*/for (inquotes = 0, sep = str; *sep; sep++){if ((*sep=='\'') &&(*(sep-1) != '\\'))inquotes=!inquotes;if (!inquotes &&(*sep == ';') && /* separator */( sep != str) && /* past string start */(*(sep-1) != '\\')) /* and NOT escaped */break;}/** Limit the token to data between separators*/token = str;if (*sep){str = sep + 1; /* start of command for next pass */*sep = '\0';}elsestr = sep; /* no more commands for next pass */
#ifdef DEBUG_PARSERcmd_printf ("token: \"%s\"\n", token);
#endif/* Extract arguments */if ((argc = parse_line (token, argv)) == 0){rc = -1; /* no command at all */continue;}/* Look up command in command table */if ((cmdtp = find_cmd(argv[0])) == NULL){cmd_printf ("Unknown command '%s' - try 'help'\n", argv[0]);rc = -1; /* give up after bad command */continue;}/* found - check max args */if (argc > cmdtp->maxargs){cmd_usage(cmdtp);rc = -1;continue;}/* OK - call function to do the command */if ((cmdtp->cmd) (argc, argv) != 0){rc = -1;}}//return rc ? rc : repeatable;return rc;
}
主要过程有解析字符串parse_line,查找命令结构数组find_cmd,最后运行函数指针。
四、主函数
入口函数及倒计时函数如下:
int abortboot(int delay)
{int abort = 0;int i = 0;myprintf("Hit any key to stop autoboot: %2d ", delay);while ((delay > 0) && (!abort)){--delay;for (i = 0; !abort && i < 100; ++i){if (mytstc()){abort = 1;delay = 0;mygetc();break;}mysleep(10);}myprintf("\b\b\b%2d ", delay);}myputc('\n');return abort;
}int readline_test(void)
{static char lastcommand[CB_SIZE] = {0};int len;if (!abortboot(5)){myprintf("Aotu run.\n");return 0;}myprintf("You abort.\n");while (1){len = readline(PROMPT, lastcommand);if (len > 0){//printf("len: %d\n", len);if (len >= CB_SIZE){myprintf("command line too large.\n");break;}//strcpy(lastcommand, console_buffer);//printf("[echo]: %s\n", lastcommand);}else if (len == 0){//printf("nothing input.\n");// do nothing}if (len == -1){myputs("<INTERRUPT>\n");}else{run_command(lastcommand);}}return 0;
}
至此,一个“命令终端”实现完毕。可对其底层的字符处理进行修改,以适应不同环境。除此外,其它业务逻辑完全无变化。
李迟 2020.9.30
“命令终端”的实现3-命令的执行相关推荐
- linux查看执行过的命令行,在Linux命令终端中查看和编辑曾执行过的命令 – LINUX笔记 – CFEI.NET...
今天我们来讲讲linux的知识,积累的这些知识就是我们以后的财富,各位加油. 因为水平有限,难免有疏忽或者不准确的地方,希望大家能够直接指出来,我会及时改正.一切为了知识的分享. history 命令 ...
- linux看以前敲过的指令,在Linux命令终端中查看和编辑曾执行过的命令
history history 命令可以用来显示曾执行过的命令,也可以根据显示的治疗来重新执行需要的命令 n 显示n个最近的记录 -a 添加记录 -r 读取记录,但不会添加内容记录 -w 覆盖原有的h ...
- Linux 的命令终端(CMD)的快捷键(Keyboard of MacBook)
文章目录 常用 移动光标 编辑命令 查找历史命令 控制命令 命令终端界面滚屏 命令终端页签切换 奇葩 常用 快捷键 说明 Ctrl + A 光标跳到本行的行首 Ctrl + E 光标跳到本行的行尾 C ...
- 如何通过命令终端访问本地/局域网/远程的MySQL数据库_访问数据库_连接数据库_登录数据库
文章目录 Windows系统下 访问本地MySQL数据库 访问远程主机的MySQL数据库 本地安装了MySQL数据库 本地没有安装MySQL Linux系统下 退出数据库登录 Windows系统下 访 ...
- “命令终端”的实现1-准备篇
李迟注: 这几篇文章写于2012年底,因故未发表,前不久,音视频群里的树哥询问一个技术方案,想到以前曾经实现过,就把工程发给他.现在发表出来,除修正个别严重的病句外,其它没有修改.从行文看,还有很大提 ...
- 【图文解析 】Linux命令终端,又看会了,浮夸
Linux命令终端 在桌面空白处,点击鼠标右键,在下拉菜单选项中,选择Open in Terminal,然后出现右下图对话框,这个对话框就是命令终端,将是我们与该linux打交道最多的地方,任何该li ...
- Ubuntu命令终端查看使用过的命令
使用history命令 cyf@ubuntu:~$ history 但是这样会显示出所有使用过的命令,可以在history后加上less cyf@ubuntu:~$ history | less 会显 ...
- 用命令行编译java并生成可执行的jar包
1.编写源代码 编写源文件:CardLayoutDemo.java并保存,例如:I:\myApp\CardLayoutDemo.java.程序结构如下: package test;import jav ...
- linux按顺序运行命令,linux – 安排cron作业打开终端并按顺序运行命令
以下答案[在分隔符之后]假定您要在终端的[内部]内运行第二个命令.否则你只需要交换cronjob上的命令(因为目前,就像你拥有它一样,echo只会在终端退出后执行),例如: 0 23 * * * ec ...
最新文章
- Unity创建游戏VFX视觉特效-初级到中级
- 今天重新建立了个eclipse 的maven项目,提示org.junit找不到
- 【连载】优秀程序员的45个习惯之45——及时通报进展与问题
- vsftpd配置文件详细介绍
- Spring的Bean实例化、属性注入、对象注入、复杂注入(基于xml配置方式)
- php怎么把字符转成大写,php怎么把字符串转换为大写
- Java集合系列---红黑树(基于HashMap 超详细!!!)
- linux work有关的命令,VM workstation 中linux 命令
- 区块链需要学习哪些东西_区块链主要学习哪些知识?
- 怎么用debug看jdbc查询的resultset中查出的数据_用了这个 ORM 工具,我只用一天就把项目数据库给换了
- android selector 开始自定义样式
- Eureka 注册中心 简单搭建
- java用1234组成_java编程题,java1234组成三位数不重复
- phpWord 读取word模板,替换相应变量
- 26个大小写字母对应的哈希值
- Dbeaver Phoenix 各种报错
- git pages部署静态页面,可以免费发布简历之类的静态网页。
- java编写活期储蓄帐目管理_活期储蓄账目管理系统
- Hive 3.1.2Linux CentOs 安装,踩坑 Dbeaver 连接Hive
- 【CSS3 属性】CSS 3 transform:scale 与 transform:translate 作用在同一div元素,导致元素位置偏离
热门文章
- “我没搞懂元宇宙,但一天能赚9w块”
- 英特尔成立集成光电研究中心 加速光互连I/O创新
- OPPO Reno7/Reno7 Pro今天开售:首发IMX709超感光猫眼镜头
- 外媒:苹果公司将在美国为其“苹果汽车”生产电池
- 马斯克再带货狗狗币:超7成网友票选狗狗币为未来货币
- 钱准备好!苹果官方账号泄密:iPhone 12明晚发布有戏
- 云集CEO肖尚略直播首秀,4小时带货5150万元
- 10大淘宝直播之城公布:杭州、广州、连云港位列前三
- 华为P40系列发布时间或敲定:继续在3月26日亮相?
- 苹果悬赏100万美元找漏洞 辞职的理由找到了!