三种生成菜单的方式,C编写的菜单解析程序、dialog工具、select语法。前两种方式是基于curses库的实现,可以处理光标移动,第三种方式由ksh或bash所提供的select菜单方式实现,不支持光标移动。
第一种方式由两部分组成,C语言编写的菜单解析程序和shell编写的菜单处理程序。
菜单解析程序getkey.c:
/********************************************************
*程序功能:根据传入的菜单文件和坐标起始位置,显示菜单并
* 获取选中条目,返回选中位置,该程序配合shell编写
* 通用界面,菜单项开始的1. 2. 3. 表示快捷键,
* 只支持单屏显示,暂不支持多屏显示.
*程序参数:argv[1] 全路径菜单文件名 argv[2] 起始行位置
* argv[3] 起始列位置 argv[4] 上次选中菜单项
*程序返回:返回的是选中的菜单编号,正确的菜单编号从1开始,
0表示程序错误退出。
*程序注意:该程序违反一般UNIX程序的返回惯例,主要是为了解决
shell下不能处理负数返回值的问题,0表示出错。
*********************************************************/
#include
#include
#include
#include
#include
/**定义支持的菜单条数和菜单长度**/
#define MAX_ITEM_NUM 40
#define MAX_ITEM_LEN 80
/**菜单项的前缀字符个数**/
#define MENU_OPT_LEN 2
struct Menu
{
int beg_l;/**起始行坐标**/
int beg_c;/**起始列坐标**/
char menu_item[MAX_ITEM_NUM][MAX_ITEM_LEN];/**菜单项**/
int item;/**菜单项数目**/
int lst_sel;/**上次选择菜单项**/
};
void endscr();
int getchoice(const struct Menu* menu);
int get_menu(struct Menu* menu, char* file);
void clear_all_screen();
int draw_menu(const struct Menu* menu, int selected);
/**屏幕最大行坐标和列坐标**/
static int max_l=0,max_c=0;
static int show_error_l=20,show_error_c=0;
static int g_ret=0;
int main(int argc, char *argv[])
{
if(argc<5)
{
printf("usage:%s menu_file line col def_sel\n", argv[0]);
return 0;
}
struct Menu stMenu;
char cMenu[MAX_ITEM_NUM][MAX_ITEM_LEN];
memset(&stMenu, 0x00, sizeof(stMenu));
stMenu.beg_l=atoi(argv[2]);
stMenu.beg_c=atoi(argv[3]);
stMenu.lst_sel=atoi(argv[4]);
initscr();
curs_set(0);/**设置光标不可见**/
getmaxyx(stdscr, max_l, max_c);/**返回的是最大行数和列数**/
max_l-=1;/**行坐标从0开始**/
max_c-=1;/**列坐标从0开始**/
if(stMenu.beg_l<0 || stMenu.beg_l>max_l) stMenu.beg_l=0;
if(stMenu.beg_c<0 || stMenu.beg_c>max_c) stMenu.beg_c=0;
g_ret = get_menu(&stMenu, argv[1]);
if(g_ret){
mvprintw(show_error_l++, show_error_c, "Get menu error!");
endscr();
return 0;
}
if(stMenu.lst_sel<1 || stMenu.lst_sel>stMenu.item) stMenu.lst_sel=1;
int selected = getchoice(&stMenu);
curs_set(0);/**设置光标可见**/
endscr();
return selected;
}
int get_menu(struct Menu* menu, char* file)
{
if(menu==NULL||file==NULL)
{
clear();
mvprintw(show_error_l++, show_error_c, "Func [%s] NULL POINTER!",__func__);
return 1;
}
char cLine[256];
memset(cLine, 0x00, sizeof(cLine));
FILE* fp=fopen(file, "r");
if(fp==NULL){
clear();
mvprintw(show_error_l++, show_error_c, "Open file [%s] error!",file);
return 1;
}
while(fgets(cLine, sizeof(cLine), fp))
{
menu->item++;
if(menu->item>MAX_ITEM_NUM)
{
break;
}
strncpy(menu->menu_item[menu->item-1], cLine, sizeof(menu->menu_item[0])-1);
memset(cLine, 0x00, sizeof(cLine));
}
fclose(fp);
return 0;
}
/*clear_all_screen函数*/
void clear_all_screen()
{
clear();
}
/**0表示程序出错,>0值表示用户选择的菜单序号**/
int getchoice(const struct Menu* menu)
{
if(menu==NULL)
{
clear();
mvprintw(show_error_l++, show_error_c, "Func [%s] NULL POINTER!",__func__);
return 0;
}
int selected=0;
int key = 0;
clear_all_screen();
keypad(stdscr, TRUE);/**打开功能键模式**/
cbreak();
noecho();
selected = menu->lst_sel-1;/**默认选中的菜单项**/
while(1)
{
if(key == KEY_UP)
{
/**默认可循环**/
if(selected<=0){
selected=menu->item-1;
}else{
--selected;
}
}
if(key == KEY_DOWN)
{
/**默认可循环**/
if(selected>=menu->item-1){
selected=0;
}else{
++selected;
}
}else if(key=='\n' || key==KEY_ENTER)/**通过光标选中了某个选项**/
{
break;
}
else
{
for(int i=0; iitem; ++i)
{
if(key==menu->menu_item[i][0])
{
selected=i;
break;
}
}
}
g_ret=draw_menu(menu, selected);
if(g_ret){
mvprintw(show_error_l++, show_error_c, "Draw menu error!");
selected=-1;
break;
}
key = getch();
}
keypad(stdscr, FALSE);
nocbreak();
echo();
return selected+1;
}
int draw_menu(const struct Menu* menu, int selected)
{
if(menu==NULL)
{
clear();
mvprintw(show_error_l++, show_error_c, "Func [%s] NULL POINTER!",__func__);
return 1;
}
int pos_l=menu->beg_l,
pos_c=menu->beg_c;
char item_opt[MENU_OPT_LEN]={0};
for(int i=0; iitem; ++i)
{
memset(item_opt, 0x00, sizeof(item_opt));
strncpy(item_opt, menu->menu_item[i], MENU_OPT_LEN);
mvprintw(pos_l, pos_c, "%s", item_opt);/**选项a,b,c,...不高亮**/
if(i == selected){
attron(A_STANDOUT);/**设置高亮属性**/
}
mvprintw(pos_l++, pos_c+strlen(item_opt), "%s", menu->menu_item[i]+strlen(item_opt));/**不包含选项a,b,c,**/
if(i == selected){
attroff(A_STANDOUT);/**关闭高亮属性**/
}
}
refresh();
return 0;
}
void endscr()
{
refresh();
endwin();
}
通过cc -O2 -o getkey -lcurses getkey.c编译得到getkey可执行程序,以便在菜单处理程序中使用。
shell编写的菜单处理程序:
#!/usr/bin/sh
old_stty_setting=`stty -g`
trap " stty \"$old_stty_setting\";exit 1" 1 2 15
#确认完成返回
confirm_over()
{
echo "\033[22;0H按确认完成\c"
read
}
#确认是否继续
confirm_ok()
{
echo "\033[22;0H按确认执行\c"
read
if [ "$REPLY" = "y" ];then
return 0
else
return 1
fi
}
#选中退出选项
f0()
{
stty "$old_stty_setting"
exit 0
}
#选中第一个菜单
f1()
{
clear
if confirm_ok ;then
#要执行的命令
:
clear
confirm_over
fi
}
#选中第二个菜单
f2()
{
clear
confirm_over
}
#选中第三个菜单
f3()
{
clear
confirm_over
}
#选中第四个菜单
f4()
{
clear
confirm_over
}
MENU_FILE=menu.txt
cat >$MENU_FILE <<-EOF
1.执行批前备份
2.执行日终程序
3.查询日终日志
4.查看系统状态
q.退出菜单
EOF
#定义菜单的响应程序
set -A menu_acts f1 f2 f3 f4 f0
if [ `wc -l<$MENU_FILE` -ne $((${#menu_acts[*]})) ]
then
echo "请检查菜单条目和响应程序是否一致!"
exit 1
fi
while true
do
getkey $MENU_FILE 0 0 "$ans"
ans=$?
#将菜单编号转换为从0开始
m_i=$((ans-1))
if [ $m_i -lt 0 ]
then
exit 1
fi
${menu_acts[$m_i]}
done
菜单效果如下: