Linux shell 脚本编程-高级篇 (二)

Linux shell 脚本编程-高级篇 (一)


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 thing on?" 10 20

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


■ inputbox 部件
-----------------------------------------------------------------------------------------------------------------------------------------
inputbox 部件为用户提供了一个简单的文本框区域来输入文本字符串。dialog 命令会将文本字符串的值发给 STDERR。必须重定向 STDERR来获取用户输入。

inputbox提供了两个按钮:OK 和 Cancel。如果选择了 OK 按钮,命令的退出状态码就是 0;反之,退出状态码就会是 1。

示例:
    dialog --inputbox "Enter your age:" 10 20 2>age.txt
    [devalone@devalone 18]$ echo $?
    0
    [devalone@devalone 18]$ cat age.txt
    12[devalone@devalone 18]$

使用 cat 命令显示文本文件的内容时,该值后面并没有换行符。这能够轻松地将文件内容重定向到 shell 脚本中的变量里,以提取用户输入的字符串。


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

示例:
    [devalone@devalone 18]$ dialog --textbox /etc/passwd 15 45

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


■ menu 部件
-----------------------------------------------------------------------------------------------------------------------------------------
menu 部件允许创建之前所制作的文本菜单的窗口版本。只要为每个选项提供一个选择标号和文本就行了。

示例:
    [devalone@devalone 18]$ dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" \
    3 "Display memory usage" 4 "Exit" 2> test.txt

第一个参数定义了菜单的标题,之后的两个参数定义了菜单窗口的高和宽,而第四个参数则定义了在窗口中一次显示的菜单项总数。如果有更多的选项,可以
用方向键来滚动显示它们。

在这些参数后面,必须添加菜单项对。第一个元素是用来选择菜单项的标号。每个标号对每个菜单项应该是唯一的,可以通过在键盘上按下对应的键来选择。
第二个元素是菜单中使用的文本。

如果用户通过按下标号对应的键选择了某个菜单项,该菜单项会高亮显示但不会被选定。直到用户用鼠标或回车键选择了 OK 按钮时,选项才会最终选定。
dialog 命令会将选定的菜单项文本发送到 STDERR。可以根据需要重定向 STDERR。


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

示例:
    
    [devalone@devalone 18]$ dialog --title "Select a file" --fselect $HOME/ 10 50 2>file.txt

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


2.2.2 dialog 选项
-----------------------------------------------------------------------------------------------------------------------------------------
除了标准部件,还可以在 dialog 命令中定制很多不同的选项。已经看过了 —title 选项的用法。它允许设置出现在窗口顶部的部件标题。

另外还有许多其他的选项可以让程序员全面定制窗口外观和操作。dialog 命令中可用的选项如下表所示:


    dialog 命令选项
    +---------------------------+----------------------------------------------------------------------------
    | 选 项                        | 描 述
    +---------------------------+----------------------------------------------------------------------------
    | --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                | 在password部件中键入内容时显示星号
    +---------------------------+----------------------------------------------------------------------------
    | --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-items                | 修改对话窗口中制表符的停留位置,使其包括选项列表
    +---------------------------+----------------------------------------------------------------------------
    | --yes-label label            | 为Yes按钮指定替代标签
    +---------------------------+----------------------------------------------------------------------------

--backtitle 选项是为脚本中的菜单创建公共标题的简便办法。如果为每个对话窗口都指定了该选项,那么它在应用中就会保持一致,这样会让脚本看起来
更专业。

可以重写对话窗口中的任意按钮标签。该特性允许创建任何需要的窗口。

2.2.3 在脚本中使用 dialog 命令
-----------------------------------------------------------------------------------------------------------------------------------------
在脚本中使用 dialog 命令必须记住两件事:

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

示例:
    [devalone@devalone 18]$ cat menu3.sh
    #!/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 memusage {
            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 $temp2 2> /dev/null

这段脚本用 while 循环和一个真值常量创建了个无限循环来显示菜单对话。这意味着,执行完每个函数之后,脚本都会返回继续显示菜单。

由于 menu 对话包含了一个 Cancel 按钮,脚本会检查 dialog 命令的退出状态码,以防用户按下 Cancel 按钮退出。因为它是在 while 循环中,所以退出
该菜单就跟用 break 命令跳出 while 循环一样简单。

脚本用 mktemp 命令创建两个临时文件来保存 dialog 命令的数据。第一个临时文件 $temp用

来保存 df 和 meminfo 命令的输出,这样就能在 textbox 对话中显示它们。第二个临时文件 $temp2 用来保存在主菜单对话中选定的值。

运行:
    [devalone@devalone 18]$ menu3.sh

2.3 使用图形
-----------------------------------------------------------------------------------------------------------------------------------------
如果想给交互脚本加入更多的图形元素,可以再进一步。KDE 和 GNOME 桌面环境都扩展了 dialog 命令的思路,包含了可以在各自环境下生成 X Window
图形化部件的命令。


2.3.1 KDE 环境
-----------------------------------------------------------------------------------------------------------------------------------------
KDE 图形化环境默认包含 kdialog包。kdialog 包使用 kdialog 命令在 KDE 桌面上生成类似于 dialog 式部件的标准窗口。生成的窗口能跟其他 KDE 应用
窗口很好地融合,不会造成不协调的感觉。这样就可以直接在 shell 脚本中创建能够和 Windows相媲美的用户界面了。


■ kdialog 部件
-----------------------------------------------------------------------------------------------------------------------------------------
就像 dialog 命令,kdialog 命令使用命令行选项来指定具体使用哪种类型的窗口部件。下面是 kdialog 命令的格式:

    kdialog display-options window-options arguments

window-options 选项允许指定使用哪种类型的窗口部件。可用的选项如下表所示:


    kdialog 窗口选项
    +---------------------------------------+--------------------------------------------------------------------------
    | 选 项                                    | 描 述
    +---------------------------------------+--------------------------------------------------------------------------
    | --checklist title [tag item status]    | 带有状态的多选列表菜单,可以表明选项是否被选定
    +---------------------------------------+--------------------------------------------------------------------------
    | --error text                            | 错误消息框
    +---------------------------------------+--------------------------------------------------------------------------
    | --inputbox text [init]                | 输入文本框。可以用init值来指定默认值
    +---------------------------------------+--------------------------------------------------------------------------
    | --menu title [tag item]                | 带有标题的菜单选择框,以及用tag标识的选项列表
    +---------------------------------------+--------------------------------------------------------------------------
    | --msgbox text                            | 显示指定文本的简单消息框
    +---------------------------------------+--------------------------------------------------------------------------
    | --password text                        | 隐藏用户输入的密码输入文本框
    +---------------------------------------+--------------------------------------------------------------------------
    | --radiolist title [tag item status]    | 带有状态的单选列表菜单,可以表明选项是否被选定
    +---------------------------------------+--------------------------------------------------------------------------
    | --separate-output                        | 为多选列表和单选列表菜单返回按行分开的选项
    +---------------------------------------+--------------------------------------------------------------------------
    | --sorry text                            | “对不起”消息框
    +---------------------------------------+--------------------------------------------------------------------------
    | --textbox file [width] [height]        | 显示file的内容的文本框,可以指定width和height
    +---------------------------------------+--------------------------------------------------------------------------
    | --title title                            | 为对话窗口的TitleBar区域指定一个标题
    +---------------------------------------+--------------------------------------------------------------------------
    | --warningyesno text                    | 带有Yes和No按钮的警告消息框
    +---------------------------------------+--------------------------------------------------------------------------
    | --warningcontinuecancel text            | 带有Continue和Cancel按钮的警告消息框
    +---------------------------------------+--------------------------------------------------------------------------
    | --warningyesnocancel text                | 带有Yes、No和Cancel按钮的警告消息框
    +---------------------------------------+--------------------------------------------------------------------------
    | --yesno text                            | 带有Yes和No按钮的提问框
    +---------------------------------------+--------------------------------------------------------------------------
    | --yesnocancel text                    | 带有Yes、No和Cancel按钮的提问框
    +---------------------------------------+--------------------------------------------------------------------------
    
在使用 kdialog 窗口部件时,它看起来更像是KDE桌面上的一个独立窗口,而不是在终端仿真器会话中的。checklist 和r adiolist 部件允许在列表中
定义单独的选项以及它们默认是否选定。

示例:

    kdialog --checklist "Items I need" 1 "Toothbrush" on 2 "Toothpaste" off 3 "Hair brush" on 4 "Deodorant" off 5 "Slippers" off

指定为 on 的选项会在多选列表中高亮显示。要选择或取消选择多选列表中的某个选项,只要单击它就行了。如果选择了 OK 按钮,kdialog 就会将标号值
发到 STDOUT上。

当按下回车键时,kdialog 窗口就和选定选项一起出现了。当单击 OK 或 Cancel 按钮时,kdialog 命令会将每个标号作为一个字符串值返回到 STDOUT
(这些就是在输出中看到的 "1" 和 "3")。脚本必须能解析结果值并将它们和原始值匹配起来。


■ 使用 kdialog
-----------------------------------------------------------------------------------------------------------------------------------------
可以在 shell 脚本中使用 kdialog 窗口部件,方法类似于 dialog 部件。最大的不同是 kdialog 窗口部件用 STDOUT 来输出值,而不是 STDERR。

示例:
    $ cat menu4
    #!/bin/bash
    # using kdialog to create a menu
    
    temp=$(mktemp -t temp.XXXXXX)
    temp2=$(mktemp -t temp2.XXXXXX)
    
    function diskspace {
        df -k > $temp
        kdialog --textbox $temp 1000 10
    }
    
    function whoseon {
        who > $temp
        kdialog --textbox $temp 500 10
    }
    
    function memusage {
        cat /proc/meminfo > $temp
        kdialog --textbox $temp 300 500
    }
    
    while [ 1 ]
    do
        kdialog --menu "Sys Admin Menu" "1" "Display diskspace" "2" "Display users" "3" "Display memory usage" "0" "Exit" > $temp2
        
        if [ $? -eq 1 ]
        then
            break
        fi
        
        selection=$(cat $temp2)
        
        case $selection in
        1)
            diskspace ;;
        2)
            whoseon ;;
        3)
            memusage ;;
        0)
            break ;;
        *)
            kdialog --msgbox "Sorry, invalid selection"
        esac
    done


2.3.2 GNOME 环境
-----------------------------------------------------------------------------------------------------------------------------------------
GNOME 图形化环境支持两种流行的可生成标准窗口的包:

    □ gdialog
    □ zenity

zenity 是大多数 GNOME桌面 Linux 发行版上最常见的包(在 Ubuntu 和 Fedora 上默认安装)


■ zenity 部件
-----------------------------------------------------------------------------------------------------------------------------------------
zenity 允许用命令行选项创建不同的窗口部件。下表列出了zenity 能够生成的不同部件。


    zenity 窗口部件
    +-------------------+---------------------------------------------------------------------------
    | 选 项                | 描 述
    +-------------------+---------------------------------------------------------------------------
    | --calendar        | 显示一整月日历
    +-------------------+---------------------------------------------------------------------------
    | --entry            | 显示文本输入对话窗口
    +-------------------+---------------------------------------------------------------------------
    | --error            | 显示错误消息对话窗口
    +-------------------+---------------------------------------------------------------------------
    | --file-selection    | 显示完整的路径名和文件名对话窗口
    +-------------------+---------------------------------------------------------------------------
    | --info            | 显示信息对话窗口
    +-------------------+---------------------------------------------------------------------------
    | --list            | 显示多选列表或单选列表对话窗口
    +-------------------+---------------------------------------------------------------------------
    | --notification    | 显示通知图标
    +-------------------+---------------------------------------------------------------------------
    | --progress        | 显示进度条对话窗口
    +-------------------+---------------------------------------------------------------------------
    | --question        | 显示yes/no对话窗口
    +-------------------+---------------------------------------------------------------------------
    | --scale            | 显示可调整大小的窗口
    +-------------------+---------------------------------------------------------------------------
    | --text-info        | 显示含有文本的文本框
    +-------------------+---------------------------------------------------------------------------
    | --warning            | 显示警告对话窗口
    +-------------------+---------------------------------------------------------------------------

zenity 命令行程序与 kdialog 和 dialog 程序的工作方式有些不同。许多部件类型都用另外的命令行选项定义,而不是作为某个选项的参数。

zenity 命令能够提供一些非常酷的高级对话窗口。calendar 选项会产生一个整月的日历,当在日历中选择了日期时,zenity 命令会将值返回到 STDOUT 中。


■ 在脚本中使用 zenity
-----------------------------------------------------------------------------------------------------------------------------------------
zenity 在 shell 脚本中表现良好。

示例:

    $cat menu5
    #!/bin/bash
    # using zenity to create a menu

    temp=$(mktemp -t temp.XXXXXX)
    temp2=$(mktemp -t temp2.XXXXXX)

    function diskspace {
        df -k > $temp
        zenity --text-info --title "Disk space" --filename=$temp
        --width 750 --height 10
    }

    function whoseon {
        who > $temp
        zenity --text-info --title "Logged in users" --filename=$temp
        --width 500 --height 10
    }

    function memusage {
        cat /proc/meminfo > $temp
        zenity --text-info --title "Memory usage" --filename=$temp
        --width 300 --height 500
    }

    while [ 1 ]
    do
        zenity --list --radiolist --title "Sys Admin Menu" --column "Select" --column "Menu Item" FALSE "Display diskspace" FALSE \
        "Display users" FALSE "Display memory usage" FALSE "Exit" > $temp2
        
        if [ $? -eq 1 ]
        then
            break
        fi
        
        selection=$(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 中使用选项的全文本。如果文本中有任何空格,要给文本加上引号。

-----------------------------------------------------------------------------------------------------------------------------------------
参考:

    《Linux 命令行与 shell 脚本编程大全》 第 3 版 —— 2016.8(美)Richard Blum  Cristine Bresnahan

猜你喜欢

转载自blog.csdn.net/devalone/article/details/81407479