简单的CLI(command line interface)

2024-08-21 22:32

本文主要是介绍简单的CLI(command line interface),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、简介

        CLI(command line interface)是系统为用户提供的命令行接口。相信熟悉linux的同学肯定都必须了解一些linux的命令,在linux的shell上运行以实现一些功能。如最简单的ls命令,显示目录内容。

        CLI可以让用户实时的与系统进行交互,获取系统的实时信息,完成用户自定义的功能。所有的系统都必须为用户提供接口,只不过接口的形式多种多样,如web、传统的C/S结构的图像界面,当然CLI是其中的一种方式。

 

二、为什么要研究CLI

        你在做项目(主要指C/C++项目)的时候是否遇到过如下问题:

               1、程序运行过程中出现故障,想实时的查看一些运行状态;

               2、需要实时的与运行的系统进行一些简单的交互;

               3、遇到这些问题时,你又不想使用GDB跟踪;

 

        这时可以考虑为自己的程序增加一个小小的CLI,会极大的方便你的开发。

 

        问题来了,实现像linux 强大CLI系统,那可不是一件简单的事情。如果从零DIY一个自己的CLI感觉有点困难,这时软件复用就派上用场了。只要你可以找一个已有的项目,带CLI的将其移植一下,相信很快就可以为你的程序添加一个属于你自己私有的CLI。自定义一下命令,不需要华丽的用户体验,只要实用即可。

 

三、自己的CLI

        当时我想到的简单CLI就是uboot,熟悉uboot的同学肯定都知道uboot有一套命令集,如果做过uboot移植的同学肯定经常用它。只需要几个命令就可以将linux kernel引导起来,非常实用。那很自然的如果将uboot这套CLI移植到你的程序中,自定义一下适合自己的命令,那就大功告成了。

        那就废话少说,看看uboot源码,将CLI部分移植出来。

        下面是本人从uboot中移植的代码:

                1、很简单只有两个文件(command.h和command.c);

                2、将这两个文件编译一下:如gcc command.c -o ct,然后直接执行./ct;

                3、下面是测试结果

       源码:command.h

#ifndef __COMMAND_H__
#define __COMMAND_H__/* Monitor Command Prompt */
#define CONFIG_SYS_PROMPT "->"/* Buffer size for input from the Console */
#define CONFIG_SYS_CBSIZE		256#endif


 

       源码:command.c

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>#include "command.h"#define MAX_ARGC 10#define MAX_CMD_NUM 50uint8_t console_buffer[CONFIG_SYS_CBSIZE + 1];	/* console I/O buffer	*/
static char erase_seq[] = "\b \b";		/* erase sequence	*/
static char   tab_seq[] = "        ";		/* used to expand TABs	*/typedef void (*cmd_fun_t)(int , char *[])  ;
//命令结构体 
typedef struct CMD_STRUCT
{ char name[32]; /* Command Name */ char usage[64];/* Usage message */ cmd_fun_t CmdFun;//void (*CmdFun)(int , char *[]);/* Command execute function */ 
}CMD_STRUCT_T; int cmd_num_current = 0;//命令列表 
CMD_STRUCT_T CmdTbl[MAX_CMD_NUM]; #ifdef debug_cmd
#define debug_print() printf("[%s] line:%d\n", __FUNCTION__, __LINE__)
#else
#define debug_print() 
#endifvoid HelpCmdExeFun(int agrc, char *argv[])
{
debug_print() ;int i = 0;while (i < cmd_num_current){printf("%3d. %-32s -- %s\n", i, CmdTbl[i].name,  CmdTbl[i].usage);i++;}return; }void read_fun(int agrc, char * argv [ ])
{
debug_print() ;int i = 0;while (i < cmd_num_current) {if (read_fun == CmdTbl[i].CmdFun){printf("%s -- %s\n", CmdTbl[i].name,  CmdTbl[i].usage);break;}i++;}return; 
}void write_fun(int agrc, char * argv [ ])
{
debug_print() ;int i = 0;while (i < cmd_num_current) {if (write_fun == CmdTbl[i].CmdFun){printf("%s -- %s\n", CmdTbl[i].name,  CmdTbl[i].usage);break;}i++;}return; 
}void setenv_fun(int agrc, char * argv [ ])
{
debug_print() ;int i = 0;while (i < cmd_num_current) {if (setenv_fun == CmdTbl[i].CmdFun){printf("%s -- %s\n", CmdTbl[i].name,  CmdTbl[i].usage);break;}i++;}return; 
}int register_cmd(char *name, char *usage, cmd_fun_t fun)
{int ret;if (cmd_num_current < MAX_CMD_NUM){strcpy(CmdTbl[cmd_num_current].name, name);strcpy(CmdTbl[cmd_num_current].usage , usage);CmdTbl[cmd_num_current].CmdFun = fun;cmd_num_current++;}else{printf("%s error\n");return 1;}return 0;
}char parse_buf[256] ;
int parse_line(const char * const line, char *argv[])
{
debug_print();int argc = 0;char *ptr = parse_buf;memset(parse_buf, '\0', 256);strncpy(parse_buf, line, strlen(line));while ((argv[argc]=strtok(ptr, " "))!=NULL){
debug_print();
//printf("argv[%d]:%s\n", argc, argv[argc]);argc++;if (argc > MAX_ARGC)break;ptr = NULL;}
debug_print();return argc;
}static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen)
{char *s;if (*np == 0) {return (p);}if (*(--p) == '\t') {			/* will retype the whole line	*/while (*colp > plen) {puts (erase_seq);(*colp)--;}for (s=buffer; s<p; ++s) {if (*s == '\t') {puts (tab_seq+((*colp) & 07));*colp += 8 - ((*colp) & 07);} else {++(*colp);putc (*s, stdout );}}} else {puts (erase_seq);(*colp)--;}(*np)--;return (p);
}int32_t readline_into_buffer ( int8_t *const prompt, int8_t * buffer)
{int8_t *p = buffer;int8_t * p_buf = p;int32_t n = 0;				/* buffer index		*/int32_t plen = 0;			/* prompt length	*/int32_t col;				/* output column cnt	*/int8_t c;int8_t *ptr = prompt;/* print prompt */if (prompt) {plen = strlen (prompt);while (*ptr)putc(*ptr++, stdout);//puts (prompt);}col = plen;while (1){c = getc(stdin);/** Special character handling*/switch (c) {case '\r':				/* Enter		*/case '\n':*p = '\0';//puts ("\r");return (p - p_buf);case '\0':				/* nul			*/continue;case 0x03:				/* ^C - break		*/p_buf[0] = '\0';	/* discard input */return (-1);case 0x15:				/* ^U - erase line	*/while (col > plen) {puts (erase_seq);--col;}p = p_buf;n = 0;continue;case 0x17:				/* ^W - erase word	*/p=delete_char(p_buf, p, &col, &n, plen);while ((n > 0) && (*p != ' ')) {p=delete_char(p_buf, p, &col, &n, plen);}continue;case 0x08:				/* ^H  - backspace	*/case 0x7F:				/* DEL - backspace	*/p=delete_char(p_buf, p, &col, &n, plen);continue;default:/** Must be a normal character then*/if (n < CONFIG_SYS_CBSIZE-2){if (c == '\t') {	/* expand TABs		*///puts (tab_seq+(col&07));col += 8 - (col&07);} else{++col;		/* echo input		*///putc (c, stdout);}*p++ = c;++n;} else {			/* Buffer full		*/putc ('\a', stdout);}}}}int32_t readline ( int8_t *const prompt)
{/** If console_buffer isn't 0-length the user will be prompted to modify* it instead of entering it from scratch as desired.*/memset(console_buffer, '\0', CONFIG_SYS_CBSIZE+1);console_buffer[0] = '\0';return readline_into_buffer(prompt, console_buffer);
}int find_cmd(char *cmd)
{
debug_print();
//printf("cmd:%s\n",cmd);int cmd_index = 0;if (('0' == cmd[0]) && ('\0' == cmd[1]))return cmd_index;cmd_index = atoi(cmd);if ((cmd_index >0 ) && (cmd_index < cmd_num_current)){return cmd_index;}cmd_index = 0;while (cmd_index < MAX_CMD_NUM){if (0 == strncmp(CmdTbl[cmd_index].name, cmd, strlen(cmd)))return cmd_index;cmd_index++;}printf("Command  [%s ]  don't support!\n", cmd);debug_print();return -1 ;
}
/***************************************************************************** returns:*	1  - command executed, repeatable*	0  - command executed but not repeatable, interrupted commands are*	     always considered not repeatable*	-1 - not executed (unrecognized, bootd recursion or too many args)*           (If cmd is NULL or "" or longer than CONFIG_SYS_CBSIZE-1 it is*           considered unrecognized)** WARNING:** We must create a temporary copy of the command since the command we get* may be the result from getenv(), which returns a pointer directly to* the environment data, which may change magicly when the command we run* creates or modifies environment variables (like "bootp" does).*/
int run_command (const char * const cmd, int flag)
{
debug_print();//puts(cmd);int cmd_index = 0;int argc = 0;char *argv[MAX_ARGC];argc = parse_line(cmd, argv);if ((argc > 0) &&(argc < MAX_ARGC))cmd_index = find_cmd(argv[0]);elsereturn 1;if ( -1 != cmd_index)CmdTbl[cmd_index].CmdFun(argc, argv);debug_print();return 0;#if 0cmd_tbl_t *cmdtp;char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/char *token;			/* start of token in cmdbuf	*/char *sep;			/* end of token (separator) in cmdbuf */char finaltoken[CONFIG_SYS_CBSIZE];char *str = cmdbuf;char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/int argc, inquotes;int repeatable = 1;int rc = 0;#ifdef DEBUG_PARSERprintf ("[RUN_COMMAND] cmd[%p]=\"", cmd);puts (cmd ? cmd : "NULL");	/* use puts - string may be loooong */puts ("\"\n");
#endifclear_ctrlc();		/* forget any previous Control C */if (!cmd || !*cmd) {return -1;	/* empty command */}if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {puts ("## Command too long!\n");return -1;}strcpy (cmdbuf, cmd);/* Process separators and check for invalid* repeatable commands*/#ifdef DEBUG_PARSERprintf ("[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_PARSERprintf ("token: \"%s\"\n", token);
#endif/* find macros in this token and replace them */process_macros (token, finaltoken);/* Extract arguments */if ((argc = parse_line (finaltoken, argv)) == 0) {rc = -1;	/* no command at all */continue;}/* Look up command in command table */if ((cmdtp = find_cmd(argv[0])) == NULL) {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;}#if defined(CONFIG_CMD_BOOTD)/* avoid "bootd" recursion */if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSERprintf ("[%s]\n", finaltoken);
#endifif (flag & CMD_FLAG_BOOTD) {puts ("'bootd' recursion detected\n");rc = -1;continue;} else {flag |= CMD_FLAG_BOOTD;}}
#endif/* OK - call function to do the command */if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {rc = -1;}repeatable &= cmdtp->repeatable;/* Did the user stop this? */if (had_ctrlc ())return -1;	/* if stopped then not repeatable */}return rc ? rc : repeatable;#endif
}int main(int argc, char **argv)
{int32_t rc = -1;int32_t len;static int8_t lastcommand[CONFIG_SYS_CBSIZE] = { 0, };memset(CmdTbl, 0, sizeof(CMD_STRUCT_T)*MAX_CMD_NUM);register_cmd("help", "list all cmd\n\r",       HelpCmdExeFun);register_cmd("read", "read memory\n\r",    read_fun);register_cmd("write", "write memory\n\r",  write_fun);register_cmd("setenv", "set env\n\r",        setenv_fun);debug_print();while (1){len = readline (CONFIG_SYS_PROMPT);if (len > 0){memset(lastcommand, '\0', CONFIG_SYS_CBSIZE);strncpy (lastcommand, console_buffer, strlen(console_buffer));rc = run_command (lastcommand, 0);if (rc <= 0){/* invalid command or not repeatable, forget it */lastcommand[0] = 0;}}}return 0;
}


四、总结

        目的基本达到,实现了四个命令。

        这个小程序本身没有什么,但是从有实现简单CLI的想法,到寻找开源代码,最后移植调试。最关键的必须注重软件复用,现在的open source那么多,一定要学会好好利用。

 

这篇关于简单的CLI(command line interface)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1094462

相关文章

redis-cli命令行工具的使用小结

《redis-cli命令行工具的使用小结》redis-cli是Redis的命令行客户端,支持多种参数用于连接、操作和管理Redis数据库,本文给大家介绍redis-cli命令行工具的使用小结,感兴趣的... 目录基本连接参数基本连接方式连接远程服务器带密码连接操作与格式参数-r参数重复执行命令-i参数指定命

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

四种简单方法 轻松进入电脑主板 BIOS 或 UEFI 固件设置

《四种简单方法轻松进入电脑主板BIOS或UEFI固件设置》设置BIOS/UEFI是计算机维护和管理中的一项重要任务,它允许用户配置计算机的启动选项、硬件设置和其他关键参数,该怎么进入呢?下面... 随着计算机技术的发展,大多数主流 PC 和笔记本已经从传统 BIOS 转向了 UEFI 固件。很多时候,我们也

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

MyBatis框架实现一个简单的数据查询操作

《MyBatis框架实现一个简单的数据查询操作》本文介绍了MyBatis框架下进行数据查询操作的详细步骤,括创建实体类、编写SQL标签、配置Mapper、开启驼峰命名映射以及执行SQL语句等,感兴趣的... 基于在前面几章我们已经学习了对MyBATis进行环境配置,并利用SqlSessionFactory核

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu2289(简单二分)

虽说是简单二分,但是我还是wa死了  题意:已知圆台的体积,求高度 首先要知道圆台体积怎么求:设上下底的半径分别为r1,r2,高为h,V = PI*(r1*r1+r1*r2+r2*r2)*h/3 然后以h进行二分 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#includ

usaco 1.3 Prime Cryptarithm(简单哈希表暴搜剪枝)

思路: 1. 用一个 hash[ ] 数组存放输入的数字,令 hash[ tmp ]=1 。 2. 一个自定义函数 check( ) ,检查各位是否为输入的数字。 3. 暴搜。第一行数从 100到999,第二行数从 10到99。 4. 剪枝。 代码: /*ID: who jayLANG: C++TASK: crypt1*/#include<stdio.h>bool h

uva 10387 Billiard(简单几何)

题意是一个球从矩形的中点出发,告诉你小球与矩形两条边的碰撞次数与小球回到原点的时间,求小球出发时的角度和小球的速度。 简单的几何问题,小球每与竖边碰撞一次,向右扩展一个相同的矩形;每与横边碰撞一次,向上扩展一个相同的矩形。 可以发现,扩展矩形的路径和在当前矩形中的每一段路径相同,当小球回到出发点时,一条直线的路径刚好经过最后一个扩展矩形的中心点。 最后扩展的路径和横边竖边恰好组成一个直