注:本人虽然学习计算机已经很多年了,但是一直没有写过技术文章,从今天开始,要把自己知道的做一些分享。
写作缘由:
为已经开发的系统提供一个命令操作界面以方便使用人员操作。
基本功能:
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;
}
}
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");
}
程序截图: