多年来, shell脚本一直都被认为是枯燥乏味的。但如果你准备在图形化环境中运行脚本时,就未必如此了。有很多与脚本用户交互的方式并不依赖read和echo语句。
9.1 创建文本菜单
创建交互式shell脚本最常用的方法是使用菜单。提供各种选项可以帮助脚本用户了解脚本能做什么和不能做什么。通常菜单脚本会清空显示区域,然后显示可用的选项列表。用户可以按下与每个选项关联的字母或数字来选择选项。shell脚本菜单的核心是case命令。 case命令会根据用户在菜单上的选择来执行特定命令。
9.1.1 创建菜单布局
创建菜单的第一步显然是决定在菜单上显示哪些元素以及想要显示的布局方式。在创建菜单前,通常要先清空显示器上已有的内容。这样就能在干净的、没有干扰的环境中显示菜单了。clear命令用当前终端会话的terminfo数据来清理出现在屏幕上的文本。运行clear命令之后,可以用echo命令来显示菜单元素。默认情况下, echo命令只显示可打印文本字符。在创建菜单项时,非可打印字符通常也很有用,比如制表符和换行符。要在echo命令中包含这些字符,必须用-e选项。因此,命令如下:
[root@kittod ~]# echo -e "1.\tDisplay disk space"
1. Display disk space
这极大地方便了菜单项布局的格式化。只需要几个echo命令,就能创建一个看上去还行的菜单。
[root@kittod ~]# cat menu01.sh
#!/bin/bashclear
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选项来限制只读取一个字符。这样用户只需要输入一个数字,也不用按回车键:
[root@kittod ~]# cat menu01.sh
#!/bin/bashclear
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: "
read -n 1 option[root@kittod ~]# bash menu01.shSys Admin Menu1. Display disk space2. Display logged on users3. Display memory usage0. Exit menuEnter option:
接下来,你需要创建自己的菜单函数。
9.1.2 创建菜单函数
shell脚本菜单选项作为一组独立的函数实现起来更为容易。这样你就能创建出简洁、准确、容易理解的case命令。
要做到这一点,你要为每个菜单选项创建独立的shell函数。创建shell菜单脚本的第一步是决定你希望脚本执行哪些功能,然后将这些功能以函数的形式放在代码中。
[root@kittod ~]# cat menu02.sh
#!/bin.bash function menu {clearecho 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:"read -n 1 option
}
menu
这样一来,任何时候你都能调用menu函数来重现菜单。
9.1.3 添加菜单逻辑
现在你已经建好了菜单布局和函数,只需要创建程序逻辑将二者结合起来就行了。前面提到过,这需要用到case命令。case命令应该根据菜单中输入的字符来调用相应的函数。用默认的case命令字符(星号)来处理所有不正确的菜单项是种不错的做法。
下面的代码展示了典型菜单中case命令的用法。
menu
case $option in
0)break ;;
1)diskspace ;;
2)whoseon ;;
3)memusage ;;
*)clearecho "Sorry, wrong selection" ;;
esac
这段代码首先用menu函数清空屏幕并显示菜单。 menu函数中的read命令会一直等待,直到用户在键盘上键入了字符。然后, case命令就会接管余下的处理过程。 case命令会基于返回的字符调用相应的函数。在函数运行结束后, case命令退出。
9.1.4 整合 shell 脚本菜单
现在已经看到了构成shell脚本菜单的各个部分,让我们将它们组合在一起,看看彼此之间是如何协作的。这里是一个完整的菜单脚本的例子。
[root@kittod ~]# cat menu03.sh
#!/bin.bash function diskspace {cleardf -k
}
function whoseon {clearwho
}
function memusage {clearcat /proc/meminfo
}
function menu {clearecho 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:"read -n 1 option
}
while [ 1 ]
domenucase $option in0)break ;;1)diskspace ;;2)whoseon ;;3)memusage ;;*)clearecho "Sorry,wrong selection" ;;esacecho -en "\n\n\t\t\tHit ant key to continue"read -n 1 line
done
clear
这个菜单创建了三个函数,利用常见的命令提取Linux系统的管理信息。它使用while循环来一直菜单,除非用户选择了选项0,这时,它会用break命令来跳出while循环。可以用这个模板创建任何shell脚本菜单界面。它提供了一种跟用户交互的简单途径。
9.1.5 使用 select 命令
创建文本菜单的一半工夫都花在了建立菜单布局和获取用户输入。 bashshell提供了一个很容易上手的小工具,帮助我们自动完成这些工作。
select命令只需要一条命令就可以创建出菜单,然后获取输入的答案并自动处理。 select命令的格式如下
select variable in list
docommands
done
list参数是由空格分隔的文本选项列表,这些列表构成了整个菜单。 select命令会将每个列表项显示成一个带编号的选项,然后为选项显示一个由PS3环境变量定义的特殊提示符。
注意:PS1变量补充
[root@kittod ~]# echo $PS1
[\u@\h \W]\$
PS1的常用参数以及含义:\d :代表日期,格式为weekday month date,例如:"Mon Aug 1"\H :完整的主机名称\h :仅取主机名中的第一个名字\t :显示时间为24小时格式,如:HH:MM:SS\T :显示时间为12小时格式\A :显示时间为24小时格式:HH:MM\u :当前用户的账号名称\v :BASH的版本信息\w :完整的工作目录名称\W :利用basename取得工作目录名称,只显示最后一个目录名\# :下达的第几个命令\$ :提示字符,如果是root用户,提示符为 # ,普通用户则为 $颜色设置参数在PS1中设置字符颜色的格式为:\[\e[F;Bm\]........\[\e[0m\],其中“F“为字体
颜色,编号为30-37,“B”为背景颜色,编号为40-47,\[\e[0m\]作为颜色设定的结束。颜色对照表:F B30 40 黑色31 41 红色32 42 绿色33 43 黄色34 44 蓝色35 45 紫红色36 46 青蓝色37 47 白色只需将对应数字套入设置格式中即可。比如要设置命令行的格式为绿字黑底(\[\e[32;40m\]),显示当前用户的账号名称
(\u)、主机的第一个名字(\h)、完整的当前工作目录名称(\w)、24小时格式时间(\t),可以
直接在命令行键入如下命令:
# PS1='[\[\e[32;40m\]\u@\h \w \t]$ \[\e[0m\]'
也可以尝试:
# PS1="\[\e[37;40m\][\[\e[32;40m\]\u\[\e[37;40m\]@\h \[\e[36;40m\]\w\
[\e[0m\]]\\$ "如果需要永久保存,保存到用户变量文件中。
注意:PS2变量补充
一个非常长的命令可以通过在末尾加 \ 使其分行显示
PS2多行命令的默认提示符,默认值是 >
[root@kittod ~]# echo \
> ^C
[root@kittod ~]# PS2=">+ "
[root@kittod ~]# echo \
>+
注意:PS3变量补充
你可以像下面示范的那样,用环境变量PS3定制shell脚本的select提示:
没有PS3提示符时候的情况:
[root@kittod ~]# cat ps3.sh
select i in mon tue wed exit
docase $i inmon) echo "Monday";;tue) echo "Tuesday";;wed) echo "Wednesday";;exit) exit;;esacdone
[root@kittod ~]# bash ps3.sh
1) mon
2) tue
3) wed
4) exit
#? 1
Monday
#? 2
Tuesday
#? 3
Wednesday
#? 4
有PS3提示符的时候的情况:
[root@kittod ~]# cat ps301.sh
PS3="Select a day (1-4): "
select i in mon tue wed exit
docase $i inmon) echo "Monday";;tue) echo "Tuesday";;wed) echo "Wednesday";;exit) exit;;esac
done[root@kittod ~]# bash ps301.sh
1) mon
2) tue
3) wed
4) exit
Select a day (1-4): 1
Monday
Select a day (1-4): 2
Tuesday
Select a day (1-4): 3
Wednesday
Select a day (1-4): 4
这里有一个select命令的简单示例。
[root@kittod ~]# cat menu04.sh
#!/bin/bash
function diskspace {cleardf -k
}
function whoseon {clearwho
}
function memusage {clearcat /proc/meminfo
}
PS3="Enter option: "
select option in "Display disk space" "Display logged on users" "Display memory usage" "Exit program"
docase $option in"Exit program")break ;;"Display disk space")diskspace ;;"Display logged on users")whoseon ;;"Display memory usage")memusage ;;*)clearecho "Sorry, wrong selection" ;;esac
done
clear
select语句中的所有内容必须作为一行出现。这可以从行接续字符中看出。运行这个程序时, 它会自动生成如下菜单。
[root@kittod ~]# bash menu04.sh
1) Display disk space 3) Display memory usage
2) Display logged on users 4) Exit program
Enter option: 1
...
在使用select命令时,记住,存储在变量中的结果值是整个文本字符串而不是跟菜单选项相关联的数字。文本字符串值才是你要在case语句中进行比较的内容。
9.2 创建文本图形脚本
使用文本菜单没错,但在我们的交互脚本中仍然欠缺很多东西,尤其是相比图形化窗口而言。dialog包最早是由Savio Lam创建的一个小巧的工具,现在由Thomas E. Dickey维护。该包能够用ANSI转义控制字符在文本环境中创建标准的窗口对话框。你可以轻而易举地将这些对话框融入自己的shell脚本中,借此与用户进行交互。
检查系统是否安装,如果没有安装则进行安装:
[root@kittod ~]# dnf install dialog
9.2.1 dialog软件包
dialog命令使用命令行参数来决定生成哪种窗口部件( widget)。部件是dialog包中窗口元素类型的术语。
dialog包现在支持下表中的部件类型:
正如在表中看到的,我们可以选择很多不同的部件。只用多花一点工夫,就可以让脚本看起来更专业。
要在命令行上指定某个特定的部件,需使用双破折线格式。
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文件中。
[root@kittod ~]# cat age.txt
20[root@kittod ~]#
9.2.2 部件演示
1、消息框
[root@kittod ~]# dialog --title "Testing" --msgbox "This is a test" 10 20
2、yesno 框
[root@kittod ~]# dialog --title "Yes/No" --no-shadow --yesno "Delete the file /tmp/test.txt?" 10 20
3、输入框
[root@kittod ~]# dialog --title "Input your name" --inputbox "Please input your name:" 10 20 2>/tmp/name.txt
[root@kittod ~]# cat /tmp/name.txt
hehe[root@kittod ~]#
4、密码框
[root@kittod ~]# dialog --title "Password" --passwordbox "Please give a password for the new user: " 10 20
这样我们的密码就暴露出来了,不是很不安全,所以通常我们会加上一个安全选项,-- insecure 将每个字符用 * 来显示出来
5、文本框
[root@kittod ~]# dialog --title "The fstab" --textbox /etc/fstab 10 40
6、菜单框
[root@kittod ~]# dialog --title "Pick a choice" --menu "Choose one" 12 35 5 1 "say hello to everyone" 2 "thanks for your support" 3 "exit"
7、文本选择框
[root@kittod ~]# dialog --title "Pick a choice" --fselect /root/ 7 40
8、复选框
格式:
dialog --checklist "Test" height width menu-height tag1 item1 tag2
item2 …[root@kittod ~]# dialog --backtitle "Checklist" --checklist "Test" 20 50 10 Memory Memory_Size 1 Disk Disk_size 2
9、显示日历
格式:
dialog --calendar "Date" height width day month year[root@kittod ~]# dialog --title "Calendar" --calendar "Date" 5 50
10、进度框架
dialog --gauge text height width [<percent>]
·固定进度显示
[root@kittod ~]# dialog --title "installtion pro" --gauge "installtion" 10 30 10
·实时动态进度
[root@kittod ~]# for i in {1..100};do echo $i;done | dialog --title "installation pro" --gauge "installtion" 10 30
·编辑到脚本中
[root@kittod ~]# cat gauge01.sh
#!/bin/bash
declare -i PERCENT=0
(for I in /etc/*;doif [ $PERCENT -le 100 ]; thencp -r $I /tmp/test 2> /dev/nullecho "XXX"echo "Copy the file $I ..."echo "XXX"echo $PERCENTfilet PERCENT+=1sleep 0.1done
) | dialog --title "coping" --gauge "starting to copy files..." 6 50 0
11、表单框架
dialog --form text height width formheight [ label y x item y x flen ilen ] ...
其中,
·flen 表示 Field Length,定义了:选定字段中显示的长度
·ilen 表示 Input Length,定义了:输入的数据允许的长度
使用 Up/Down(或 Ctrl - N,Ctrl - P)在使用领域之间移动。使用 Tab 键在窗口之间切换。
[root@kittod ~]# dialog --title "Add a user" --form "please input the
information of new user: " 12 40 4 \
> "Username: " 1 1 "" 1 15 15 0 \
> "Fullname: " 2 1 "" 2 15 15 0 \
> "HomeDir: " 3 1 "" 3 15 15 0 \
> "Shell: " 4 1 "" 4 15 15 0
9.2.3 综合应用实例
[root@kittod ~]# cat dia01.sh
#!/bin/bashyesno(){
dialog --title "First screen" --backtitle "Test Program" --clear --yesno \"Start this test program or not ? \nThis decesion have to make by you. " 16 51# yes is 0, no is 1 ,esc is 255result=$?if [ $result -eq 1 ]; thenexit 1;elif [ $result -eq 255 ]; thenexit 255;fiusername
}username(){cat /dev/null > /tmp/test.usernamedialog --title "Second screen" --backtitle "Test Program" --clear --inputbox \"Please input your username (default: hello) " 16 51 "hello" 2>/tmp/test.usernameresult=$?if [ $result -eq 1 ]; thenyesnoelif [ $result -eq 255 ]; thenexit 255;fipassword
}password(){cat /dev/null >/tmp/test.passworddialog --insecure --title "Third screen" --backtitle "Test Program" --clear --passwordbox "Please input your password (default: 12345) " 16 51 "12345" 2>/tmp/test.passwordresult=$?if [ $result -eq 1 ];thenusernameelif [ $result -eq 255 ]; thenexit 255;fioccupation
}occupation(){cat /dev/null > /tmp/test.occupationdialog --title "Forth screen" --backtitle "Test Program" --clear --menu \"Please choose your occupation: (default: IT)" 16 51 3 \IT "The worst occupation" \CEO "The best occupation" \Teacher "Not the best or worst" 2> /tmp/test.occupationresult=$?if [ $result -eq 1 ]; thenpasswordelif [ $result -eq 255 ]; thenexit 255;fifinish
}finish(){dialog --title "Fifth screen" --backtitle "Test Program" --clear --msgbox \"Congratulations! The test program has finished !\n Username:
$(cat /tmp/test.username)\n Password: $(cat /tmp/test.password)\n
Occupation: $(cat /tmp/test.occupation)" 16 51result=$?if [ $result -eq 1 ]; thenoccupationelif [ $result -eq 255 ]; thenexit 255;fi
}yesno
执行该脚本:
9.3 创建真正图形的脚本
如果想给交互脚本加入更多的图形元素,你可以再进一步。 KDE和GNOME桌面环境都扩展了dialog命令的思路,包含了可以在各自环境下生成X Window图形化部件的命令。
KDE图形化环境默认包含kdialog包。 kdialog包使用kdialog命令在KDE桌面上生成类似于dialog式部件的标准窗口。生成的窗口能跟其他KDE应用窗口很好地融合,不会造成不协调的感觉。这样你就可以直接在shell脚本中创建能够和Windows相媲美的用户界面了。
GNOME图形化环境支持两种流行的可生成标准窗口的包:
·gdialog
·zenity
zenity是大多数GNOME桌面Linux发行版上最常见的包 这个包可以创建真正的基于图形窗口的脚本。我们的操作系统在安装时使用了GNOME图形环境,而在默认环境里,系统已经安装了zenity软件包。所以我们可以使用该软件来创建真正的图形脚本。
9.3.1 基本命令
查看日历
[root@kittod ~]# zenity --calendar 1
查看文件选择
[root@kittod ~]# zenity --file-selection
9.3.2 综合实例
[root@kittod ~]# cat zen01.sh
#!/bin/bashtemp=`mktemp -t temp.XXXXXX`
temp2=`mktemp -t temp.XXXXXX`function diskspace(){df -h > $tempzenity --text-info --title "Disk space" --filename=$temp --width 750 --height 10
}function whoseon(){who > $tempzenity --text-info --title "Logged in users" --filename=$temp --width 750 --height 10
}function memusage(){cat /proc/meminfo > $tempzenity --text-info --title "Memory usage" --filename=$temp --width 750 --height 10
}while [ 1 ]
dozenity --list --radiolist --title "Sys Admin Menu" --column "Select" --column "Menu Item" FALSE "Display disk space" FALSE "Display users" FALSE "Display memory usage" FALSE "Exit" > $temp2if [ $? -eq 1 ]then break;fiselection=$(cat $temp2)
case $selection in"Display disk space")diskspace;;"Display users")whoseon;;"Display memory usage")memusage;;"Exit")break;;*)zenity --info "Sorry, invalid selection"esac
done
由于zenity并不支持菜单对话窗口,我们改用单选列表窗口来作为主菜单,如上所示。该单选列表用了两列,每列都有一个标题:第一列包含用于选择的单选按钮,第二列是选项文本。单选列表也不用选项里的标号。当选定一个选项时,该选项的所有文本都会返回到
STDOUT。这会让case命令的内容丰富一些。必须在case中使用选项的全文本。如果文本中有任何空格,你需要给文本加上引号。
使用zenity包,你可以给GNOME桌面上的交互式shell脚本带来一种Windows式的体验。