第25章:启动一个项目
从本章开始,我们开始构建一个程序。该项目的目的是了解各种创建程序用的 shell 特性,更重要的是,创建优秀的(good)程序。
我们要写的程序是一个报表生成器(report generator)。它将会展示各种关于我们系统的统计数据及其状态,并生成 HTML 格式的报表,以便我们能通过如 Firefox 或 Chrome 这样的网页浏览器查看报表。
程序通常是在一系列阶段中构建出来的,每个阶段会加入一些特性和功能。我们程序的第一个阶段是一个最小化的 HTML 文档,其中没有包含什么系统信息。
第一阶段:最小的文档
首先,我们需要知道组织良好的 HTML 文档的格式。看起来像这样:
如果我们在文本编辑器中键入这些文本并保存为 foo.html
文件,就可以通过下面这个 URL 在 Firefox 中看到文件:
file:///home/username/foo.html
程序的第一阶段可以将这个 HTML 文件输出到标准输出。我们可以写一个程序,轻松完成这个任务。来启动文本编辑器创建一个新的文件 ~/bin/sys_info_page
。
键入下列程序:
这是对这个问题的初次尝试,包含了一个 shebang、一条注释(永远是个好主意)和一系列的 echo
命令,一条命令输出一行。在保存文件之后,我们会使其可执行化,并尝试运行它。
当程序运行,我们应该可以看到 HTML 文档的文本显示在屏幕上,是因为脚本中的 echo
命令将输出结果送到了标准输出。我们将再次运行程序,并将程序的输出重定向到文件 sys_info_page.html
以便可以在网页浏览器中查看结果。
目前为止一切很好。
当写程序时,努力保持简单明了,永远是一个好主意。当一个程序易于阅读和理解,维护起来也就更容易,更不用提减少输入量会使得程序更容易写。我们当前版本的程序运行得很好,但是还可以更简单。我们可以合并所有的 echo
命令为一个,无疑将使在程序输出中添加更多行变得更加容易。所以,来将程序作如下改变:
一个带引号的字符串可以包含新行,所以就可以包含多行文本。shell 会持续读取文本,直到遇到闭合引用标识。在命令行中也一样奏效:
前置的 ">" 字符是包含在 PS2 shell 变量中的 shell 提示符。当在 shell 中键入一个多行文本时,它就会出现。这个特性现在还有点模糊,但是晚些时候,单我们学习到多行编程陈述时,就会显出其相当便利。
第二阶段:加一点数据
既然程序可以生成一个微型文档了,就来加一些数据到报表中吧。我们将作如下变更:
我们加入了页面标题和报表体中的标题。
变量和常量
然而我们有一个问题。注意字符串 "System Information Report" 是如何重复的吧?对于我们的小型脚本而言这不是问题,但是想象一下要是脚本足够长而且有多个这样的字符串实例。如果我们想要变更标题,将要变更许多地方,工作量不会小。要是我们能整理脚本,使该字符串仅出现一次而非反复出现呢?那就会使脚本更易于维护。下面就是我们所能做的:
创建一个名为 title
的变量,将其赋值为 System Information Report
,我们可以利用参数扩展的优势并将字符串放置在多个位置。
那么,我们要如何创建变量呢?很简单,我们只需要用就好了。当 shell 遇到一个变量时,会自动创建的。这与许多编程语言中必须在使用变量前明确声明(declared)或定义的不同。shell 对此则很松散,所以也可以导致很多问题。例如,考虑发生在命令行中的这类情形:
首先,我们将值 yes
分配给变量 foo
,随后用 echo
将其显示出来。接下来,我们显示一个拼写错误的 fool
变量的值,而得到了一个空白的结果。这是因为 shell 在遇到 fool
变量时就愉快地创建了这个变量,并给它赋了一个空值。从这里,我们学习到必须注意拼写!同样重要的是,要理解这个例子中到底发生了什么。从刚才看到的 shell 如何执行扩展,我们学习了下列命令:
承担参数扩展,并导致如下:
对比下面这个命令:
扩展为:
空的变量扩展为空!这对需要参数的命令来说是一场浩劫。例如:
我们对两个变量赋值,foo
和 foo1
。随后执行 cp
,但是拼错了第二个参数名。扩展后,cp
命令仅收到一个参数,但是其需要两个参数。
关于变量名称,有一些规则:
变量名可以由字母数字字符和下划线字符所组成。
变量名的第一个字符必须是字母或下划线。
不能使用空格和标点符号。
「变量」这个词,暗示其值是会变化的,在许多应用中,变量就是这么用的。然而,在我们应用中的这个变量,title
,则用作一个常量(constant)。常量如同变量,有一个名称并包含一个值。区别在于常量的值是不变的。在一个执行几何运算的应用程序中,我们可以定义 PI
为常量并将其赋值为 3.1415
,而不是在程序中使用字面上的数字。shell 不区分变量和常量,只为程序员方便。一般的惯例是,用大写字母指定常量,用小写字母指定变量。来修改脚本以适应这个惯例:
我们还优化了标题,加上了一个 shell 变量 HOSTNAME
,使其更绚丽些。这个变量是机器在网络上的名称。
注意: shell 实际上提供了一个方法来强制常量的不变性,使用内建的带
-r
(read-only 只读)选项的declare
命令。可以这样给TITLE
赋值:
declare -r TITLE="Page Title"
shell 会防止任何随后对
TITLE
的赋值。这个功能很少用到,但是会出现在非常正式的脚本中。
为变量和常量赋值
在这里,我们关于扩展的知识才真正开始给我们回报了。我们已经看到,变量是用这种方法赋值的:
variable=value
其中 variable 是变量名,而 value 是一个字符串。和其它一些编程语言不同,shell 不在乎分配到变量的数据类型,都把它们当字符串看待。你可以用带 -i
选项的 declare
命令限制其为整数,但是和设置变量为只读一样,这也是很少用到的。
注意,在赋值过程中,在变量名、等号、变量值之间不能有空格。那么,变量值都可以是什么呢?我可以将任何东西扩展成字符串。
多个变量可以在一行中完成赋值。
在扩展期间,变量名可以用花括号 {}
包含。这在变量名称由于其周围的上下文而变得模棱两可的情况下很有用。这里,我们尝试用一个变量,将 myfile
文件重命名为 myfile1
文件。
尝试失败,因为 shell 将 mv
命令的第二个参数解释为一个新的(也是空的)变量。这个问题可以这样解决:
通过加上一对括号,shell 不再将末尾的 1
当作是变量名的一部分了。
注意:良好的操作是将变量和命令替换放在双引号内,以限制 shell 的粉刺作用。当一个变量名包含有一个文件名时,引号更是非常重要。
我们将借此机会向报告中添加一些数据,即报告的创建日期和时间以及创建者的用户名。
here 文档
我们已经看到了输出文本的两种方式,都是使用 echo
命令。还有第三种途径叫作 here 文档(here document)或者 here 脚本(here script)。一份 here 文档是输入输出重定向的另一形式,其中我们将一段文本嵌入到脚本中,并将其输入到命令的标准输入中。工作模式是这样的:
其中 command
是接收标准输入的命令名称,token
是用来指示内嵌文本结束的字符串。我们来用 here 文档修改脚本:
取代 echo
,现在的脚本启用了 cat
命令和一份 here 文档。字符串 _EOF_
(意即文件结尾 End of File,一个共同约定)用来作为令牌,标记内嵌文本的结束。记住这个令牌必须单独出现在一行内,且不允许有尾随的空格。
那么,使用 here 文档有什么优势呢?它大部分和 echo
一样, 不一样的是,默认情况下,单引号和双引号在 here 文档中都失去了其在 shell 中的特殊意义。这里有一个命令行示例:
可以看到,shell 不理会引号。把它们当普通字符看待。这就允许我们自如地在 here 文档中内嵌引号。使得更方便地生成报告程序。
here 文档可以用在任何能接收标准输入的命令中。在下面的示例中,我们用一份 here 文档传输一系列命令到 ftp
程序,从远程 FTP 服务器取回一份文件:
如果我们将重定向符 <<
改成 <<-
,shell 将忽略 here 文档中的前置制表符(但是不是空格)。这样就允许 here 文档缩进,可以改进可读性。
这个功能存在一些问题,因为许多文本编辑器(和程序员们自己)更喜欢在脚本中使用空格来代替制表符实现缩进。
总结
本章中,我们启动了一个项目,可以带我们成功地完成创建一个脚本。我们介绍了变量和常量的概念,以及如何使用它们。它们是我们将在参数扩展中找到的许多应用中的第一个。我们还学习了如何从脚本产生输出,以及多种嵌入文本块的方法。
扩展阅读
要了解更多的 HTML 的知识,可以看下面的文章和教程:
bash 手册页包含了一个题为 "HERE DOCUMENTS" 的章节,对该功能有完整的描述。
Last updated
Was this helpful?