使用 Zenity 让你的脚本更用户友好

作者:Mike Diehl

我第一次接触 Zenity 时,就认识到它有几个潜在的用途。虽然我很习惯通过命令行界面与计算机交互,但我知道很多人并不习惯。Zenity 通过简单的命令行创建 GUI 小部件,并且可以从任何 shell 脚本中使用。这允许管理员编写执行特定功能的 shell 脚本,同时使程序易于不太复杂的用户交互。

很多时候,你需要执行重复性的任务,但你不想让你的用户接触到 shell 提示符。尽管我不太喜欢必须用鼠标点击才能让计算机做我想做的事情,但我理解有时候,使用 GUI 可以减少犯简单错误的机会。例如,如果程序需要用户输入日期,它想要 YYYY-MM-DD 格式的日期,还是 MM-DD-YY 格式的日期?你的程序可以告诉用户它期望的日期格式,但为什么要让事情比它们需要更复杂呢?为什么不简单地让用户从他们熟悉的界面(日历)中选择日期呢?你上次输错带有混合大小写字符的长文件名是什么时候?显然,点击文件名会更容易。

使用 Zenity,你的程序可以显示日历、文件选择、文本输入和文本消息小部件。用户与这些小部件交互的结果随后可用于 shell 脚本。

在继续实际示例之前,让我们先看几个简单的示例。

以下命令将向用户展示一个日历,标题栏中显示“示例 1”,窗口顶部显示文本“你的生日是什么时候?”。当用户选择日期时,Zenity 将选定的日期打印到 STDOUT,然后将其分配给变量 birthday。


export birthday=`zenity --calendar --text='你的生日是什么时候?'
--title='示例 1'`

用户已经了解了错误窗口的含义,甚至无需阅读文本,Zenity 允许你的脚本提供直观的错误指示。只需使用如下命令即可。


zenity --error --text='发生了非常糟糕的事情!' --title='示例 2'

正如你所看到的,Zenity 非常易于使用。你只需告诉它你想显示哪个小部件,然后使用各种其他参数自定义该小部件。然后,你可以通过读取 Zenity 的 STDOUT 接受结果(如果有)。

让我们考虑一个在商业中经常出现的假设情况。你每天执行用户主目录的备份,他们经常删除文件并需要你从备份中恢复它们。问题是你不希望被简单的任务打扰,但你也不想培训所有用户执行他们自己的文件恢复。相反,像大多数 Linux 管理员一样,你编写了一个脚本。

这是一个我编写的快速脚本,作为备份文件恢复程序的示例。


#!/bin/bash

export repository="/tmp/backups"
export user=`whoami`

############# 步骤 1 #########################
zenity --info --title="文件恢复程序" --text="你即将被
要求提供你希望从中恢复的备份文件的日期。选择
日期以继续。"

############# 步骤 2 #########################
export date=`zenity --calendar --date-format=%Y-%m-%d`

if [ ! -f ${repository}/${user}/${date}.tgz ] ; then

echo No such file ${repository}/${user}/${date}.tgz
zenity --error --text="对不起,找不到 $date 的备份。"
found."
exit;
fi

############# 步骤 3 #########################
export files=`tar -tzf ${repository}/${user}/${date}.tgz |
zenity --list--title "选择要
恢复的文件" --column "文件" --separator=" " --multiple`

############# 步骤 4 #########################
zenity --info --title="文件恢复程序" --text="接下来,你必须选择
将文件恢复到哪个目录"

export target_dir=`zenity --file-selection --directory`

############# 步骤 5 #########################
zenity --info --title="文件恢复程序" --text="如果你愿意,我可以
为你恢复的文件创建一个新目录。如果你
想要这样做,请输入新目录的名称。否则,只需点击
确定"

export dir=`zenity --entry --title="文件恢复程序"`

############# 步骤 6 #########################
if [ -f ${target_dir}/${dir} ] ; then
zenity --error --text="我无法创建一个名为
${target_dir}/
${dir} 的目录,因为已经存在一个同名的文件"
exit
fi

if [ ! -d ${target_dir}/${dir} ] ; then
mkdir ${target_dir}/${dir}
fi

############# 步骤 7 #########################
tar -xzvf ${repository}/${user}/${date}.tgz -C ${target_dir}/${dir}/ $files |
zenity --progress –pulsate

zenity --info --title="文件恢复程序" --text="文件恢复完成。"

我将脚本分解为 7 个部分,以便更容易讨论脚本的每个部分的作用。

脚本首先定义了一些重要的变量。由于这是一个虚构的场景,我将备份存储库定义为 /tmp/backups;你可能会将备份放在其他地方。然后我们开始用户对话的第一步。

第一步非常直观。它只是告诉用户从下一个对话框中期望什么。当用户点击“确定”按钮时,我们进入第二步,用户将看到一个日历。用户选择从中恢复丢失文件的日期。显示日历的程序行使用格式字符串来告诉 Zenity 如何格式化结果日期。我希望日期采用可排序的格式,所以我选择了 YYYY-MM-DD。我用简单的破折号替换了默认的斜杠字符,因为否则,shell 会认为 YYYY/MM/DD 指的是 3 个目录级别,而我们实际上希望它表示单个文件名。格式字符串与 date(1) 中使用的格式字符串完全相同。

步骤 2 然后检查备份文件是否实际存在。如果不存在,则显示错误消息并终止。否则,我们继续脚本中的下一步。

在步骤 3 中,我们将 tar 命令的输出发送到 Zenity,以便我们的用户可以选择他们想要从 tarball 中恢复哪些文件。我们使用 --multiple 参数,以便他们可以选择多个文件进行恢复。另请注意,我们将 --separator 设置为一个空格。通过这样做,我们使 Zenity 生成一个用于恢复的文件名列表,并且我们可以将该列表用作命令行上的单个变量替换。

接下来,我们希望让用户有机会将他们的文件恢复到当前工作目录以外的目录。我们为此目的使用了 --file-selection 小部件。通过使用 --directory 参数,我们将用户限制为只能选择目录,而不能选择文件。

在步骤 5 中,我们告诉用户,程序可以在他们刚刚选择的目录中创建一个子目录,并且所有恢复的文件都将进入该子目录。然后我们使用 --enter 小部件让用户告诉我们要创建哪个子目录。如果用户在不输入任何内容的情况下按下“确定”按钮,我们的脚本将简单地将文件恢复到选定的目录中。

在步骤 6 中,我们进行一些错误检查,以确保已经存在一个与用户输入的子目录同名的文件。如果存在,我们会向用户显示错误消息,并且我们的程序终止。

然后,如果子目录尚不存在,我们创建它。

最后,在步骤 7 中,我们从选定的 tarball 中恢复用户的文件,并将它们放置在相应的目录中,同时以进度条的形式向用户提供视觉反馈。这样,我们的用户无需猜测程序是否正在工作,并且他们不会面对文件名列表,甚至更糟糕的是,错误消息。

正如你所看到的,Zenity 使编写通过 GUI 与用户交互的 shell 脚本变得非常容易。诚然,这个例子有点牵强。该示例旨在足够丰富,以演示尽可能多的 Zenity 功能,同时仍然具有实用性。只需进行少量更改,即可使恢复脚本使用 ssh 和 scp 访问远程备份存储库,可能位于远程位置。用户不必知道任何细节,因为他们看到的只是友好的用户对话框系列。

大多数 Linux 管理员都习惯使用命令行界面,许多人可能认为使用 Zenity 来“简化”简单的 shell 脚本是浪费时间。然而,许多 Linux 用户并不精通 shell 界面,并且会欣赏你为使他们的工作尽可能“点击”所做的努力。

加载 Disqus 评论