Linux 学徒:使用 Dialog 改进 Bash Shell 脚本

作者:Mihai Bisca

Shell 脚本是包含 shell 命令的文本文件,常用于处理重复性任务。为了避免一遍又一遍地输入相同的命令,我们将它们放入文件中并进行一些修改,赋予其执行权限并运行它。

为了在运行时控制程序,需要交互式 shell 脚本。对于这种情况,dialog 命令提供了一种绘制文本模式彩色窗口的简便方法。这些窗口可以包含文本框、消息框或不同类型的菜单。甚至可以使用来自用户的输入来修改脚本行为。

dialog 程序的当前版本是 cdialog-0.9,可以从 Sunsite 的 /pub/Linux/utils/shell 目录免费下载。Dialog 使用 ncurses 库,因此也必须安装它。一些 Linux 发行版(即 Slackware)包含 dialog 程序,因为有些实用程序依赖于它(setup, pkgtool)。顺便说一句,这些实用程序是使用 dialog 的绝佳示例。

让我们检查一下最流行的示例程序的 dialog 版本。使用您最喜欢的文本编辑器,创建一个名为 hello 的文件,其中包含以下行

#!/bin/sh
# First shell script with "dialog"
dialog --title "Dialog message box" \
       --msgbox "\n Hello world !" 6 25

此文件的第一行将其标识为 “sh” shell 的 shell 脚本。每个 shell 脚本都必须以字符 “#!” 开头,后跟要执行的 shell 的名称(和路径)。例如,我们可以将此行写为 #!/bin/bash。下一行只是注释,就像文件中除了第一行之外的任何以 “#” 开头的行一样。然后是 dialog 命令,它将在屏幕上绘制一个 6 行高、25 列宽的消息框,其中包含标题 “Dialog message box” 和消息 “Hello world !”。消息框有一个 OK 按钮,当选择它时,脚本将结束。请注意 --msgbox 选项的通用格式

--msgbox
在编写并保存此文件后,键入
chmod a+x hello
=“./2460f1.gif” 图 1. Dialog 框的屏幕截图

结果屏幕如图 1 所示。这个例子非常简单,只需在 shell 提示符下使用一个命令即可生成。但是,当 shell 脚本中需要用户输入时,事情会变得更加复杂。

例如,要列出目录的内容,请使用如 清单 1 所示的 dialog。这引入了两个新的 dialog 框:输入框和文本框。输入框的通用格式为

--inputbox

在清单 1 中,输入框中显示的默认值是通过运行命令 pwd 获得的,该命令返回当前工作目录。每当命令用反引号括起来时,bash 会将其替换为其标准输出。

当然,这个默认值可以在运行时更改,使用退格键删除,使用常规字母键写入。最终值由 dialog 在 STDERR 上打印。为了从 shell 脚本中使用它,必须首先将其重定向到一个文件。使用重定向执行此操作

2>/tmp/dialog.ans

如果用户决定选择输入框中的 Cancel 按钮,则需要下一行。当这种情况发生时,dialog 命令的退出状态将为 1。Bash 将上次执行的命令的退出状态保存在变量 $? 中,因此如果它是 1,shell 脚本将在清除屏幕后停止。

如果 $? 为 0(用户单击了 OK 按钮),则读取 answer 文件以设置变量 ANS。再次证明反引号非常有用。另一种方法是使用

ANS=$(cat /tmp/dialog.ans)

所选目录的内容输出到之前使用的同一个文件。这样做是安全的,因为 > 运算符会覆盖此文件之前的 contents。

现在一切都已设置好,可以执行下一个 dialog 命令,该命令生成文本框以显示文本文件的内容。它的通用格式为

--textbox

文本框允许使用箭头键或 home/end/pgup/pgdown 键导航,甚至具有简单的搜索功能。在显示文本框时键入 / 会导致另一个窗口出现,提示用户输入要在文件中向前搜索的字符串。键入 ? 执行反向搜索,就像 less 分页器一样。包含该字符串的第一行显示在文本框的顶部。

经验丰富的程序员可能会抱怨此 shell 脚本中存在明显的缺陷。如果目录名称错误怎么办?shell 脚本不会抱怨,但会显示一个空文本框,因为不存在的目录中没有文件。为了解决这个问题,需要检查指定的目录是否存在。实际上,如果目录存在,ls 命令返回退出状态 0,如果不存在,则返回 1。因此,可以通过添加以下行来修改脚本

ls -al $ANS > /tmp/dialog.ans 2>/dev/null
if [ $? = 1 ]; then
   clear
   echo no such directory
   exit 1
fi

首先,更改 ls 行,将标准错误重定向到 /dev/null。这意味着来自 ls 的任何错误消息都不会出现在屏幕上。然后,如果退出状态 ($?) 为 1,则脚本将以错误消息退出。

通过允许用户在脚本退出之前检查更多目录,可以使此脚本更有用。(参见 清单 2。)进行了一些更改。首先,整个脚本已包含在始终为真的 while-do 循环中。这使其可以多次运行。现在,退出脚本的唯一方法(除了键入 ctrl-c)是选择 dialog 输入框中的 Cancel 按钮。第二个更改是引入了一个消息框,当 ls 命令返回退出状态 1 时将显示该消息框。continue 命令值得特别评论。它的意思是跳过 while 循环的当前迭代(即显示文本框的部分)并开始新的迭代。因此,在错误消息之后,用户将再次看到输入框,提示输入另一个目录名称。

菜单框是通过使用 --menu 选项运行 dialog 生成的,格式如下

dialog --menu
   tag2 item2...

此选项显示一个带有两个按钮(OK 和 Cancel)和一个由一行或多行组成的菜单的框。每行都有一个 “tag”(数字或单词)和一个 “item”,通常是描述菜单项的文本。当用户选择一个项目,然后单击 OK 按钮时,相应的 tag 将打印在 STDERR 上。此外,对于选择 OK 按钮,dialog 的退出状态为 0;对于 Cancel 按钮,退出状态为 1。

菜单框很有用,因为它们允许用户从几个固定的选项中进行选择。例如,在生成 LaTeX 文档时,必须执行三个步骤:使用文字处理器编辑源文件,使用 LaTeX 编译它,以及查看生成的 .dvi 文件。很容易构建一个 shell 脚本来执行这些步骤。(参见 清单 3,它假设文本编辑器是 jed,.dvi 文件查看器是 dvisvga,并且两者都在路径中。)完整脚本再次包含在 “while” 循环中,目的是使其可以多次工作。退出此脚本的唯一方法是选择第一个菜单框中的 “Cancel” 按钮。否则,用户必须在三个选项之间进行选择

  • 编辑文本文件。

  • 编译 LaTeX 文件。

  • 查看 .dvi 文件。

答案存储在文件 /tmp/ans 中,并在变量 R 中检索。如果用户选择编辑文件,则会出现一个新的 dialog 框。它是一个输入框,提示输入文件名。答案进入变量 F。然后脚本检查文件是否存在并运行命令

jed $F # where $F is the name of the file
如果文件不存在,则要么是新文件,要么是输入错误。为了区分这两种可能性,提供了一个 yes/no dialog 框。这种框的通用格式为
--yesno
该框有两个按钮,YES 和 NO。文本通常是一个问题,用户通过选择按钮来回答。如果是 YES,$?(dialog 命令的退出状态)为 0;如果是 NO,$? 为 1。

在清单 3 中,如果答案是 YES,则调用文本编辑器;如果是 NO,脚本通过 continue 命令返回主菜单。其他两个选项的工作方式相同,唯一的区别是使用 LaTeX 处理文件或查看生成的 DVI 文件的命令

latex $F
dvisvga $F

还有其他几个 dialog 框可用,例如 checklist 或 radiolist;但是,它们的使用方式与菜单框非常相似。

我想以 --guage dialog 框的示例结束。这用于以图形方式显示百分比。语法是

dialog --guage

启动后,guage 框会不断从 STDIN 读取百分比值,直到达到 EOF 并相应地更改显示。这是一个简单(但不是很有用)的 guage 脚本

#!/bin/bash
{ for I in 10 20 30 40 50 60 70 80 90 \
      80 70 60 50 40 30 20 10 0; do
   echo $I
   sleep 1
done
echo; } | dialog --guage "A guage demo" 6 70 0
将其复制到文件中,赋予其执行权限,运行并享受!脚本的第一部分(包含在花括号中)是一个组命令。每秒钟,它将列出的值之一发送到 guage dialog 框。最后的 echo 命令用于终止 dialog 框。

Shell 脚本是一种使您的 Linux 系统“更智能”的便捷方式。这些最常见的 dialog 框示例应该可以帮助您使脚本更具吸引力。

资源

Linux Apprentice: Improve Bash Shell Scripts Using Dialog
Mihai Bisca 是一位对 Linux 着迷的眼科医生。1998 年,他出版了第一本罗马尼亚语 Linux 入门书。目前,他正与他三岁的女儿 Andra 竞争键盘的位置。您可以通过 mbasca@ottonel.pub.ro 联系他们。
加载 Disqus 评论