三種生成菜單的方式,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
菜單效果如下:
