C语言开发命令行系统

本文详细介绍了如何使用C语言开发了一个支持命令注册、分组、执行和显示的嵌入式设备命令行交互系统,包括接口定义、关键函数如FindCommand、ExecuteCommand等的实现和示例应用。
摘要由CSDN通过智能技术生成

1 概述

在嵌入式开发中,经常会利用命令行交互系统测试功能,本文描述使用C语言开发的一个命令行交互系统,支持命令注册,命令分组,执行命令,显示命令提示。

2 设计

2.1 接口

typedef int (*CommandHandler)(int argc, char* argv[]);

int RegisterCommand(const char* cmd, CommandHandler handler,
	const char* help);
	
int RegisterGroupCommand(const char* cmd, const char* group, 
	CommandHandler handler, const char* help);

void ShowCommands(const char* group);
void ExecCommand(const char* cmdString);
int CommandReturnValue(void);

int WaitForCommand(void);

接口说明:

  • CommandHandler 命令Hanlder类型
  • RegisterCommand 注册命令
  • RegisterGroupCommand 按组注册命令
  • ShowCommands 按组显示命令,如果组名称为NULL显示所有命令
  • ExecCommand 执行命令
  • CommandReturnValue 获取最近命令返回值
  • WaitForCommand 等待输入命令

2.2 实现

2.2.1 Command类型

#define COMMAND_MAX_SIZE 300
#define ARG_MAX_SIZE     20
#define CMD_PROMPT_STR "CMD->"

typedef struct Command
{
	const char *cmd;
	const char *help;
	const char *group;
	CommandHandler handler;
}Command;
static Command commands[COMMAND_MAX_SIZE] = { 0 };

说明:

  • Command结构包括命令,帮助,组名称及命令handler。
  • commands 静态全局命令,最大支持注册命令300个。

2.2.2 FindCommand

static int FindCommand(const char * str, Command ** pCmd)
{
	if(0 == str || 0 == pCmd)
		return 0;

	int i;
	for(i = 0; i < COMMAND_MAX_SIZE; i++)
	{
		if( 0 == commands[i].handler)
			break;

		int lenStr = strlen((const char *)str);
		int len = strlen(commands[i].cmd);

		if(0 == memcmp(commands[i].cmd, (const char *)str, (len > lenStr) ? len : lenStr))
		{
			*pCmd = &commands[i];
			return 1;
		}
	}
	return 0;
}

2.2.3 ExecuteCommand

static int ExecuteCommand(Command *pCmd, int argc, char* argv[])
{
	if(0 == pCmd || 0 == pCmd->handler)
	{
		printf("\r\nExecuteCmd para error");
		return 0;
	}
	return (pCmd->handler)(argc, argv);
}

2.2.4 ParseCommand

static int command_ret = -9999;
static void ParseCommand(const char *cmdString)
{
	Command *pCmd = 0;
	char *p = 0,*cmd = 0;
	char* argv[ARG_MAX_SIZE] = { 0 };
	int argc = 0;
	if(!strlen(cmdString))
		return;

	add_history(cmdString);
	if((('/' == cmdString[0]) && ('/' == cmdString[1]))
			|| ('@' == cmdString[0])
			|| ('#' == cmdString[0]))
	{
		printf("\r\nAnnotating code!");
		printf("\r\n"CMD_PROMPT_STR);
		fflush(stdout);
	}
	else
	{
		p = strtok((char *)cmdString," ");
		if(0 != p)
		{
			cmd = p;
			while(p)
			{
				p = strtok(0," ");
				if(0 == p)
					break;
				if(argc < ARG_MAX_SIZE)
					argv[argc++] = p;
				else
					break;
			}
		}
		if(!FindCommand(cmd, &pCmd))
			printf("\r\nUnknown command \r\n");
		else
		{
			int ret = ExecuteCommand(pCmd, argc, argv);
		 	if(ret < 0)
				printf("\nusage: %s\n\n", pCmd->help);
			command_ret = ret;
		}
	}
}

2.2.5 RegisterCommand/RegisterGroupCommand

int RegisterCommand(const char* cmd, CommandHandler handler, const char* help)
{
	return RegisterGroupCommand(cmd, 0, handler, help);
}

int RegisterGroupCommand(const char* cmd, const char* group, 
	CommandHandler handler, const char* help)
{
		if(!cmd || !help || !handler)
	{
		printf("%s: invalid param\n", __func__);
		return 1;
	}

	uint32_t i;

	for(i = 0; i < COMMAND_MAX_SIZE; i++)
	{
		if(!commands[i].handler)
		{
			commands[i].cmd = cmd;
			commands[i].group = group;
			commands[i].help = help;
			commands[i].handler = handler;
			break;
		}
	}

	if(i == COMMAND_MAX_SIZE)
	{
		printf("%s: is max size=%u\n", __func__, i);
		return 2;
	}

	return 0;
}

2.2.6 注册系统命令

static uint8_t runFlag = 1;
static void ExitCommand(void) { runFlag = 0; }
static int exit_cmd(int argc, char* argv[])
{
	ExitCommand();
	return 0;
}
static int show_cmd(int argc, char* argv[])
{
	const char* group = NULL;
	if(argc > 0)
		group = argv[0];

	ShowCommands(group);
	return 0;
}
static int ret_cmd(int argc, char* argv[])
{
	if(argc == 0)
		printf("ret = %d\n", command_ret);
	else
		fwrite(&command_ret, sizeof(command_ret), 1, stdout);

	return command_ret > 0 ? command_ret : 0;
}
static int print_cmd(int argc, char* argv[])
{
	int i;
	for(i = 0; i < argc; i++)
	{
		printf("%s", argv[i]);
		if(i != argc - 1)
			printf(" ");
		else
			printf("\n");
	}
}
static int test_cmd(int argc, char* argv[])
{
	if(argc < 2)
		return command_ret > 0 ? command_ret : 0;

	int value = strtol(argv[1], NULL, 0);
	if(value == command_ret)
		printf("Test of %s is passed(ok)!\n", argv[0]);
	else
		printf("Test of %s is not passed!\n", argv[0]);
	return command_ret > 0 ? command_ret : 0;
}
static int pause_cmd(int argc, char* argv[])
{
	readkey();
	return 0;
}

static void RegisterDefaultCommand(void)
{
	static int isRegister = 0;
	if(isRegister)
		return;
		
	RegisterGroupCommand("ret", "sys", ret_cmd, "get return value of last command");
	RegisterGroupCommand("print", "sys", print_cmd, "print text");
	RegisterGroupCommand("test", "sys", test_cmd, "test return value of last command");
	RegisterGroupCommand("pause", "sys", pause_cmd, "pause util press enter key");
	RegisterGroupCommand("exit", "sys", exit_cmd, "exit the program");
	RegisterGroupCommand("help", "sys", show_cmd, "show this page");
	isRegister = 1;
}

2.2.7 ExecCommand/CommandReturnValue

void ExecCommand(const char* cmdString) 
{
	printf("\n%s\n", cmdString); 
	ParseCommand(cmdString); 
}
int CommandReturnValue(void)
{
	return command_ret;
}

2.2.8 ShowCommands

void ShowCommands(const char* group)
{
	uint32_t i;
	uint32_t count = 0;

	RegisterDefaultCommand();
	if(!group || !strlen(group))
	{
		for(i = 0; i < COMMAND_MAX_SIZE; i++)
		{
			if(commands[i].handler)
			{
				printf("%-10s: %s\n", commands[i].cmd, commands[i].help);
				count++;
			}
		}
		printf("\nshow %u commands\n", count);	
	}
	else
	{
		for(i = 0; i < COMMAND_MAX_SIZE; i++)
		{
			if(commands[i].handler 
				&& commands[i].group 
				&& strcmp(commands[i].group, group) == 0)
			{
				printf("%-10s: %s\n", commands[i].cmd, commands[i].help);
				count++;
			}
		}
	}
}

2.2.9 WaitForCommand

int WaitForCommand(void)
{
	int value;
	uint8_t len = 0;
	uint8_t cursor = 0;

	RegisterDefaultCommand();
	while(runFlag)
	{
		char *line_read = readline(CMD_PROMPT_STR);
		if(!line_read)
			continue;
		ParseCommand(line_read);
		free(line_read);
	}
	return 0;
}

2.2.10 readline/readkey/add_history

有readline库情况下,直接调用readline库的readline和add_history接口。

没有readline库情况下如下简单实现readline和add_history:

#define CMDLINE_MAX_LEN 1024
char *readline(const char* prompt)
{
	static char cmdline[CMDLINE_MAX_LEN + 1];
	memset(cmdline, 0, sizeof(cmdline));
	printf("%s", prompt);
	fgets(cmdline, CMDLINE_MAX_LEN, stdin);
	cmdline[strlen(cmdline) - 1] = 0;
	return strdup(cmdline);
}

void readkey(void)
{
	static char keys[CMDLINE_MAX_LEN + 1];
	fgets(keys, CMDLINE_MAX_LEN, stdin);
}

int add_history(const char* line)
{
	return 0;
}

3 实例

#include "command.h"
#include <stdio.h>

static int test_args(int argc, char* argv[])
{
	int i;
	for(i = 0; i < argc; i++)
	{
		printf("%s", argv[i]);
		if(i != argc - 1)
			printf(" ");
		else
			printf("\n");
	}
	return 0;
}

static int test_return(int argc, char* argv[])
{
	if(argc < 1)
		return -1;
	return 0;
}

int main(int argc, char* argv[])
{
	RegisterCommand("test1", test_args, "test1 arg1 arg2 ...");
	RegisterCommand("test2", test_return, "test2 arg1");
	WaitForCommand();
	return 0;
}

运行结果:

CMD->help
test1     : test1 arg1 arg2 ...
test2     : test2 arg1
ret       : get return value of last command
print     : print text
test      : test return value of last command
pause     : pause util press enter key
exit      : exit the program
help      : show this page

show 8 commands
CMD->test1 this is a test
this is a test
CMD->test2 a b
CMD->ret
ret = 0
CMD->test2

usage: test2 arg1

CMD->ret
ret = -1
CMD->exit
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flysnow010

你的鼓励就是我最大的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值