分享
 
 
 

在Unix系统下用shell制作通用界面

王朝system·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

目前在Unix系统下用shell编写的菜单程序大都还是采用多级菜单的模式,这种模式的弊端在于菜单的层次多,界面本身不直观,而且在编程过程中,将菜单的显示格式和内容以及所调用的子程序包括在菜单主程序中,使得程序只能满足某个方面的需求,菜单程序本身不具备通用性。本程序设计采用了一种新的设计思路,将下拉菜单界面作为二维表格来处理,把下拉菜单的内容以及所调用的子程序名称分别存放在这两个二维表中,通过对表的读取,实现了控制光标移动、选择菜单内容以及调用子程序的目的。采用这种方式编写出来的程序易于维护,通用性强。在程序本身不做任何改动的情况下,可以在同一操作平台中进行任意移植,因而具有广泛的应用价值。这种思维模式并不局限在Unix系统下的shell编程,而且对于像C这样的过程化语言也具有一定的借鉴意义。

设计思路

在下拉菜单制作过程中,整个下拉菜单界面所包含的菜单名称以及所调用的子程序名之间的相互关系构成了二维表,其中子菜单名称和子程序名称作为表的元素,通过选择光标在表中上下左右移动,将表中元素读出来,再进行处理运算,从而达到控制菜单的选择以及子程序调用等目的。 <BR><BR>

<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>

<TBODY>

<TR bgColor=#99ccff>

<TD colSpan=5><FONT class=a14>表1 菜单项</FONT></TD></TR>

<TR bgColor=#efefef>

<TD>菜单1</TD>

<TD>菜单2</TD>

<TD>菜单3</TD>

<TD>……</TD>

<TD>菜单n</TD></TR>

<TR bgColor=#efefef>

<TD>菜单11</TD>

<TD>菜单12</TD>

<TD>菜单13</TD>

<TD>……</TD>

<TD>菜单1n</TD></TR>

<TR bgColor=#efefef>

<TD>菜单21</TD>

<TD>菜单22</TD>

<TD>菜单23</TD>

<TD> </TD>

<TD>菜单2n</TD></TR>

<TR bgColor=#efefef>

<TD>菜单31</TD>

<TD>菜单32</TD>

<TD>菜单33</TD>

<TD> </TD>

<TD>菜单3n</TD></TR>

<TR bgColor=#efefef>

<TD>……</TD>

<TD> </TD>

<TD> </TD>

<TD> </TD>

<TD> </TD></TR>

<TR bgColor=#efefef>

<TD>菜单m1</TD>

<TD>菜单m2</TD>

<TD>菜单m3</TD>

<TD> </TD>

<TD>菜单mn</TD></TR></TBODY></TABLE><BR><BR>

<TABLE cellSpacing=1 cellPadding=3 width="90%" align=center border=0>

<TBODY>

<TR bgColor=#99ccff>

<TD colSpan=5><FONT class=a14>表2 对应各菜单项的子程序</FONT></TD></TR>

<TR bgColor=#efefef>

<TD>子程序11</TD>

<TD>子程序12</TD>

<TD>子程序13</TD>

<TD>……</TD>

<TD>子程序1n</TD></TR>

<TR bgColor=#efefef>

<TD>子程序21</TD>

<TD>子程序22</TD>

<TD>子程序23</TD>

<TD> </TD>

<TD>子程序2n</TD></TR>

<TR bgColor=#efefef>

<TD>子程序31</TD>

<TD>子程序32</TD>

<TD>子程序33</TD>

<TD> </TD>

<TD>子程序3n</TD></TR>

<TR bgColor=#efefef>

<TD>……</TD>

<TD> </TD>

<TD> </TD>

<TD> </TD>

<TD> </TD></TR>

<TR bgColor=#efefef>

<TD>子程序m1</TD>

<TD>子程序m2</TD>

<TD>子程序m3</TD>

<TD> </TD>

<TD>子程序mn</TD></TR></TBODY></TABLE>

从上面的两个表中不难看出除表1中的第一行为标题行(菜单栏),表1与表2有相同结构,两个表之间的元素存在着一一对应的关系,即每个菜单名称下对应着所调用的程序名(备注: 由于每个菜单标题栏下的子菜单的内容是不一样的,因而每个子菜单下的菜单数目也各不相同,表中一些元素可以是空值,它表示在此没有菜单选择项)。

文中介绍方法的技术难点在于选择光标位置与实际光标位置的关系。所谓选择光标位置是指在上下左右键的控制下,光标在菜单界面的位置,也就是光标在表中的行和列的位置。而实际光标位置是指光标在计算机屏幕上的实际位置。如何通过选择光标位置计算出实际光标位置是本程序的一个难点。本程序的处理办法是将选择光标的行列位置分别作为计算函数的参数,通过函数计算出实际光标的位置。

实现步骤

先将菜单的内容按照一定的格式显示在计算机屏幕上。显示格式要依据表的结构与内容而定,而不能固定不变。如果事先固定下来,会使显示格式与内容之间产生矛盾,难以达到相互之间的统一,程序就不具备通用性。

选择光标在菜单栏左右移动确定选择项目的同时将菜单栏下所包含的子菜单内容显示出来。菜单栏最右端的菜单选择项一般情况下表示“退出”,当选择光标处于这个位置时,回车后退出整个菜单的选择。

在菜单栏中回车或按↓键进入菜单栏下一级子菜单,按照所显示的子菜单内容,选择光标上下移动确定所选定的子菜单内容,回车执行所调用的子程序,←、→两个键退出子菜单的选择。

需要说明的是由于在Unix系统中,光标在上下左右移动时, Unix系统的read命令无法捕获←、↑、→、↓键的控制字符,无法对光标进行有效的控制,为了获取移动光标的控制字符,这里需要用C语言编写一个函数,其主要功能是在光标进行上下左右移动时,能够准确地返回←、↑、→、↓控制键的ASCII值,函数名为getchar。

程序分析

由于光标移动过程中涉及光标的行列位置等重复运算,运用函数可减少程序自身的长度,使程序变得短小、精悍。这里涉及以下一些函数:

1. 画框函数

前面提到显示格式依据表的结构而定,对菜单的边框长度的设置不能固定不变,它要依据菜单标题栏的长度以及标题栏的标题个数而定。这个函数的功能就是依据菜单界面宽度画边框,参数$1表示边框的横线与竖线。

menu_x()

{

_R=$1

col_x=1

while [ col_x -le ${S_LENGTH} ]

do

if [ $_R ]; then echo $_R“\c”

else echo “\c”

fi

col_x=‘expr $col_x + 2'

done

}

2. 计算实际光标在屏幕上的行列位置函数

选择光标在标题栏左右移动的过程中,需要计算光标在屏幕上的实际位置,通过这个函数可以准确地计算出这个实际位置。其运算过程是将选择光标在表中的行列位置作为函数的参数,依据这两个参数计算出光标在屏幕上的准确位置,并将选择光标按照计算出的位置在屏幕上准确显示。其中变量SCREEN- CUR表示表1的元素内容,也就是菜单界面的菜单名称,变量SCREEN-R和SCREEN-C分别表示实际光标在屏幕上位置。执行的结果是将选择光标的内容按实际光标的位置显示在屏幕上。

menu_c()

{ _C=$1 # 选择光标在菜单界面的列位置

_R=$2 # 选择光标在菜单界面的行位置

SCREEN_CUR=‘awk -F“|” “NR==$_R { print }”menu|cut -d“|” -f$_C'

if [ $_C -gt 1 ]; then F_C=‘expr $_C - 1'

SCREEN_LENG=‘head -1 menu| cut -d“|” -f0-$F_C|sed -e ‘s/|//g' \ | awk ‘{ print length($0)}''

else

SCREEN_LENG=0

fi

SCREEN_R=‘expr $_R + 2'

SCREEN_C=‘expr $C_COL + $SCREEN_LENG + 2'

SCREEN_CUR_X=“\033[${SCREEN_R};${SCREEN_C}H${SCREEN_CUR}”

}

3. 计算选择光标在移动过程中位置的函数

选择光标在上下左右的移动过程中,其在菜单界面的位置也随之发生变化,需要通过运算,以确定选择光标在菜单界面的准确位置。其中参数$1表示上下左右键所返回的ASCII值,当参数$1等于2或3时,表示选择光标在上移或左移; 等于1或4时表示选择光标在向下移动或向右移动。参数$2表示选择光标移动过程中在表1中的位置,参数$3表示选择光标移动过程中所限定的区间范围。

menu_x_y()

{ _Z=$1

_S=$2

_L=$3

case $_Z in

2|3) if [ $_S -gt 1 ]

then _S=‘expr $_S - 1'

else _S=$_L

fi ;;

1|4) if [ $_S -lt $_L ]

then _S=‘expr $_S + 1'

else _S=1

fi;;

esac

return $_S

}

4. 计算菜单界面每个菜单栏下的菜单数目函数

通常情况下每个菜单标题下所包含的内容是不一样的,因而每个菜单栏下菜单的数目也是不相同的,需要对每个菜单栏下的菜单数目进行计算,参数$1表示选择光标在菜单栏下的列位置。

menu_row_number()

{ _H=$1

S_NUMBER=‘cut -d“|” -f$_H menu|sed -e ‘s/ //g'-e ‘/^$/d'|\ awk ‘END { print NR}''

}

5. 执行子程序函数

子程序名存在prg文件中,表2中的元素就是子程序名。调用子程序的过程实际就是根据选择光标在菜单界面的行列位置将相应位置的元素读出来,然后依据表2所提供的程序名判断是否真实存在,如果存在则执行。

menu_prg()

{ _C=$1 # 选择光标在菜单界面的列位置

_R=$2 # 选择光标在菜单界面的行位置

prg_name=‘awk -F“|” “NR==$_R { print }” prg|cut -d“|” -f$_C'

if [ -s $prg_name ]

then

eval $prg_name

# 执行所调用的子程序

else

echo “\007”

fi

}

下面是主程序:

# 设置菜单界面前景与背景颜色

COLOR1=“\033[32;44;1m” # 菜单界面的前景色

COLOR2=“\033[33;45;1m” # 菜单界面的背景色

COLOR3=“\033[37;40;1m” # 选择光标的颜色

# 对程序中所用的一些变量进行初始化设置

CUR_R=1 #选择光标在菜单界面的行位置

CUR_C=1 #选择光标在菜单界面的列位置

S_LENGTH=‘head -1 menu|sed -e ‘s/|//g' | \ awk ‘{ print length($0)}''

# 确定菜单界面的宽度

S_MENU=‘head -1 menu| \ awk -F“|” ‘{ print NF}''

# 确定菜单标题栏的字段数

C_COL=‘expr \( 80 - $S_LENGTH - 4 \) / 2 ' # 确定菜单界面的起始位置

echo ${COLOR1}; clear # 按格式显示菜单界面

row=2 # 显示行 [2-23]

while [ row -le 23 ]

do

case $row in

2) echo “\033[${row};${C_COL}H┏\c”; menu_x “━”; echo “┓” ;;

3) echo “\033[${row};${C_COL}H┃\c”;

head -1 menu |sed -e ‘s/|//g' |awk ‘{ print $0 “┃” }';;

23) echo “\033[${row};${C_COL}H┗\c”; menu_x “━”; echo “┛\c” ;;

*) echo “\033[${row};${C_COL}H┃\c”; menu_x “ ”; echo “┃” ;;

esac

row=‘expr $row + 1'

done

while true

do

menu_c $CUR_C $CUR_R # 计算选择光标的位置

echo “${COLOR2}${SCREEN_CUR_X}\c”

stty -echo

getchar # 等待选择

ANS_X=$? # 返回ASCII值

stty echo

echo “${COLOR1}${SCREEN_CUR_X}\c”

case $ANS_X in

3|4) menu_x_y $ANS_X $CUR_C $S_MENU

#选择光标在菜单标题栏中左右移动

CUR_C=$?;;

1|10) if [ $CUR_C = $S_MENU ] #按回车键或↓键进入子菜单

then setcolor -n ; clear; break

fi

menu_row_number $CUR_C

# 在菜单标题栏下将所包含子菜单内容显示在屏幕上

row=2

while [ row -le ${S_NUMBER} ]

do

menu_c $CUR_C $row

echo “${COLOR3}${SCREEN_CUR_X}\c”

row=‘expr $row + 1 '

done

while true do

menu_c $CUR_C $CUR_R

echo “${COLOR2}${SCREEN_CUR_X}\c”

stty -echo

getchar

ANS_Y=$?

stty echo

echo “${COLOR3}${SCREEN_CUR_X}\c”

case $ANS_Y in

1|2) menu_x_y $ANS_Y $CUR_R $S_NUMBER #上下移动选择光标

CUR_R=$?;;

3|4) menu_x_y $ANS_Y $CUR_C $S_MENU #左右移动选择光标退出子菜单选择

CUR_C=$?

CUR_R=1

break;;

10) menu_prg $CUR_C $CUR_R;; #回车后执行子程序

*) echo “\007”;;

esac

done

;;

*) echo “\007\c”;;

esac

done

小结

本文所论述的是如何在Unix系统下利用shell制作通用的下拉菜单。这种通用性集中体现在实现了菜单下的菜单名称以及所调用的子程序名称与菜单主程序的分离,菜单界面下子菜单名称以及所调用的子程序名称分别存放在两个文本文件中,主程序通过对这两个文件的读取实现了菜单程序的正确显示与选择功能。只要对这两个文本文件进行编辑,不需要对主程序进行任何改动,即可完成Unix系统下拉菜单的制作,使得菜单制作非常快捷、灵活。同时可以很方便地进行移植,因而有较强的通用性。而且采用这种方式制作出来的下拉菜单界面比较直观、明了,操作起来更加简单、方便。

备注:在编辑menu和prg文件时,子菜单名称和子程序名称是一一对应的关系,所以子菜单与子程序在文件中位置要摆放正确,不能乱放。由于在本程序中awk语句的所有分隔符都是“|”,而不是空格,因而文本文件中的分隔符也是“|”,而不能用空格,这一点在编辑这两个文件时要特别注意。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有