子菜单
shell 允许选择循环中的任何语句成为另一个选择循环。
这使我们能够创建具有一个或者多个子菜单的菜单。
我们通常在子菜单上的一个或者多个选项的操作中包含一个 break 语句,以退出子菜单循环并返回到外部菜单循环。
因为 PS3 变量也用于任何子菜单中的提示,所以在从一个菜单移动到另一个菜单时要小心,以将此变量重置为我们要移动到的菜单的提示。
接下来提供一个说明子菜单和重置 PS3 变量的示例。
PS3 保留变量
在为用户显示菜单选项列表后,shell 会打印提示并等待用户输入。
提示值为 PS3 变量的值。
我们可以将此变量设置为任何值,但如果该值对于呈现给用户的选择有意义,则它会有所帮助。
有时,脚本会产生如此多的输出——从菜单列表第一次显示到菜单提示显示之间——以至于用户不再能够看到(或者记住)菜单选项。
为了重新显示菜单选项,用户按下回车键以响应菜单提示。
select 语法创建一个循环。
用户会反复看到菜单选项,然后是提示(PS3 变量的值)。
当用户键入系统的 EOF 字符作为对菜单提示的响应时,选择循环将退出。
Korn shell 中 PS3 变量的默认值是 #?。
因此,如果设置 PS3 变量失败,系统将使用字符串 #?作为提示,然后等待用户输入。
如果发生这种情况,用户可能不知道脚本正在等待输入。
退出select循环
用户可以随时通过在他们的系统上输入 EOF 字符来退出选择循环;但是,除非用户知道这一点,或者我们在菜单提示中包含解释这一点的消息,否则用户可能会卡在选择循环中。
在菜单选项中包含一个字符串以帮助用户退出菜单。
该字符串可以简单地表示退出菜单。
然后,此菜单选项的 case 语句中的操作将是 break 语句。
break 语句退出 select 循环,脚本的执行移动到 select/do/done 语法中 done 语句之后的第一个语句。
如果 done 语句之后没有语句,则脚本终止。
$ cat menu1.ksh #!/bin/ksh # Script name: menu1.ksh PS3="Enter the number for your fruit choice: " select fruit in apple orange banana peach pear "Quit Menu" do case $fruit in apple) print "An apple has 80 calories." ;; orange) print "An orange has 65 calories." ;; banana) print "A banana has 100 calories." ;; peach) print "A peach has 38 calories." ;; pear) print "A pear has 100 calories." ;; "Quit Menu") break ;; *) print "You did not enter a correct choice." ;; esac done
$ ./menu1.ksh 1) apple 2) orange 3) banana 4) peach 5) pear 6) Quit Menu Enter the number for your fruit choice: 3 A banana has 100 calories. Enter the number for your fruit choice: 6
Korn shell 中的 select 语句创建一个菜单。
此构造仅适用于 Korn shell。
创建菜单的语法是:
select var in list do statement1 ... statementN done
变量 var 和 list 遵循 for 循环中使用的相同语法规则(尽管 select 循环的操作有很大不同)。
如果列表包含元字符,则元字符将扩展为文件名。
列表中的元素是呈现给用户的菜单选项。
Korn shell 会自动为每个菜单选项编号(从 1 开始)。
用户必须输入与用户选择相对应的数字才能使输入有效。
尽管我们可以在 select 循环中使用任何语句,但 case 语句最常用于匹配用户选择的选项并根据该选项执行某些操作。
用户提供的输入保存在 REPLY 变量中。
如果用户除了按下 Return 键之外没有输入任何输入,则 REPLY 变量被设置为空字符串,并且 shell 重新显示菜单选项。
当用户键入文件结尾 (EOF) 字符(在大多数系统上是 Control-D)时,将退出选择循环。
使用选择循环的示例
以下脚本为用户提供了五种水果选择,然后提示他们选择水果。
在 select 循环中使用 case 语句对用户选择的选项采取适当的操作。
默认情况下,* 模式匹配用户输入的任何不是菜单上选项数字之一的输入。
然后执行脚本。
显示了几个有效的选择和一些无效的选择来说明选择循环的工作原理。
$ cat menu.ksh #!/bin/ksh # Script name: menu.ksh PS3="Enter the number for your fruit choice: " select fruit in apple orange banana peach pear do case $fruit in apple) print "An apple has 80 calories." ;; orange) print "An orange has 65 calories." ;; banana) print "A banana has 100 calories." ;; peach) print "A peach has 38 calories." ;; pear) print "A pear has 100 calories." ;; *) print "Please try again. Use ’1’-’5’" ;; esac done
$ ./menu.ksh 1) apple 2) orange 3) banana 4) peach 5) pear Enter the number for your fruit choice: 3 A banana has 100 calories. Enter the number for your fruit choice: 7 Please try again. Use ’1’-’5’ Enter the number for your fruit choice: apple Please try again. Use ’1’-’5’ Enter the number for your fruit choice: 1 An apple has 80 calories. Enter the number for your fruit choice: ^d
使用子菜单的示例
下面的 submenu.ksh 示例脚本首先创建两个变量 main_prompt 和descert_prompt,分别用于保存主菜单和甜点子菜单的提示。
当用户选择 Order Completed 时,将退出主菜单的选择循环,此时脚本会打印消息请享用餐点。
当用户选择主菜单选择甜点时,会出现一个子菜单,要求用户输入甜点选择的编号。
在甜点菜单的选择循环的 case 语句中,break 语句用于每个菜单选择。
(默认选择不使用 break 语句,因为我们希望用户重新输入甜点选择。
)这将退出甜点菜单。
然后菜单提示重置为 main_prompt 变量的值,并且控制返回到主(外部)选择循环。
所有打印语句都使用单引号,以便打印价格中的 $。
此外,由于本类书中的格式,以下脚本中有几行换行。
$ cat submenu.ksh #!/bin/ksh # Script name: submenu.ksh main_prompt="Main Menu: What would you like to order? " dessert_menu="Enter number for dessert choice: " PS3=$main_prompt select order in "broasted chicken" "prime rib" "stuffed lobster" dessert "Order Completed" do case $order in "broasted chicken") print 'Broasted chicken with baked potato, rolls, and salad is .95.' ;; "prime rib") print 'Prime rib with baked potato, rolls, and fresh vegetable is .95.' ;; "stuffed lobster") print 'Stuffed lobster with rice pilaf, rolls, and salad is .95.' ;; dessert) PS3=$dessert_menu select dessert in "apple pie" "sherbet" "fudge cake" "carrot cake" do case $dessert in "apple pie") print 'Fresh baked apple pie is .95.' break ;; "sherbet") print 'Orange sherbet is .25.' break ;; "fudge cake") print 'Triple layer fudge cake is .95.' break ;; "carrot cake") print 'Carrot cake is .95.' break;; *) print 'Not a dessert choice.' ;; esac done PS3=$main_prompt ;; "Order Completed") break ;; *) print 'Not a main entree choice.';; esac done print 'Enjoy your meal.'
$ ./submenu.ksh 1) broasted chicken 2) prime rib 3) stuffed lobster 4) dessert 5) Order Completed Main Menu: What would you like to order? 3 Stuffed lobster with rice pilaf, rolls, and salad is .95. Main Menu: What would you like to order? 4 1) apple pie 2) sherbet 3) fudge cake 4) carrot cake Enter number for dessert choice: 3 Triple layer fudge cake is .95. Main Menu: What would you like to order? 5 Enjoy your meal.