在Linux下实现一个简单的命令 交互UI(类shell交互界面)

注:本人虽然学习计算机已经很多年了,但是一直没有写过技术文章,从今天开始,要把自己知道的做一些分享。

写作缘由:

为已经开发的系统提供一个命令操作界面以方便使用人员操作。

基本功能:

1、实现help、clear、dump、exit这些命令,这里仅作为示例;

2、简单的行文本编辑功能(光标的左右移动、backspace删除字符、上下方向键显示历史命令);

3、回车即完成一次命令的读取,然后解析命令、执行操作、再次回到命令接收状态;

原理:

很简单,只要有些计算机的编程功底,基本上都能想到实现的思路;

即依靠一个循环来接收命令、解析命令、执行操作、再次返回命令接收界面。

实现:

通过调研,发现curses.h基本上能够实现所有的窗口操作,因此本人暂时选择了curses.h,该头文件是libncurses5-dev包中带有的,本人的系统是centos5.5,系统自带相关的头文件和库文件,同时也尝试过在ubuntu11.04上安装,只要下载相应的安装包即可,下面就介绍本人的开发步骤:

1、数据结构

//行编辑器,用于shell命令行的输入,其实shell命令行就相当于一个行编辑器,目前本程序支持一行在512字符内的编辑操作
struct line_editor
{
char chars[512];//字符内容
int tail;//内容结尾游标
int cur;//当前光标前字符的游标
};
//命令节点,用于存储历史命令,使用双链表实现
struct Cmd_Node
{
char* cmd_p;//命令内容指针
struct Cmd_Node* pre;//前一个命令
struct Cmd_Node* next;//后一个命令
int len;//命令内容的长度
};

//链表结构
struct Cmd_List
{
struct Cmd_Node* head;//链表的头指针
struct Cmd_Node* tail;//链表的尾指针
};
//支持的命令集
char* cmd_set[]={
"run",
"exit",
"help",
"clear",
"dump"
};
static struct Cmd_List cmd_list; //代表历史命令链表
2、接口函数

int get_cmd();//获取命令输入内容
void shell_exit();//shell初始化函数
void shell_init();//shell退出函数

3、辅助函数

static void hprintf(char* fmt,...);//封装所有的print操作,即如果想在屏幕上输出,就调用此函数,用printf会出问题
static void mem_dump(char* args);//实现dump操作的函数
static void run(const char* cmd_p);//实现run操作的函数
static void help();//实现help操作的函数
//clear如果用system("clear")实现的话会出问题,因此还是使用curses中的clear()函数
//exit调用exit即可
4、其实最核心的就是实现一个行编辑器就OK,下面我们就开始吧,先介绍一下思路:

要实现一个shell行编辑器要考虑的因素如下:

a、支持的字符集(所有可见字符)

b、支持的编辑操作,比如左右光标的移动、backspace删除字符

c、 上下方向键切换历史命令

d、回车结束行编辑,返回输入内容

         输入内容再由命令解释程序分析,然后再调用相应的子程序,那么我们就先说一下怎么实现一个行编辑器吧,其实这是一个数据结构和屏幕操作相互结合的操作

        先说下算法吧,如下:

a、如果是可见字符,那么就插入光标所在的位置,光标及光标之后的字符后移一个字符

b、如果是backspace,那么删除光标之前的那个字符,同时光标及光标之后的字符前移一个字符,并且光标也要前移一个字符

c、如果是左方向键,那么光标左移一个字符,如果是右方向键,那么光标右移一个字符

d、如果是向上方向键,那么历史命令链表中的游标前移一个节点,清除当前命令行上的内容,显示历史命令中的内容,光标移动到末尾

e、如果是向下方向键,那么历史命令链表中得游标后移一个节点,清除当前命令行上的内容,显示历史命令中的内容,光标移动到末尾

d、如果是回车,那么就结束输入循环,创建一个命令节点,将该节点插入到历史命令链表的尾部后返回命令中字符的个数

   接下来就上代码:

int get_cmd()
{
	struct line_editor l;
	memset(l.chars,0,512);
	l.tail=-1;
	l.cur=-1;
	
	int c=' ';
	int x,y;
	struct Cmd_Node* cmd_cur=NULL;
	while(1)
	{
		c=getch();
		if(c=='\n')
		{
			break;	
		}
		// for normal characters
		else if(c >= 0x20 && c <= 0x7E)	
		{
			if(l.cur>=l.tail)
			{
				l.cur++;
				l.chars[l.cur]=c;
				l.tail=l.cur;
				l.chars[l.tail+1]='\0';
			}
			else
			{
				//for data structure
				l.cur++;
				memmove(&(l.chars[l.cur])+1,&(l.chars[l.cur]),l.tail-l.cur+1);
				l.tail++;
				l.chars[l.cur]=c;
				l.chars[l.tail+1]='\0';

				//for screen edit
				getyx(stdscr,y,x);
				mvaddstr(y,x+1,&(l.chars[l.cur])+1);
				move(y,x);
				refresh();
			}
			echochar(c);
			refresh();
		}
		//for backspace
		else if(c==263)	
		{
			if(l.cur>=0)
			{
				if(l.cur")+l.tail+1,' ');
				refresh();
				mvaddstr(y,x-1,&(l.chars[l.cur])+1);	
				move(y,--x);
				refresh();
			}
		}
		// for arrow key left
		else if(c==KEY_LEFT)
		{
			if(l.cur>=0)
			{
				getyx(stdscr,y,x);
				move(y,--x);
				refresh();
				l.cur--;
			}
		}
		// for arrow key right
		else if(c==KEY_RIGHT)
		{
			getyx(stdscr,y,x);
			if(l.curpre;
			}
			//if the history command cycle bi-direct list is null
			//and the current position is not the tail
			if(cmd_cur!=NULL)
			{
				getyx(stdscr,y,x);
				int len=l.tail;
				int i=0;
				while(i<=len)
				{
					mvaddch(y,strlen("lte_cmd>")+i++,' ');
				}
				refresh();
				mvaddstr(y,strlen("lte_cmd>"),cmd_cur->cmd_p);
				refresh();
				memcpy(l.chars,cmd_cur->cmd_p,cmd_cur->len);
				l.tail=cmd_cur->len-2;
				l.cur=l.tail;
			}
		}

5、在进行上述操作之前需要初始化一下curse环境,使用结束后需要恢复正常环境以及内存释放操作,代码如下:

void shell_init()
{
	printf("shell_init\n");
	initscr();
	cbreak();
	noecho();
	scrollok(stdscr,TRUE);
	//setscrreg(0,1000);
	//idlok(stdscr,TRUE);
	keypad(stdscr,TRUE);
	refresh();

}
void shell_exit()
{
	struct Cmd_Node* p=cmd_list.head;
	
	nocbreak();
	echo();
	keypad(stdscr,FALSE);
	endwin();
	while(p)
	{
		cmd_list.head=p->next;
		free(p->cmd_p);
		free(p);
		p=cmd_list.head;
	}
	printf("shell exit\n");
}

程序截图:





你可能感兴趣的:(LINUX软件开发)