【shell笔记>脚本】使用shell创建文本菜单和窗口部件

内容:

  • 创建文本菜单
  • 创建文本窗口部件

创建文本菜单

创建交互式shell脚本最常用的方法是使用菜单,它提供了各种选项帮助脚本用户了解脚本能做到的和不能做的。

shell脚本菜单的核心是case命令,该命令会根据用户在菜单上的选择来执行特定命令。

下面我们逐步了解和创建基于菜单的shell脚本的步骤。

创建菜单布局

第一步是决定在菜单上显示哪些元素以及想要显示的布局方式。

在创建菜单前,通常先清空显示器上已有的内容。这样能在干净的,没有干扰的环境中显示菜单了。

clear命令使用当前终端的terminfo数据来清理出现在屏幕上的文字。运行clear命令后可以使用echo命令显示菜单元素。

默认,echo命令只显示可打印的文本字符。而在创建菜单时一些非文本字符也非常有用,比如制表符和换行符。我们需要添加-e选项使得echo命令能解析包含在其中的非文本字符。

例如,

wsx@wsx:~/tmp$ 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 an option: "

最后一行-en选项去掉末尾换行符使得菜单更专业点,光标会在行尾等待用户输入。

创建菜单的最后一步是获取用户输入。这一步用read命令。因为我们只期望用户使用单字符输入,在命令加-n选项进行限定。这样用户只需要输入一个数字,不用摁回车键。

read -n 1 option

创建菜单函数

shell脚本菜单选项作为一组独立的函数实现起来更为容易。要做到这一点,你要为每个菜单项创建独立的shell函数。第一步是决定你希望脚本执行那些功能,然后将这些功能以函数的形式放在代码中。

通常我们会为还没有实现的函数先创建一个桩函数,它是一个控函数,或者只有一个echo语句,说明最终这里需要什么内容。**

function diskspace {
  clear
  echo "This is where the diskspace commands will do"
}

这允许菜单在我实现某个函数时仍然能正常操作。不需要我们写出所有函数之后才能让菜单投入使用。函数从clear命令开始,这样我们就能在一个干净的屏幕上执行该函数,不会收到原先菜单的干扰。

另外,将菜单布局本身作为一个函数来创建有利于菜单制作。

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 menu\n\n"
    echo -en "\t\tEnter an option: "
    read -n 1 option
}

这样我们能在任何时候调用该函数以此重现菜单。

添加菜单逻辑

下一步我们需要创建程序逻辑将菜单布局和函数结合起来。这需要使用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命令会接管余下的处理过程,基于字符调用相应的函数。

整合shell脚本菜单

现在让我们将前面的步骤全部组合起来,看看它们是如何协作的。

这是一个完整的菜单脚本例子:

wsx@wsx:~/tmp$ cat test14
#!/bin/bash
# simple script menu

function diskspace {
    clear
    df -k
}

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 menu\n\n"
    echo -en "\t\tEnter an 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

使用:

            Sys Admin Menu

    1. Display disk space
    2. Display logged on users
    3. Display memory usage
    0. Exit menu


        Enter an option: 

输入1:

文件系统           1K-块     已用      可用 已用% 挂载点
udev             4006080        0   4006080    0% /dev
tmpfs             807220    81004    726216   11% /run
/dev/sda4      305650672 14226064 275828680    5% /
tmpfs            4036100     1724   4034376    1% /dev/shm
tmpfs               5120        4      5116    1% /run/lock
tmpfs            4036100        0   4036100    0% /sys/fs/cgroup
/dev/sda3         524272     4684    519588    1% /boot/efi
tmpfs             807220       52    807168    1% /run/user/1000
tmpfs             807220       16    807204    1% /run/user/125
/dev/sda2      421886972 23340376 398546596    6% /media/wsx/存储


            Hit any key to continue

其他都可以自己测试一下,我就不赘言了。

使用select命令

select命令只需要一条命令就可以创建出菜单,然后获取输入的答案并自动处理。

命令格式如下:

select variable in list
do
    commands
done

list参数是由空格分隔的文本选项列表,这些列表构成了整个菜单。select命令会将每个列表项显示成一个带编号的选项,然后为选项显示一个由PS3环境变量定义的特殊提示符。

wsx@wsx:~/tmp$ cat smenu1 
#!/bin/bash
# using select in the menu

function diskspace {
    clear
    df -k
}

function whoseon {
    clear
    who
}

function memusage {
    clear
    cat /proc/meminfo
}

PS3="Enter an 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")
        memusage ;;
    "Display memory usage")
        memusage ;;
    *)
        clear
        echo "Sorry, wrong selection";;
    esac
done
clear

运行会自动生成如下菜单项:

wsx@wsx:~/tmp$ ./smenu1 
1) Display disk space       3) Display memory usage
2) Display logged on users  4) Exit program
Enter an option:

在使用select命令时,记住存储在变量中的结果值是整个文本字符串而不是跟菜单项相关联的数字。文本字符串是要在case语句中比较的内容。

制作窗口

dialog包能够用ANSI转义控制字符在文本环境中创建标准的窗口对话框。我们可以将这些对话框融入自己的shell脚本中,借此与用户进行交互。这部分我们来学习如何使用dialog包。

安装:

sudo apt-get install dialog

dialog包

dialog包使用命令行参数来决定生成哪种窗口部件(widget)。部件是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按钮,命令会返回状态码0。如果选择了Cancer或No按钮,命令会返回状态码1。可用标准的$?变量来确定dialog部件具体选择了哪个按钮。

如果部件返回了数据,dialog命令会将数据发送到STDERR。我们可以用标准的bash shell方法将其重定向到另一个文件或文件描述符中。

dialog --inputbox "Enter your age:" 10 20 2>age.txt

这条命令将文本框输入的文本重定向到age.txt文本中。

msgbox部件

msgbox部件是对话框中最常见的类型。它会在窗口中显示一条简单的信息,直到用户点击OK后消失。

使用格式:

dialog --msgbox text height width

text参数是要在窗口显示的字符串,heightwidth参数设定自动换行的窗口大小。如果想要在窗口加一个标题,可以使用--title参数,后接作为标题的文本。

例子:

dialog --title Testing --msgbox "This is a test" 10 20

输入命令后,消息框会显示在终端上。如果你的终端仿真器支持鼠标,可以单击OK来关闭对话框,也可以按下回车键。

yesno部件

该部件在窗口底部生成两个按钮:一个是Yes,一个是No。用户可以用鼠标、制表符或者键盘方向键来切换按钮。选择按钮则使用空格或者回车键。

下面是一个例子:

wsx@wsx:~$ dialog --title "Please answer" --yesno "Is this thing on?" 10 20
# 中间终端有输出
wsx@wsx:~$ echo $?
0

dialog命令的退出状态码会根据用户选择的按钮来设置。选择No返回1,选择Yes就是0。

inputbox部件

inputbox部件提供了一个简单的文本框区域来输入文本字符串,dialog会将它传到STDERR,我们需要重定向获得输入。inputbox提供了两个按钮:OK和Cancel。如果选择了OK,命令退出状态码为0,否则为1。

wsx@wsx:~/tmp$ dialog --inputbox "Enter your age:" 10 20 2>age.txt
wsx@wsx:~/tmp$ cat age.txt 
24

如果你自己运行过的话就会注意到该值后面没有换行符,这让我们能够轻松将文本内容重定向到脚本变量中,以获得用户输入的值。

textbox部件

textbox部件是在窗口中显示大量信息的极佳办法。它会生成一个滚动窗口来显示由参数所指定的文件中的文本。

wsx@wsx:~/tmp$ dialog --textbox /etc/passwd 15 45

/etc/passwd文件内容显示在可滚动的文本窗口中,可以用方向键来左右或上下滚动显示文件的内容。窗口底部的行会显示当前查看文本处于文件中的哪个位置(百分比)。文本框只包含一个用来选择退出部件的Exit按钮。

menu部件

我们可以用这个部件来创建之前(上一篇笔记)中制作的文本菜单的窗口版本。只要为每个选项提供一个选择标号和文本就行。

wsx@wsx:~/tmp$ dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 4 "Exit" 2>test.txt

第一个参数定义了菜单的标题,之后的两个参数定义了菜单窗口的高和宽,而第四个参数则定义了在窗口中一次显示的菜单项总数。如果存在更多选择,则有滚动条。

菜单项必须使用菜单对:第一个元素是用来选择菜单项的标号(必须唯一);第二个元素是菜单中使用的文本。

dialog命令会将选定(鼠标点击或回车键或选择OK)的菜单项文本发送到STDERR。

fselect部件

该部件在处理文件名时非常方便。不用强制用户键入文件名,我们就可以用fselect部件来浏览文件的位置并选择文件。

使用格式:

wsx@wsx:~/tmp$ dialog --title "Select a file" --fselect $HOME/ 10 50 2>file.txt

第一个参数是窗口使用的其实目录位置。fselect部件窗口由左侧的目录列表、右侧的文件列表和含有当前选定的文件或目录的简单文本框组成。可以手动在文本框键入文件名,或者用目录和文件列表来选定(使用空格键选定)

dialog选项

除了标准部件,dialog还有大量定制的选项。前面我们使用的title就是一个。

下面显示了命令可用的选项:

选项 描述
--add-widget 继续下一个对话框直到按下Esc或Cancel选项
--aspect ratio 直到窗口宽度和高度的宽高比
--backtitle title 直到显示在屏幕顶部背景上的图标
--begin x y 指定窗口左上角的起始位置
--cancel-label label 指定Cancel按钮的替代标签
--clear 用默认的对话背景色来清空屏幕内容
--colors 在对话文本中嵌入ANSI色彩编码
--cr-wrap 在对话文本中允许使用换行符并强制换行
--create-rc file 将示例配置文件的内容复制到指定的file文件中
--defaultno 将yes/no对话框的默认答案设为no
--default-item string 设定复选列表、表单或菜单对话中的默认项
--exit-label label 指定Exit按钮的替代标签
--extra-button 在OK按钮和Cancel按钮之间显示一个额外按钮
--extra-label label 指定额外按钮的替换标签
--help 显示dialog命令的帮助信息
--help-button 在OK按钮和Cancel按钮后显示一个Help按钮
--help-label label 指定Help按钮的替换标签
--help-status 当选定Help按钮后,在帮助信息后写入多选列表、单选列表或表单信息
--ignore 忽略dialog不能识别的选项
--input-fd fd 指定STDIN之外的另一个文件描述符
--insecure 在passwd部件中键入内容时显示星号
--item-help 为多选列表、单选列表或菜单中的每个标号在屏幕底部添加一个帮助栏
--keep-window 不要清除屏幕上显示过的部件
--max-input size 指定输入的最大字符串长度。默认为2048
--nocancel 隐藏Cancel按钮
--no-collapse 不要将对话文本中的制表符转换为空格
--no-kill 将tailboxbg对话放到后台,并禁止该进程的SIGHUP信号
--no-label label 为No按钮指定替换标签
--no-shadow 不要显示对话窗口的阴影效果
--ok-label label 指定OK按钮的替换标签
--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秒后退出并返回错误代码
--title title 指定对话窗口的标题
--trim 从对话文本中删除前导空格和换行符
--visit-tiems 修改对话窗口制表符的停留位置,使其包括选项列表
--yes-label label 为Yes按钮指定替换标签

--backtitle选项是为脚本中的菜单创建公共标题的简便办法。上表提供的强大特性允许我们创建任何需要的窗口。

dialog命令支持运行时配置。该命令会根据配置文件模板创建一份配置文件。dialog启动时会先去检查是否设置了DIALOGRC环境变量,该变量会保存配置文件名信息。如果未设置该变量或未找到该文件,它会将$HOME/.dialogrc作为配置文件。如果这个文件还不存在的话就尝试查找编译时指定的GLOBALRC文件,也就是/etc/dialogrc。如果还不存在就用编译时的默认值。

在脚本中使用dialog命令

必须记住两件事:

  • 如果有Cancel或No按钮,检查dialog命令的退出状态码
  • 重定向STDERR来获得输出值

接下来是一个简单的实例,使用dialog部件生成我们之前(上一篇笔记)所创建的系统管理菜单。

wsx@wsx-laptop:~$ cat menu3
#!/bin/bash
# using dialog to create a menu

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 ]
do
dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 0 "Exit" 2> $temp2
if [ $? -eq 1 ]
then
  break
fi

selection=$(cat $temp2)

case $selection in
1)
        diskspace ;;
2)
        whoseon ;;
3)
        memusage ;;
0)
        break ;;
*)
        dialog --msgbox "Sorry, invalid selection" 10 30
esac
done
rm -f $temp 2> /dev/null
rm -f $temp 2> /dev/null

使用while循环加一个真值常量创建了一个无限循环来显示菜单对话。当执行完每个函数后,脚本会返回继续显示菜单。

脚本使用了mktemp命令创建两个临时文件来保存dialog命令的数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,391评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,350评论 0 17
  • 一:LoadRunner常见问题整理1.LR 脚本为空的解决方法:1.去掉ie设置中的第三方支持取消掉2.在系统属...
    0100阅读 4,064评论 0 11
  • 虽然这是tk的教程,但是我个人还是比较推荐使用pyqt来写窗口,实际上pyqt配合qtdesigner写的窗口不仅...
    遗步看风景阅读 39,973评论 3 39
  • 尊敬的王庆坨镇政府领导 您好! 我们是北京中集宏达房地产开发有限公司,2001年成立,注册资金...
    张铭轩阅读 806评论 0 0