如何将 GUI 添加到 Linux Shell 脚本
已发表: 2022-01-29 您可以在 Bash 脚本中使用 GUI 窗口、滑块、单选按钮、进度条等。 了解如何使用zenity
工具包,并让您的 Bash 脚本焕然一新。 我们会告诉你怎么做。
Bash 脚本是一种功能强大的编程语言,因为它内置在 Bash shell 中,所以每个人都可以轻松使用它。 它是一种易于开始编程的语言。因为它是解释型的,所以您不需要编译脚本。 一旦您编辑了脚本文件并使其可执行,您就可以运行它。 这使得编码、运行和调试周期非常高效。
人们对 Bash 脚本有两个主要抱怨,首先是速度。 因为 Bash shell 解释脚本中的命令,所以它们的执行速度不如编译的代码。 然而,这就像抱怨拖拉机不如汽车快; 它们适用于不同的事物。
不过,有两种速度。 您通常可以拼凑一个快速脚本并使用它来执行任务,这比使用编译语言(例如 C)开发解决方案要快得多。
人们对 Bash 脚本的第二个抱怨是用户界面——它是一个终端窗口。 当然,有时界面并不重要。 如果唯一会使用脚本的人是它的作者,那么界面可能就没有那么重要了。 对于执行后台和批处理类型处理的脚本也无关紧要。 通常,此类脚本不需要太多(如果有的话)用户交互。
有时您确实需要比终端窗口更直观和现代的东西。 大多数人都熟悉图形用户界面 (GUI)。 要为人们提供尽可能顺畅的体验,您必须从脚本中创建和使用 GUI 元素。
zenity 应用程序
zenity
允许您在 Bash 脚本中加入各种图形界面元素。 这是一个功能强大的工具包,可为您的脚本提供现代感和现代、熟悉的外观。
zenity
预装在 Ubuntu、Fedora 和 Manjaro 发行版上。 它是 GNOME 的一部分。 如果您使用 KDE,您可能想要查看kdialog
,尽管zenity
确实可以在任何桌面环境中运行。
本文中的示例向您展示了如何从命令行创建不同的对话框窗口,如何在变量中捕获它们的返回值和用户选择,以及如何在脚本中使用对话框窗口。
我们完成了一个使用所有三种类型的对话窗口的小应用程序。
日历对话框窗口
日历对话窗口允许某人选择日期。 要创建一个zenity
需要两个单词的单个命令:
zenity--日历

出现日历对话窗口。 它具有标准日期选择器所期望的所有功能。 您可以更改月份和年份,然后单击某一天以选择该日期。 默认情况下,当窗口出现时,今天的日期会突出显示。
单击“确定”关闭对话窗口并选择突出显示的日期。 双击一个日期做同样的事情。
如果您不想选择日期,请单击“取消”,按键盘上的“Esc”键,或关闭对话窗口。
在上面的示例中,选择了 2019 年 8 月 19 日。 如果用户单击“确定”,则日历将关闭,所选日期将打印在终端窗口中。
你可以忽略这一行,“GTKDialog mapped without a transient parent。 这是不鼓励的。”
GTK 代表 GIMP Tool Kit,它是用于开发 GNOME 界面的工具包。 它最初是由 GNU Image Manipulation Program (GIMP) 的作者设计的。 GNU 代表 GNU's Not Unix。
GTK 引擎警告zenity
的作者,他们以非标准方式使用了 GTK 组件。
捕获日期值
将日期打印到终端对我们来说并没有多大作用。 如果我们要从我们的脚本之一调用这个日历,我们需要捕获选定的日期值,以便我们可以在我们的脚本中使用它做一些有用的事情。 我们还将稍微定制日历。
我们将在日历中使用以下选项。 它们都必须与双破折号“–”标志一起使用:
- –text :指定要在日历中显示的文本字符串。 它取代了默认的“从下面选择一个日期”。
- –title :设置日历对话窗口的标题。
- –day :设置日历打开时选择的日期。
- –month :设置日历打开时选择的月份。
- –year :设置日历打开时选择的年份。
我们使用一个名为ChosenDate
的变量来捕获从日历返回的日期。 我们正在使用echo $ChosenDate
将该日期打印到终端窗口。
是的,我们在前面的示例中获得了相同的结果,但是在这里,我们将选定的日期存储在一个变量中。 在前面的示例中,它被打印并忘记了。
ChosenDate=$(zenity -- calendar --text "Choose a date" --title "How-To Geek Rota" --day 1 --month 9 --year 2019); 回声 $ChosenDate
现在,日历显示我们的提示和我们的窗口标题。 日期设置为我们选择的开始日期,而不是今天的日期。
我们还可以自定义选择时返回的日期字符串的格式。 --date-format
选项必须后跟格式说明符。 这是一串标记,用于定义要包含在输出中的数据和格式。 这些标记与用于strftime()
C 语言函数的标记相同,并且有大量的选择。
我们使用的令牌是:
- %A :星期几的全名。
- %d :以数字表示的月份中的日期。
- %m :以数字表示的月份。
- %y :年份为两位数(无世纪)。
ChosenDate=$(zenity -- calendar --text "选择一个日期" --title "How-To Geek Rota" --date-format="%A %d/%m/%y" --day 1 --第 9 个月——2019 年); 回声 $ChosenDate
有人选择日期:
并且使用我们的格式返回日期。 它显示星期几的名称,后跟按欧洲顺序排列的日期:日、月、年。
文件选择对话框窗口:选择文件
文件选择对话框窗口相当复杂。 人们可以浏览文件系统,突出显示一个或多个文件,然后单击“确定”以选择这些文件或完全取消选择。
zenity
提供了所有这些功能,甚至更多。 它与日历对话窗口一样易于使用。
我们将使用的新选项是:
- –file-selection :告诉
zenity
我们要使用文件选择对话框窗口。 - –multiple :允许某人选择多个文件。
- –file-filter :告诉文件对话框窗口要显示哪些文件类型。
zenity --file-selection --tile "How-To Geek" --multiple --file-filter='*.mm *.png *.page *.sh *.txt'
文件选择对话框窗口的功能与任何其他文件选择窗口一样。
用户可以浏览文件系统并选择她选择的文件。
我们已经浏览到一个新目录并选择了一个名为“button_hybrid.png”的文件。
单击“确定”时,文件选择对话窗口关闭,文件名和路径打印在终端窗口中。
如果您需要在任何进一步的处理中使用文件名,您可以在变量中捕获它,就像您对日历中的日期所做的那样。
文件选择对话框窗口:保存文件
如果我们添加一个选项,我们可以将文件选择对话框窗口变成文件保存对话框窗口。 选项是--save
。 我们还将使用--confirm-overwrite
选项。 这会提示该人确认他想要覆盖现有文件。
响应=$(zenity --file-selection --save --confirm-overwrite); 回声$响应
出现文件保存对话框窗口。 请注意,有人可以在其中键入文件名的文本字段。
用户可以浏览到他在文件系统中选择的位置,为文件提供名称,或单击现有文件以覆盖它。
在上面的示例中,用户突出显示了现有文件。
当他单击“确定”时,会出现一个确认对话框窗口,要求他确认是否要替换现有文件。 请注意文件名出现在警告对话框中。 正是这种对细节的关注赋予了zenity
专业的外观。
如果我们没有使用--confirm-overwrite
选项,该文件将被静默覆盖。
文件的名称存储在变量Response
中,该变量会打印到终端窗口。
通知对话框窗口
使用zenity
,在您的脚本中包含光滑的通知对话框窗口是毫不费力的。 您可以调用一些常用的对话窗口来为用户提供信息、警告、错误消息和问题。
要创建错误消息对话框窗口,请使用以下命令:
zenity --error --width 300 --text "权限被拒绝。无法写入文件。"
我们使用的新选项是:
- –error :告诉
zenity
我们要使用错误对话框窗口。 - –width :设置窗口的初始宽度。
错误对话框窗口以指定的宽度出现。 它使用标准的 GTK 错误图标。
要创建信息对话窗口,请使用以下命令:
zenity --info --width 300 --text "更新完成。点击确定继续。"
我们使用的新选项是--info
,它告诉zenity
创建一个信息对话窗口。
要创建问题对话窗口,请使用以下命令:
zenity --question --width 300 --text "你乐意继续吗?"; 回声$?
我们使用的新选项是--question
,它告诉zenity
创建一个问题对话窗口。
$?
是一个特殊的参数。 它保存最近执行的前台管道的返回值。 一般而言,这是来自最近关闭的流程的值。 零值表示“OK”,一个或多个值表示“取消”。
这是一种通用技术,您可以将其应用于任何zenity
对话窗口。 通过在您的脚本中检查该值,您可以确定从对话窗口返回的数据是否应该被处理或忽略。
我们单击“是”,因此返回代码为零,表示“确定”。
要创建警告对话框窗口,请使用以下命令:
zenity --warning --title "硬盘空间不足" --width 300 --text "可能没有足够的硬盘空间来保存备份。"
我们使用的新选项是--warning
,它告诉zenity
创建一个警告对话框窗口。
出现警告对话框窗口。 这不是一个问题,所以它只有一个按钮。

进度对话框窗口
您可以使用zenity
进度对话框窗口来显示一个进度条,指示您的脚本距离完成有多近。
进度条根据从您的脚本中输入的值进行推进。 为了演示原理,请使用以下命令:
(for i in $(seq 0 10 100); do echo $i; sleep 1; done)
该命令分解如下:
-
seq
命令以 10 为步长遍历从 0 到 100 的序列。 - 在每一步,值都存储在变量
i
中。 这将打印到终端窗口。 - 由于
sleep 1
命令,该命令暂停一秒钟。
我们可以将它与zenity
进度对话框窗口一起使用来演示进度条。 请注意,我们将上一个命令的输出通过管道传输到zenity:
(for i in $(seq 0 10 100); do echo $i; sleep 1; done) | zenity --progress --title "How-To Geek" -- 自动关闭
我们使用的新选项是:
- –progress :告诉
zenity
我们要使用进度对话框窗口。 - –auto-close :当进度条达到 100% 时关闭对话框。
出现进度对话框窗口,进度条向 100% 前进,在每一步之间暂停一秒钟。
我们可以使用管道值到zenity
的概念来在脚本中包含进度对话框窗口。
在编辑器中输入此文本并将其保存为“progress.sh”。
!/bin/bash 功能工作清单(){ echo "# 第一个工作项" 回声“25” 睡觉 1 echo "# 第二个工作项" 回声“50” 睡觉 1 echo "# 第三个工作项" 回声“75” 睡觉 1 echo "# 最后一个工作项" 回声“100” 睡觉 1 } 工作清单 | zenity --progress --title "How-To Geek" --auto-close 出口 0
以下是脚本的细分:
- 该脚本定义了一个名为
work-list
的函数。 这是您放置命令和指令以执行实际工作的地方。 将每个sleep 1
命令替换为您的真实命令。 -
zenity
接受echo "# ..."
行并在进度对话框窗口中显示它们。 更改这些行的文本,以便它们将信息性消息传递给用户。 - 包含数字的
echo
显行,例如echo "25"
,也被zenity
接受并设置进度条的值。 - 工作列表函数被调用并通过管道传输到
zenity
。
使用此命令使脚本可执行:
chmod +x 进度.sh
使用此命令运行脚本:
./progress.sh
脚本运行,并且文本消息随着脚本的每个阶段的执行而变化。 进度条逐步向 100% 移动。
比例对话框窗口
比例对话框窗口允许用户移动滑块来选择数值。 这意味着她不能输入太高或太低的值。
我们使用的新选项是:
- –scale :告诉
zenity
我们要使用比例对话框窗口。 - –min-value :设置刻度的最小值。
- –max-value :设置刻度的最大值。
- –step :设置使用箭头键时滑块移动的量。 如果有人使用鼠标,这不会影响滑块的移动。
- –value :设置滑块的初始值和位置。
这是我们正在使用的命令:
Response=$(zenity --scale --title "How-To Geek" --text "选择放大倍率。" --min-value=0 --max-value=30 --step=3 --value15); 回声$响应
滑块对话窗口出现,滑块设置为 15。
用户可以移动滑块来选择一个新值。
当她单击“确定”时,该值将传输到变量Response
并打印到终端窗口。
条目对话窗口
输入对话窗口允许某人输入文本。
我们使用的新选项是:
- –entry :告诉
zenity
我们要使用一个条目对话窗口。 - –entry-text :如果您想在文本输入字段中输入建议值,可以使用它。 我们使用“”来强制一个空字段。 这不是严格要求的,但我们想记录该选项。
完整的命令如下所示:
Response=$(zenity --entry --text "输入你的搜索词" --title "Howe-To Geek" --entry-text=""); 回声$响应
将出现一个简单的对话窗口,其中包含一个文本输入字段。
有人可以键入和编辑文本。
当他单击“确定”时,他键入的值将分配给变量 Response。 我们使用 echo 在终端窗口中打印变量的值。
把它们放在一起
让我们将这些技术放在一起并创建一个功能性脚本。 该脚本将执行硬件信息扫描并在滚动文本窗口中将结果呈现给用户。 她可以选择长或短扫描类型。
对于这个脚本,我们将使用三种类型的对话窗口,其中两种对我们来说是新的:
- 第一个是列表对话窗口。 它允许某人做出选择。
- 第二个是进度对话框窗口,让用户知道正在发生的事情,她应该等待。
- 第三个是文本信息窗口,向用户显示结果。
在编辑器中输入此文本并将其保存为“hardware-info.sh”。
#!/bin/bash # 显示此计算机的硬件列表 临时文件=$(mktemp) ListType=`zenity --width=400 --height=275 --list --radiolist \ --title '硬件扫描' \ --text '选择扫描类型:' \ --列'选择'\ --column '扫描类型' TRUE "Short" FALSE "Long"` 如果 [[ $? -eq 1]]; 然后 # 他们按下了取消或关闭了对话窗口 zenity --error --title="扫描被拒绝" --width=200 \ --text="跳过硬件扫描" 1号出口 elif [ $ListType == "短" ]; 然后 # 他们选择了短单选按钮 标志="--短" 别的 # 他们选择了长单选按钮 标志="" 菲 # 在 $Flag 中搜索具有适当值的硬件信息 hwinfo $标志 | 三通 >(zenity --width=200 --height=100 \ --title="整理信息" --progress \ --pulsate --text="正在检查硬件..." \ --auto-kill --auto-close) >${TempFile} # 在滚动窗口中显示硬件信息 zenity --width=800 --height=600 \ --title "硬件详细信息" \ --text-info --filename="${TempFile}" 出口 0
使用此命令使其可执行:
chmod +x 硬件信息.sh
此脚本创建一个临时文件,文件名保存在 TempFile 变量中:
临时文件=$(mktemp)
该脚本使用--list
选项创建一个称为列表对话窗口的zenity
对话窗口。 行尾的“\”字符告诉脚本将它们视为一个环绕的长行。 这是过程:
- 我们指定窗口的宽度和高度。
- 列表对话框窗口支持列。
--radiolist
选项使第一列成为单选按钮列。 - 我们为窗口设置了标题和文本提示。
- 我们将第一列的标题设置为“选择”。 此列的内容将是单选按钮。
- 我们将第二列的标题设置为“选择”,并提供第二列的内容。 此列包含两个文本标签:“短”和“长”。 TRUE 和 FALSE 指示符表示对话框窗口出现时默认选择“Short”选项。
- 我们将此对话窗口的结果存储在一个名为
ListType
的变量中。
ListType=`zenity --width=400 --height=275 --list --radiolist \ --title '硬件扫描' \ --text '选择扫描类型:' \ --列'选择'\ --column '扫描类型' TRUE "Short" FALSE "Long"`
如果用户按下“取消”,我们不需要检查ListType,
我们可以简单地退出。 如果他按下“OK”,我们需要确定他是否选择了“Short”或“Long”单选按钮:
- 特殊参数
$?
如果用户按下“确定”,则为零。 如果他按下“取消”或关闭窗口,则等于 1。 - 如果它等于 1,则脚本显示错误信息对话框窗口并退出。 如果他按下“OK”,我们继续测试
ListType
变量中的值。 - 如果
ListType
变量包含值“Short”,则脚本将名为Flag
的变量设置为等于“–short”。 - 如果
ListType
变量不包含值“Short”,则它必须包含值“Long”。 该脚本将一个名为Flag
的变量设置为等于“”,这是一个空字符串。 - 该脚本将在下一节中使用
Flag
变量。
如果 [[ $? -eq 1]]; 然后 # 他们按下了取消或关闭了对话窗口 zenity --error --title="扫描被拒绝" --width=200 \ --text="硬件扫描被跳过" 1号出口 elif [ $ListType == "短" ]; 然后 # 他们选择了短单选按钮 标志="--短" 别的 # 他们选择了长单选按钮 标志="" 菲
现在脚本知道用户想要哪种类型的扫描,我们可以执行硬件信息扫描:
- 该脚本调用
hwinfo
命令并将Flag
变量中的值传递给它。 - 如果
Flag
包含“–short”,则hwinfo
命令执行短扫描。 如果Flag
的值为“”,则不会向hwinfo
传递任何内容,并且会执行默认的长扫描。 - 该脚本将
hwinfo
的输出通过管道传输到tee
。tee
将输出发送到zenity
和TempFile
。 - 该脚本创建一个进度条对话窗口。 它设置对话窗口的宽度和高度,以及标题和提示文本。
- 该脚本无法预先知道
hwinfo
命令将产生多少信息,因此它无法将进度条正确设置为 100%。--pulsate
选项使进度对话框显示移动指示器。 这会通知用户正在发生某些事情,他应该等待。 - 如果有人单击“取消”,
--auto-kill
选项将终止脚本。 -
--auto-close
选项会导致进度对话框在其监控的进程完成时自动关闭。
# 在 $Flag 中搜索具有适当值的硬件信息 hwinfo $标志 | 三通 >(zenity --width=200 --height=100 \ --title="整理信息" --progress \ --pulsate --text="正在检查硬件..." \ --auto-kill --auto-close) >${TempFile}
当hwinfo
扫描完成时,脚本调用zenity
来创建一个带有--text-info
选项的文本信息对话窗口。 文本信息对话框窗口显示TempFile
文件的内容:
- 该脚本设置对话窗口的宽度和高度以及标题文本。
-
--flename
选项用于读取保存在TempFIle
变量中的文件的内容。
# 在滚动窗口中显示硬件信息 zenity --width=800 --height=600 \ --title "硬件详细信息" \ --text-info --filename="${TempFile}"
当用户关闭文本信息对话窗口时,脚本退出。
出口 0
让我们把它烧起来看看。
./硬件信息.sh
出现列表框。 默认选择“短”选项。
让我们选择“Long”,然后单击“OK”。
进度窗口出现,带有一个滑动指示器。 它会一直保留在屏幕上,直到硬件扫描完成。
硬件扫描完成后,将出现文本信息对话框窗口,其中包含扫描的详细信息。
点击“确定”。
即使是顽固的命令行专家也不得不承认,几个 GUI 对话框窗口可以给不起眼的 Bash 脚本带来专业的感觉。