shell 创建文本菜单
创建文本菜单
核心是case命令,根据用户的选择来执行特定的命令。
创建菜单布局
使用echo命令打印字符,生成一个菜单,还可以包括标题等信息:
clearecho# -e 选项,打印特殊字符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"# -en 会去掉末尾的换行符,光标会留在行尾echo -en "\t\tEnter option: "# 获取用户输入,只期望获取到单个字符,-n选择限制只读1个字符,并且用户不用回车read -n 1 option
clear命令,先清空当前会话的内容。
echo -e选项,可以打印特殊字符。
echo -en选项,会去掉末尾的换行符。这让菜单看上去更专业,光标会一直行尾等待用户输入。
获取用户输入
打印出菜单后,就要等待并获取用户输入。这步使用read命令。这里期望只要单个字符,所以用 -n 选项限制只读取一个字符。这样用户只需要输入一个数字,并且不用按回车。
创建菜单函数
把上面的部分封装成一个函数,这样,在任何时候只要调用函数,就能重现菜单。
创建桩函数
桩函数(stub function),是一个空函数,或者只有一个echo语句,说明最终这里需要什么内容:
function diskspace { clear echo "Display disk space"}
这样,就不需要事先写出所有函数。菜单能够直接投入使用,之后在来实现具体的操作。
添加菜单逻辑
菜单布局和函数都有了,下面需要创建程序逻辑将二者结合起来。这里就需要case命令。
case命令根据菜单中输入的字符来调用相应的函数。用默认的case命令字符星号来处理所有不正确的菜单项。
下面就是这个完整的菜单脚本的例子:
#!/bin/bash# 为脚本创建文本菜单# --------------------# 定义函数# --------------------# 打印菜单function menu { clear echo # -e 选项,打印特殊字符 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" # -en 会去掉末尾的换行符,光标会留在行尾 echo -en "\t\tEnter option: " # 获取用户输入,只期望获取到单个字符,-n选择限制只读1个字符,并且用户不用回车 read -n 1 option}function diskspace { clear df -k}function whoseon { clear who}function menusage { clear cat /proc/meminfo}# --------------------# 函数主体# --------------------while [ 1 ]do # 菜单逻辑 menu case $option in 0) break ;; 1) diskspace ;; 2) whoseon ;; 3) menusage ;; *) clear echo "Wrong selection";; esac echo -en "\n\n\t\tHit any key to continue" read -n 1 linedoneclear
菜单的显示效果如下:
Sys Admin Menu 1. Display disk space 2. Display logged on users 3. Display memory usage 0. Exit program Enter option:
使用 select 命令
创建文本菜单的过程中,花了一半的精力在建立菜单布局和获取用户输入上。bash shell 提供了一个很容易上手的小工具,可以帮助自动完成这些工作。
select命令只需要一条命令就可以创建出菜单,然后获取输入并自动处理。命令格式如下:
select 选项变量 in "选项1" "选项2" "选项3"do 命令done
select命令会将每个选项自动加上编号,然后为选项显示一个由PS3环境变量定义的特殊提示符。所以还要定义PS3环境变量。
示例代码
这里是一个select命令的示例:
#!/bin/bash# 为脚本创建文本菜单function diskspace { clear df -k}function whoseon { clear who}function menusage { 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") menusage ;; *) clear echo "Wrong selection";; esacdone
菜单效果如下:
$ menu2.sh 1) Display disk space 3) Display memory usage2) Display logged on users 4) Exit programEnter option:
使用这个工具可以快速的创建一个简易的菜单,不过视觉效果就差很多。
制作窗口(dialog包)
dialog包能够用ANSI转义控制字符在文本环境中创建标准的窗口对话框。
窗口部件
dialog包提供了很多窗口部件(widget),使用的时候命令格式如下:
dialog --widget parameters
常用的部件如下:
- msgbox部件:在窗口中显示一条简单的消息,会有一个OK按钮
- yesno部件:允许用户对窗口中显示的问题选择yes或no,会有两个按钮
- inputbox部件:提供一个简单的文本框区域来输入文本
- textbox部件:可以在窗口中显示大量信息,会生成一个滚动窗口
- menu部件:创建文本菜单,需要为每个选项提供一个选择标号和显示文本
- fselect部件:可以用来浏览文件和文件夹
更多窗口部件,后面会详细列出。
获取部件的输出
每个dialog部件都提供了两种形式的输出:
- 使用退出状态码
- 使用STDERR
返回选项
如果选择了YES或OK,返回退出状态码0。如果选择了Cancel或No,返回退出状态码1。可以用标准的$?变量来判断具体选择了哪个按钮。
返回数据
如果是返回数据,则会将数据发送到STDERR。可以用标准的bash shell方法来将STDERR输出重定向到另一个文件或文件描述符中:
dialog --inputbox "Enter your age: " 10 20 2>age.txt
具体怎么用可以参考下面的示例。
示例代码
内容比较多,感觉也不一定需要制作这么好的窗口来交换。编写脚本时记住两件事:
- 如果有按钮,检查dialog命令的退出状态码
- 否则就重定向STDERR来获取输出的值
前面的例子使用dialog包实现的示例代码:
#!/bin/bash# 为脚本创建文本窗口菜单temp=$(mktemp -t test.XXXXXX)temp2=$(mktemp -t test2.XXXXXX)function diskspace { df -k > $temp dialog --textbox $temp 20 60}function whoseon { who > $temp dialog --textbox $temp 20 50}function menusage { cat /proc/meminfo > $temp dialog --textbox $temp 20 50}while [ 1 ]dodialog --menu "Sys Admin Menu" 20 30 10 \1 "Display disk space" \2 "Display logged on users" \3 "Display memory usage" \0 "Exit program" 2> $temp2if [ $? -eq 1 ]then breakfiselection=$(cat $temp2)case $selection in0) break ;;1) diskspace ;;2) whoseon ;;3) menusage ;;*) dialog --msgbox "Wrong selection" 10 30esacdonerm -f $temp 2> /dev/nullrm -f $temp2 2> /dev/null
这里用到了临时文件,并且使用mktemp命令来创建临时文件,看着挺专业。
安装dialog包
另外系统可能默认没有安装dialog包,要运行这个脚本,需要先安装dialog包:
[root@Ansible ~]# yum info dialog已加载插件:fastestmirrorLoading mirror speeds from cached hostfile * base: mirrors.aliyun.com * extras: mirrors.aliyun.com * updates: mirrors.aliyun.com已安装的软件包名称 :dialog架构 :x86_64版本 :1.2发布 :5.20130523.el7大小 :505 k源 :installed来自源:base简介 : A utility for creating TTY dialog boxes网址 :http://invisible-island.net/dialog/dialog.html协议 : LGPLv2描述 : Dialog is a utility that allows you to show dialog boxes (containing : questions or messages) in TTY (text mode) interfaces. Dialog is called : from within a shell script. The following dialog boxes are implemented: : yes/no, menu, input, message, text, info, checklist, radiolist, and : gauge. : : Install dialog if you would like to create TTY dialog boxes.
dialog部件详表
dialog部件
部件 | 描述 |
---|---|
calendar | 提供选择日期的日历 |
checklist | 显示多个选项(其中每个选项都能打开或关闭) |
form | 构建一个带有标签及文本字段(可以填内容)的表单 |
fselect | 提供一个文件选择窗口来浏览选择文件 |
gauge | 显示完成的百分比进度条 |
infobox | 显示一条消息,但不用等待回应 |
inputbox | 提供一个输入文本用的文本表单 |
inputmenu | 提供一个可编辑的菜单 |
menu | 显示可选择的一系列选项 |
msgbox | 显示一条消息,并要求用户选择OK按钮 |
pause | 显示一个进度条来显示暂停期间的状态 |
passwordbox | 显示一个文本框,但会隐藏输入的文本 |
passwordform | x牧场一个带标签和隐藏文本字段的表单 |
radiolist | 提供一组菜单选项,单只能选择其中一个。就是单选 |
tailbox | 用tail命令在滚动窗口中显示文件的内容 |
tailboxbg | 更tailbox一样,但是在后台模式中运行 |
textbox | 在滚动窗口中显示文件的内容 |
timebox | 提供一个选择小时、分钟和秒的窗口 |
yesno | 提供一条带有Yes和No按钮的简单消息 |
dialog选项
除了标准部件,还可以在dialog命令中定制不同的选项。这些选项可以让你全面定制窗口外观和操作。
dialog命令选项
选项 | 描述 |
---|---|
--add-widget | 继续下个对话框,直到按下Esc或Cancel按钮 |
--aspect ratio | 指定窗口宽度和高度的宽高比 |
--backtitle title | 指定显示在屏幕顶部背景上的标题 |
--begin x y | 指定窗口左上角的起始位置 |
--clear | 用默认的对话背景色来清空屏幕内容 |
--colors | 在对话文本中嵌入ANSI色彩编码 |
--cr-wrap | 在对话文本中允许使用换行符并强制换行 |
--create-rc file | 将示例配置文件的内容复制到指定的file文件中 |
--default-item string | 设定复选列表、表单或菜单对话中的默认项 |
--help | 显示dialog命令的帮助信息 |
--help-status | 当选定Help按钮后,在帮助信息后写入多选列表、单选列表或表单信息 |
--ignore | 忽略dialog不能识别的选项 |
--input-fd fd | 指定STDIN之外的另一个文件描述符 |
--insecure | 在password部件中键入内容时显示星号 |
--item-help | 为多选列表、单选列表或菜单中的每个标号在屏幕底部添加一个帮助栏 |
--keep-window | 不要清除屏幕上显示过的部件 |
--max-input size | 指定输入的最大字符串长度。默认为2048 |
--no-collapse | 不要将对话文本中的制表符转换成空格 |
--no-kill | 将tailbox对话框放到后台,并禁止该进程的SIGHUP信号 |
--no-shadow | 不要显示对话窗口的阴影效果 |
--output-fd fd | 指定除STDERR之外的另一个输出文件描述符 |
--print-maxsize | 将对话窗口的最大尺寸打印到输出中 |
--print-size | 将对话窗口的大小尺寸打印到输出中 |
--print-version | 将dialog版本号打印到输出中 |
--separate-output | 一次一行地输出checklist部件的结果,不使用引号 |
--separator string | 指定用于分隔部件输出的字符串 |
--separate-widget string | 指定用于分隔部件输出的字符串 |
--shadow | 在每个窗口的右下角绘制阴影 |
--single-quoted | 需要时对多选列表的输出采用单引号 |
--sleep sec | 在处理完对话窗口之后延迟指定的秒数 |
--stderr | 将输出发送到STDERR(默认行为) |
--stdout | 将输出发送到STDOUT |
--tab-correct | 将制表符转换成空格 |
--tab-len n | 指定一个制表符占用的空格数(默认为8) |
--timeout sec | 指定无用户输入时,sec秒后退出并返回错误代码 |
--titel title | 指定对话窗口的标题 |
--trim | 从对话文本中删除前导空格和换行符 |
--visit-items | 修改对话窗口中制表符的停留位置,使其包括选项列表 |
dialog命令选项2
按钮的选项功能都差不多,单独列出在这里。可以重写对话窗口中的任意按钮标签:
选项 | 描述 |
---|---|
--cancel-label label | 指定Cancel按钮的替代标签 |
--defaultno | 将yes/no对话框的默认答案设为No |
--exit-label label | 指定Exit按钮的替代标签 |
--extra-button | 在OK按钮和Cancel按钮之间显示一个额外按钮 |
--extra-label label | 指定额外按钮的替代标签 |
--help-button | 在OK按钮和Cancel按钮后显示一个Help按钮 |
--help-label label | 指定Help按钮的替代标签 |
--nocancel | 隐藏Cancel按钮 |
--no-lable label | 为No按钮指定替代标签 |
--ok-label label | 指定OK按钮的替代标签 |
--yes-label label | 为Yes按钮指定替代标签 |
举例说明
- --title选项,允许你设置出现在窗口顶部的部件标题。
- --backtitle选项,是为脚本中的菜单创建公共标题的简便办法。
- --create-rc选项,dialog命令支持运行时配置。该命令会根据配置文件模板创建一份配置文件。
创建本地临时文件(mktemp)
在需要临时将内容保存到文件的时候,有个特殊命令可以用来创建临时文件。mktemp命令可以在/tmp目录中创建一个唯一的临时文件。shell会创建这个文件,但不用默认的umask值。它会将当前用户设置为文件的属主,并且只有属主有读写权限。
创建文件
默认情况下,mktemp会在当前目录中创建一个文件。使用命令的时候,需要指定一个文件名模板。模板可以包含任意文本文件名,在文件名末尾加上6个X(几个X都没关系,书上建议6位):
$ mktemp test1.XXXXXXtest1.Acrugq$ ls -al test1*-rw-------. 1 steed steed 0 12月 12 14:26 test1.Acrugq$
mktemp命令会用6位字符码替换这6个X,从而保证文件名在目录中是唯一的。
在脚本中使用
在脚本中使用mktemp命令时,需要将文件名保存到变量中,这样就能在后面的脚本中引用了:
$ mktemp test2.XXXXXXtest2.leOFBZ$ mktemp test2.XXXXXXtest2.5kDbKn$ mktemp test2.XXXXXXtest2.domeOC$ mktemp test2.XXXXXXtest2.CJX702$ tempfile=$(mktemp test2.XXXXXX)$ exec 3>>$tempfile$ echo "test2 Line1" >&3$ echo "TEST2 LINE2" >&3$ exec 3>&-$ cat $tempfiletest2 Line1TEST2 LINE2$ ls -al test2*-rw-------. 1 steed steed 0 12月 12 14:41 test2.5kDbKn-rw-------. 1 steed steed 0 12月 12 14:41 test2.CJX702-rw-------. 1 steed steed 0 12月 12 14:41 test2.domeOC-rw-------. 1 steed steed 0 12月 12 14:41 test2.leOFBZ-rw-------. 1 steed steed 24 12月 12 14:41 test2.tMVTwN$ rm -f $tempfile 2> /dev/null$ ls -al test2*-rw-------. 1 steed steed 0 12月 12 14:41 test2.5kDbKn-rw-------. 1 steed steed 0 12月 12 14:41 test2.CJX702-rw-------. 1 steed steed 0 12月 12 14:41 test2.domeOC-rw-------. 1 steed steed 0 12月 12 14:41 test2.leOFBZ$
这里先创建了几个临时文件,干扰一下。使用的时候,创建了文件描述符3来使用,使用完之后关闭了文件描述符。最后精确的把用完的临时文件给删除了。
在/tmp目录创建临时文件
-t选项会强制mktemp命令在系统的临时目录创建文件。此时返回的就是创建的文件的全路径:
$ mktemp -t test3.XXXXXX/tmp/test3.aPIXIy$
创建临时目录
-d选项则是创建一个临时目录。如果要在/tmp目录下创建临时目录,就是-dt:
$ mktemp -d dir.XXXXXXdir.aBDmsd$ mktemp -dt dir.XXXXXX/tmp/dir.pqW927$