三种生成菜单的方式,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; i
item; ++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; i
item; ++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
菜单效果如下:
