Contents

在Shell脚本中创建一个简单的选择菜单

1. 概述

操作系统应该允许与用户交互。菜单等组件促进了它并增强了用户体验。此外,将用户的输入范围缩小到一组明确定义的可能性可以提高数据的一致性和安全性。

在本教程中,我们将介绍在 Bash 中创建菜单的方法。

2. select循环

我们应该使用select命令在终端中创建一个简单的菜单。然后,该命令会显示一个以数字开头的选项列表。 让我们学习命令语法:

select NAME [in WORDS ... ;] do COMMANDS; done

select从标准输入中重复读取一个数字。随后,如果数字对应于字符串在WORDS中的位置,则该命令将NAME设置为相应的文本。否则,什么都没有设置。

此外,我们应该将PS3变量设置为提示文本。

最后,让我们注意该命令在 Bash 和 Korn ( ksh ) shell 中可用,但在 Bourne ( sh) shell 中不可用。至于内置的 bash,我们应该使用help select来获取更多信息。

2.1. 基本使用

现在让我们测试一个简单的例子:

#!/bin/bash
PS3="Select your language please: "
select lng in Bash Java C++ Quit
do
    case $lng in
        "Bash")
            echo "$lng - that's what we're talking about";;
        "Java")
           echo "$lng - is your VM ready?";;
        "C++")
           echo "$lng - let's prepare for a lot of compilation";;
        "Quit")
           echo "We're done"
           break;;
        *)
           echo "Ooops";;
    esac
done

我们使用case语句来处理用户的输入。此外,选择循环是无限的,所以我们需要明确地打破 它:

/uploads/shell_script_simple_select_menu/1.png

2.2. case的索引选项

除了返回选定的名称之外,该命令还将REPLY变量设置为用户输入的数字。让我们在case语句中使用它而不是NAME

#!/bin/bash
PS3="Select item please: "
items=("Item 1" "Item 2" "Item 3")
select item in "${items[@]}" Quit
do
    case $REPLY in
        1) echo "Selected item #$REPLY which means $item";;
        2) echo "Selected item #$REPLY which means $item";;
        3) echo "Selected item #$REPLY which means $item";;
        $((${#items[@]}+1))) echo "We're done!"; break;;
        *) echo "Ooops - unknown choice $REPLY";;
    esac
done

请注意,我们将菜单的位置存储在一个数组 中。因此,我们只需要输入一次,使代码不易出错。 此外,我们在项目数组之后添加了Quit选项。因此,其对应的REPLY值为 item 数组的长度加一 - $((${#items[@]}+1)))

最后,我们可以很容易地通知用户错误的选择。

2.3. 使select菜单永久化

让我们观察项目列表从终端向上滚动。当我们在运行select的同一终端中为用户打印信息时,就会发生这种情况。 因此,我们需要将select放入一个无限的*while true*循环中来刷新菜单:

#!/bin/bash
PS3="Select item please: "
items=("Item 1" "Item 2" "Item 3")
while true; do
    select item in "${items[@]}" Quit
    do
        case $REPLY in
            1) echo "Selected item #$REPLY which means $item"; break;;
            2) echo "Selected item #$REPLY which means $item"; break;;
            3) echo "Selected item #$REPLY which means $item"; break;;
            $((${#items[@]}+1))) echo "We're done!"; break 2;;
            *) echo "Ooops - unknown choice $REPLY"; break;
        esac
    done
done

让我们注意进一步的修改——每个 case 指令在做出选择后立即中断select循环。如此有效,我们每次重新启动select以再次显示选项列表。

此外,在Quit选项终止两个循环(selectwhile循环)之后break 2

3. dialog命令

使用dialog ,我们可以创建文本mc 样式的对话框组件。该命令可从同名包中获得。

3.1.基本语法

让我们直接从终端开始一个简单的对话框。我们将选项作为标签描述对传递:

dialog \
    --backtitle "This is an options panel" \
    --menu "Please select" \
    10 40 3 \
    1 "First option" \
    2 "Second option"

**让我们注意标签显示在描述的前面。此外,标签的第一个字母是菜单热键。**另外,我们应该定义菜单的尺寸,10、40和3分别代表宽度、高度和菜单高度。最后,返回值是选择选项的标签或按下Cancel按钮后的空字符串。

3.2. 创建菜单

让我们创建一个在用户按下Cancel时消失的菜单。因此,我们需要在while循环中检查dialog的返回值。 此外,我们将在 bash 数组items中收集对话框的选项:

#!/bin/bash
items=(1 "Item 1"
       2 "Item 2")
while choice=$(dialog --title "$TITLE" \
                 --menu "Please select" 10 40 3 "${items[@]}" \
                 2>&1 >/dev/tty)
    do
    case $choice in
        1) ;; # some action on 1
        2) ;; # some action on 2
        *) ;; # some action on other
    esac
done
clear # clear after user pressed Cancel

我们应该注意到,默认情况下,命令输出到stderr。因此,对于命令替换,我们需要使用 2>&1 >/dev/tty构造将其重定向 到终端:

/uploads/shell_script_simple_select_menu/2.png

4. 带有zenity的图形菜单

现在让我们试试zenity 。**它是一个基于GTK 的图形组件工具包。**因此,我们使用它在 Bash 脚本和图形环境之间进行交互。

要使用zenity,我们需要安装一个同名的包。 **我们将创建一个简单的图形小部件来向用户展示选项。**此外,用户的选择将显示在终端中:

#!/bin/bash
items=("Item 1" "Item 2" "Item 3")
while item=$(zenity --title="$title" --text="$prompt" --list \
               --column="Options" "${items[@]}")
do
    case "$item" in
        "${items[0]}") echo "Selected $item, item #1";;
        "${items[1]}") echo "Selected $item, item #2";;
        "${items[2]}") echo "Selected $item, item #3";;
        *) echo "Ooops! Invalid option.";;
    esac
done

我们应该提供一个选项列表作为zenity的参数。此外,while 循环会一直显示对话框,直到它返回与取消操作相关的空字符串:

/uploads/shell_script_simple_select_menu/3.png