2. 图形化桌面环境中的脚本编程
2.1 创建文本菜单
创建交互式 shell 脚本最常用的方法是使用菜单。提供各种选项可以帮助脚本用户了解脚本能做什么和不能做什么。
通常菜单脚本会清空显示区域,然后显示可用的选项列表。用户可以按下与每个选项关联的字母或数字来选择选项。
shell 脚本菜单的核心是 case 命令。case 命令会根据用户在菜单上的选择来执行特定命令。
2.1.1 创建菜单布局
-----------------------------------------------------------------------------------------------------------------------------------------
创建菜单的第一步显然是决定在菜单上显示哪些元素以及想要显示的布局方式。
在创建菜单前,通常要先清空显示器上已有的内容。这样就能在干净的、没有干扰的环境中显示菜单。
clear 命令用当前终端会话的 terminfo 数据来清理出现在屏幕上的文本。运行 clear 命令之后,可以用 echo 命令来显示菜单元素。
默认情况下,echo 命令只显示可打印文本字符。在创建菜单项时,非可打印字符通常也很有用,比如制表符和换行符。要在 echo 命令中包含这些字符,必
须使用 -e 选项。因此,命令如下:
echo -e "1.\tDisplay disk space"
会生成如下输出行:
1. Display disk space
这极大地方便了菜单项布局的格式化。只需要几个 echo 命令,就能创建一个看上去还行的菜单。
clear
echo
echo -e "\t\t\tSys Admin Menu\n"
echo -e "\t1. Display disk space"
echo -e "\t2. Display logged on users"
echo -e "\t3. Display memory usage"
echo -e "\t0. Exit menu\n\n"
echo -en "\t\tEnter option: "
最后一行的 -en 选项会去掉末尾的换行符。这让菜单看上去更专业一些,光标会一直在行尾等待用户的输入。
创建菜单的最后一步是获取用户输入。这步用 read 命令。因为我们期望只有单字符输入,所以在 read 命令中用了 -n 选项来限制只读取一个字符。这样
用户只需要输入一个数字,也不用按回车键:
read -n 1 option
接下来,需要创建自己的菜单函数。
2.1.2 创建菜单函数
-----------------------------------------------------------------------------------------------------------------------------------------
shell 脚本菜单选项作为一组独立的函数实现起来更为容易。这样就能创建出简洁、准确、容易理解的 case 命令。
要做到这一点,要为每个菜单选项创建独立的shell函数。创建 shell 菜单脚本的第一步是决定希望脚本执行哪些功能,然后将这些功能以函数的形式放在
代码中。
通常会为还没有实现的函数先创建一个桩函数(stub function, 或者脚手架代码)。桩函数是一个空函数,或者只有一个 echo 语句,说明最终这里里需要
什么内容。
function diskspace {
clear
echo "This is where the diskspace commands will go"
}
这允许菜单在实现某个函数时仍然能正常操作。不需要写出所有函数之后才能让菜单投入使用。函数从 clear 命令开始。这样就能在一个干净的屏幕上执行
该函数,不会受到原先菜单的干扰。
还有一点有助于制作 shell 脚本菜单,那就是将菜单布局本身作为一个函数来创建。
function menu {
clear
echo
echo -e "\t\t\tSys Admin Menu\n"
echo -e "\t1. Display disk space"
echo -e "\t2. Display logged on users"
echo -e "\t3. Display memory usage"
echo -e "\t0. Exit program\n\n"
echo -en "\t\tEnter option: "
read -n 1 option
}
这样一来,任何时候都能调用 menu 函数来重现菜单。
2.1.3 创建菜单函数
-----------------------------------------------------------------------------------------------------------------------------------------
现在已经建好了菜单布局和函数,只需要创建程序逻辑将二者结合起来就行了,这需要用到case命令。
case 命令应该根据菜单中输入的字符来调用相应的函数。用默认的 case 命令字符(*星号)来处理所有不正确的菜单项是种不错的做法。
下面的代码展示了典型菜单中 case 命令的用法:
menu
case $option in
0)
break ;;
1)
diskspace ;;
2)
whoseon ;;
3)
memusage ;;
*)
clear
echo "Sorry, wrong selection";;
esac
这段代码首先用 menu 函数清空屏幕并显示菜单。menu 函数中的 read 命令会一直等待,直到用户在键盘上键入了字符。然后,case 命令就会接管余下的
处理过程。case 命令会基于返回的字符调用相应的函数。在函数运行结束后,case 命令退出。
2.1.4 整合 shell 脚本菜单
-----------------------------------------------------------------------------------------------------------------------------------------
现在已经看到了构成shell脚本菜单的各个部分,将它们组合在一起,看看彼此之间是如何协作的。
示例:
[devalone@devalone 18]$ cat menu1.sh
#!/bin/bash
# simple script menu
function diskspace {
clear
df -h
}
function whoseon {
clear
who
}
function memusage {
clear
cat /proc/meminfo
}
function menu {
clear
echo
echo -e "\t\t\tSys Admin Menu\n"
echo -e "\t1. Display disk space"
echo -e "\t2. Display logged on users"
echo -e "\t3. Display memory usage"
echo -e "\t0. Exit program\n\n"
echo -en "\t\tEnter option: "
read -n 1 option
}
while [ 1 ]
do
menu
case $option in
0)
break ;;
1)
diskspace ;;
2)
whoseon ;;
3)
memusage ;;
*)
clear
echo "Sorry, wrong selection" ;;
esac
echo -en "\n\n\t\t\tHit any key to continue"
read -n 1 line
done
clear
运行:[devalone@devalone 18]$ ./menu1.sh
Sys Admin Menu
1. Display disk space
2. Display logged on users
3. Display memory usage
0. Exit program
Enter option:
这个菜单创建了三个函数,利用常见的命令提取 Linux 系统的管理信息。它使用 while 循环来一直菜单,除非用户选择了选项 0,这时,它会用 break
命令来跳出 while 循环。
2.1.5 使用 select 命令
-----------------------------------------------------------------------------------------------------------------------------------------
可能已注意到,创建文本菜单的一半工夫都花在了建立菜单布局和获取用户输入。bash shell提供了一个很容易上手的小工具,帮助我们自动完成这些工作。
select 命令只需要一条命令就可以创建出菜单,然后获取输入的答案并自动处理。select 命令的格式如下:
select variable in list
do
commands
done
list 参数是由空格分隔的文本选项列表,这些列表构成了整个菜单。select 命令会将每个列表项显示成一个带编号的选项,然后为选项显示一个由 PS3
环境变量定义的特殊提示符。
示例:
[devalone@devalone 18]$ cat smenu1.sh
#!/bin/bash
# using select in the menu
function diskspace {
clear
df -h
}
function whoseon {
clear
who
}
function memusage {
clear
cat /proc/meminfo
}
PS3="Enter option:"
select option in "Display disk space" "Display logged on users" "Display memory usage" "Exit program"
do
case $option in
"Exit program")
break ;;
"Display disk space")
diskspace ;;
"Display logged on users")
whoseon ;;
"Display memory usage")
memusage ;;
*)
clear
echo "Sorry, wrong selection" ;;
esac
done
clear
运行:
[devalone@devalone 18]$ ./smenu1.sh
1) Display disk space 3) Display memory usage
2) Display logged on users 4) Exit program
Enter option:4
在使用 select 命令时,存储在变量中的结果值是整个文本字符串而不是跟菜单选项相关联的数字。文本字符串值才是要在 case 语句中进行比较的内容。
2.2 制作窗口
-----------------------------------------------------------------------------------------------------------------------------------------
dialog 包最早是由 Savio Lam 创建的一个小巧的工具,现在由 Thomas E. Dickey 维护。该包能够用 ANSI 转义控制字符在文本环境中创建标准的窗口对
话框。可以将这些对话框融入自己的 shell 脚本中,借此与用户进行交互。
NOTE:
-------------------------------------------------------------------------------------------------------------------------------------
并非在所有的 Linux 发行版中都会默认安装 dialog 包。如果未安装可以根据自己系统包管理器功能安装,下面是 Fedora 24 安装指令:
[devalone@devalone ~]$ sudo dnf install dialog
2.2.1 dialog 包
-----------------------------------------------------------------------------------------------------------------------------------------
dialog 命令使用命令行参数来决定生成哪种窗口部件(widget)。部件是 dialog 包中窗口元素类型的术语。dialog 包支持的部件类型如下表所示:
dialog 部件
+---------------+--------------------------------------------------------------------------------
| 部 件 | 描 述
+---------------+--------------------------------------------------------------------------------
| calendar | 提供选择日期的日历
+---------------+--------------------------------------------------------------------------------
| checklist | 显示多个选项(其中每个选项都能打开或关闭)
+---------------+--------------------------------------------------------------------------------
| form | 构建一个带有标签以及文本字段(可以填写内容)的表单
+---------------+--------------------------------------------------------------------------------
| fselect | 提供一个文件选择窗口来浏览选择文件
+---------------+--------------------------------------------------------------------------------
| gauge | 显示完成的百分比进度条
+---------------+--------------------------------------------------------------------------------
| infobox | 显示一条消息,但不用等待回应
+---------------+--------------------------------------------------------------------------------
| inputbox | 提供一个输入文本用的文本表单
+---------------+--------------------------------------------------------------------------------
| inputmenu | 提供一个可编辑的菜单
+---------------+--------------------------------------------------------------------------------
| menu | 显示可选择的一系列选项
+---------------+--------------------------------------------------------------------------------
| msgbox | 显示一条消息,并要求用户选择OK按钮
+---------------+--------------------------------------------------------------------------------
| pause | 显示一个进度条来显示暂定期间的状态
+---------------+--------------------------------------------------------------------------------
| passwordbox | 显示一个文本框,但会隐藏输入的文本
+---------------+--------------------------------------------------------------------------------
| passwordform | 显示一个带标签和隐藏文本字段的表单
+---------------+--------------------------------------------------------------------------------
| radiolist | 提供一组菜单选项,但只能选择其中一个
+---------------+--------------------------------------------------------------------------------
| tailbox | 用tail命令在滚动窗口中显示文件的内容
+---------------+--------------------------------------------------------------------------------
| tailboxbg | 跟tailbox一样,但是在后台模式中运行
+---------------+--------------------------------------------------------------------------------
| textbox | 在滚动窗口中显示文件的内容
+---------------+--------------------------------------------------------------------------------
| timebox | 提供一个选择小时、分钟和秒数的窗口
+---------------+--------------------------------------------------------------------------------
| yesno | 提供一条带有Yes和No按钮的简单消息
+---------------+--------------------------------------------------------------------------------
要在命令行上指定某个特定的部件,需使用双连字符格式:
dialog --widget parameters
其中 widget 是上表中的部件名,parameters 定义了部件窗口的大小以及部件需要的文本。
每个dialog部件都提供了两种形式的输出:
□ 使用STDERR
□ 使用退出状态码
可以通过 dialog 命令的退出状态码来确定用户选择的按钮。如果选择了 Yes 或 OK 按钮,dialog 命令会返回退出状态码 0。如果选择了 Cancel 或 No
按钮,dialog 命令会返回退出状态码 1。可以用标准的 $? 变量来确定 dialog 部件中具体选择了哪个按钮。
如果部件返回了数据,比如菜单选择,那么 dialog 命令会将数据发送到 STDERR。可以用标准的 bash shell 方法来将 STDERR 输出重定向到另一个文件
或文件描述符中。
dialog --inputbox "Enter your age:" 10 20 2>age.txt
这个命令会将文本框中输入的文本重定向到 age.txt 文件中。
■ msgbox 部件
-----------------------------------------------------------------------------------------------------------------------------------------
msgbox 部件是对话框中最常见的类型。它会在窗口中显示一条简单的消息,直到用户单击 OK 按钮后才消失。使用 msgbox 部件时要用下面的格式。
dialog --msgbox text height width
text 参数是想在窗口中显示的字符串。dialog 命令会根据由 height 和 width 参数创建的窗口的大小来自动换行。如果想在窗口顶部放一个标题,也可以
用 --title 参数,后接作为标题的文本。
示例:
dialog --title Testing --msgbox "This is a test" 10 20
如果终端仿真器支持鼠标,可以单击 OK 按钮来关闭对话框。也可以用键盘命令来模拟单击动作——按下回车键。
■ yesno 部件
-----------------------------------------------------------------------------------------------------------------------------------------
yesno 部件进一步扩展了 msgbox 部件的功能,允许用户对窗口中显示的问题选择 yes或 no。它会在窗口底部生成两个按钮:一个是 Yes,一个是No。用户
可以用鼠标、制表符键或者键盘方向键来切换按钮。要选择按钮的话,用户可以按下空格键或者回车键。
示例:
dialog --title "Please answer" --yesno "Is this