这里写自定义目录标题
前言
最近看到不少服务自带命令行,可以查询当前服务运行状态,感觉非常有用,这样就可以实时查询状态,不必打印各种日志,简化后续维护工作
Readline示例
借用人家的,稍微改了点点
#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
/* 提示符 */
#define PROMPT "clover>>"
//命令结构体
typedef int (*CmdProcFunc)(void);
typedef struct{
char *pszCmd;
CmdProcFunc fpCmd;
}CMD_PROC;
//命令处理函数定义
#define MOCK_FUNC(funcName) \
int funcName(void){printf(" Enter "#funcName"!\n"); return 0;}
MOCK_FUNC(ShowMeInfo);
MOCK_FUNC(SetLogCtrl);
MOCK_FUNC(TestBatch);
MOCK_FUNC(TestEndianOper);
//命令表项宏,用于简化书写
#define CMD_ENTRY(cmdStr, func) {cmdStr, func}
#define CMD_ENTRY_END {NULL, NULL}
//命令表
static CMD_PROC gCmdMap[] = {
CMD_ENTRY("ShowMeInfo", ShowMeInfo),
CMD_ENTRY("SetLogCtrl", SetLogCtrl),
CMD_ENTRY("TestBatch", TestBatch),
CMD_ENTRY("TestEndian", TestEndianOper),
CMD_ENTRY_END
};
#define CMD_MAP_NUM (sizeof(gCmdMap)/sizeof(CMD_PROC)) - 1/*End*/
//返回gCmdMap中的CmdStr列(必须为只读字符串),以供CmdGenerator使用
static char *GetCmdByIndex(unsigned int dwCmdIndex)
{
if(dwCmdIndex >= CMD_MAP_NUM)
return NULL;
return gCmdMap[dwCmdIndex].pszCmd;
}
//执行命令
static int ExecCmd(char *pszCmdLine)
{
if(NULL == pszCmdLine)
return -1;
unsigned int dwCmdIndex = 0;
for(; dwCmdIndex < CMD_MAP_NUM; dwCmdIndex++)
{
if(!strcmp(pszCmdLine, gCmdMap[dwCmdIndex].pszCmd))
break;
}
if(CMD_MAP_NUM == dwCmdIndex)
return -1;
gCmdMap[dwCmdIndex].fpCmd(); //调用相应的函数
return 0;
}
//剔除字符串首尾的空白字符(含空格)
static char *StripWhite(char *pszOrig)
{
if(NULL == pszOrig)
return "NUL";
char *pszStripHead = pszOrig;
while(isspace(*pszStripHead))
pszStripHead++;
if('\0' == *pszStripHead)
return pszStripHead;
char *pszStripTail = pszStripHead + strlen(pszStripHead) - 1;
while(pszStripTail > pszStripHead && isspace(*pszStripTail))
pszStripTail--;
*(++pszStripTail) = '\0';
return pszStripHead;
}
void ReadCmdLine(char *cmd, size_t cmdlen)
{
//读取用户输入的命令行
char *pRead = readline(PROMPT);
//剔除命令行首尾的空白字符。若剔除后的命令不为空,则存入历史列表
char *pTrim = StripWhite(pRead);
if(pTrim && *pTrim)
{
add_history(pTrim);
}
snprintf(cmd, cmdlen, "%s", pTrim);
free(pRead);
pRead = NULL;
return;
}
static char *CmdGenerator(const char *pszText, int dwState)
{
static int dwListIdx = 0, dwTextLen = 0;
if(!dwState)
{
dwListIdx = 0;
dwTextLen = strlen(pszText);
}
//rl_line_buffer用户全部输入内容
//printf("%s, %d,%s\n", pszText, dwState,rl_line_buffer);
//当输入字符串与命令列表中某命令部分匹配时,返回该命令字符串
const char *pszName = NULL;
while((pszName = GetCmdByIndex(dwListIdx)))
{
dwListIdx++;
if(!strncmp (pszName, pszText, dwTextLen))
{
return strdup(pszName);
}
}
rl_attempted_completion_over = 1;//不进行文件匹配
return NULL;
}
static char **CmdCompletion(const char *pszText, int dwStart, int dwEnd)
{
char **pMatches = NULL;
if(0 == dwStart) /* 进处理第一个单词 */
{
pMatches = rl_completion_matches(pszText, CmdGenerator);
}
else
{
/* 后续单词不处理,可根据需要修改 */
rl_attempted_completion_over = 1;//不进行文件匹配
}
return pMatches;
}
int main(void)
{
char cmd[128];
/* 初始化自动补全函数 */
rl_attempted_completion_function = CmdCompletion;
/* 主循环 */
while(1)
{
cmd[0] = '\0';
/* 读取输入 */
ReadCmdLine(cmd, sizeof(cmd));
/* 执行命令 */
ExecCmd(cmd);
}
return 0;
}
编译:
gcc -Wall -o ReadLine Readline.c -lreadline -lncurses
示例比较简单,就是输入一些东西,然后能通过TAB自动补全或显示待选项
先调用readline函数进行输入处理,readline中会响应tab键,然后在屏幕上输出待选项
readline会调用rl_attempted_completion_function,该函数会进行补全处理
如果输入的内容不能匹配预制的内容,那么默认动作会去匹配文件夹下的文件T_T
如果不想匹配文件那么把rl_attempted_completion_over设置成非0即可^_^
未解决问题
内存检查发现有泄漏
valgrind --leak-check=full --show-reachable=yes ./ReadLine
==21728== LEAK SUMMARY:
==21728== definitely lost: 0 bytes in 0 blocks
==21728== indirectly lost: 0 bytes in 0 blocks
==21728== possibly lost: 0 bytes in 0 blocks
==21728== still reachable: 90,251 bytes in 167 blocks
==21728== suppressed: 0 bytes in 0 blocks
还不知道怎么解决,请各路大神指教
参考
https://blog.youkuaiyun.com/xuancbm/article/details/81436681
https://www.cnblogs.com/clover-toeic/p/3892688.html
https://tiswww.case.edu/php/chet/readline/readline.html#IDX256