您的位置:首页 > 运维架构 > Shell

使用 shell 脚本创建好的图形应用程序

2012-05-08 11:00 288 查看
如果您走进一个拥挤的机房,可能会听到有关 “shebangs”、斜线、点、根、管道、端口等等这个那个的闲聊。如果讲到 UNIX®,您无疑会理解本地术语 — 有关 UNIX 的缩略词、命令名、快捷键、选项、文件名和方言 — 且有宾至如归的感觉。与其他艺术工作者一样,UINX 用户拥有广泛的术语来描述其工作细节。

常用缩略语

GUI:图形用户界面

HTML:超文本标记语言

并非每个人都探讨 UNIX;事实上,有些人可能发现命令行很复杂,令人却步。此外,您可能不希望将全部命令行寄托给临时或无经验的用户。要帮助那些不习惯使用命令行的人,或构建围绕 shell 的自定义解决方案,您可以为您的脚本构建 GUI。有了这样的工具 —
dialog
和 Zenity 是两个值得一提的工具(参见 参考资料)— 您就可以使用对话框、文件浏览器和其他常见的 “windowing” 控件和技术来与您的用户交互。事实上,对话框提供更多自然对话:您提出问题,请求响应,并相应地予以响应。

本期的 “对话 UNIX” 探讨
dialog
和 Zenity,并展示如何将任何脚本转化成一个令人信服的 GUI 应用程序。对于传统的、基于文本的界面使用
dialog
,Zenity 提供现代风格的视窗化桌面。

向任何 shell 脚本添加对话框

一个命令行实用程序通常提供足够的选项来完全控制每个调用。一些 DOS 命令可能启用或禁用一个特性,而其他 DOS 命令可能处理参数,比如名称列表。在命令行,您将(几乎)所有信息呈现在前面,然后执行任务。图形应用程序很不同。选择是通过菜单、复选框和文件浏览器做出的。一个图形应用程序接受一点信息,处理它,然后通常要求获得更多信息。据说 GUI 应用程序是事件驱动的。

dialog
实用程序跨越两个世界。当您需要来自用户的输入时调用该实用程序,然后返回到您的脚本继续处理提供的任何数据。换言之,如果您写一个脚本来使用
dialog
,就有可能忽略命令行参数,而是使用
dialog
在必要时发出提示信息。

如果您的系统缺少
dialog
实用程序,您可以轻松使用当前版本自带的包管理器来安装它,或者您可以直接通过源代码编译它。例如,如果您的系统使用 Aptitude,您可以通过如下命令安装
dialog


sudo apt-get install dialog

否则就要通过源代码编译,可以下载维护人员 Thomas Dickey 的 Web 站点上的代码(参见 参考资料)并运行典型的三个命令:
./configure && make && make install


$ wget http://invisible-island.net/datafiles/release/dialog.tar.gz $ tar xzf dialog.tar.gz
$ cd dialog-1.1-20100428
$ ./configure
$ make
$ sudo make install

安装完成之后,您的路径中应当会有一个名为
dialog
的新实用程序。输入
man dialog
来查看捆绑文档。

dialog
使用起来很简单:它仅是另一个 UNIX 命令。您使用命令选项显示您选择的对话框,然后捕获结果并基于该值执行一些逻辑。
dialog
的一些变体直接将命令结果放在特殊的 shell 状态变量
$?
中,您应当在
dialog
命令退出后立即保存或询问该变量(因为随后的一个命令会立即改变其值)。另外,通常更为复杂的
dialog
命令变体同时设置 shell 状态变量并生成其他结果。为将事情简单化,
dialog
提供
--stdout
选项来将其结果发出到标准输出,因而便于通过命令求值捕获数据(带左引号的命令和赋值语句的组合)。

例如,
dialog --yesno
命令是最简单的变体之一。它提出一个问题,提示做出是或否的响应,并返回
$?
中的
0
1
,具体取决于用户选择了 “Yes” 还是 “No”。您可以测试
$?
的值并执行一些条件代码。这里是您可以添加到 shell 脚本的一个工作代码段:

dialog --yesno "Do you want to continue?" 0 0
rc=$?
if [ "${rc}" == "0" ]; then
echo Yes
else
echo No
fi

--yesno
选项需要至少三个参数:问题文本以及对话框本身的高度和宽度,后者用行和列度量。如果您不需要特定尺寸,总是可以为高度或宽度使用
0
,以自动调整对话框大小。(还有相对于窗口左下角放置窗口的选项。)图 1 展示运行中的
--yesno


图 1. --yesno 操作



dialog
选项
--calendar
呈现一个日历来允许用户选择特定日期。如果用户选择一个日期,然后单击 OK,命令返回
0
。但是,如果用户单击 Cancel,命令返回
1
。此外,如果用户单击 OK,命令将选定日期发出为标准输出。这里是使用命令求值产生日期的一个例子:

RESULT=`dialog --stdout --title "CALENDAR"
--calendar "Please choose a date..." 0 0 9 1 2010`
retval=$?

--title
选项使用下一个参数来将一个标题添加到对话框,且可用于任何
dialog
命令。非常像
--yesno
,您提供一些文本来提示用户。接下来,选项
0 0
再次指定自动高度和宽度,选项
9 1 2010
分别指示日历中显示的初始日、月和年。选项卡和箭头键改变日历并选择一个日期。对话框退出后,如果
retval
0
RESULT
的值就是选定的日期。图 2 显示日历对话框。

图 2. 日历对话框



dialog
命令提供通常在图形应用程序中找到的大部分控件:

--infobox
仅仅展示信息:它不要求任何输入。信息框仍然只是简单地在屏幕上。要延长其显示,在它和下一个命令之前置入一个
sleep
命令。

--input
收集单一输入响应。您可能会使用该命令来收集您的用户的姓名或邮政编码。

--textbox
显示一个文本文件的内容。如果文件超出对话框的垂直高度,一个控件支持简单的向上和向下滚动。

--menu
--radiolist
提供一个选择列表,供用户进行选择。两种对话框在功能上是等同的,但是略有不同的视觉风格,以更好地模拟一个 GUI 可能展示的东西。特别地,
--radiolist
命令呈现
( )
来模拟单选按钮。

--checklist
显示用户可单独启用或禁用的一个项目列表。

每个
dialog
变体的输出不同,或是一个单一值,或是一列由空格分隔的带引号值。例如,
--checklist
是用于选择一个或多个选项的一个不错的控件,它发出一列带引号值,其中每个值与一个启用的选项相关。下面演示了一个操作示例:

RESULT=`dialog --stdout
--checklist "Enable the account options you want:" 10 40 3 \
1 "Home directory" on \
2 "Signature file" off \
3 "Simple password" off`

行 1、2 和 3 结尾的反斜杠(
\
)是延续标记;从
RESULT
off`
的一切内容是一个命令。如果用户启用了 Home directorySimple password
$RESULT
将会是
"1" "3"
--checklist
的参数是高度和宽度,任何时间内的列表元数量(如果有些项目被挡住,您可以通过滚动查看这些项目),以及清单选项(其中每个选项是一个值)、一个描述、在最初启用或禁用该选项。

您可以随时输入
dialog --help
来查看常规列表,输入
dialog
来查看特定选项。
dialog
有无数用法。

回页首

有像素?使用 Zenity。

Zenity 是 UNIX 桌面,如同
dialog
是简单的终端窗口。您可以使用 Zenity 从任何 shell 脚本打开 GTK+ 对话框。事实上,Zenity 与
dialog
有着许多相同的功能;惟一的区别在于,Zenity 在一个 X Window System 环境中工作。Zenity 与 GNOME 相捆绑。如果您不运行 GNOME,可以单独安装 Zenity(但是,也要安装大量 GTK+ 库)。您还可以从 GNOME 项目页面下载 Zenity 的源代码(参见参考资料 获取链接)。

下面是一个简单的例子。命令为:

zenity --question --text "Do you want to continue?"

生成的结果如 图 3 所示。(用于演示的机器在运行 Ubuntu 10。)如果您单击 OK,命令返回
0
。否则,它返回
1


图 3. 一个简单问题



如同
dialog
,Zenity 有很多选项 — 甚至比
dialog
还多 — 但是选项命名贴切,因而不言自明。您可能发现 Zenity 比
dialog
更有优势,特别是由于大部分计算机用户都有某种 X 桌面。

Zenity 提供与
dialog
相同的许多控件。这里是收集名称的一个代码段:

ENTRY=`zenity --entry --text "Please enter your name"
--entry-text "Your name" --title "Enter your name"
if [ $? == 0 ]; then
zenity --info --text "Hello $ENTRY\!"
fi

再次说明,如果
zenity
的退出代码是
0
,那么
ENTRY
有某人的姓名。这里是为使用 Zenity 而重写后的日历示例:

DATE=`zenity --calendar --day "9" --month "1" --year "2010" --format "%Y-%m-%d"
if [ $? == 0 ]; then
echo $DATE
fi

尽管 Zenity 更详细一点 — 例如,对于年、月、日有单独的选项 — 其他 DOS 命令使您免于记住精确的参数使用顺序。Zenity 的日历还允许您指定输出格式,即使用标准
strftime()
代码。该命令的结果类似于
2010-1-9
,它表示 2010 年 1 月 9 日。

Zenity 还提供一个过程表来展示一个操作的状态。它从标准输入逐行读取数据。如果一个行的前缀是井号(
#
),文本被更新为该行文本。如果一个行仅包含一个数字,百分比被更新为该数字。清单 1 展示 Zenity 文档中的一个示例。

清单 1. Zenity 过程表

#!/bin/sh
(
echo "10" ; sleep 1
echo "# Updating mail logs" ; sleep 1
echo "20" ; sleep 1
echo "# Resetting cron jobs" ; sleep 1
echo "50" ; sleep 1
echo "This line will just be ignored" ; sleep 1
echo "75" ; sleep 1
echo "# Rebooting system" ; sleep 1
echo "100" ; sleep 1
) |
zenity --progress \
--title="Update System Logs" \
--text="Scanning mail logs..." \
--percentage=0

if [ "$?" = -1 ] ; then
zenity --error \
--text="Update canceled."
fi

sub-shell(包含在括号中)执行一系列任务 — 在这个人为例子中 albeit sleep 延迟 — 且通过一个管道将输出发出到一个 Zenity 过程表。在每一步之前,sub-shell 发出一个数字来推进过程表,每个
--percentage 0
起始于
0
,然后发出一个以
#
开头的字符串来改变状态消息。因此,过程表沿着步骤标记脚本工作。如果 Zenity 的退出代码是
-1
,单击的是 Cancel 按钮。

再次说明,要使用
dialog
或 Zenity,用对话框替换您之前引用过命令行参数的代码。用一个小创意,您可以将您的 shell 脚本转化为一等桌面公民。

回页首

其他高级工具

有些时候,您可能发现您的需求超过了 shell 脚本以及
dialog
和 Zenity 工具的功能范围之外。在那些实例中,您可能转向
C/C++
并为桌面构建本机应用程序,但是您还可以使用高级脚本语言和许多强大的 GUI 框架的语言绑定。

一个组合是 Ruby 脚本语言和 wxWidgets 框架的 Ruby 绑定。Ruby 是面向对象的、富于表现力的且简洁的,运行于大部分操作系统之上。wxWidgets 框架还可用于每个主流平台,包括 Mac OS X、Windows®、Linux® 和 UNIX。由于两者都是可移植的,您可以用 Ruby 编写一个应用程序一次,然后随处运行它。另一个更简单的选择是 Shoes。尽管不如 wxWidgets 丰富,Shoes 学习和使用起来相当简单。清单 2 使用 70 行代码实现了一个计算器。

清单 2. 用 Shoes 实现的一个计算器

class Calc
def initialize
@number = 0
@previous = nil
@op = nil
end

def to_s
@number.to_s
end

(0..9).each do |n|
define_method "press_#{n}" do
@number = @number.to_i * 10 + n
end
end

def press_clear
@number = 0
end

{'add' => '+', 'sub' => '-', 'times' => '*', 'div' => '/'}.each do |meth, op|
define_method "press_#{meth}" do
if @op
press_equals
end
@op = op
@previous, @number = @number, nil
end
end

def press_equals
@number = @previous.send(@op, @number.to_i)
@op = nil
end
end

number_field = nil
number = Calc.new
Shoes.app :height => 250, :width => 200, :resizable => false do
background "#EEC".."#996", :curve => 5, :margin => 2

stack :margin => 2 do

stack :margin => 8 do
number_field = para strong(number)
end

flow :width => 218, :margin => 4 do
%w(7 8 9 / 4 5 6 * 1 2 3 - 0 Clr = +).each do |btn|
button btn, :width => 46, :height => 46 do
method = case btn
when /[0-9]/; 'press_'+btn
when 'Clr'; 'press_clear'
when '='; 'press_equals'
when '+'; 'press_add'
when '-'; 'press_sub'
when '*'; 'press_times'
when '/'; 'press_div'
end

number.send(method)
number_field.replace strong(number)
end
end
end
end
end

对 Ruby 和 Shoes 的介绍不在本文讨论范围之内,但是这里是一些最重要的构造:

大多数 Ruby 类
Calc
使用 Ruby 的元编程功能,在运行时为所有数字键和数学操作键定义功能。

代码开头
Shoes.app...
创建计算器的 GUI,为其呈现布局和按钮。Shoes 提供两个容器来装配布局:
stack
flow
。一个 stack 是元素的一个垂直堆栈,其中每个元素直接放在前一个元素下面。一个 flow 尽量紧密地包裹元素,直至它达到其边框局限,然后包装其余的元素。(您可以将一个堆栈看作是一个 HTML
<div>
,将一个流看作 HTML
<p>
。)您可以使用 Ruby 块创建一个堆栈或一个流。

最里面的
flow
快循环创建应用程序中的所有按钮,并有效地将每个按钮绑定到其方法。(
case
语句返回一个方法名称;
number.send(method)
行调用实例化计算器上的那个方法。)

number_field.replace strong(number)
行通过最新计算结果更新计算器显示。发出
number
致使类调用其自己的
to_s
(“to string”) 方法。

其他脚本语言拥有类似的库,且 Ruby 本身有更多选择,包括 Ruby Cocoa,可使用 Ruby 在 Mac OS X 上开发 Cocoa 应用程序。选择您喜欢的开源脚本语言,找到一个轻量级 GUI 工具包,然后开始编码。

回页首

您不需要讨厌的编译器!

如果您已经掌握了 shell 脚本编写,将您的工作与
dialog
或 Zenity 结合起来,以增加互操作性。如果您需要的编程功能比 shell 提供的更多,考虑 Ruby 或 Python 这样的语言以及任何窗口工具包。您无需一个编译器来编写良好的桌面应用程序。

参考资料

学习

对话 UNIX:查阅本系列中的其他部分。

wxWidgets:进一步了解 wxWidgets,跨平台 GUI 框架。

AIX 和 UNIX developerWorks 专区:AIX 和 UNIX 专区提供关于 AIX 系统管理各个方面和提高您的 UNIX 技能的丰富信息。

New to AIX and UNIX?访问 New to AIX and UNIX 页面了解更多。

技术书店:阅读关于这些和其他技术主题的图书。

获得产品和技术

dialog
项目页面
:下载
dialog
的源代码。

Zenity:从 GNOME 项目页面获取 Zenity 的源代码。

Shoes:了解如何使用 Shoes,Ruby 的一个 GUI 库,来编写应用程序脚本。

讨论

加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

参与 AIX 和 UNIX 论坛:

AIX 5L — 技术论坛

AIX for Developers 论坛

集群系统管理

IBM 支持帮助论坛

性能工具 — 技术

更多 AIX 和 UNIX 论坛
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: