Linux 系统管理的哲学(四)

原文:The Linux Philosophy for SysAdmins

协议:CC BY-NC-SA 4.0

十六、使用开源软件

这个原则可能并不完全是你所想的那样。大多数时候,我们认为开源软件就像 Linux 内核、LibreOffice 或成千上万个开源软件包中的任何一个,它们构成了我们最喜欢的发行版。在系统管理的上下文中,开源意味着我们编写的自动化工作的脚本。

开源软件是任何人都可以检查、修改和增强源代码的软件。 1

— Opensource.com

引用上述内容的网页包含了对开源软件的精彩讨论,包括开源的一些优势。我建议你阅读那篇文章,并考虑它如何应用于我们编写的代码——我们的脚本。如果我们去寻找,其中的含义就在那里。这一章希望能帮助你获得一些洞察力,就像写它启发了我一样。

开源的定义

开源的官方定义非常简洁。opensource.org 的开源定义 2 的注释版本包含十个部分,明确简洁地定义了软件被认为是真正开源所必须满足的条件。

这个定义对于 SysAdmins 的 Linux 哲学非常重要,所以我在这里包含了注释定义的文本。您不必阅读这个定义,但是我建议您这样做,以便更完整地理解术语“开源”的真正含义。

注意

开源定义不是一个许可。它描述了任何许可证要被认为是开源许可证所必须满足的条件。

开源定义(带注释)

版本 1.9

下面缩进的斜体部分显示为开源定义(OSD)的注释,并不是 OSD 的一部分。没有注释的普通版本的 OSD 可以在这里找到。

介绍

开源不仅仅意味着获得源代码。开源软件的发布条款必须符合以下标准:

1.自由再分配

许可证不应限制任何一方将软件作为包含多个不同来源的程序的聚合软件分发的一个组件进行销售或赠送。此类销售不需要版税或其他费用。

基本原理: 通过约束许可要求免费再分发,我们消除了许可方为了获得短期收益而放弃许多长期收益的诱惑。如果我们不这样做,合作者将面临巨大的背叛压力。

2.源代码

程序必须包含源代码,并且必须允许以源代码和编译形式发布。当某种形式的产品不与源代码一起发布时,必须有一种广为人知的方法以不超过合理的复制成本获得源代码,最好是通过互联网免费下载。源代码必须是程序员修改程序的首选形式。不允许故意混淆源代码。不允许使用中间形式,如预处理程序或翻译程序的输出。

基本原理: 我们需要访问未混淆的源代码,因为你不能在不修改它们的情况下进化程序。因为我们的目的是使进化变得容易,所以我们要求修改变得容易。

3.衍生作品

许可证必须允许修改和衍生作品,并且必须允许它们在与原始软件许可证相同的条款下分发。

基本原理: 仅仅阅读源代码的能力不足以支持独立的同行评审和快速进化选择。为了实现快速进化,人们需要能够试验和重新发布修改。

4.作者源代码的完整性

如果许可证允许出于在构建时修改程序的目的分发带有源代码的“补丁文件”,则许可证可以限制源代码以修改后的形式分发*。许可证必须明确允许分发由修改后的源代码构建的软件。许可证可能要求衍生作品带有与原始软件不同的名称或版本号。*

理由: 鼓励大量改进是好事,但用户有权知道谁对他们使用的软件负责。作者和维护者有相互的权利知道他们被要求支持和保护他们的名誉。

因此,一个开源 许可 必须保证源代码随时可用,但可能要求它作为原始基础源代码和补丁来分发。通过这种方式,可以得到“非官方的”改变,但是容易与基本源区分开来。

5.不歧视个人或群体

许可证不得歧视任何个人或群体。

基本原理: 为了从过程中获得最大的利益,最大的 多样性 的个人和团体应该同样有资格为开源做出贡献。因此,我们禁止任何开源 许可 将任何人锁在进程之外。

包括美国在内的一些国家对某些类型的软件有出口限制。符合 OSD 的许可证可以警告被许可人适用的限制,并提醒他们必须遵守法律;但是,它本身可能不包含此类限制。

6.没有对努力领域的歧视

许可证不得限制任何人在特定领域使用程序。例如,它可能不限制程序用于商业,或用于基因研究。

理由: 该条款的主要意图是禁止那些阻止开源被商业使用的许可陷阱。我们希望商业用户加入我们的社区,而不是感觉被排除在外。

7.许可证的分发

程序附带的权利必须适用于程序被重新分发到的所有人,而不需要这些人执行额外的许可。

理由: 该条款旨在禁止通过要求签署保密协议等间接方式关闭软件。

8.许可证不能特定于某个产品

附加在程序上的权利不得依赖于程序是某一特定软件分发的一部分。如果程序是从该分发中提取出来的,并在程序许可证的条款范围内使用或分发,那么程序被再分发到的所有各方都应该拥有与最初的软件分发所授予的权利相同的权利。

基本原理: 该条款排除了另一类许可证陷阱。

9.许可证不得限制其他软件

许可证不得对与许可软件一起分发的其他软件施加限制。例如,许可证不得坚持在同一媒体上发布的所有其他程序必须是开源软件。

理由: 开源软件的经销商有权对自己的软件做出自己的选择。

是的,GPL v2 和 v3 符合这个 要求 。与 GPLed 库链接的软件只继承 GPL,如果它形成一个单一的作品,而不是任何软件,它们只是被分发。

10.许可证必须是技术中立的

许可证的任何条款均不得基于任何单独的技术或界面风格。

理由: 该条款专门针对需要明确表示同意才能在许可人和被许可人之间建立合同的许可。要求所谓“点击包装”的规定可能与 FTP 下载、CD-ROM 选集和网络镜像等重要的软件分发方法相冲突;这样的规定也可能阻碍代码的重用。一致性许可必须考虑到以下可能性:软件的 (a) 再分发将通过不支持下载的点击包装的非网络渠道进行,并且***【b】***涵盖代码(或重复使用的部分涵盖代码)可能会在不支持弹出对话框的非 GUI 环境中运行。

开源的定义最初来自于 Debian 自由软件指南 (DFSG)。

Opensource.org 网站内容以知识共享署名 4 获得许可。0 国际许可证

为什么这很重要

开源的定义对我们这些系统管理员来说很重要,原因有几个。首先,这个定义为我们提供了一个评估现有的许多许可证的框架。其中一些是真正的开源许可证,而另一些只是假装开源。

真正的开源许可让我们可以轻松合法地找到、下载和使用开源代码。如果不能保证我们使用的代码是开源的,我们将无法使用大量已经满足我们许多需求的现有代码。在任何被认为是开放源代码的许可下发布的代码没有任何产权负担。理解真正的开源许可的要求可以让我们确保我们使用的代码得到适当的许可。经过适当授权的开源代码是免费提供的,我们可以在任意多的计算机上使用它,并复制给其他人。对我们如何使用或分享它没有任何限制。有许多好的但不同的开源许可证。

开放源码倡议是公认的批准开放源码许可的权威。他们的网站上有一个最新的被批准的开源许可列表。当我们将代码开源时,我们应该应用这些许可中的一个。我们还应确保我们从他人处获得的供自己使用的软件是在这些经批准的许可证之一下分发的。

创造了这个术语

我喜欢了解 Unix、Linux 和开源的历史,所以我认为承认克里斯汀·皮特森 4 创造了“开源”这个术语是很重要的 1998 年 2 月,Peterson 与 Eric S. Raymond、Jon “maddog” Hall 和许多其他领导人举行了一系列会议,讨论 Netscape 作为自由软件的许可问题。许多人,尤其是 Peterson,不认为“自由软件”恰当地定义了他们想要完成的目标。

她提出了“开源”这个术语,并向其他一些与会者提出了这个想法。在 2 月 5 日的一次会议上,一些与会者开始使用“开放源代码”来描述不受限制且源代码容易获得的软件。这个故事确实是她的,所以请阅读她在 Opensource.com 的文章 5 。在关于 Opensource.com 的文章结尾的评论中,Eric Raymond 证实并支持 Peterson 对这个现在无处不在的术语的诞生的解释。

许可我们自己的代码

我所知道的回报开源社区的最好方法之一就是通过适当的许可开源我们自己的程序和脚本,开源社区为我们提供了所有这些不可思议的程序,比如 GNU 实用程序、Linux 内核、LibreOffice、WordPress 等等。

仅仅因为我们写了一个程序,我们相信开源并同意我们的程序应该是开源代码,并不意味着它是开源的。作为系统管理员,我们确实写了很多代码,但是我们中有多少人考虑过授权我们自己的代码的问题呢?我们必须做出选择,并明确声明代码是开源的,以及在何种许可下发布。如果没有这一关键步骤,我们创建的代码就会受到专有许可证的束缚,这样社区就不能利用我们的成果。

还记得我们在第十章创建的 bash shell 模板吗?我们在代码中包含了 GPL V2 许可证头声明作为注释,我们甚至提供了一个命令行选项,可以在终端上打印许可证头。在分发代码时,我还建议我们在代码中包含整个许可证的文本副本。

我发现非常有趣的是,在我读过的所有书籍和参加过的所有课程中,没有一次有人告诉我,作为一名系统管理员,一定要对我在任务中编写的任何代码进行许可。所有这些资料都完全忽略了系统管理员也编写代码的事实。即使在我参加的关于许可的会议中,焦点也是在应用代码、内核代码,甚至是 GNU 类型的实用程序上。没有一个演示甚至暗示了这样一个事实,即我们系统管理员编写了大量的代码来自动化我们的工作,或者我们甚至应该考虑以任何方式许可它。也许你有过不同的经历,但这是我的经历。至少,这让我很沮丧;它至多激怒我。

当我们忽视许可时,我们就贬低了代码的价值。我们大多数系统管理员甚至不考虑许可,但是如果我们希望我们的代码对整个社区可用,这是很重要的。这既不是信用的问题,也不是钱的问题。这是为了确保我们的代码现在和将来都可以以最好的自由和开源的方式提供给其他人。

Eric Raymond 写道,在计算机编程的早期,尤其是在 Unix 的早期,共享代码是一种生活方式。起初,这只是简单地重用现有的代码。随着 Linux 和开源许可的出现,这变得容易多了。它满足了系统管理员合法共享和重用开源代码的需求。

Raymond 说,“软件开发人员希望他们的代码是透明的,此外,当他们换工作时,他们不想失去他们的工具包和专业知识。他们厌倦了成为受害者,受够了被生硬的工具和知识产权壁垒挫败,以及不得不重复发明轮子。” 7 这句话同样适用于系统管理员。

这让我们想到了与组织代码共享和开源相关的问题。

组织代码共享

作为系统管理员,我们自然倾向于共享代码。我们喜欢帮助人们,这也是我们成为系统管理员的首要原因。是的,我们中的一些人更喜欢计算机而不是人,但是我们都喜欢分享我们的代码。

许多组织不知道如何共享代码,也不知道这样做的好处。其他人已经发现了这一点,有些人甚至付钱给员工来编写开源代码。

筒仓很烂

作为一名系统管理员,我在许多不同的组织中工作过,我发现许多人不擅长在内部和外部共享代码。我工作过的大多数地方甚至从未想过内部共享代码,更不用说外部了。每个开发项目都是相互独立的。部门就像筒仓,又高又窄,里面有很多青贮饲料,自我封闭的领地避免与外界接触。在许多方面,他们表现得像竞争对手,而不是为同一组织工作的团队。

我总是发现很难从这些组织的其他部门获得代码。其他内部组织的 PHB 似乎总是认为我们在与他们进行某种竞争,共享代码是一场零和游戏,共享代码的人是输家。至少,这需要数周的讨论,有时还需要某种包括保密协议在内的书面法律形式。我不是说两个部门编写的商业代码可能会在外部市场中以某种方式重叠或竞争;例如,我说的是两个内部实验室组织,它们每天执行基本相同的任务。共享代码会有很大的意义,节省很多工作,而且很容易做到。

在某些情况下,编写我们自己的代码比通过官僚主义的废话获取我们已经知道可以解决问题的代码更容易。真是浪费时间!

开放组织和代码共享

那种导致沟通不畅的小仓库的内部组织需要被一种开放的组织所取代,这种开放的组织将至少在内部鼓励代码共享。红帽公司的首席执行官吉姆·怀特赫斯特写了一本书,书名为*《开放式组织*, 9 ,书中讨论了开放式组织的优势和品质以及如何进行转变。对于 Opensource.com,怀特赫斯特也写了一篇非常有趣的文章“欣赏开放的全部力量”,他在文章中讨论了共享的概念,“共享往往会增加它的价值,因为共享让越来越多聪明、有创造力的人得到它。如果你能和尽可能多的人分享尽可能多的东西,那么当你消除分享的限制时,这个价值实际上会增加。正如开源倡议组织所说,这意味着分享你的指令、配方、源代码,并向所有人开放,而不是限制某些人、团体或“努力的领域”

2005 年,Karl Fogel 写了一本有趣的书,《生产开源软件*——如何运行一个成功的自由软件* 项目12 ,并在 2017 年推出了第二版。Fogel 详细介绍了创建开源软件的技巧、技术、法律问题以及社会和政治基础设施。这是一本有趣的书,详细介绍了创建真正开源软件的许多实际方面。它讨论了使用开源许可在内部和外部共享代码的优点。

一些组织付钱给他们的员工来写开源代码。例如,许多公司雇佣一些员工为内核编写代码,如果得到 Linus Torvalds 的批准,这些代码最终将与全世界的程序员共享。这并不总是纯粹的利他主义,因为许多这样做的公司希望让内核更好地为他们自己的软件工作。在许多情况下,这种新的或修改过的代码将使 Linux 更好地为每个人工作,Torvalds 可能会接受它进入内核源代码树。

除了内核之外,许多开源项目都得到组织的支持,这些组织理解在金钱和代码上支持开源软件的价值主张。

要避免的事情

这一章是关于使用开源软件的,但这意味着我们也需要区分真正的开源软件和那些有隐藏限制或根本不符合他们声称他们的软件被分发的许可的软件。令人难过的是,我们需要讨论那些错误地宣称他们的软件是开源的公司。

这也是我在本章中加入开源定义的原因之一。理解开源的目标可以帮助你理解什么时候一个许可不能满足需求。但是还有其他的事情需要小心。

如果软件供应商声明他们的软件是开源的,那么源代码应该很容易从互联网上下载。在某些情况下,我对软件很感兴趣,在浏览网站时没有发现源代码可用的迹象。在这些情况下,没有人回答我关于这个问题的询问。

如果为了下载某些软件,您需要提供您的姓名、电子邮件地址和其他识别信息,那么该软件肯定不是来自一家声誉良好的公司,即使他们声称使用开源许可证。我见过许多所谓的“免费白皮书”下载,如果它们不需要某种形式的“注册”,我会很感兴趣。我建议避开这些公司。他们可能利用开源软件的虚假或误导性承诺来建立垃圾邮件发送者的电子邮件地址列表。

代码可用性

用开源许可证许可代码是一回事;把它提供给其他人则完全是另一回事。我在本章开始引用的开源代码的定义意味着我们的代码必须以某种方式提供,这样任何人都可以下载和查看源代码。在本章的前一节中,我提到了像填写注册表单这样的要求表明,如果代码确实是在开源许可下发布的,那么它就是被非法限制的。

我们如何让我们的代码在开源许可下自由使用?有许多分享我们代码的好方法。我们来看一些。

如何共享我的代码?

既然我们的代码可以在经过批准的开源许可下发布,那么我们如何实际发布它并让其他人可以使用它呢?请注意,本章开头的开源定义并没有规定开源软件应该如何交付。

我所读到的任何东西都没有定义一个被认可的发布开源软件的机制。我读过的许可证以及我读过的关于开源软件分发的法律意见都是关于让源代码和可执行文件一起可用的。对于脚本来说,可执行文件是源

分享我们的开源代码非常容易。对我来说,当我安装了一些我写的脚本,以减轻我为客户和朋友建造或维修的计算机上的系统管理任务时,它就开始了。然后我开始把我的一些脚本放到 u 盘上,这样我就可以把它们给别人了。并不是说我做了很多。我有一些客户,但没有多少人对一些 bash 脚本感兴趣。比起我的脚本,更多的人对 Fedora 的现场 DVD 或 USB 驱动器感兴趣。

下一步是让脚本可以从互联网上下载,我已经用我的完成了。为了让这些脚本更广泛地可用,我已经将它们发布在我的技术网站上,Linux 的数据手册, 13 at http://www.linux-databook.info/?page_id=5245 。您可以下载它们,并在您认为合适的时候使用它们。代码全部在 GPL V2 下发布,PDF 文档在知识共享署名-共享许可下发布。

使用 SourceForge 14 和 GitHub 等开发者协作网站也是合适的。这些网站允许其他人轻松下载你的代码,以便参与开发。它们提供版本管理,并允许您作为主要开发人员只合并您认为合适的代码。

我曾经在我的一个项目中使用过 SourceForge,但是那个项目已经死了很久,并且已经被另一个项目取代了。像 SourceForge 和 GitHub 这样的网站的优势之一是,当当前的首席开发人员决定离开时,他们可以让其他人轻松接管项目。这就是我领导的项目所发生的事情。我从另一个需要把时间花在其他项目上的开发人员那里接管了它。

代码共享注意事项

关于共享您的代码,有几件重要的事情需要考虑。我将在这里简单地谈一谈。重要的是你意识到了它们,并能得到更多你需要的信息。

机密

保密是许多人关心的问题,这是理所当然的。意图保密的数据或代码可能会在开源软件中暴露。

当然,隐藏据称是机密或商业秘密的代码会使整个程序成为专有程序。如果你从一个程序中删除你的代码并隐藏它,那么从开源的角度来看,整个程序都是无用的。要成为真正的开源,你必须坚持到底。所有代码要么开放,要么不开放。

数据完全是另一种野兽。Eric Raymond 的分离法则 16 论述了政策与执行的分离。这意味着程序的用户界面,也就是实现策略的地方,应该与程序中实现机制的部分分开。这使得使用文本模式或 GUI 界面以及在不改变程序底层逻辑的情况下改变这些界面成为可能。

我们也可以将这种分离规则应用于程序所使用的数据。数据永远不应该作为程序的一部分存储,尽管我看到过这样做。我自己也犯了这个错误。

作为一种良好的编程形式,程序使用的数据必须与程序代码分开。这确保了当数据所涉及的外部事物发生变化时,数据本身很容易被改变。即使使用脚本,配置数据也应该与组成程序逻辑的代码分开维护。使用单独的配置文件使不熟练的用户可以进行修改,而不用担心损坏代码本身。

数据与程序的分离也意味着没有必要分发任何可能与代码本身一起保密的数据。

提供支持

当我分发我的代码时,有人发现了一个 bug,会发生什么?我有义务修理它吗?我有必要回答使用我的软件的人的问题吗?

这些问题的答案是“不”。我们没有任何义务支持我们提供的开源代码。为什么呢?因为开源许可——至少我使用的 GPL V2 17 明确声明没有保修。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 16-1

我使我的脚本可用的 GPL V3 包含了这样一个条款,它清楚地表明代码没有隐含或明确的保证(原文是大写的。)

如果你在 opensource.org 阅读各种批准的许可证,你会发现他们都有类似的措辞。然而,尽管许可证中有声明,我们大多数人都希望我们的代码能为任何获得它的人工作——也包括我们自己。所以我们修复任何出现的问题,因为这对我们和我们的用户都有好处。

像 Red Hat 这样的公司,以及像 Document Foundation、 18 这样负责 LibreOffice 办公程序套件的实体,都有支持结构、bug 和问题报告程序,以及帮助解决使用问题并提供指导和支持的志愿者。

离别的思绪

使用其他人创造的开源软件是重要的,但是我并不是建议我们完全放弃使用专有软件,当它满足了一个无法满足的需求时。我的意思是,在广泛的谷歌搜索未能找到合适的开源软件后,我们考虑专有软件,然后探索编写一个脚本来执行该任务的可能性。

如果你选择写一个脚本来解决问题,开源你自己的代码。将它提供给其他人,因为如果您需要执行此任务,其他人也需要这样做。您将为他人节省您在创建脚本时投入的工作。

远离总是在已经有可用的东西时编写自己的代码的陷阱。我们可以编写自己的基于网络的内容管理或博客软件,但是已经有很多这样的软件了。WordPress、Drupal、Joomla、Plone、OpenCms、Mambo 以及更多的工具已经可以使用,这比你自己编写要简单得多。如果您需要的某个部分还不可用,那么为您选择的任何一个编写一个插件。

可以的话用别人写的开源软件;剩下的时间写别人能用的开源软件。

Opensource.com,什么是开源?https://opensource.com/resources/what-open-source

2

【opensource.org】【开源定义(注释)】https://opensource.org/osd-annotated

3

开源倡议,许可, https://opensource.org/licenses

4

维基百科,【Christine Peterson】https://en.wikipedia.org/wiki/Christine_Peterson

5

彼得森、克里斯汀、Opensource.com、我是如何创造出‘开源’这个术语的https://opensource.com/article/18/2/coining-term-open-source-software

6

Eric s . raymond,Unix 编程的艺术,Addison-Wesley (2004),380,ISBN 0-13-13-142901-9。

7

同上。

8

Opensource.com,什么是开放组织https://opensource.com/open-organization/resources/what-open-organization

9

吉姆·怀特赫斯特,《开放组织》,哈佛商业评论出版社(2015 年 6 月 2 日),ISBN 978-1625275271

10

Opensource.com, https://opensource.com/open-organization/16/5/appreciating-full-power-open

11

参见本章中的带注释的开源定义

12

Fogel,Kark,生产开源软件, https://producingoss.com/en/index.html

13

双双,http://www.linux-databook.info

*  14

https://sourceforge.net/

15

https://github.com/

16

Raymond,Unix 编程的艺术,15–16 页。

17

开源倡议,许可证–GPL V2, https://opensource.org/licenses/GPL-2.0 ,第十一部分。

18

https://www.documentfoundation.org/ 文档基础】

*

十七、力求优雅

优雅是很难定义的事物之一。当我看到它的时候我就知道了,但是把我看到的用一个简洁的定义表达出来是一个挑战。使用 Linux dict命令,Wordnet 给优雅下了一个定义:“在解决一个问题(特别是在科学或数学方面)时整洁和巧妙简单的品质”;他的发明的简单和优雅。"

在本书的上下文中,我认为优雅是硬件和软件的设计和工作中的一种美丽和简单的状态。当一个设计是优雅的,软件和硬件工作得更好,效率更高。简单、高效、易懂的工具有助于用户。

在技术环境中创造优雅很难。也是必要的。优雅的解决方案产生优雅的结果,并且易于维护和修复。优雅不是偶然发生的;你必须为之努力。

简单的品质是技术优雅的很大一部分。如此之大,事实上它应该有自己的一章,第十八章,“寻找简单”,但我们在这里不忽略它。本章讨论了硬件和软件优雅的含义。

硬件优雅

是的,硬件可以是优雅的——甚至是漂亮的,赏心悦目的。设计良好的硬件也更可靠。优雅的硬件解决方案提高了可靠性。

我们许多系统管理员既负责软件,也负责硬件。对于我们这些在小型组织中工作的人来说尤其如此,但在大型环境中也是如此。在我为思科工作的五年中,我的系统管理员工作需要我给新服务器装架和布线,识别和修复硬件问题,帮助设计机架布局和电源要求,以及更多与硬件相关的任务。

理解硬件的优雅与理解软件和操作系统的优雅同样重要。

印刷电路板

在谷歌上搜索“pcb 1 可靠性”,可以找到许多关于 pcb 设计和可靠性的文章和论文。Darvin Edwards 的一篇文章“PCB 设计及其对器件可靠性的影响”, 2 讨论了影响可靠性的 PCB 设计的四大领域。爱德华兹讨论的一个因素是热机械可靠性。反复的功率循环会导致快速的热变化,进而导致元件、走线(电导体)和焊点的膨胀和收缩。随着时间的推移,这些重复的热应力循环会导致 PCB 出现各种类型的故障。

在我大学的一门技术制图课上,我的作业之一是为印刷电路板绘制一组图纸。布局已经提供了,我真正需要做的就是使用我们刚刚学到的一些新技术重新绘制它。

当我查看图中的元件布局、走线和焊盘图形(元件焊接在 PCB 上的位置)时,我有了一点感悟。我们在课堂上学到的一件事是,每个焊点都是潜在的故障点,PCB 上使用的每个跳线(用于“跳过”其他走线的短导线)都会增加两个故障点,每个焊点一个。在这个板上有两个跳线,我决定看看我是否可以改变设计来消除它们。我把几条线路改到了不同的位置,消除了跳线。我把这个给老师看了,他的回答是这是一个更好的解决方案。他在那个项目上给了我一个好分数。

底板

硬件优雅可能意味着主板的简单和布局良好的设计,这是一个相当大的 PCB。如前所述,良好的主板设计可以提高可靠性。

在我看来,一个布局良好的主板,看起来很好,表面有光滑的导线和焊盘图案(元件焊接的安装焊盘),是优雅的。主板上放置的组件不会相互干扰,也不会与以后可能添加的其他组件(如功能强大但较长的视频适配器)相互干扰,CPU 插槽位于主板上,以便 RAM 内存和其他主板组件不会干扰添加高容量空气冷却风扇或液体冷却设备,这是一种优雅的设计。坦率地说,我一直很欣赏设计良好的主板的外观。这些主板是真正的艺术品。

计算机

设计良好的电脑是优雅的。这包括一个 Shell,其设计便于接触内部组件,提供大量不受限制的气流,大量安装风扇和液冷散热器的位置,以及大量便于电缆布线的选项。

这与主板上的大量 LED 照明、风扇、LED 灯条和花哨的照明控制器无关。这种东西是用来表演和娱乐的。我为我的主工作站购买的最新主板是华硕 TUF X299,它满足了我对这台工作站的所有要求。这也是唯一一个满足我所有需求的。它的背面恰好有一串发光二极管,可以产生简单的滚动颜色的光显示。我没有在 BIOS 中关闭它们,因为它们有点好玩,但我不会特意在我的需求中添加 LED 显示。

当外部电源和硬盘活动指示灯容易看到,并且亮度适中,因此在任何照明条件下都能看到时,计算机就是优雅的。电源和复位开关也很容易操作,但不要太显眼,否则可能会被意外碰到,导致计算机复位或关机

我可以继续说下去,但你应该明白。

数据中心

硬件优雅还意味着精心规划和建造的机房或数据中心。机架式盘柜的布局方式使得多个电源可以轻松接入,并且前后通道畅通无阻。布线整洁有序——不像图 17-1 中那样杂乱无章;它被切割成一定长度,在整个计算机房的电缆通道和电缆槽中无扭结或缠结…

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 17-1

它可能会完成工作,但这种布线工作肯定不是优雅的。知识共享 CC0

应使用不间断电源来维持所有设备的电力供应,直到发电机可以联机,并且在电源故障的情况下,电力可以切换到该发电机以实现长期稳定。电源和接地导线本身的布线应既安全又方便。

电源和接地

很难想象计算机的电源和接地应该有一个像“优雅”这样的术语。但是这里有一些事情需要考虑。

大约在 1976 年,我在俄亥俄州利马的 IBM 公司担任客户工程师。除了修理坏掉的电脑和安装新电脑,我的职责之一是协助客户规划新电脑的安装。这包括规划合适的电源和接地。在这种特殊情况下,我与客户就电源和良好接地的要求进行了长时间的讨论。良好的接地对于正确的电子操作和计算机的稳定性至关重要。

那么什么是好的理由呢?这是一根带有绿色绝缘(绿色地线)的大规格电线,从受保护的设备(计算机)连接到一个埋在潮湿土壤中至少 10 英尺深的铜桩。绿线接地不得连接任何其他接地线,也不得连接到它所经过的任何配电箱中的任何接地或中性母线。请注意,这是 IBM 对计算机逻辑操作完整性以及人类安全的定义。

经过我们的讨论后,客户说他们有一口旧井,内衬铜,至少有 80 英尺深,水深超过 60 英尺。这是一个很好的接地,我同意将计算机的绿线接地连接到井套管是合适的接地。使用这个预先存在的非常好的地面点是一个优雅的解决方案。

安装后,我们除了问题什么也没有。这些看似随机但经常发生的问题,某一天可能是内存故障,第二天可能是磁盘问题,第三天可能是处理器问题,等等。在大约两周的时间里,我们更换了内存、CPU 板和几乎所有我们能想到的东西,客户感到不安是可以理解的。

我现在相当确定这是接地问题。我和工厂的电工讨论了接地问题,他说他已经按照我们讨论的那样接好了地线。但我必须亲眼看看才能确定。

我拿出示波器,用一个感应钳夹住地线,这样我就能看到地线的任何电噪声。当我把这一切都搞定后,IT 副总裁过来了,我告诉他我在做什么。至少可以说,他有点怀疑。

就在我们结束讨论的时候,我们都听到了一个电机启动的声音,在示波器上出现了一大串电噪声。不一会儿,有人从机房跑出来,大喊电脑坏了。我不可能找到比心怀不满的副总统更好的听众了。特别是因为我们听到启动的马达是我们旁边的大型软饮料机上的压缩机马达。

我们让电工开始拆除所有配电箱的前面板,以便查看和验证绿线接地的完整性。在我们看的第一个盒子里,我立刻发现了问题。有一根又大又丑、看起来很脏、很旧的电线被嫁接到我们曾经干净的绿色电线地上。

大约就在这个时候,有人走进我们一直站着的那个小房间,把几封信放进了邮资机。地线产生的噪音说明了一切,因为这两个设备都插入了同一个电源插座。我们让电工剪掉了我漂亮的新的干净的绿色地线的丑陋的旧地线,客户再也没有因为接地问题而引起的其他问题。

有时候,优雅是一个原始的绿色电线地面。

软件优雅

在这里,我回到专门讨论 shell 脚本,这是系统管理员通常做的编码类型。对于一个系统管理员来说,用像 C 这样的语言编写代码是很不寻常的,这需要更多的开发工作,并且需要编译。对于系统管理员来说,这是对时间的浪费。

关于软件优雅有很多观点。是什么让软件变得优雅,在软件世界中“优雅”是什么意思?以下是我的一些观点,并对每一个观点做了一些解释。

一般来说,优雅是看起来很好,甚至很漂亮的代码,并且遵循本书中概述的原则。在我看来,当你使用这些指导方针时,软件是优雅的。这是我列出的特征,我敢打赌,其他系统管理员对于什么是软件优雅有他们自己的想法。在任何情况下,这些都不是硬性规定,只是指导方针。任何软件最重要的方面是它应该执行你写它要做的任务。使用这些指导方针可以让其他人——和你——更容易理解你做了什么,并维护你写的代码。

  1. 使用一致的缩进——代码应该在过程和流程控制结构的缩进中保持一致。这有助于在各种情况下更容易可视化程序的结构和执行流程。

    • 我知道一些开发人员使用制表符缩进,其他人使用空格。人们使用的制表符或空格的数量也各不相同。只要代码能被任何没有编写它的人容易地阅读——也能被那些确实编写了它的人容易地阅读,那么这基本上是无关紧要的。
  2. 布局清晰的设计 -代码应该很好地布局和排序,以便在各种条件下容易看到执行的流程。

    • 最有效的代码是以直通方式执行的代码,它不会来回跳转,也不会有不必要的流控制结构来降低速度。

    • 使用过程有很好的理由,例如防止在多个地方复制相同的代码。然而,在可能的情况下,程序的主体应该以简单明了的方式流动。

  3. 使用****STDIO——我们已经看到 STDIO 是一个强大的使能器;它允许我们将许多小程序链接在一起,以执行单个程序无法完成的复杂任务。具有强制用户界面(CUI)的程序(如菜单)不提供 STDIO。这种程序仅限于独立存在,不能在数据流中工作。应该避免使用强制用户界面,因为它们的局限性太大,不能很好地与命令行管道和重定向配合使用。

    • fdisk 程序是使用菜单界面的有用且强大的实用程序的一个例子。这样做的问题是 fdisk 不能在脚本中使用。有人编写了一个单独的程序,从脚本中执行 fdisk 函数。目前的工具是 sfdisk。
  4. 添加有意义的注释 -程序被很好地注释了有意义的信息。这有助于让维护人员清楚代码的目的,并确保可以快速定位和修复问题。

  5. 每个程序都应该做好一件事——这个指导原则一直是 Unix 和 Linux 哲学的宗旨,并产生了核心实用程序,以及其他小型的、针对单一任务并能很好地执行该任务的核心实用程序。这就产生了强大而灵活的命令行程序,可以组合成管道来执行单个程序无法完成的复杂任务。

    • 这样做的一个副作用是做一件事的程序往往很小。这使得它们易于理解,并在必要时进行修改。

    • 这个原则的推论是,给这些小程序增加更多的功能通常不是一个好主意。对所谓“新特性”的需求实际上应该被看作是对新程序的需求,新程序也应该遵循这些指导原则。大多数新特性,当修补到现有程序上时,只会造成代码膨胀,使程序更难使用和维护。

  6. 沉默是金——Linux 命令行工具通常不会向系统管理员显示一切正常的消息。这可以防止不需要的消息进入 STDOUT 数据流,进入管道并给后面的程序造成混乱。

  7. 总是使用最少的必要代码 -使用执行所需任务所需的最少代码。其他的都是糟粕,应该淘汰。这就是简单的症结所在,也是复杂的对立面。

    • 一些程序员喜欢炫耀错综复杂的代码,这些代码无法确定入口和出口。这种类型的代码不太实用,容易出现错误。

    • 在另一个极端,有一种游戏,一种竞赛,真的,叫做“代码高尔夫” 3 目标是用最小可能的结果可执行二进制实现指定的算法。这绝对是而不是我们在这个特定的指导方针中所做的。诸如此类的竞争是好的,只要它们不被带入系统管理的实际实践中。在 SysAdmin 的上下文中,使用最少的必要代码意味着尽可能地满足这些准则的其余部分。Code golf 则不然,因为它在追求最小化的过程中忽略了其他一切。

  8. 输出易于阅读和理解 -当需要任何输出时,用户应易于理解。对于许多程序来说,输出是它们存在的理由。

    • 充斥着与程序目的没有什么关系的消息和其他信息的输出混淆了重要的数据。输出的实际结构并不重要,只要它清楚地服务于预期的目的。
  9. 使用有意义的变量名**——我喜欢在我的命令行和 shell 编程中使用有意义的变量名。对于几年后需要调试代码的人来说,随机变量名或像$X 这样的名字没有什么意义;这包括最初编写代码的人。

    • A c c o u n t T o t a l 和 AccountTotal 和 AccountTotalNumberOfUsers 这样的名字远比 A 1 、 A1、 A1B3 更有意义。它们使得阅读代码更加容易。它们也是第二十章中“记录一切”原则的良好开端。命名良好的变量告诉程序维护人员,在调试程序时,变量如何适应程序的逻辑以及预期的值的种类。

    • 回到我负责清理的 Perl 程序,变量名是如此的随机,以至于其中几个变量名指向同一个东西。我重命名了程序中的所有变量,然后能够用一个单独的变量名代替同一个变量的其他不同名称。仅仅这一小步就在清理该特定程序方面向前迈出了一大步。

    **
    *** 遵循埃里克 S. 雷蒙的 17 Unix 规则4**-**这是包括系统管理员在内的所有开发人员都应该阅读和理解的 17 条规则。Raymond 在他的书《Unix 编程的艺术》中详细阐述了这些规则。 5 维基百科对这些规则有一个很好的总结(见脚注 3)。

    • 如果你认为我的列表中缺少一个重要的指导方针,它可能在雷蒙德的规则列表中。请务必阅读这些规则,因为它们适用于系统管理员和开发人员。

      * 测试一切 -这难道不明显吗?!显然不是,因为我遇到过很多显然没有经过很好测试的软件。

    • 我在思科的工作是双重的。有一段时间我是实验室经理的助理,测试部门的测试就是在那里进行的。其余时间,我是测试人员之一,负责测试 Linux 驱动的设备。

    • 测试不仅仅是运行一系列测试程序来验证被测软件可以执行其设计任务,它还确保软件在遇到意外输入时不会失败。黑客用来获得对由软件运行的计算机和其他设备的未授权访问的最常见的漏洞之一是软件不能处理意外输入。

    • 我做的其他测试是简单地阅读文档和代码,以确定代码是否符合文档中概述的规范。如果没有,我不得不使它失败,或者开发团队不得不获得一个异常,这是很少见的。

    • 在审查代码和文档时,我所做的一部分工作是确保代码的设计支持 Linux 理念和文档完善的标准,如文件系统分层标准,以及为确保所有 Linux 发行版使用的一致性而创建的标准。

      * 清除 cruft - Cruft 是程序中从未使用过的所有旧代码。许多程序随着时间的推移而发展,有时曾经有用的代码不再需要。当我修改自己的脚本或添加新的特性或选项时,我有时会发现自己的代码和变量不再被使用,需要清理掉。

    **

**遵循这些准则将有助于确保您编写的代码易于阅读和修改。它看起来很好,运行也很好。会很优雅的。

修复我的网站

现在你已经知道我使用 WordPress 来托管我自己的网站和其他网站。我使用它是因为它是免费的开源软件,能够很好地完成任务并提供很大的灵活性。然而,事情确实会出错。你可以指望它。在这个特殊的例子中,我在不同的网站上遇到过两次这个问题,现在已经修复了两次。

这个问题的症状是令人费解的,直到我确定了来源。在博客页面上看起来一切正常,这是我的 both.org 网站的主页。这个问题只在我试图显示这个网站的任何静态页面时才出现。

静态页面显示主题元素,如顶部横幅和网站名称。每一页都显示了正确的标题,但没有显示内容。这是一个没有内容的页面。我试图改变主题,但没有成功,这意味着问题不在于主题本身。我安装了另一个 WordPress 实例,并将其指向 both.org 网站现有的 MySQL 数据库。症状没有改变或消失。

唯一需要检查的地方是 MySQL 数据库。太糟糕了,我忘记了mysqlcheck工具;我可能会很容易地解决这个问题。幸好我忘记了mysqlcheck工具,因为我学到了比其他方式更多的东西。

这个问题很容易解决。我将该网站的 mysql 数据库文件从我的日常备份复制到/var/lib/mysql/wordpress 目录,并重新启动 MySQL。

WordPress 可以为每个网站使用不同的 MySQL 数据库,或者为每个网站使用一个带有不同表格的 MySQL 数据库。每个网站的表都有不同的表名前缀。这个前缀在 WordPress wp-config.php 文件中为每个站点定义。

我在/var/lib/mysql/wordpress 目录中找到了正确的数据库文件集,并把它们保存到另一个位置以防万一。然后我去了我的一个几天前的备份,因为我不确定这个问题是什么时候开始的。我把备份文件复制到/var/lib/mysql/wordpress 目录,重启 mysql。在这一点上一切都很好。我可能不需要重新启动 MySQL,但是我想我可能还需要一个干净的重新启动来刷新可能在某个地方的缓存中的任何东西。

唯一可行的方法是 WordPress 和 MySQL 都打开,这样我就可以查看 WordPress 的代码以及 WordPress 和 MySQL 的配置和数据文件。我本来可以下载 MySQL 的源代码,但是不需要。对于 WordPress 我也不需要这么做,但是因为它是用 PHP 编写的,所以它是完全开放的。

MySQL 的数据文件存储在 Linux FHS 定义的位置/var,这是数据库文件!我能够找到它们,确定哪些文件属于我的网站,并轻松地用以前的备份替换它们。

另一个促成因素是,我编写的备份脚本创建了以正常格式和目录结构存储文件的备份。它不会将它们压缩成 tarballs、zip 文件,或者——更糟糕的是——某种专有的备份格式。我可以用 cd 和 cp 之类的命令行工具访问我的文件;午夜指挥官(mc)文本模式文件管理器;或者诸如 Krusader、Dolphin 等 GUI 文件管理器。当我找到我想要的备份文件时,我可以简单地将它们复制到所需的位置来替换损坏的文件。

也可以使用mysqldump命令将数据导出到一个文件中,该文件是将重建数据库的 SQL 命令的脚本。我过去曾经尝试过这种方法,发现效果相当好。两种方法都可以,但是我更喜欢我自己的方法。

WordPress、MySQL 和我的备份解决方案的优雅结合在一起,可以轻松解决手头的问题。我从解决这个问题中学到的一件事是,对于 MySQL 数据库来说,花哨的备份解决方案是不必要的。

拆卸脚

创造优雅是一项艰苦的工作。维护它会更加困难。Cruft 是程序中多余的程序和代码,旧的数据文件,以及包含已删除程序中剩余文件的目录。

作为系统管理员,清除 cruft 是我们工作的一个重要部分。我们可以搜索 cruft 的东西包括旧的或未使用的软件、我们自己脚本中的旧代码以及旧的配置和数据文件。幸运的是,我们有一些工具可以帮助我们完成这项任务。

旧的或未使用的程序

我刚刚删除了一些我不用的程序。我正在使用 KDE 应用启动器,注意到列表中有几个 Calligra 办公套件程序。我从来不用 Calligra,更喜欢 LibreOffice。它被默认安装在 Fedora 上,我在几个月前测试的时候用了它。因为我永远不会在我的生产工作中使用它,所以我决定删除它。

但是我们如何主动搜索不用的程序呢?有一种方法可以找到所谓的孤儿——任何其他程序都不需要的程序。这种情况通常很少,所以使用我们必须尝试找到它们的工具不会有什么坏处。对于此任务,我们使用rpmorphan实用程序列出 RPM 软件包,这些软件包不依赖于主机上安装的任何其他软件包。

实验 17-1

如果 rpmorphan 包还没有安装,请安装它。

[root@testvm1 ~] dnf -y install rpmorphan

列出孤立的软件包。

[root@testvm1 ~]# rpmorphan
liberation-sans-fonts
liberation-serif-fonts
libertas-usb8388-firmware
libkolab
libsss_autofs
libsss_sudo
libyui-mga-gtk
libyui-mga-qt
libyui-qt-graph
[root@testvm1 ~]#

您的孤立包列表将与我的不同。我的测试虚拟机上的一些“孤立”包可能可以删除,但我真的需要额外的字体。我也不能说在没有很好的研究的情况下是否可以安全地删除其他包,但是我确实查看了 libkolab 包,它看起来可以从我的 VM host 上安全地删除。如果已经安装了它,请将其删除,然后重新安装;如果还没有安装,请安装它,这样我们可以看到另一个选项。

[root@testvm1 ~]# dnf -y remove libkolab ; dnf -y install libkolab

让我们使用 rpmorphan 的时间函数来识别最新的包。首先让我们看看一天前安装的孤儿。

[root@testvm1 ~]# rpmorphan -install-time +1
liberation-sans-fonts
liberation-serif-fonts
libertas-usb8388-firmware
libsss_autofs
libsss_sudo
libyui-mga-gtk
libyui-mga-qt
libyui-qt-graph

注意 libkolab 不在这个列表中。现在找到不到一天前安装的孤儿。

[root@testvm1 ~]# rpmorphan -install-time -1
libkolab

我们看到的唯一孤儿是 libkolab。如果在过去的一天里安装了其他软件包,它们也会被发现并被删除。

rpmorphan 工具有许多有趣的选项,使我们能够做一些事情,比如定位那些超过某个日期的孤立包。它还可以查找比特定日期更新的包。后一个选项允许我们删除为了测试而添加的包。

阅读手册页,了解更多关于一些有趣选项的信息。您会看到它也有一个 GUI 选项,但我更喜欢命令行界面。这个程序说明了许多程序员喜欢遵循的一个重要考虑。它确实与埃里克·雷蒙德的分离法则有关。 6 在这种情况下,程序员将程序的逻辑和功能方面从用户界面中分离出来。这种逻辑与用户界面的分离允许他们创建一个命令行界面和两个图形界面,一个基于 tk,另一个基于 curses。

小心!

不要乱拆孤儿包。这可能会导致删除所需的包。他们是孤儿并不意味着不需要他们。删除孤立包时要谨慎。rpmorphan 工具只允许我们识别应该进一步调查的包,以便我们可以确定删除它们是否真正安全。

deborphan工具可以用于 Debian 发行版。事实上,rpmorphan 是基于 deborphan。rpmorphan 工具仅定位孤立项,而不会删除它们。如果您决定删除任何孤立包,您可以使用包管理器,如 yum 或 dnf。

这些工具无法发现我们希望从系统中删除的所有软件包。找到孤儿是一回事,但是许多没有显示为孤儿的包也可能被删除。例如,我在本章前面提到的 Calligra office 套件不会显示为孤儿,LibreOffice 或许多其他用户级应用也不会。有时删除这些大程序可以恢复大部分磁盘空间。

您可以使用桌面的应用启动器来定位您从不使用的用户级包。这也可以帮助你找到一些你从来不使用的 GUI 管理工具。

小心!

不要试图使用软件包管理器的-y 选项删除软件包。这可能会导致删除许多您不想删除的包。当您的软件包管理器显示如果您回复“y”它将删除的软件包列表时,请务必仔细检查该列表。检查列表后,您可以选择“y”或“n”。这样安全多了。

如果您决定删除找到的软件包,请注意不要删除其他需要的软件。我总是使用不带-y 选项的 package removal 命令,该命令会不停地删除所有依赖于我要删除的包的包,以及我要删除的包所依赖的包。请务必检查您的软件包管理员准备删除的软件包列表,如果有您认为不应该删除的软件包,请回答“否”。

我曾经试图移除一个我认为不需要的包。作为依赖项被删除的软件包有数百个,它们会把 KDE 桌面从我的系统中完全删除。那绝对不是我想做的。

脚本中的旧代码

在脚本中寻找 cruft 代码也是系统管理员至少应该偶尔承担的任务。清除未使用的代码和定位语法错误可能具有挑战性,但是有一些工具可以帮助我们。

shellcheck实用程序就像 C 和其他语言的 lint 7 。它扫描为 bash 和 bash 类 shells、sh、dash 和 ksh 编写的脚本,寻找可以改进的语法和语法。一如既往,你可以选择是否做出建议的改变。

让我们看看这个工具是如何工作的。

实验 17-2

让我们从安装 ShellCheck 包开始——是的,用所示的大写字母。

[root@testvm1 student]# dnf -y install ShellCheck

现在让我们使用 shellcheck 检查 shell 脚本模板。

[student@testvm1 ~]$ shellcheck script.template.sh | less

我收到了许多类似这样的 SC2086 错误。

In script.template.sh line 92:
   if [ $verbose = 1 ]
        ^-- SC2086: Double quote to prevent globbing and word splitting.

shellcheck 实用工具有点过分热心于让我们在变量周围加上双引号。有一些边缘情况 8 时,这可能是一个问题,但我自己从来没有遇到过。因此,在检查以确保我们没有这些边缘情况之一后,我们可以在下一个命令中排除这些错误。

[student@testvm1 ~]$ shellcheck --exclude SC2086 script.template.sh

In script.template.sh line 152:
RC=0
^-- SC2034: RC appears unused. Verify it or export it.

In script.template.sh line 153:
Test=0
^-- SC2034: Test appears unused. Verify it or export it.

In script.template.sh line 160:
if [ `id -u` != 0 ]
     ^-- SC2046: Quote this to prevent word splitting.
     ^-- SC2006: Use $(..) instead of legacy `..`.

现在更容易看到一些可以从代码中删除的未使用的变量。我们还看到了一些关于 if 语句的语法建议。

现在您可以看到 shellcheck 突出显示的几个问题。使用此信息进行您想要的任何更改。

除了shellcheck所能告诉你的关于语法、孤儿变量和其他事情之外,有时你只需要浏览代码。例如,shellcheck没有发现的一种 cruft 是多余的过程,这些过程不会在脚本中的任何地方被调用。如果不需要,也可以删除。

您是否在脚本模板中看到了一个未使用的过程?有一个SelectPkgMgr()没有在模板中使用,并且shellcheck没有发现它是多余的。

旧文件

有时旧软件不再需要时就会被删除。在许多情况下,软件包删除过程会留下它们的用户级配置文件。这些通常是我们在主目录中找到的隐藏的“点”文件。

这些遗留下来的配置文件的好处是,如果软件包被重新安装,我们将不会丢失我们的个人配置。糟糕的是,经过很长一段时间,这些文件会大量积累。

例如,我最近删除的 Calligra 的个人配置文件仍然位于我的主目录中。

旧的数据文件也留在我们的硬盘上,它们可能已经没有任何用处了。这通常是因为我们很少花时间评估我们拥有的所有文件,以确定它们是否可以删除、归档或保留。找到旧文件的一个简单方法是使用find命令来确定文件被访问的最后时间。

实验 17-3

首先,让我们创建几个旧文件。我们用触摸命令来做。如果没有参数,touch 会将 atime、mtime 和 ctime 全部设置为当前系统时间。首先让我们使用stat命令来查看您在之前的实验中创建的文件之一 file0.txt 的属性。如果您没有这个文件,现在就创建它。

[student@testvm1 ~]$ stat file0.txt
  File: file0.txt
  Size: 15            Blocks: 8          IO Block: 4096   regular file
Device: fd03h/64771d    Inode: 393236      Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1001/ student)  Gid: ( 1001/ student)
Context: system_u:object_r:user_home_t:s0
Access: 2018-02-02 15:39:56.415630341 -0500
Modify: 2018-01-27 11:41:36.056367865 -0500
Change: 2018-01-28 12:15:03.176000000 -0500
 Birth: -

这显示了文件的当前访问、修改和更改时间(atime、mtime 和 ctime)。它们可能相同,但也可能不同,除非您刚刚创建了该文件。现在不使用任何选项来触摸文件,将这三个属性设置为当前时间。然后再次检查时间。

[student@testvm1 ~]$ touch file0.txt
[student@testvm1 ~]$ stat file0.txt
  File: file0.txt
  Size: 15            Blocks: 8          IO Block: 4096   regular file
Device: fd03h/64771d    Inode: 393236      Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1001/ student)  Gid: ( 1001/ student)
Context: system_u:object_r:user_home_t:s0
Access: 2018-02-23 10:28:25.794938943 -0500
Modify: 2018-02-23 10:28:25.794938943 -0500
Change: 2018-02-23 10:28:25.794938943 -0500
 Birth: -
[student@testvm1 ~]$

请注意,这三个时间现在完全相同。这里我们使用touch来设置 atime——上次访问文件的时间——更早。下面命令中的-a 选项告诉 touch 命令只设置 atime。t 选项使用以下时间戳将日期和时间设置为 2013 年 7 月 15 日 16:45:23。

[student@testvm1 ~]$ touch -a -t 1307151645.23 file0.txt
[student@testvm1 ~]$ stat file0.txt
  File: file0.txt
  Size: 15            Blocks: 8          IO Block: 4096   regular file
Device: fd03h/64771d    Inode: 393236      Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1001/ student)  Gid: ( 1001/ student)
Context: system_u:object_r:user_home_t:s0
Access: 2013-07-15 16:45:23.000000000 -0400
Modify: 2018-02-23 10:28:25.794938943 -0500
Change: 2018-02-23 10:48:13.781669926 -0500
 Birth: -

请注意,ctime 也已更改。ctime 是最后一次更改文件索引节点,并且是在我们设置 atime 时发生的。

到目前为止,我们所做的只是为实验设置了条件。现在,我们可以使用 find 命令根据 atime 查找旧文件。使用如下所示的 find 命令查找超过两年的文件。find 命令上的 atime 选项使用以天为单位的年龄,实际上是以“现在”开始的 24 小时周期。因此,我们需要使用 365*2 = 730 天作为我们的时间段。我们将 atime 设置为五年前,因此测试文件应该在这个测试中出现。

[student@testvm1 ~]$ find . -atime +730
./file0.txt

file0.txt 文件按预期显示。您还可以显示最近超过 730 天被访问的文件。通过 sort 实用程序传输结果,以便更容易看到 file0.txt 不在列出的列表中。

[student@testvm1 ~]$ find . -atime -730 | sort
.
./.bash_history
./.bash_logout
./.bash_profile
./.bashrc
./.cache
./.cache/mc
./.cache/mc/Tree
./.config
./.config/mc
./.config/mc/ini
./error.txt
./file1.txt
./file2.txt
./file3.txt
./file4.txt
./file5.txt
./file6.txt
./file7.txt
./file8.txt
./file9.txt
./good.txt
./index.cgi
./.lesshst
./.local
./.local/share
./.local/share/mc
./.local/share/mc/history
./.mozilla
./.mozilla/extensions
./.mozilla/plugins
./mymotd
./perl.index.cgi
./script.template.sh
./test1.html
./test1.txt
./.viminfo

find命令可以根据大小、权限、名称和其他标准来定位文件。但是,它所能做的只是找到值得进一步研究的文件。这一调查是以任何程度的把握知道应该如何处理找到的文件的唯一途径。这通常意味着调查内容,但有时也可以根据文件名或文件位置来确定处理方式。

使用 find 命令的一个潜在问题是,最近从备份中恢复的文件没有保留它们的属性。这可能会使旧文件看起来比实际更新,并妨碍轻松识别最旧的文件。在这种情况下,再次需要使用基本工具,如ls命令或您最喜欢的文件管理器来搜索文件,打开它们检查内容,如果不再需要,就删除它们。

另一个可用于定位可能被归档或删除的文件的标准是大小。有两种方法可以做到这一点。我们可以使用find命令或du命令。find 命令让我们对结果有了更多的控制,因为我们可以组合参数并做一些有趣的事情,例如查找所有大于 15MB、最后一次访问是在五年多以前,并且属于特定用户的文件。在下一个实验中,我们将首先查看du命令,然后查看find命令。

实验 17-4

以学生用户的身份执行此实验。

我们再次需要做一些设置,以便使这个实验比学生用户的主目录中只有几个小文件的实验更有趣。首先,我们将创建~/Documents 目录(如果它不存在的话),然后我们将向其中添加一些大小不断增加的文件。

[student@testvm1 ~]$ mkdir Documents

下一个命令应该在一行中输入。它会在~/Documents 目录中创建 100 个数据量不断增加的文件。

[student@testvm1 ~]$ count=0;while [ $count -lt 100000 ]; do count=$((count+1000)); echo $count;dd if=/dev/urandom of=~/Documents/file-$count.txt bs=256 count=$count ;done

make ~/记录 PWD 并列出内容。为了简洁起见,我在这里只显示了前 20 个文件。如果你愿意,你可以去掉 head 实用程序,这样你就可以看到它们了。

[student@testvm1 Documents]$ ls -l | head -20
total 1262600
-rw-rw-r--. 1 student student 25600000 Feb 23 15:32 file-100000.txt
-rw-rw-r--. 1 student student  2560000 Feb 23 15:31 file-10000.txt
-rw-rw-r--. 1 student student   256000 Feb 23 15:31 file-1000.txt
-rw-rw-r--. 1 student student  2816000 Feb 23 15:31 file-11000.txt
-rw-rw-r--. 1 student student  3072000 Feb 23 15:31 file-12000.txt
-rw-rw-r--. 1 student student  3328000 Feb 23 15:31 file-13000.txt
-rw-rw-r--. 1 student student  3584000 Feb 23 15:31 file-14000.txt
-rw-rw-r--. 1 student student  3840000 Feb 23 15:31 file-15000.txt
-rw-rw-r--. 1 student student  4096000 Feb 23 15:31 file-16000.txt
-rw-rw-r--. 1 student student  4352000 Feb 23 15:31 file-17000.txt
-rw-rw-r--. 1 student student  4608000 Feb 23 15:31 file-18000.txt
-rw-rw-r--. 1 student student  4864000 Feb 23 15:31 file-19000.txt
-rw-rw-r--. 1 student student  5120000 Feb 23 15:31 file-20000.txt
-rw-rw-r--. 1 student student   512000 Feb 23 15:31 file-2000.txt
-rw-rw-r--. 1 student student  5376000 Feb 23 15:31 file-21000.txt
-rw-rw-r--. 1 student student  5632000 Feb 23 15:31 file-22000.txt
-rw-rw-r--. 1 student student  5888000 Feb 23 15:31 file-23000.txt
-rw-rw-r--. 1 student student  6144000 Feb 23 15:31 file-24000.txt
-rw-rw-r--. 1 student student  6400000 Feb 23 15:31 file-25000.txt

du -a命令简单地列出了文件及其大小,以及每个目录中所有文件的累积大小。我们可以使用它轻松快速地找到最大的文件和包含最大数据量的目录。我们通过sort实用程序运行结果,得到一个按数字排序的列表,最大的文件和目录在最后。在这种情况下,我只显示列表中的最后 20 项。

[student@testvm1 ~]$ du . -a | sort -n | tail -20
20752   ./Documents/file-83000.txt
21000   ./Documents/file-84000.txt
21252   ./Documents/file-85000.txt
21500   ./Documents/file-86000.txt
21752   ./Documents/file-87000.txt
22000   ./Documents/file-88000.txt
22252   ./Documents/file-89000.txt
22500   ./Documents/file-90000.txt
22752   ./Documents/file-91000.txt
23000   ./Documents/file-92000.txt
23252   ./Documents/file-93000.txt
23500   ./Documents/file-94000.txt
23752   ./Documents/file-95000.txt
24000   ./Documents/file-96000.txt
24252   ./Documents/file-97000.txt
24500   ./Documents/file-98000.txt
24752   ./Documents/file-99000.txt
25000   ./Documents/file-100000.txt
1262604 ./Documents
1262780 .

结果以千字节为单位。请注意,由于目录中包含的文件,这些目录在底部附近排序。使用du时,很难将目录与文件分开。

find命令可以更具体一点。让我们找到所有大于 20MB 的文件。

[student@testvm1 ~]$ find . -size +20M
./Documents/file-93000.txt
./Documents/file-94000.txt
./Documents/file-90000.txt
./Documents/file-92000.txt
./Documents/file-89000.txt
./Documents/file-88000.txt
./Documents/file-91000.txt
./Documents/file-98000.txt
./Documents/file-84000.txt
./Documents/file-85000.txt
./Documents/file-83000.txt
./Documents/file-97000.txt
./Documents/file-100000.txt
./Documents/file-96000.txt
./Documents/file-95000.txt
./Documents/file-82000.txt
./Documents/file-87000.txt
./Documents/file-86000.txt
./Documents/file-99000.txt
[student@testvm1 ~]$

注意,find命令没有列出文件大小。我们可以在find命令中添加一些代码来实现这一点。

[student@testvm1 ~]$ find . -size +20M -exec ls -l {} \;
-rw-rw-r--. 1 student student 23808000 Feb 23 15:32 ./Documents/file-93000.txt
-rw-rw-r--. 1 student student 24064000 Feb 23 15:32 ./Documents/file-94000.txt
-rw-rw-r--. 1 student student 23040000 Feb 23 15:32 ./Documents/file-90000.txt
-rw-rw-r--. 1 student student 23552000 Feb 23 15:32 ./Documents/file-92000.txt
-rw-rw-r--. 1 student student 22784000 Feb 23 15:32 ./Documents/file-89000.txt
-rw-rw-r--. 1 student student 22528000 Feb 23 15:32 ./Documents/file-88000.txt
-rw-rw-r--. 1 student student 23296000 Feb 23 15:32 ./Documents/file-91000.txt
-rw-rw-r--. 1 student student 25088000 Feb 23 15:32 ./Documents/file-98000.txt
-rw-rw-r--. 1 student student 21504000 Feb 23 15:32 ./Documents/file-84000.txt
-rw-rw-r--. 1 student student 21760000 Feb 23 15:32 ./Documents/file-85000.txt
-rw-rw-r--. 1 student student 21248000 Feb 23 15:32 ./Documents/file-83000.txt
-rw-rw-r--. 1 student student 24832000 Feb 23 15:32 ./Documents/file-97000.txt
-rw-rw-r--. 1 student student 25600000 Feb 23 15:32 ./Documents/file-100000.txt
-rw-rw-r--. 1 student student 24576000 Feb 23 15:32 ./Documents/file-96000.txt
-rw-rw-r--. 1 student student 24320000 Feb 23 15:32 ./Documents/file-95000.txt
-rw-rw-r--. 1 student student 20992000 Feb 23 15:32 ./Documents/file-82000.txt
-rw-rw-r--. 1 student student 22272000 Feb 23 15:32 ./Documents/file-87000.txt
-rw-rw-r--. 1 student student 22016000 Feb 23 15:32 ./Documents/file-86000.txt
-rw-rw-r--. 1 student student 25344000 Feb 23 15:32 ./Documents/file-99000.txt
[student@testvm1 ~]$

我们现在有了主目录中最大文件的列表。在这种情况下,它们都在~/Documents 目录中。

我们又一次拥有了可以帮助我们识别主目录中最大文件的工具。仍然需要一些判断来决定这些文件中的哪些(如果有的话)可以被删除或存档。

最后一句话

要做到本章和我给你指出的参考文献中讨论的每一件事并不总是可能的。如果我们能做到,那就太好了,但在现实生活中,我们不可能总是这样做。我们的脚本永远不会完全摆脱 cruft,它们永远不会达到最高水平的优雅。

这一章的标题应该暗示了这一点。优雅是我们努力追求的目标,但我们可能永远也不会达到这样的巅峰:所有的 cruft 都被删除,所有的代码都尽可能地高效,为我们的代码添加了准确数量的清晰简洁的完美注释,所有的编程规则和建议都被遵循。

由于多种原因,这是不可能的。我最常遇到的两个问题是,PHB 不关心我们,也不允许我们有时间,以及这些指导方针中的一些——至少在某种程度上——是冲突的。

我们确实有一些工具可以帮助定位脚本中的 cruft 和硬盘上的文件。虽然这些工具可能有所帮助,但它们并不完善,只能做这么多。作为系统管理员,在我们的代码和目录中搜索 cruft 真的取决于我们;有时,这意味着手动浏览,看看有什么可以消除。这很费时间,我不喜欢这样做,但确实需要这样做。

使用这些工具在我们的系统上找到我们的主目录(或其他非 root 用户的主目录)中最大和最老的文件,这可能是清理 cruft 的第一步。它给了我们一个起点,让我们可以用最少的努力获得最好的结果。删除最大和最早的文件后,继续寻找较小和较新的文件来删除或移动到归档存储变得不太有效。

印制电路板

2

Edwards,Darvin,电子设计, PCB 设计及其对器件可靠性的影响, http://www.electronicdesign.com/boards/pcb-design-and-its-impact-device-reliability

3

维基百科、高尔夫码https://en.wikipedia.org/wiki/Code_golf

4

维基百科,Unix 哲学,章节:埃里克雷蒙的 17 条 Unix 规则https://en.wikipedia.org/wiki/Unix_philosophy#Eric_Raymond%E2%80%99s_17_Unix_Rules

5

雷蒙德,埃里克·s .,Unix 编程的艺术http://www.catb.org/~esr/writings/taoup/html/

6

Raymond,Eric S .,Unix 编程的艺术,章节分离的法则http://www.catb.org/~esr/writings/taoup/html/ch01s06.html#id2877777

7

维基百科、 Lint 、 [https://en.wikipedia.org/wiki/Lint_(software](https://en.wikipedia.org/wiki/Lint_(software) )

8

GitHub,shellcheck,双引号防止 globbing 和 word splitting】, https://github.com/koalaman/shellcheck/wiki/SC2086

**

十八、力求简单

UNIX 基本上是一个简单的操作系统,但是你必须是一个天才才能理解这种简单性。 1

—丹尼斯·里奇

我绝不会屈尊不同意 Unix 的创造者之一。然而,自从我开始使用 Unix 和 Linux 以来,我自己的观点已经发生了变化。Linux 哲学的原则帮助我巩固了我对这样一个事实的理解,即 Linux 是简单的,并且这种简单是由哲学所阐明的。

本书中的许多原则相互交叉并相互加强。我毫不怀疑你已经开始自己看到了这一点。在第十七章中,我讨论了优雅,但有一件事我没有列出,那就是简单,尽管它在书中顺便提到了。我相信简单的概念应该在系统 管理员的 Linux 哲学中有自己的一章。

在这一章中,我们寻找 Linux 的简单性。

数字的复杂性

是的,GNU/Linux 表面上很复杂。据我所知的一本书, Linux 简而言之、、2 、包含了 372 个 Linux 命令的列表。是的,我数过了。另一本书,我最喜欢的初学者的书,Linux、命令、编辑器和 Shell 编程实用指南3 涵盖了“… 98 个实用程序…”。

但是这些数字与我想出的另一个数字相比是微不足道的。实验 1 展示了一种估计 Linux 计算机上命令总数的方法。大多数作为命令行命令的可执行文件都位于/usr/bin 目录中,因此计算该目录中文件的数量是一个非常好的估计。

实验 18-1

以学生用户的身份执行此实验。确定/usr/bin 中有多少可执行文件。

[student@testvm1 ~]$ ls  /usr/bin | wc -w
2635

是的,这是一大堆命令。当然你看到的数字会不一样。我的技术审查人员本·科顿告诉我,他的笔记本电脑上的/usr/bin 中有 1,992 个文件。您可以看到,根据您拥有的发行版和已安装的软件包,会有一个范围。

我用来创建和测试这些实验的测试虚拟机是一个非常基本的安装,带有 KDE 和 MATE 桌面以及一些应用,如 LibreOffice。该虚拟机有 2,633 个可执行 Linux 文件,其中大部分是 CLI 命令。对于刚刚学习 Linux 的人来说,这些数字看起来太多了。我刚开始做系统管理员的时候,他们就是这么对我的。

大约在 1996 年或 1997 年,当我刚开始学习 Linux 的时候,我拿起了几本关于 Linux 的书——当时并没有那么多可用的——并且发现了当时对我来说难以想象的大量命令。我认为我不可能学会所有这些命令。

当我看到标题为“您将实际使用的 77 个 Linux 命令和实用程序”, 4 和“50 个最常用的 UNIX / Linux 命令(带示例)”的文章时,我感到很害怕这些标题暗示着你必须记住一些命令,或者知道大量的命令是很重要的。

我确实读过许多这样的文章,但是我通常寻找新的和有趣的命令:可以帮助我解决问题或简化命令行程序的命令。

简单的基础

虽然我妈妈认为我是个天才,但我真的不是。但是我很执着。我从来没有尝试去学习所有的 Linux 命令,不管你会想出多少个“所有”的总数。

我只是开始学习在任何给定的时刻,无论手头有什么项目,我都需要的命令。我开始学习更多的命令,因为我接受个人项目和工作项目,这些项目将我的知识延伸到极限,并迫使我找到以前我不知道的命令,以完成这些项目。随着时间的推移,我的命令越来越多,在解决问题时,我越来越熟练地应用这些命令。我开始寻找报酬越来越高的工作,让我玩我最喜欢的玩具 Linux。

随着我对管道和重定向、标准流和标准 I/O 的了解,以及对 Unix 哲学和 Linux 哲学的了解,我开始理解命令行是如何以及为什么让 Linux 和核心实用程序变得如此强大。我学到了编写命令行程序的优雅之处,这些程序以惊人的方式操纵数据流。

我还发现,有些命令即使没有完全过时,也很少使用,而且只在不寻常的情况下使用。仅仅因为这个原因,找到一个 Linux 命令列表并记住它们是没有意义的。作为一名系统管理员,学习许多可能永远都不需要的命令并不是对时间的有效利用。

这里的简单性是了解你需要什么来完成手头的任务。将来会有大量的任务需要您学习其他命令。当你需要的时候,总有办法发现和学习这些命令。我发现在需要的时候发现和学习新的命令对我来说非常有效。几乎任何新项目,包括写这本书,都会导致寻找新的命令来学习。

永无止境的简化过程

然而,仅仅因为一个解决方案有效并不意味着你应该停止寻找更好的方法。系统管理员的一个共同特点是,我们总是在寻找更好的方法来做我们已经在做的事情。有时我发现一个我以前不知道的命令,我意识到它比我已经用来完成任务的一个、两个或更多的命令更合适。

在我十多年前编写的一个程序中,我在一个管道中使用了一系列以dmidecode开头的命令来确定系统的硬件架构是 32 位还是 64 位。这很麻烦,但大部分工作。我后来发现了一个 Linux 命令,arch,它可以完成以前需要几个命令才能完成的工作。我修改了我的剧本;结果没有改变,但是程序更简单、更高效、更优雅。

简单与性能或效率无关——至少没有直接关系——它更关乎优雅。通过简化,我的程序变得更有效率,性能也提高了。这就是优雅。

简单是一个永无止境的过程。它从未停止,因为我总是在学习新的东西和新的方法来应用我已经知道的东西。

简单的程序做一件事

我们大多数直接与计算机打交道的人确实喜欢找乐子。早期的计算机程序员也不例外。他们写了大量的程序,让我们都有一些严肃的乐趣。我们极客也只是想找乐子!

大约在 1970 年,我是俄亥俄州托莱多一家小公司的夜间电脑操作员之一。在所有真正的工作完成之后,我们将在 IBM 1401 大型机上获得一些乐趣。我们会玩像井字游戏这样的游戏,或者打印不应该在这里复制的 ASCII 艺术页面。井字游戏很有趣,但在那台旧电脑上玩也很有趣,也很有挑战性。计算机总是将第一步棋作为“X ”,并在一张计算机纸上打印出最终的 3×3 矩阵。人类玩家必须打开前面板上的一个感应开关来指示他们想要放置“O”的方块的号码,然后按下一个按钮来告诉计算机继续运行程序。那些是美好的旧时光。

早期的 Unix 程序员给了我们一些有趣的东西,比如 adventure、fortune 和 cowsay。最后两个可以用来说明简单性。这种简单性是因为这两个程序都被设计为只做一件事。fortune 程序将随机的运气打印到 STDOUT,cowsay 从 STDIN 获取文本字符串,并将它们显示在一只卡通牛的语音气球中。

使用您的软件包管理器安装 fortune 和 cowsay,因为它们不太可能已经安装在您的计算机上。在 Fedora 的当前版本上,它们是“fortune-mod”和“cowsay”。对于 Fedora 和其他发行版的早期版本,您可能需要使用“fortune”作为包名。

实验 18-2

安装 fortune-mod 和 cowsay,以防它们还没有安装。以 root 身份做这部分实验。

[root@testvm1 ~]# dnf -y install fortune-mod cowsay

本实验的其余部分应该以学生用户的身份进行。现在运行几次 fortune 命令来查看结果。

[student@testvm1 ~]$ fortune
Vulcans believe peace should not depend on force.
-- Amanda, "Journey to Babel", stardate 3842.3

我承认在这个特殊的结果显示出来之前,我尝试了几次。如果您想检查,现在/usr/bin 中可能还有一两个文件。

没关系——继续玩一会儿财富程序吧。

完成了吗?那么让我们继续和考赛玩吧。cowsay 程序需要一个文本串作为输入,所以做实验 18-3 中所示的事情。cowsay 获取文本字符串并将其放入奶牛的语音气球中。看起来很傻,但它会让人上瘾。只是当你使用它的时候,要小心谁在你身后看着你。

实验 18-3

让我们单独尝试一下 cowsay 程序。

[root@testvm1 ~]# cowsay hello world!
 ______________
< hello world! >
 --------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

这个玩一会儿也是可以的。我可以看到有人可能在 shell 程序中使用 cowsay 而不是 echo 来打印消息,但是 cowsay 不维护原始消息文本的列格式;它只是把所有东西混在一起。

我们有两个小程序,每个程序只做一件事。让我们把它们放在一起,利用 cowsay 程序在 STDIN 上接受输入这一事实。实验 18-4 展示了如何做和结果。在这里,我不得不再次承认,为了得到这个结果,我运行了几次程序,但这是其中一次运行的真实输出。

实验 18-4

fortunecowsay的管道输出。

[student@testvm1 ~]$ fortune | cowsay
 ________________________________________
/ But I have a holy crusade. I dislike   \
| waste. I dislike over-engineering. I   |
| absolutely detest the "because we can" |
| mentality. I think small is beautiful, |
| and the guildeline should always be    |
| that performance and size are more     |
| important than features.               |
|                                        |
\ - Linus Torvalds on linux-kernel       /
 ----------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

将两个简单的程序结合起来,每个程序都很小,每个程序都做一件事,这样就可以创建更复杂的程序。我也花了一段时间才得到这个结果。拼写错误在原文中。

fortune 和 cowsay 都有简单的接口,他们都执行一个单一的任务,他们做得很好,他们使用 STDIO。它们都有一些命令行选项,可以用来稍微修改它们的行为,但是一旦你像实验 18-4 中所示的那样一起使用它们,就没有什么其他的东西可以学习了。如果您想了解他们为数不多的命令行选项,可以查看他们的手册页。

简单的程序很小

为了看看这两个程序有多小,请运行实验 18-5 中的命令来查找信息。两个都不是很大。小程序易于理解和维护。

实验 18-5

这个命令让我们找到cowsayfortune程序的大小。

[student@testvm1 ~]$ ls -l `which cowsay` `which fortune`
-rwxr-xr-x 1 root root  4460 Nov 20 11:20 /usr/bin/cowsay
-rwxr-xr-x 1 root root 28576 Aug  2 19:54 /usr/bin/fortune

这些程序之所以小,是因为它们都只做一件事。向这些程序中的任何一个添加更多的功能都会显著增加它们的大小,并使它们更难维护。此外,这有什么意义呢?这两个程序是完美的,因为它们都符合为它们设置的要求。

现在以同样的方式考虑其余的 GNU/Unix/Linux 实用程序。这个ls程序应该完成什么?它唯一的功能是列出一个目录中包含的文件,记住目录本身就是文件。它可以通过使用一个或多个选项,或者根本不使用选项,以多种不同的方式完成这项任务。

如果没有选项,ls命令只列出当前目录(PWD)中的非隐藏文件名,并且在每行输出中列出尽可能多的文件名。l 选项是一个很长的列表,在一个易于阅读的漂亮的柱状列表中显示了文件的权限、大小和其他数据。-a 选项显示所有文件,包括隐藏的文件。r 选项列出文件,递归遍历每个子目录,并列出每个子目录中的文件。如果没有参数,ls命令会列出 PWD 中的文件。使用不同的目录路径作为参数,它可以列出其他目录中的文件。该参数的其他变体允许您列出特定的文件。

ls实用程序有许多其他有趣的选项和参数变体,可以与它一起使用。阅读ls的手册页,查看所有的可能性。

注意,文件 globbing 是由 shell 而不是由ls命令处理的。因为 shell 为所有以文件名作为参数的程序和脚本处理文件 globbing,所以这些程序都不需要这样做。shell 将与 globs 匹配的文件名扩展到程序和脚本操作的文件列表中。这也是简单。为什么要在每个程序中包含文件打包功能,而它只需要在一个地方,即 shell 中。

关于ls实用程序,您应该注意的是,每个选项、每个参数变化都有助于生成文件列表。就是这样——这就是它所做的一切,列出文件。这就是它的简单之处,它只做一件事,而且做得很好。给这个程序增加更多的功能是没有意义的,因为它不需要这些功能。

简单和哲学

起初我希望这样一个技术上不健全的项目会失败,但我很快意识到它注定会成功。只要有足够的决心,软件中的几乎任何东西都可以实现、出售甚至使用。一个单纯的科学家说什么都无法抵挡一亿美元的洪流。但是有一种品质不能用这种方式买到,那就是可靠性。 可靠性的代价是追求极致的简单性。 这是非常富有的人最难支付的价格。 6

— C. A. R. Hoare, 7 写关于编程语言 PL/I 8 (重点是我的。)

我遇到的许多更有趣的软件问题都涉及到现有代码的简化——尤其是我自己的代码。给程序增加新的功能会增加它的复杂性。一个快速的新特性被添加到现有的代码中,并被用来满足最后期限,这增加了复杂性。

最难做的事情之一是降低代码的复杂性。但从长远来看,这是有回报的。

简化我自己的程序

我自己编写的一个程序,一个 bash shell 脚本,在一个基本的 Fedora 安装程序不止一次失控后,我编写它来执行许多任务。我已经在第九章“自动化一切”中提到了这个后安装程序但现在我需要讨论它的阴暗面。

由于 Fedora 版本之间的变化,程序的需求也发生了变化。需要修改程序来安装一些在默认安装过程中不再安装的软件包。有时我需要添加代码来删除自动安装的包,因为我不想要或不需要它们。

添加新代码来做这些事情增加了程序的复杂性。在某些情况下,当程序初始化时,我添加了更多的选项进行评估,以便我的选项保持开放——可以说——关于我的程序所需的更改。经过几年的时间,这个项目变得很大,资金也很充足。我最近花了一些时间使用shellcheck实用程序和我自己对代码的观察来删除 cruft——大部分是未使用和不再需要的过程——这将代码的大小减少了几百行。

简化他人的程序

谈论我如何修复别人的代码总是更有趣。我过去的一项咨询工作涉及到对一组现有的 Perl 程序进行几乎完全的重写。在一台小型英特尔服务器上运行着大约 25 个这样的程序。由于杂乱的代码和缺乏注释,维护这些程序来添加新功能、定位和修复错误变得不可能了。我被分配的任务是修复这些程序的缺陷,并给它们增加一些额外的功能。

当我开始尝试理解复杂得吓人的代码时,很明显我的首要任务是简化代码。在对代码进行了大量的注释并修复了一些错误之后,我开始收集一些已经被插入到两个或更多程序中的代码,并将它们收集到 Perl 库中。这使得修复问题变得更加容易,因为它们只需要在一个地方修复——库。我理顺了其他代码,简化了常见的执行路径。

修改后的程序更快,更小,更容易维护。问题可以在几小时和几分钟内找到并解决,而不是几天。

未注释的代码

我翻遍了我的个人档案,找到了图 18-1 中的代码。我根本不知道它是从哪里来的。我不知道为什么我还留着它。它没有任何评论。少数几个变量名的长度不仅仅是几个字符,但是它们几乎没有告诉我们程序的目的或者它应该如何工作。

即使是图 18-1 中的“用法”程序——显然是“帮助”功能——也不是特别有用,因为它只显示了一点关于用法语法的内容,实际上仍然没有说明程序的目的。嗯——除了程序名。这表明它可能与 USB 库有关。这是可以理解的吗?我不这么认为。我花了不少时间想弄明白。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 18-1

这段代码是做什么的?

我看到的几个变量在第二个 case 语句中被赋值,然后用于确定通过底部的一系列 if 语句的流程。事实上,可以通过删除所有 if 语句并将 echo 语句移到 case 语句的匹配部分来重构这段代码,使之更简单。这将消除代码中对这些变量的需求。

我将这个脚本复制到一个虚拟机上,用不同的选项组合尝试了几次。结果如图 18-2 所示,并没有太多启发性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 18-2

运行这个程序的结果也没有帮助

一点也不像蹩脚的代码。在不知道这段代码应该做什么的情况下,我该如何修复它呢?看起来这段代码可能只是一个测试,或者是某个更大的脚本的开始,打算做些什么。这段代码的真正问题在于,它需要花费宝贵的时间来发现它显然没有做任何有用的事情。

**我最终使用了下面实验 18-6 中显示的dnf命令,发现这个脚本是 USB 开发库的一部分。我不知道它是如何出现在我的个人~/bin 目录中的。

实验 18-6

可以使用dnf命令定位到为其配置主机的某个存储库中的 RPM 包中的文件。

[root@david ~]# dnf whatprovides *libusb-config
Last metadata expiration check: 2:10:49 ago on Sat 24 Feb 2018 01:50:16 PM EST.
libusb-devel-1:0.1.5-10.fc27.i686 : Development files for libusb
Repo        : fedora
Matched from:
Other       : *libusb-config

libusb-devel-1:0.1.5-10.fc27.x86_64 : Development files for libusb
Repo        : fedora
Matched from:
Other       : *libusb-config

我们现在知道哪个 RPM 包提供了这个文件,所以让我们看看这个包是否安装在我们的主机上。

[root@david ~]# dnf list libusb-devel
Last metadata expiration check: 2:11:35 ago on Sat 24 Feb 2018 01:50:16 PM EST.
Available Packages
libusb-devel.i686         1:0.1.5-10.fc27                 fedora
libusb-devel.x86_64       1:0.1.5-10.fc27                 fedora

这些 rpm 是可用的,这意味着它们尚未安装。

在这种情况下,结果表明该脚本来自一个没有安装在我的主机上的 RPM。因为它是一个开发包,所以我也不太可能自己安装它。底线是我可以删除这个脚本,因为——至少对我来说——它是 cruft。

当然,这也是系统管理员工作的一部分。在执行一些需要的任务的脚本中找到这些无用的脚本,并去掉它们。它还包括在有用的脚本中找到无用的变量、永远不会被执行的代码行和其他垃圾,并将其删除。识别和清除积垢需要时间和一定程度的投入。

五金器具

我们已经在第十七章“追求优雅”中谈到了一些硬件在讨论简单性时,这也是一个合适的话题。毕竟,硬件是软件运行的引擎。

如今硬件并不是特别复杂。有标准的主板尺寸,ATX,迷你 ATX,微型 ATX,和扩展 ATX。大多数台式和塔式计算机机箱都是标准化的,可以接受这些尺寸中的任何一种,除了扩展 ATX。

稍加研究,就有可能买到与市场上任何标准主板兼容的 CPU 和 RAM 内存 DIMMs。GPU、SATA 和 USB 插入式适配器等其他适配器通过标准主板通用的标准化 PCI Express 总线实现。

电源是标准化的,并且都适合专门分配给它们的空间。唯一真正的区别是它们能够提供的总功率瓦数。电源连接器及其提供的电压早已标准化。

USB 和 SATA 连接器使得将设备从硬盘连接到鼠标变得非常简单和快速。在当今情况下,硬盘等设备都是标准尺寸,很容易放入为其设计的空间中。

我确实说过如今硬件并不特别复杂,但严格来说这并不正确。在主板、机箱、适配器、电源等等宏观层面上,确实如此。但是这些装置在微米和纳米水平上都变得更加复杂。随着芯片变得越来越小、越来越复杂,它们包含了越来越多的必要逻辑,使最终用户的生活变得更简单。

也许在 80 年代早期,当最初的 IBM 个人电脑首次发布时,你还不在。集成电路(IC)可能只包含现在的一小部分组件,而且它们的运行速度也只是我们现在认为理所当然的速度的一小部分,更不用说那些超频人群可以达到的速度了。

1981 年,英特尔 8088 单核 CPU 在 33 平方毫米的面积上容纳了 29,000 个晶体管。1010 核酷睿 i7 Broadwell-E 是维基百科页面脚注 10 中列出的最新英特尔 I 系列处理器,在 246 平方毫米中包含 32 亿个晶体管。这是仅 7.5 倍面积内晶体管数量的 11 万多倍。所有这些额外的能力使得在 CPU 内部完成过去手工完成的复杂任务成为可能。

在早期,集成电路比较简单,晶体管也少得多。跳线插针和 DIP 开关是配置硬件的常见和令人困惑的方式。现在,我可以将计算机引导至 BIOS 配置模式,并在 GUI 环境中进行更改。但是在大多数情况下,甚至这也不是必需的,因为硬件和操作系统几乎都是自己配置的。

Linux 和硬件

今天的 Linux 为配置硬件带来了惊人的简单性。大多数时候不需要用户干预。过去,Linux 用户经常需要为某些硬件安装设备驱动程序。现在,Linux 几乎总是为我们做所有的工作。

在第五章中,我们看了 Udev 守护进程及其机制,它使 Linux 能够在引导时识别硬件,并在引导后的任意时间热插拔硬件。让我们看一下当新设备连接到主机时会发生什么的简化版本。我在这里规定主机系统已经在 multi-user.target(运行级别 3)或 graphical.target(运行级别 5)上启动并运行。

  1. 用户插入新设备,通常是外部 USB、SATA 或 eSATA 连接器。

  2. 内核检测到这一点,并向 Udev 发送一条消息来宣布新设备。

  3. 基于设备属性及其在硬件总线树中的位置,Udev 为新设备创建一个名称(如果还不存在的话)。

  4. Udev 系统在/dev 中创建设备专用文件。

  5. 如果需要新的设备驱动程序,它会被加载。

  6. 设备已初始化。

  7. Udev 可以向桌面发送通知,使得桌面可以向用户显示新设备的通知。

将新硬件设备热插拔到正在运行的 Linux 系统并使其准备就绪的整个过程非常复杂——对于操作系统而言。对于只想插入新设备并让它工作的用户来说,这非常简单。这极大地简化了最终用户的工作。对于 USB 和 SATA 硬盘、USB 拇指驱动器、键盘、鼠标、打印机、显示器和几乎任何其他东西,作为用户,我需要做的只是将设备插入适当的 USB 或 SATA 端口,它就可以工作了。

困境

对我来说,最终目标是让最终用户尽可能地简单。我们不能忘记,我们系统管理员也是最终用户。我更喜欢完成实际工作,而不是花几个小时摆弄一个新设备,只是为了让它工作。那是旧的做事方式。但是这种新的做事方式将复杂性从等式的人的一面转移到了软件的一面。并且硬件复杂性的成倍增加也增加了软件的复杂性。

所以我们的困惑是,一方面我们被告知我们的程序应该简单,但另一方面我们应该将复杂性转移到软件中或者完全摆脱它。希望用户不需要处理它。

调和复杂性和简单性之间的矛盾是开发人员和系统管理员的任务。我们为“自动化一切”而创建的程序和脚本确实需要尽可能简单。但是他们还需要能够执行手头的任务,以便尽可能简化最终用户的任务。

计算机不可靠,但人类更不可靠。

—吉尔布不可靠性定律

当您作为系统管理员工作了一段时间后,前面那句话的真实性就显而易见了。在某些时候,我们的用户总会找到一种方法去做一些意想不到的事情,这将比我们在程序和脚本中可能做的任何事情造成更大的破坏和混乱。这意味着我们的目标必须是遵循编写小程序的基本原则,每个小程序都做好一件事,并使用 STDIO 进行交互。

让我们不要忘记最大的讽刺——我们系统管理员也是人,至少现在是这样——这使我们成为自己脚本的用户。我发现作为一名系统管理员,作为一名用户,我是自己最大的噩梦。如果我写脚本来处理我知道我会犯的粗心错误,它们将是相当可靠的。我确保我的脚本尽可能的可靠,我尽可能的简化它们,并继续努力进一步简化它们。

最后一句话

愚者忽视复杂性;实用主义者深受其害;专家回避;天才移除它。

—艾伦·珀利斯11

azquotes.com, http://www.azquotes.com/quote/246027?ref=unix

2

Siever,Figgins,Love & Robbins,第六版 (O’Reilly,2009),ISBN 978-0-596-15448-6。

3

Sobell,Linux、命令、编辑器和 Shell 编程实用指南,第三版 (Prentice Hall,2013),ISBN 978-0-13-308504-4。

4

TechTarget.com, http://searchdatacenter.techtarget.com/tutorial/77-Linux-commands-and-utilities-youll-actually-use

5

http://www.thegeekstuff.com/2010/11/50-linux-commands/?utm_source=feedburner 极客的玩意儿

6

维基百科配额, C .A.R.Hoarehttps://en.wikiquote.org/wiki/C._A._R._Hoare

7

维基百科,托尼·霍雷https://en.wikipedia.org/wiki/Tony_Hoare

8

维基百科,??/I,??,??,??,??

9

更多信息请参见第二十章“记录一切”。

10

维基百科,晶体管计数https://en.wikipedia.org/wiki/Transistor_count

11

维基百科,阿兰·珀尔利斯https://en.wikipedia.org/wiki/Alan_Perlis

**

十九、使用您最喜欢的编辑器

为什么这是系统 管理员的 Linux 理念的宗旨?因为关于编辑的争论会导致大量的精力浪费。每个人都有自己最喜欢的编辑器,可能和我的编辑器不一样。那又怎样?

我使用 vim 作为我的编辑器。我用了好几年了,非常喜欢。我已经习惯了。它比我尝试过的任何其他编辑器都更符合我的需求。如果你可以这样说你的编辑——不管是哪一个——那么你就进入了编辑天堂。

二十多年前,当我开始学习 Solaris 时,我就开始使用 vi。我的导师建议我开始学习用 vi 编辑,因为它会一直存在于每个系统中。事实证明,无论操作系统是 Solaris 还是 Linux,都是如此。vi 编辑器总是在那里,所以我可以依靠它。对我来说,这行得通。

vi 编辑器也可以用作 bash 命令行编辑的编辑器。虽然命令编辑的默认选项是 emacs,但是我使用 vi 选项,因为我已经知道 vi 击键。bash 中使用 vi 样式编辑的选项可以通过在~/中添加行“set -o vi”来设置。bashrc 文件仅供您自己使用。为了全局设置 vi 选项,使用了/etc/profile.d/中的一个配置文件,这样所有用户,无论是根用户还是非特权用户,都可以将它作为 bash 配置的一部分。

其他使用 vi 编辑的工具有 crontab 和 visudo 命令;这两个都是 vi 的包装器。懒惰的系统管理员使用已经存在的代码,尤其是当它是开源的时候。为这些工具使用 vi 编辑器就是一个很好的例子。

还有许多其他的编辑器也很棒,很强大,很神奇。我还是更喜欢 vi 或者 vim。你应该使用你想要的,不要担心其他人都在使用什么。仅仅因为我使用 vim 并不意味着你也必须使用它。使用最好的编辑器对你的生产力很重要。一旦您学会了在编辑器中最常用的击键组合和命令,您就可以非常高效地编辑所有类型的文件。

不仅仅是编辑

这一章不仅仅是编辑。这实际上是关于使用为你工作的工具,关于最好的编辑器的讨论是关于所有类型工具的讨论的原型。

关于使用哪些工具的讨论,无论是关于编辑器、桌面、shells、编程语言还是其他任何东西,都是正常的,并且会非常有帮助。这些讨论提供了新事物的知识或关于已知事物如何工作以及如何使它们更好工作的新信息。作为一名系统管理员,深思熟虑和尊重他人的话语对提高我的知识和技能很有帮助,甚至是至关重要的。我希望这对你也有用。

当这些讨论退化为不尊重和无用的火焰战争,只会在参与者之间制造愤怒和不和谐时,问题就出现了。我总是试图退出这些讨论,以便为更有成效的活动保存精力。让我们看一些例子。

Linux 启动

SystemV 和 systemd 是执行 Linux 启动序列的两种不同方法。SystemV 启动脚本和 init 程序是旧方法,systemd 使用目标是新方法。

为了确保我们都在同一页上,Linux 启动序列在内核加载 init 或 systemd 之后开始,这取决于发行版分别使用新的还是旧的启动。init 和 systemd 程序启动并管理所有其他进程,也就是程序,它们都是各自系统中所有进程的母进程。

尽管许多现代 Linux 发行版使用较新的 systemd 进行启动、关闭和进程管理,但仍有一些发行版不使用。其中一个原因是一些发行版维护者和一些系统管理员更喜欢旧的 SystemV 方法而不是新的 systemd 方法。

我认为两者各有优点,所以让我解释一下我的理由。

为什么我更喜欢系统

我更喜欢 SystemV 的主要原因是它更开放,因为启动是使用 bash 脚本完成的。内核启动 init 程序(一个编译后的二进制文件)后,init 启动 rc.sysinit 脚本,该脚本执行许多系统初始化任务。rc.sysinit 完成后,init 启动/etc/rc.d/rc 脚本,该脚本依次启动/etc/rc.d/rcX.d 中 SystemV 启动脚本定义的各种服务,其中“X”是正在启动的运行级别的编号。

所有这些程序都是开放的,容易理解的脚本。可以通读这些脚本,并准确了解整个启动过程中发生了什么。每个脚本都进行了编号,这样它就可以按特定的顺序启动预期的服务。服务按顺序启动,一次只能启动一个服务。

Systemd 是一个单一的大型编译二进制可执行文件,不访问源代码就无法理解。它代表了对 Linux 哲学的多重原则的一个重要反驳。作为一个二进制文件,SysAdmin 不能直接查看或修改 systemd。

为什么我更喜欢系统

我更喜欢使用 systemd 作为我的启动机制,因为根据启动过程的当前阶段,它可以并行启动尽可能多的服务。这加快了整体启动速度,并使主机系统以比 SystemV 更快的速度进入登录屏幕。

systemd 启动机制是开放的,因为所有的配置文件都是 ASCII 文本文件。可以通过各种 GUI 和命令行工具修改启动配置,也可以添加或修改各种配置文件。

我们中有多少人真正看过 rc.sysinit 或 rc 程序,更不用说对它们进行修改了?我确实看了它们,但我绝不会以任何方式改变它们。这两个脚本的代码之外有一些配置文件,可以根据需要修改启动过程。

真正的问题是

你认为我不能同时喜欢两个启动系统吗?我知道,我可以和任何一个一起工作。

SystemV 与 systemd 的真正问题是在 SysAdmin 级别上没有选择。各种发行版的开发人员、维护人员和打包人员已经做出了是使用 SystemV 还是 systemd 的选择。

尽管这个特殊的选择已经为我们做出,我们的 Linux 主机启动并工作,这是我通常最关心的。作为一个最终用户,甚至作为一个系统管理员,我最关心的是我是否能完成我的工作:比如写这本书,安装更新,写脚本来自动化一切。只要我能做我的工作,我真的不在乎我的发行版上使用的启动序列。

然而,当启动过程中出现问题时,我确实很关心。不管任何主机上使用的是哪种启动系统,我都知道得足够多,并且能够按照事件的顺序找到故障并修复它。这才是最重要的。

桌面

我的首选台式机是 KDE 等离子。几年前,大约在 2008 年,随着 Fedora 9 的发布,KDE 从 V3.x 迁移到了 V4,这一重大变化导致了一些严重的问题。我最喜欢的一些 KDE 应用不再工作了,因为它们还没有更新,无法与 KDE 的新版本兼容。我经历了频繁的桌面崩溃,这使得我无法完成任何真正的工作。有时候,KDE 一小时会崩溃几次。这对生产力没有好处。

幸运的是,我能够切换到不同的桌面,我使用 GNOME 2 一年,直到 KDE 再次可用。

然后在 2016 年末,KDE 经历了另一系列的变化,导致了更多的不稳定。这一次,我优先考虑学习更多关于其他几种可用的桌面环境的知识。从 2016 年 12 月开始,我使用了三种不同的台式机一个月,以便真正了解它们的工作方式。仅仅试用几个小时并不能让你真正了解台式机是如何工作的,也不能让你知道如何配置它才能更符合你自己的风格。

我尝试了 Cinnamon、LXDE 和 GNOME 3,并学会了喜欢它们各自的优点。作为这些试验的结果,我分别写了一篇文章,“使用 Cinnamon 作为 Linux 桌面环境的 10 个理由”, 2 “使用 LXDE 的 8 个理由”, 3 和“使用 GNOME 3 Linux 桌面环境的 11 个理由”, 4 来匹配我之前写的关于 KDE 的文章,“使用 KDE 的 9 个理由” 5

我能够将一个问题转化为尝试新事物的机会:在这种情况下是台式机。每一款台式机都有很多优点,但我在使用时发现它们都有一些缺点。

甚至我最喜欢的台式机 KDE 也有一些问题。它确实会经历多次循环而变得不可用。它很大,占用大量内存。它安装的一些默认应用,当 KDE 登录时启动,会消耗 CPU 周期。我的安装后脚本代码删除了更有问题的 KDE 应用,并关闭了其他应用的后台守护进程,这样我的系统就不会受到它们的影响。因此,当它可用时,我会继续使用它。

出汗还是不出汗

我认为,作为一名系统管理员和使用您最喜欢的工具的一部分是正确使用我们拥有的工具,并让它们不受任何限制地可用。在这种情况下,我发现 sudo 命令被用在了一个它从来没有想过的地方。我特别不喜欢 sudo 工具在某些发行版中的使用方式,尤其是因为它被用来限制从事系统管理工作的人访问他们履行职责所需的工具。

【系统管理员】不要用 sudo。

— 保罗·威尼斯 6

Venezia 在他的 InfoWorld 文章中解释道,sudo 被用作系统管理员的拐杖。他没有花太多时间来捍卫或解释这一立场。他只是陈述了这一事实。我同意他的观点——对于系统管理员来说。我们不需要辅助轮来完成我们的工作。事实上,他们碍手碍脚。

一些发行版,比如 Ubuntu,使用sudo命令的方式是为了让需要提升(root)权限的命令的使用变得更加困难。在这些发行版中,不能以 root 用户身份直接登录,所以 sudo 命令用于允许非 root 用户临时访问 root 权限。这应该使人们在发出需要提升权限的命令时更加小心,例如添加和删除用户、删除不属于他们的文件、安装新软件,以及管理现代 Linux 主机所需的所有任务。强制系统管理员使用 sudo 命令作为其他命令的序言,应该会使使用 Linux 更加安全。

在我看来,这些发行版以这种方式使用 sudo 是给新手系统管理员提供一种错误的安全感的可怕而无效的尝试。它完全不能提供任何程度的保护。使用 sudo 时,我可以发出与不使用它时一样不正确或有害的命令。使用 sudo 来麻醉我们可能发出错误命令的恐惧感的发行版对系统管理员造成了极大的伤害。这些发行版对 sudo 工具可以使用的命令没有任何限制。没有人试图通过保护系统免受用户的伤害来限制可能造成的损害,也不应该有这样的可能性。

所以让我们明确这一点——这些发行版期望用户执行所有的系统管理任务。他们哄骗用户——如果你还记得我在第一章列出的清单,他们实际上是系统管理员——认为他们在某种程度上免受做坏事的影响,因为他们必须采取这个限制性的额外步骤来输入自己的密码,以便运行命令。

sudo 旁路移植

像这样工作的发行版通常为根用户锁定密码,Ubuntu 就是其中之一。这样,没有人可以登录到 root 并不受阻碍地开始工作。我已经安装了一个装有 Ubuntu 16.04 LTS(长期支持)的虚拟机,所以我可以向你展示如何设置密码来避免使用 sudo。

注意

实验 19-1 是可选的。它旨在指导您使用 sudo 通过设置密码来解锁 root 帐户。如果您使用的发行版不强制您使用 sudo,那么您应该跳过这个实验。

实验 19-1

让我规定一下这里的设置,这样如果你愿意,你就可以复制它。我安装了 Ubuntu 16.04 LTS 7 ,用 VirtualBox 安装在一个 VM 里。在安装过程中,我创建了一个非 root 用户 student,并为这个实验提供了一个简单的密码。

以用户 student 的身份登录,并打开一个终端会话。让我们看看/etc/shadow 文件中的 root 条目,加密的密码就存储在这里。

student@ubuntu1:~$ cat /etc/shadow
cat: /etc/shadow: Permission denied

权限被拒绝,因此我们无法查看/etc/shadow 文件。这在所有发行版中都很常见,因此非特权用户无法看到和访问加密的密码。这种访问将使使用普通黑客工具破解这些密码成为可能,因此允许这样做是不安全的。

现在,让我们尝试 su–to root。

student@ubuntu1:~$ su -
Password:
su: Authentication failure

这将失败,因为 root 帐户没有密码并且被锁定。让我们使用 sudo 来查看/etc /shadow 文件。

student@ubuntu1:~$ sudo cat /etc/shadow
[sudo] password for student: <enter the password>
root:!:17595:0:99999:7:::
<snip>
student:$6$tUB/y2dt$A5ML1UEdcL4tsGMiq3KOwfMkbtk3WecMroKN/:17597:0:99999:7:::
<snip>

我已经截断了结果,只显示了根用户和学生用户的条目。我还缩短了加密密码,这样条目就可以放在一行中。

这些字段用冒号(:)分隔,第二个字段是密码。请注意,root 的密码字段是一个“bang”,世界上的其他人都知道它是一个感叹号(!).这表明该帐户已被锁定,无法使用。

现在,我们需要做的就是为 root 帐户设置一个密码。

student@ubuntu1:~$ sudo su -
[sudo] password for student: <Enter password for student>
root@ubuntu1:~# passwd root
Enter new UNIX password: <Enter new root password>
Retype new UNIX password: <Re-enter new root password>
passwd: password updated successfully
root@ubuntu1:~#

现在,我们可以以 root 或 su 身份直接登录到控制台——直接登录到 root,而不必对每个命令都使用 sudo。当然,我们可以只使用 sudo su——每当我们想以 root 身份登录时——但是为什么要这么麻烦呢?

请不要误解我。像 Ubuntu 这样的发行版以及它们的上游和下游版本都非常好,这些年来我已经使用了好几个。当使用 Ubuntu 和相关发行版时,我做的第一件事就是设置一个 root 密码,这样我就可以以 root 身份直接登录。

sudo 的有效用法

sudo 设备确实有它的用处。sudo 的真正意图是让根用户能够委托给一个或两个非根用户,访问他们经常需要的一个或两个特定的特权命令。这背后的原因是懒惰的系统管理员;允许用户访问一个或两个命令,这需要提升权限,并且他们每天会多次不断地使用这些命令,这为系统管理员节省了大量来自用户的请求,并且消除了用户可能会经历的等待时间。但是大多数非 root 用户不应该拥有完全的 root 访问权限,只能访问他们需要的几个命令。

我有时需要非 root 用户来运行需要 root 权限的程序。在这种情况下,我设置一两个非 root 用户,并授权他们运行这个命令。sudo 工具还保存了使用它的每个用户的用户 ID 日志。这可能使我能够追查出是谁犯了错误。这就是它所做的一切;它不是魔法保护者。

sudo 工具从来就不是用来作为 SysAdmin 发出的命令的网关的。它无法检查命令的有效性。它不会检查用户是否在做傻事。它并不能使系统免受那些可以访问系统上所有命令的用户的攻击,即使是通过一个迫使他们说“请”的网关——这从来都不是它的预期目的。

?? 从来不说请。

—抢长枪 8

这句关于 Unix 的话对于 Linux 和 Unix 都是正确的。当我们需要以 root 用户身份工作时,我们系统管理员以 root 用户身份登录,当我们完成工作时,我们注销我们的 root 会话。有些日子我们整天都以 root 身份登录,但是我们总是在需要的时候以 root 身份工作。我们从不使用sudo,因为为了运行我们工作所需的命令,它会迫使我们输入不必要的内容。Unix 和 Linux 都不会问我们是否真的想做某件事,也就是说,它不会说“请验证您想这样做。”

是的,我不喜欢一些发行版使用sudo命令的方式。

几句结束语

你用什么工具对我来说并不重要,对其他人来说也不重要。真正重要的是完成任务。无论你是 vim 还是 EMACS,systemd 还是 SystemV,RPM 还是 DEB,这有什么区别吗?这里的底线是,你应该使用那些你觉得最舒服、最适合你的工具。

最重要的是,我们选择使用的工具不以任何方式受到限制或阻碍。误用完美的工具来帮助和助长这种障碍是不合理的,并且与 Linux 和开源所代表的所有自由相抵触。无论何时遇到都要抵制和规避。

一般来说,Unix、Linux 和开放源码的最大优势之一是,对于我们需要完成的每项任务,通常都有许多选择。我们有更多的开源文字处理器,比我记忆中专有 PC 软件时代最鼎盛时期的三个左右的处理器还要多。

OSnews”,社论:关于 Systemd 和选择自由的思考http://www.osnews.com/story/28026/Editorial_Thoughts_on_Systemd_and_the_Freedom_to_Choose

2

这两者,大卫,Opensource.com,使用肉桂作为你的 Linux 桌面环境的 10 个理由, https://opensource.com/article/17/1/cinnamon-desktop-environment

3

都有,大卫,Opensource.com, 8 个理由用 LXDEhttps://opensource.com/article/17/3/8-reasons-use-lxde

4

这两位,大卫,Opensource.com,使用 GNOME 3 桌面环境的 11 个理由, https://opensource.com/article/17/5/reasons-gnome

5

既,大卫,Opensource.com, 9 个理由用 KDEhttps://opensource.com/life/15/4/9-reasons-to-use-kde

6

Venezia,Paul,Unix 资深管理员的九个特质*,InfoWorld* ,2011 年 2 月 14 日, www.infoworld.com/t/unix/nine-traits-the-veteran-unix-admin-276?page=0,0&source=fssr

7

典范集团有限公司下载网站, https://www.ubuntu.com/download/desktop

8

维基百科,【rob pike】https://en.wikipedia.org/wiki/Rob_Pike

二十、记录一切

真正的程序员不会评论他们的代码,如果代码很难写,就应该很难理解,更难修改。

—未知

如果是我写的,我也想匿名。它甚至可能意味着讽刺或讽刺。不管怎样,这似乎是许多开发人员和系统管理员的态度。在一些开发人员和系统管理员中有一种很难掩饰的风气,即一个人必须自己搞清楚所有事情才能加入这个俱乐部——不管是什么俱乐部。他们暗示,如果你想不出来,你应该去做些别的事情,因为你不属于这里。

首先,这不是真的。其次,我认识的大多数开发人员、程序员和系统管理员肯定不同意这种观点。事实上,最优秀的人,其中一些多年来一直是我的导师,恰恰相反。精英中的精英让文档——好的文档——成为他们所做的每件事情的重中之重。

我用过很多软件,它们的创建者都认同这样一种理念,即所有代码都是不言自明的。我还被要求修复大量完全没有注释或者没有文档记录的代码。似乎许多开发人员和系统管理员认为如果程序对他们有用,就不需要文档化。

有很多类似上面的引用。他们都倾向于支持文档既不需要也不应该存在的观点。然而在我的职业生涯中。我已经看到了这种缺乏文件的灾难性后果。我已经不止一次被指派为修复未注释代码的系统管理员。这是我做过的最不愉快的任务之一。

问题的一部分是许多 PHB 没有将文档视为高优先级。我参与了 IT 行业的许多方面,幸运的是,我工作过的大多数公司都认为,文档不仅重要,而且对于手头的任务至关重要,不管这个任务是什么。

我想我从来没有听到任何人说,“这个文档太棒了。”大多数情况下,我听到一些特定的文档有多糟糕。我自己也多次重复这句话。

然而,有很多非常好的文档。例如,LibreOffice 的文档非常优秀。它包括多种格式的几个文档,包括 HTML 和 PDF,从“入门”到每个 LibreOffice 应用的非常完整的用户指南。

RHEL 和 CentOS 的文档,以及 Fedora 的文档——它们都是非常相关的发行版——也是我在 IT 行业工作的四十多年中所见过的最好的文档之一。

好的文档并不容易,而且需要时间。它还需要了解读者——不仅与文档的目的有关,还需要了解目标读者的专业技术以及读者的语言和文化。Rich Bowen 在 Opensource.com 的一篇优秀文章《RTFM?如何写出一本值得一读的手册。” 1

还有一个问题是,对于一个系统管理员来说,什么是好的文档。我们在这一章中探索这些事情,这主要是关于记录我们写的脚本。

红色男爵

在我作为客户工程师的 IBM 职业生涯中,最令人沮丧的一件事是在一家炼油厂协助解决 IBM 1800 2 过程控制计算机上的一些问题。

这台特殊的计算机与炼油厂外的许多传感器相连,它被用来对生产过程中的各个部分进行调整。根据传感器读数,该计算机将调整温度和流速等事项,以确保过程的产品是正确的和高质量的。但是当事情出错时,它可能是灾难性的。我是说,拜托!这是一个该死的炼油厂的过程控制!

似乎写代码的程序员没有很好地注释他的代码——或者据我所知根本没有——并不是说我可以直接访问他的专有源代码。这位开发人员显然也不喜欢信息性的错误消息。

我不得不说代码很擅长检测错误。它似乎还擅长在炼油厂的地面上关闭受影响的流程。毕竟没有爆炸。然而,说程序在传达错误方面有缺陷是一种保守的说法。不管是什么错误,不管出了什么问题,控制台上打印的唯一信息是,“诅咒你,红色男爵,”以及一个数字错误信息,我们必须在一个很长的错误代码列表中查找。从列表中得到的信息也没有多大帮助。

为 IBM 说句公道话,这位程序员并没有为 IBM 工作。

我的文档哲学

我的哲学是多年来我最好的导师灌输给我的,“直到文档完成,工作才算完成。”这意味着一切都必须记录在案。文档也绝对不是节省打字时间的地方。尽管如此,好的文档对系统管理员的意义不同于对最终用户的意义。

在针对系统 管理员Linux 理念的背景下,我们将考虑针对我们代码的目标受众——我们自己和其他系统管理员——的文档。我们系统管理员需要两种主要类型的文档。某种形式的体面的命令行帮助选项和注释良好的 shell 代码。

帮助选项

在寻找帮助我理解 shell 脚本的文档时,我首先去的地方是 help 工具,因为我最常见的需求是理解启动程序的命令的语法以及命令的可用选项和必需或可选参数。这种类型的信息通常可以通过对所需命令使用-h 选项来获得。

我们在第十章“总是使用 Shell 脚本”中创建的 bash 脚本模板包含代码清单 20-1 中所示的模板帮助工具。你以前见过这个。注意,这只是一个模板,就像脚本模板的其余部分一样。必要时,需要在此过程中添加和修改为脚本提供有用帮助所需的所有细节。添加新选项或功能时,该信息也应记录在帮助工具中。

代码清单 20-1

##########################################################################
# Help                                                                   #
##########################################################################
Help()
{
   # Display Help
   echo "Add description of the script functions here."
   echo
   echo "Syntax: template <option list here>"
   echo "options:"
   echo "g     Print the GPL license notification."
   echo "h     Print this Help."
   echo "v     Verbose mode."
   echo "V     Print software version and exit."
   echo
}

像这样的简单帮助工具可以回答我关于脚本做什么的大部分问题,以及可以用来修改其行为的各种可用选项。在脚本功能描述、语法图、选项列表以及每个选项的简短描述之间,运行时问题很容易回答。

好的帮助是我们作为系统管理员编写的脚本的第一行文档。所有操作文档必须包含在帮助程序中。这也意味着脚本的用户界面应该非常明显和简单,这样就可以最大限度地减少任何形式的帮助。

宽松地注释代码

代码中的注释是文档的一种形式。事实上,它们应该是系统管理员文档的第一和主要形式。

作为我自己记录一切的需要的一部分,我在我的脚本中添加了许多注释。当试图减少注释时,我回想起当我必须解释和修复别人写的没有注释和没有文档记录的代码时的感觉。

我知道许多系统管理员和其他开发人员认为他们的代码是自解释的,甚至没有注释。不管我们的代码有多好,即使有大量的、写得很好的注释,代码也永远不会是不言自明的。我们思考问题的方式不同,编写代码的方式不同,解决问题的方式也不同。因为我们理解代码及其结构的方式不同,代码的目的对你来说可能是显而易见的,即使没有注释,对我来说可能是难以理解的。

在本书的前面,我们首先创建了一个 bash 脚本模板,然后使用该模板创建了一个简短的脚本。模板和脚本都得到了好评。这样做的目的是让我在构建代码时记得注释自己的代码。我在脚本模板中包含的注释是一个良好的开端。

我认为前三个部分特别重要。这些是程序描述、变更历史和许可声明。为了方便访问,我在代码清单 20-2 中再次包含了这些。

代码清单 20-2

#!/bin/bash
##########################################################################
#                              scriptTtemplate                           #
#                                                                        #
# Use this template as the beginning of a new program. Place a short     #
# description of the script here.                                        #
#                                                                        #
# Change History                                                         #
# 04/12/2017  David Both    Original code. This is a template for creating  #
#                          new Bash shell scripts.                       #
# 01/30/2018  David Both   Add an option for setting test mode.          #
#                                                                        #
#                          Add new history entries as needed.            #
#                                                                        #
#                                                                        #
##########################################################################
##########################################################################
##########################################################################
#                                                                        #
#  Copyright (C) 2007, 2018 David Both                                   #
#  LinuxGeek46@both.org                                                  #
#                                                                        #
#  This program is free software; you can redistribute it and/or modify  #
#  it under the terms of the GNU General Public License as published by  #
#  the Free Software Foundation; either version 2 of the License, or     #
#  (at your option) any later version.                                   #
#                                                                        #
#  This program is distributed in the hope that it will be useful,       #
#  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
#  GNU General Public License for more details.                          #
#                                                                        #
#  You should have received a copy of the GNU General Public License     #
#  along with this program; if not, write to the Free Software           #
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   #
#                                                                        #
##########################################################################

程序描述定义了程序的目的和一些主要的功能和选项。变更历史告诉未来的系统管理员可能需要对脚本进行维护,添加或删除了什么特性,修复了哪些错误,谁做了哪些工作,以及这些事情发生的时间。

许可证声明用于记录许可证,在该许可证下,脚本被分发并可供其他用户使用。这一点很重要,这样就不会对脚本的使用、修改和分发条件产生疑问。

代码中嵌入的注释应该描述它们所引用的代码段的功能。它们还应该包含为什么事情要以某种方式完成的信息,以及在不明显的地方对逻辑的解释。例如,下面的代码清单 20-3 中的代码片段有关于它的功能的注释,我做的一些假设,一个使用了新方法的指示器,旧代码被保留为注释,以便可以评估差异。

代码清单 20-3

###########################################################################
# Processing Intel CPU data
###########################################################################
# NOTE :This assumes certain data to be constant in /proc/cpuinfo based on
#       data from the chipsets.
if [ $verbose == 1 ]
then
   echo "This is an Intel box"
fi
CPUtype="Intel"
# Get number of CPU cores  
# CPUs=`cat /proc/cpuinfo | grep "^processor" | wc -l`
# New method below
CPUs=`cat /proc/cpuinfo | grep "cpu cores" | uniq | awk -F: '{print $2}' | sed -e "s/^ //"`

此外,代码清单 20-2 中的代码部分有一个标题,有助于在视觉上将其与代码的其他部分区分开来。这使得可视化代码的整体结构和功能流变得容易。

我的代码文档过程

哪个先出现?程序或文档。理想情况下,文档应该放在首位。然后可以开发代码来满足文档中概述的规范。在编写代码之前,您确实创建了规范,不是吗?这是我遇到的另一个常见问题:缺乏清晰的脚本规范。

如前所述,我喜欢通过使用注释创建我提议的代码的大纲来开始编码。这让我可以看到程序的结构,并确定它是否干净和优雅,允许我在编写任何代码之前,在必要时更改结构。无论我是在写新代码还是维护现有代码,注释是我添加的第一件事。这些评论成为我正在编写或维护的脚本的规范。然后,我可以编写代码来实现注释中描述的操作。

但是我并不总是一开始就做所有的评论。我首先创建一个基本大纲,包含描述程序逻辑的注释框架。我尽可能地创建了程序主体的轮廓。如果我设想使用额外的过程,我会创建并命名空过程,然后添加注释来描述其内部功能。

然后我创建代码来实现这个基本框架。我通常从程序的主体开始,在必要时添加新的注释,然后填充代码来实现这些注释。当我到达一个分支到一个不完整函数的调用时,我编写该函数并添加任何可能仍然需要的注释,然后编写代码来实现该过程。

这是我在本节开始时提出的问题的答案。至少对我来说,文档是第一位的。我可以听到敏捷支持者的键盘已经打出了他们相反的意见。但是从一个非常真实的意义上来说,我所做的是敏捷的,因为我只写我需要的文档,正好赶上写代码。然后评论也变成了文档。

并不是每个人都想这样工作,或者像我一样发现这很适合他们的工作方式。有多少人在做,就有多少种创建代码和记录代码的方法。做最适合你的事情,但是一定要做!

手册页

手册页在什么地方适合这种记录一切的哲学?坦率地说,对于系统管理员编写的脚本来说不是很好。

在早期,我们讨论了我们系统管理员工作的时间限制,以及我们编写的大多数脚本都倾向于作为操作问题的最小化解决方案的事实。在这种环境中,我们几乎没有时间来创建手册页。底线是我不会花时间去创建手册页。

系统文档

这种类型的文档不是关于记录脚本或程序的。它是关于记录网络的状态,连接的主机,以及我在它们上面执行的任何工作。这份文件对我以前咨询公司的客户,以及我作为全职员工或承包商工作过的任何雇主来说都是至关重要的。

我曾经拥有一个小的有限责任公司,通过它我可以做一些关于 Linux 和开源的咨询。我仍然为我的教会和几个朋友做一些咨询。

当与客户一起工作时,我总是记录我与他们的互动以及我所做的工作。像这样的文件为我服务,就像医生对我的就诊记录为她服务一样。这是客户环境的永久记录,我可以在与他们打电话或进行电子邮件交谈时参考。它为我提供了对我发现的问题以及我采取的解决措施的持续评论。

在某些情况下,我有多年的文档,涵盖了从我第一次与他们联系到我在为他们工作时发现的关于他们网络的信息,我为他们安装的硬件的细节,我在项目中工作的细节,以及我每次安装更新的记录。我在这些记录中加入了数据,如网络图、网络 IP 和 MAC 地址表以及每个节点的功能说明。我还保留了我编写的脚本的输出,该脚本列出了我工作的每台 Linux 主机的硬件和一些配置细节。

这些信息有多种用途。它给了我一个记录,以便我可以回去回忆我做了什么以及我的客户环境的结构,这对我来说是一个记忆辅助工具。需要时,我可以用它来支持我对额外工作的建议。在与客户发生纠纷时,保留详细的记录也很有用。

在为客户工作之前,我总是会创建一个任务列表,这样我就不会忘记任何需要完成的事情。我在清单上做笔记,然后,在工作结束时,任务清单成为我已经完成的工作的文档的一部分,并由我在工作过程中所做的笔记进行补充。对于我的一些客户来说,我已经完成了超过 40 页的这种类型的文档。

对于这种类型的文档,我通常使用 LibreOffice Writer。Writer 使用开放文档文本(ODT)格式,这是一种开放的、众所周知的格式,被许多文字处理程序所使用。即使是 Microsoft Word 也可以使用 odt 格式。

对这种类型的文档使用文字处理程序可以让我把它做得很漂亮,这样当我把它的副本给我的客户时,它看起来就很好。

系统文档模板

我创建了一个模板——实际上是一个模板的大纲——它帮助我记录我过去工作过的组织的系统信息。下面的简化大纲对我来说很有用,如果你还没有这种类型文档的规范或模板,我建议你把它作为一个起点。请随意使用和修改它,以满足您自己的独特需求。

  1. 标题页。

  2. 目录。

  3. 表格索引。

  4. 插图索引。

  5. 代码列表的索引。

  6. 简介–对文件和组织的简要描述。

  7. 管理员–当前系统管理员及其联系信息的列表。

  8. 互联网连接–对互联网连接和提供互联网连接的 ISP 的描述。这可能包括有关合同日期和成本的信息。

    • 电缆线路——描述从 ISP 的街道连接到分界点(通常是 ISP 的调制解调器/路由器/交换机)的物理电缆位置。

    • 外部 IP 地址–外部 IP 地址(如果是静态的)和通用 IP 地址范围(如果是 DHCP)的列表。

  9. 内部网络–内部网络的描述。

    • 所有内部网络的内部 IP 地址空间。

    • 防火墙–对属于组织而非 ISP 的防火墙的描述。

    • 物理描述–包括文本描述、网络图和地址映射,其中列出了每个网络节点、其名称、MAC 地址、IP 地址、网络配置是静态还是 DHCP,以及对其功能的简短描述。

  10. 硬件–每个网络节点的列表。
    * 硬件的描述。这可以使用本书前面创建的 mymotd 程序来创建。

*   操作系统。对于 Linux,这包括分发和发布。

*   网络节点提供的功能描述。
  1. 操作系统和软件
    * 所有操作系统及其在哪些主机上运行的列表。
*   每台主机上特定软件的列表。这并不意味着所有的软件,如没有线索的 PHP 可能会要求,但主要的软件,该主机打算。例如,对于一个简单的桌面,你可以说“桌面软件”。对于服务器,这可能是,“DHCPD,HTTPD,命名”,等等。

*   许可证–可能相关的软件许可证信息,如续订信息和费用。拥有专有许可的软件应列出许可证 id 或编号,以便在需要进行许可证合规性审核时参考。
  1. 主机配置–常见的主机配置项目,如 DNS 和 DHCP 服务器、默认网关、电子邮件服务器等方面的网络配置。

  2. 管理任务–各种管理任务的列表,以及负责执行这些任务或监控这些任务(如果它们是自动执行的)的系统管理员或用户。

  3. 联系人列表–包括内部系统管理员和管理联系人及其职责,以及所有供应商的联系人,包括 ISP、硬件和软件供应商、HVAC、数据中心冷却、UPS、内部安全、外部安全公司、外部紧急联系人(如消防和警察)以及您可能想到的任何其他人。

  4. 活动日志–这是我与客户联系的日志以及我为客户完成的工作。在描述问题及其解决方案时,这一部分应该尽可能清晰明了。

这个模板是一个很好的起点。拥有这种类型的文档作为记忆辅助是很重要的——我总是很高兴不用问客户我为他们做了什么,因为我可以很容易地找到它。在最坏的情况下,当客户或 PHB 质疑你的行为时,你可能会发现有必要使用一份维护良好的文件作为证据。我很幸运,从未发现自己处于最坏的情况。

记录现有代码

为现有代码创建文档需要不同于任何其他类型的方法。

我做的第一件事是阅读源代码,对我来说几乎总是 Perl 或 bash 脚本。然后,我可以使用这些评论作为创建外部文档的起点——如果有任何评论,如果这些评论有任何意义的话。

很多年前,我接手了一份工作,负责维护和修复大量预先存在的 bash 脚本。这些脚本是该公司使用的一系列复杂内部应用的一部分。代码可以工作——大多数情况下——但是过于复杂,缺乏任何可用的注释和文档。

我的第一个任务是修复几个脚本中的一些错误。我开始阅读这些脚本,以确定它们实际上应该做什么。当我确定了代码的每一部分是做什么的时候,我添加了注释来描述我刚刚阅读和解释的代码。就在这个过程中,我能够确定一些错误的原因并纠正它们。

在这个初始阶段,通过阅读 bash 脚本和询问 IT 人员,我确定这些脚本最初是由几个不同的承包商编写的,并且由一系列其他承包商维护了多年。每个承包商都添加了一些代码,这些代码显然是为了规避他们遇到的问题。这些附加的代码没有一个试图修复根本原因。每个承包商都有自己的做事方式,比如变量的命名方案、缩进、编码风格和注释。那些剧本完全是一场灾难。

那个项目简直是一场噩梦。我花了几周时间来分析代码,并给代码添加适当的、可理解的、有用的注释。这项任务很繁琐,而且由于变量的命名明显是随机的,因此变得更加困难。这是这么多不同的人在没有任何类型的指导或监督的情况下在项目目标或编程风格方面工作的不可避免的结果之一。

在我完成了注释这些脚本的任务之后,尽可能多地重命名变量,解决剩下的问题就变得容易多了。

当然,其他人写的代码并不是唯一有这些问题的代码。我自己的代码,尤其是我的大部分旧代码,也面临着同样的问题。这是因为我还没有了解 Unix 或 Linux 的哲学。随着时间的推移,我的代码确实有所改进,当重新访问我的一些旧代码来修改它或修复一个问题变得有用时,我会修改它,以遵循从我还是一个系统管理员时就学到的更好的编程实践。

保持文档更新

我自己的文档有一些问题。首先是忽视了及时或完整地更新文档。当我需要的信息没有被正确记录时,这就造成了问题。

当我发现我在文档方面有所松懈时,我会尽快回去改正它。这通常意味着纠正和更新我的脚本中嵌入的注释。这还意味着修复帮助过程,使其与对代码所做的更改保持一致。

更新我的客户文档也是我需要跟上的一项任务。我有时会忘记这样做,因为我似乎总是急于下一个任务。

让我的文档保持最新需要自律。如果没有持续的维护,文档可能会过时。

文件兼容性

文件兼容性也可能是外部文档的一个问题——即我的代码之外的文档,如客户文档。几年来,我使用了一些开源软件,这些软件以非纯文本的格式维护我的数据,从某种意义上说,这些数据是专有的,没有文档记录,其他软件也无法访问。这至少部分是因为我不知道数据格式,这是我自己的错。这也是该程序开发者的错误,因为他们应该使用开放格式的数据。

在第十三章“以开放格式文件存储数据”中,我们探讨了使用开放格式的一些原因。重点是程序本身使用的数据。现在我们来看看系统管理员用来维护各种类型的文档的数据,比如客户访问记录和维修历史记录。这些是重要的文件,因为它们使我们能够回过头来回顾已经完成的工作,并感受我们在确定当前问题方面取得的进展。

因此,当有问题的程序升级未能正确升级存储数据的数据库时,我无法访问几年来的客户记录。即使回到程序的前一个版本也不能恢复我的数据,因为它已经被破坏了。不幸的是,我的备份不像现在这样广泛,所以我无法回溯足够远的时间来获得未损坏的副本。

我现在以开放文档格式(ODF)保存我的笔记。ODF 是一种众所周知的、开放的、文档化的格式,有许多应用可以使用它。尽管这一原则具体指的是程序数据,但我认为一个必然的结果应该是文档应该以一种开放的格式保存,比如 ODF。

一些想法

文档对系统管理员来说非常重要。在履行我们的日常职责时,我们依赖他人留给我们的文件。我们工作的质量和速度直接受到文件质量的影响。这里有一些记录我们脚本的指导方针。

  1. 脚本应该用清晰而有意义的注释来记录。

  2. 脚本应该易于阅读。这是自我记录的一种形式。

  3. 脚本应该有一个有用和简洁的帮助功能。

  4. 遵循这些原则会产生优雅的脚本。

作为与客户互动记录或内部记录保存的系统文件应始终保持最新。工作完成后应尽快进行记录,以确保尽可能准确地回忆信息。

无论您做什么,无论您选择什么工作方式,请记住,在文档完成之前,工作还没有完成。

鲍文,里奇,Opensource.com, RTFM?如何写一本值得一读的手册https://opensource.com/business/15/5/write-better-docs

2

工程与技术维基, IBM 1800http://ethw.org/IBM_1800

二十一、经常备份所有东西

我的电脑不会出任何问题,我也不会丢失我的数据。右。

我经历过各种原因造成的数据丢失,其中很多是我自己的错。保持良好的备份总是能让我在中断最少的情况下继续工作。本章讨论了数据丢失的一些常见原因,以及防止数据丢失和便于恢复的方法。

数据丢失

不去细说我自己的愚蠢,这里有一些我们可能在不恰当的时候丢失数据的原因。当然,没有丢失数据的合适时机。

自己造成的数据丢失有多种形式。最常见的形式是擦除一个或多个重要的文件或目录。

有时删除需要的文件是偶然的。我只是删除了一个目录中的一堆旧文件,后来发现还需要一两个。更多的时候,至少对我来说,我实际上是看着这些文件,然后决定不再需要它们。在我删除它们的一天、两天或一周后,我发现我至少还需要一些刚刚删除的文件。我还对一个文件进行了重大修改并保存了它。再一次,我发现在后来的某个时候,我做了一些不该做的修改,尤其是删除了一些不该删除的内容。

显然,在删除文件或对其进行更改时,有必要注意。这仍然不能阻止我们删除以后可能需要的数据。

电源故障可能因多种原因而发生。这包括像长时间断电一样无法挽回地关闭计算机的瞬间断电。不管断电的原因是什么,都有丢失数据的危险,尤其是尚未保存的文档。现代硬盘驱动器和文件系统采用有助于最小化数据丢失概率的策略,但它仍然会发生。

我也经历过停电。回到现代日志文件系统如 EXT3 和 EXT4 之前,我确实经历过一些严重的数据丢失。有助于防止因电源故障而导致数据丢失的一种方法是投资购买不间断电源(UPS ),它可以在足够长的时间内保持主机供电,以执行手动关机或由电源故障本身触发的关机。

电磁干扰,EMI ,是来自许多不同来源的各种类型的电磁辐射。这种辐射会干扰包括计算机在内的任何电子设备的正常运行。

当我在佐治亚州亚特兰大的 IBM 个人电脑客户支持中心工作时,我们的第一个办公室距离多宾斯空军基地跑道中心线大约一英里,就在跑道中心线上。各种类型的军用飞机一天 24 小时都在进进出出。大功率军用雷达有时会导致多个系统同时崩溃。在那种环境下,这就是生活的现实。

闪电、静电、微波、旧的阴极射线管显示器、地面线路上的无线电频率脉冲,所有这些以及更多的因素都会引起问题。正如我们在第十七章中看到的,良好的接地可以降低所有这些类型的电磁干扰的影响。但这并不能使我们的电脑完全免受电磁干扰的影响。

硬盘故障也会导致数据丢失。当今计算机中最常见的故障是带有移动机械部件的设备。在频率列表中领先的是冷却风扇,硬盘紧随其后。现代硬盘具有智能功能,可以进行预测性故障分析。Linux 可以监控这些驱动器,并向 root 用户发送一封电子邮件,表明故障即将发生。不要忽视这些电子邮件,因为在硬盘出现故障之前更换硬盘比在硬盘出现故障后更换硬盘并希望备份是最新的要简单得多。

心怀不满的员工可以恶意破坏数据。适当的安全程序可以减轻这种类型的威胁,但是备份仍然很方便。

盗窃也是丢失数据的一种方式。1993 年,我们搬到北卡罗莱纳州的罗利后不久,当地报纸和电视上刊登了一系列文章,报道了我们一所著名大学的一位科学家的苦难经历。这位科学家把他所有的数据都保存在一台电脑上。他确实有备份——在同一台电脑的另一个硬盘上。当他办公室的电脑被盗时,他所有的实验数据也丢失了,而且再也没有找到。

这是将良好的备份与正在备份的主机分开的一个非常好的理由。

自然灾害发生。火灾、洪水、飓风、龙卷风、泥石流、海啸以及更多种类的灾难会毁坏计算机和本地存储的备份。我可以保证,即使我有一个很好的备份,我也不会在火灾、龙卷风或自然灾害将我置于迫在眉睫的危险时花时间去保存备份。

恶意软件是可用于各种恶意目的的软件,包括破坏或删除您的数据。

勒索软件是一种特定形式的恶意软件,它会加密你的数据并以此勒索赎金。如果你付了赎金,你可能会得到可以解密你的数据的密钥——如果你够幸运的话。

如你所见,丢失数据的方式有很多种。我列出这些可能导致数据损坏或丢失的方式的目的是为了吓唬您进行备份。成功了吗?

救援的备份

最近,就在最近——实际上,当我在写这本书的时候——我遇到了一个硬盘崩溃的问题,这个问题破坏了我主目录中的数据。我已经期待了一段时间,所以这并不奇怪。

问题

我发现有问题的第一个迹象是来自 S.M.A.R.T(自我监控、分析和报告技术)支持的硬盘驱动器的一系列电子邮件,我的主目录就在这个硬盘上。 1 这些电子邮件中的每一封都表明一个或多个扇区已经变得有缺陷,并且有缺陷的扇区已经离线,并且预留扇区被分配到它们的位置。这是正常操作;正是出于这个原因,硬盘特意设计了保留扇区。

我们将在第二十二章“追随你的好奇心”中详细讨论好奇心,但几个月前当这些错误信息开始进入我的电子邮箱时,我开始使用我的好奇心。我首先使用smartctl命令查看有问题的硬盘驱动器的内部统计数据。原来有缺陷的硬盘已经被替换了,但是——是的,我保留了一些旧的有缺陷的设备,以备像这样的教学时刻使用。我将这个损坏的硬盘安装在我的扩展坞中,以展示有缺陷的硬盘的后果。

你可以和我一起做这个实验,但是你的结果会不同——希望比我有缺陷的驱动器更健康。

实验 21-1 中使用的智能报告可能有点混乱。网页“了解智能报表, 2 ”对此有所帮助。维基百科也有一个关于这项技术的有趣页面。 3 我建议在尝试解释 SMART 结果之前先阅读那些文档;它们可能非常令人困惑。

注意

请确保在不用于生产的物理主机上执行此实验。虚拟硬盘的硬件状态无关紧要。

实验 21-1

这个实验必须以 root 用户身份进行。

在扩展坞中安装驱动器并将其打开后,dmesg 命令显示该驱动器被指定为设备专用文件/dev/sdi。确保为您的硬盘驱动器使用正确的设备专用文件。您可以使用主机中安装的任何物理硬盘,即使它正在使用中。

为了便于讨论,我将命令的结果分成了几个部分,并删除了大量不相关的数据。

[root@david ~]# smartctl -x /dev/sdi | less
smartctl 6.5 2016-05-07 r4318 [x86_64-linux-4.15.6-300.fc27.x86_64] (local build)
Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org

=== START OF INFORMATION SECTION ===
Model Family:     Seagate Barracuda 7200.11
Device Model:     ST31500341AS
Serial Number:    9VS2F303
LU WWN Device Id: 5 000c50 01572aacc
Firmware Version: CC1H
User Capacity:    1,500,301,910,016 bytes [1.50 TB]
Sector Size:      512 bytes logical/physical
Rotation Rate:    7200 rpm
Device is:        In smartctl database [for details use: -P show]
ATA Version is:   ATA8-ACS T13/1699-D revision 4
SATA Version is:  SATA 2.6, 3.0 Gb/s
Local Time is:    Wed Mar 14 14:19:03 2018 EDT
SMART support is: Available - device has SMART capability.
SMART support is: Enabled
AAM level is:     0 (vendor specific), recommended: 254
APM feature is:   Unavailable
Rd look-ahead is: Enabled
Write cache is:   Enabled
ATA Security is:  Disabled, NOT FROZEN [SEC1]
Wt Cache Reorder: Unknown
=== START OF READ SMART DATA SECTION ===
SMART Status not supported: Incomplete response, ATA output registers missing
SMART overall-health self-assessment test result: PASSED
Warning: This result is based on an Attribute check.

上面显示的第一部分结果提供了有关硬盘容量和属性的基本信息,如品牌、型号和序列号。这是一个有趣且有用的信息。然而,本节显示,必须带着一点怀疑的态度来看待这份智能数据报告。请注意,我的已知故障驱动器已通过自我评估测试。这似乎意味着驱动器不会发生灾难性故障,尽管它已经发生了。

我们目前最感兴趣的数据在下面两节。请注意,我删掉了大量对这个实验不重要的信息。

=== START OF READ SMART DATA SECTION ===
<snip – removed list of SMART capabilities.>

SMART Attributes Data Structure revision number: 10
Vendor Specific SMART Attributes with Thresholds:
ID# ATTRIBUTE_NAME          FLAGS    VALUE WORST THRESH FAIL RAW_VALUE
 1 Raw_Read_Error_Rate      POSR--   116   086   006    -    107067871
 3 Spin_Up_Time             PO----   099   099   000    -    0
 4 Start_Stop_Count         -O--CK   100   100   020    -    279
 5 Reallocated_Sector_Ct    PO--CK   048   048   036    -    2143
 7 Seek_Error_Rate          POSR--   085   060   030    -    365075805
 9 Power_On_Hours           -O--CK   019   019   000    -    71783
10 Spin_Retry_Count         PO--C-   100   100   097    -    0
12 Power_Cycle_Count        -O--CK   100   100   020    -    279
184 End-to-End_Error        -O--CK   100   100   099    -    0
187 Reported_Uncorrect      -O--CK   001   001   000    -    1358
188 Command_Timeout         -O--CK   100   098   000    -    12885622796
189 High_Fly_Writes         -O-RCK   001   001   000    -    154
190 Airflow_Temperature_Cel -O---K   071   052   045    -    29 (Min/Max 22/29)
194 Temperature_Celsius     -O---K   029   048   000    -    29 (0 22 0 0 0)
195 Hardware_ECC_Recovered  -O-RC-   039   014   000    -    107067871
197 Current_Pending_Sector  -O--C-   100   100   000    -    0
198 Offline_Uncorrectable   ----C-   100   100   000    -    0
199 UDMA_CRC_Error_Count    -OSRCK   200   200   000    -    20
240 Head_Flying_Hours       ------   100   253   000    -    71781 (50 96 0)
241 Total_LBAs_Written      ------   100   253   000    -    2059064490
242 Total_LBAs_Read         ------   100   253   000    -    260980229
                            ||||||_ K auto-keep
                            |||||__ C event count
                            ||||___ R error rate
                            |||____ S speed/performance
                            ||_____ O updated online
                            |______ P prefailure warning

smartctl 命令结果的前一部分显示了硬盘上硬件寄存器中累积的原始数据。原始值对某些错误率没有特别的帮助;如你所见,有些数字显然是伪造的。“值”栏通常更有帮助。阅读参考网页,了解一点为什么。一般来说,数值栏中像 100 这样的数字意味着 100%好,像 001 这样的低数值意味着接近失败——大约 99%的使用寿命都用完了。这真的很奇怪。

在这种情况下,Reallocated_Sector_Ct 的值列中的 048——重新分配的扇区计数——可能意味着大约一半的重新分配扇区已经用完。

数字 001 表示 Reported_Uncorrect(报告的不可纠正的缺陷扇区)和 High_Fly_Writes(磁头飞出硬盘记录表面的距离超过最佳值的写入),这意味着该硬盘的寿命实际上已经结束。经验证据已经证明了这一点。

下一节实际上列出了错误以及错误发生时的相关信息。这是输出中最有帮助的部分。我不试图分析每一个错误;我只是看看是否有多个错误。下面第一行中的数字 1350 是在此硬盘上检测到的错误总数。

<Snip>

Error 1350 [9] occurred at disk power-on lifetime: 2257 hours (94 days + 1 hours)
 When the command that caused the error occurred, the device was active or idle.

 After command completion occurred, registers were:
 ER -- ST COUNT  LBA_48  LH LM LL DV DC
 -- -- -- == -- == == == -- -- -- -- --
 40 -- 51 00 00 00 04 ed 00 14 59 00 00  Error: UNC at LBA = 0x4ed001459 = 21156074585

 Commands leading to the command that caused the error were:
 CR FEATR COUNT  LBA_48  LH LM LL DV DC  Powered_Up_Time  Command/Feature_Name
 -- == -- == -- == == == -- -- -- -- --  ---------------  ------------------
 60 00 00 00 08 00 04 ed 00 14 58 40 00 11d+10:44:56.878  READ FPDMA QUEUED
 27 00 00 00 00 00 00 00 00 00 00 e0 00 11d+10:44:56.851  READ NATIVE MAX ADDRESS EXT [OBS-ACS-3]
 ec 00 00 00 00 00 00 00 00 00 00 a0 00 11d+10:44:56.849  IDENTIFY DEVICE
 ef 00 03 00 46 00 00 00 00 00 00 a0 00 11d+10:44:56.836  SET FEATURES [Set transfer mode]
 27 00 00 00 00 00 00 00 00 00 00 e0 00 11d+10:44:56.809  READ NATIVE MAX ADDRESS EXT [OBS-ACS-3]

Error 1349 [8] occurred at disk power-on lifetime: 2257 hours (94 days + 1 hours)
 When the command that caused the error occurred, the device was active or idle.

 After command completion occurred, registers were:
 ER -- ST COUNT  LBA_48  LH LM LL DV DC
 -- -- -- == -- == == == -- -- -- -- --
 40 -- 51 00 00 00 04 ed 00 14 59 00 00  Error: UNC at LBA = 0x4ed001459 = 21156074585

 Commands leading to the command that caused the error were:
 CR FEATR COUNT  LBA_48  LH LM LL DV DC  Powered_Up_Time  Command/Feature_Name
 -- == -- == -- == == == -- -- -- -- --  ---------------  ------------------
 60 00 00 00 08 00 04 ed 00 14 58 40 00 11d+10:44:53.953  READ FPDMA QUEUED
 60 00 00 00 08 00 04 f4 00 14 10 40 00 11d+10:44:53.890  READ FPDMA QUEUED
 60 00 00 00 10 00 04 f4 00 14 00 40 00 11d+10:44:53.887  READ FPDMA QUEUED
 60 00 00 00 10 00 04 f3 00 14 f0 40 00 11d+10:44:53.886  READ FPDMA QUEUED
 60 00 00 00 10 00 04 f3 00 14 e0 40 00 11d+10:44:53.886  READ FPDMA QUEUED

Error 1348 [7] occurred at disk power-on lifetime: 2257 hours (94 days + 1 hours)
 When the command that caused the error occurred, the device was active or idle.

 After command completion occurred, registers were:
 ER -- ST COUNT  LBA_48  LH LM LL DV DC
 -- -- -- == -- == == == -- -- -- -- --
 40 -- 51 00 00 00 04 ed 00 14 59 00 00  Error: UNC at LBA = 0x4ed001459 = 21156074585

 Commands leading to the command that caused the error were:
 CR FEATR COUNT  LBA_48  LH LM LL DV DC  Powered_Up_Time  Command/Feature_Name
 -- == -- == -- == == == -- -- -- -- --  ---------------  ------------------
 60 00 00 00 08 00 04 ed 00 14 58 40 00 11d+10:44:50.892  READ FPDMA QUEUED
 27 00 00 00 00 00 00 00 00 00 00 e0 00 11d+10:44:50.865  READ NATIVE MAX ADDRESS EXT [OBS-ACS-3]
 ec 00 00 00 00 00 00 00 00 00 00 a0 00 11d+10:44:50.863  IDENTIFY DEVICE
 ef 00 03 00 46 00 00 00 00 00 00 a0 00 11d+10:44:50.850  SET FEATURES [Set transfer mode]
 27 00 00 00 00 00 00 00 00 00 00 e0 00 11d+10:44:50.823  READ NATIVE MAX ADDRESS EXT [OBS-ACS-3]

Error 1347 [6] occurred at disk power-on lifetime: 2257 hours (94 days + 1 hours)
 When the command that caused the error occurred, the device was active or idle.

<Snip – removed many redundant error listings>

这些错误表明磁盘确实有问题。

我决定在更换硬盘之前,先看看还会发生什么。失败的数字在开始时并没有那么糟糕。在灾难性故障发生时,错误计数上升到 1350。

一家名为 Backblaze 的云公司对超过 67,800 个智能硬盘 4 进行了一些测试,提供了一些基于统计的关于经历了各种报告错误数量的硬盘故障率的见解。这个网页是我发现的第一个展示了报告的 SMART 错误和实际失败率之间的统计相关性的网页。他们的网页也帮助我更好地理解了他们认为应该密切关注的五个智能属性。

在我看来,Backblaze 分析的底线是,在硬盘开始出现他们建议监控的五个统计数据中的任何一个错误报告后,应该尽快更换硬盘。

我的经验似乎证实了这一点,尽管它在统计学上并不显著。在第一次出现问题迹象的几个月内,我的硬盘就失灵了。在无法恢复之前,我的驱动器经历的错误数量非常多,我非常幸运能够从导致/home 文件系统切换到只读(ro)模式的几个错误中恢复过来。只有当 Linux 确定文件系统不稳定并且不可信时,才会出现这种情况。

恢复

所以说包含我的主目录的驱动器发生了灾难性的故障是有道理的。恢复是直截了当的,如果有点费时。

我关闭了电脑,取出了有缺陷的 320GB SATA 驱动器,换上了新的 1TB SATA 驱动器,因为我想稍后将额外的空间用于其他存储,然后重新打开了电脑。我创建了一个占用驱动器上所有空间的物理卷(PV ),然后创建了一个填充 PV 的卷组(VG)。我将其中的 250GB 空间用于逻辑卷(LV ),它将成为/home 文件系统。然后,我在逻辑卷上创建了一个 EXT4 文件系统,并使用e2label命令给它加上标签“home ”,因为我使用标签挂载文件系统。此时,替换驱动器已经准备好了,所以我将它安装在/home 上。

作为我创建备份的方法的结果,我只需要使用一个简单的复制命令,如代码示例 21-1 所示,就可以将整个主目录恢复到新安装的驱动器。

代码示例 21-1

请注意,为了确保正在恢复的数据不会被损坏,我不能以任何在/home 文件系统中拥有文件的非 root 用户身份登录。我以 root 用户身份登录虚拟控制台,并使用以下命令将数据从我的备份恢复到新安装并准备好的替换硬盘。

cp -Rp /media/Backups/Backups/david/2018-03-04-RSBackup/home/ /home

“R”选项遍历整个/home 目录结构,并复制整个目录树中的所有内容。“p”选项保留文件的所有权和权限属性。

在将数据恢复到我的/home 目录之后,我使用我的非特权用户 ID 登录并检查了所有东西。一切都按预期运行,我的所有数据都已正确恢复,包括这本书的文件。

按我的方式做

我的备份 shell 脚本是那些具有精心规划优势的程序之一。这是因为在此之前,我编写、使用并发现了许多我自己的备份脚本的错误。我能够更全面地了解我真正需要的备份系统。

我再次从一组需求开始。我已经考虑了几个月了。我已经有了一个使用 tar 在 tgz 文件中创建备份的备份脚本。但是从 tar 文件中提取单个文件或目录是一项很好的工作,需要花费一些时间。每晚还需要一个多小时来进行备份。尽管有 gzip 压缩,大文件意味着只有几天的历史可以保存在我用来备份的外部 USB 硬盘上,因为所有的东西都被备份了多次。

我有很多多年积累的档案。其中一些文件非常大,尤其是我的虚拟机文件。目前,我有大约 18 台虚拟机,每台都有非常大的虚拟磁盘与之关联。这占据了大量的空间。

因此,我需要一个快速的备份解决方案,能够轻松快速地处理非常大的文件,通过节省空间而无需创建某种类型的压缩归档,允许在单个备份驱动器上保存更多历史记录,并且我或我的客户可以在需要时轻松访问特定文件。

备份选项

执行备份有许多选项。除了像tar这样的老版本,大多数 Linux 发行版都提供了一个或多个额外的开源程序,专门用于执行备份。也有许多商业选择。

这些解决方案都不能完全满足我的需求,我真的想使用另一个我听说过的工具,rsync5 设计和实施可行的备份计划并不一定需要昂贵的备份计划。

我一直在试验rsync命令,它有一些非常有趣的特性,我已经能够很好地利用它们了。我的主要目标是创建备份,用户可以从这些备份中快速定位和恢复文件,而不必从备份 tarball 中提取数据,并减少创建和备份所需的时间。

本节只打算描述我自己在备份场景中对rsync的使用。它并没有介绍rsync的所有功能,也没有介绍它的许多其他有趣的用法。

备份

命令由安德鲁·特里吉和保罗·麦克拉斯编写,于 1996 年首次发布。rsync的主要目的是远程同步一台计算机上的文件和另一台计算机上的文件。你注意到他们做了什么来创造这个名字吗?rsync是一款开源软件,并且提供了我所熟悉的所有发行版。

rsync命令可用于同步两个目录或目录树,无论它们是在同一台计算机上还是在不同的计算机上,但它能做的远不止这些。rsync创建或更新目标目录,使其与源目录相同。所有常用的 Linux 工具都可以自由访问目标目录,因为它不是存储在 tarball 或 zip 文件或任何其他归档文件类型中;它只是一个包含普通文件的普通目录,普通用户可以使用基本的 Linux 工具进行导航。这符合我的主要目标之一。

rsync最重要的特性之一是它用来同步在源目录中已经改变的预先存在的文件的方法。它不是从源文件复制整个文件,而是使用校验和来比较源文件和目标文件的块。如果两个文件中的所有块都相同,则不会传输任何数据。如果数据不同,则仅将源上已更改的数据块传输到目标。这为远程同步节省了大量的时间和网络带宽。例如,当我第一次使用我的rsync bash 脚本将我所有的主机备份到一个大的外部 USB 硬盘上时,花了大约 3 个小时。这是因为所有的数据都必须转移,因为这些数据之前都没有备份过。后续备份实际需要 3 到 8 分钟,具体取决于自上次备份以来更改或创建了多少文件。我使用了time命令来确定这一点,所以这是经验数据。例如,昨晚花了 3 分 12 秒完成了 6 个远程系统和本地工作站的大约 750GB 数据的备份。当然,只有几百兆字节的数据在一天中被更改,需要备份。

代码示例 21-2 中显示的简单的rsync命令可用于同步两个目录及其任何子目录的内容。也就是说,目标目录的内容与源目录的内容同步,以便在同步结束时,目标目录与源目录相同。

代码示例 21-2

这是使用rsync同步两个目录所需的最少命令。

rsync -aH sourcedir targetdir

-a 选项用于存档模式,它保留权限、所有权和符号(软)链接。H 用于保存硬链接,而不是为每个硬链接创建一个新文件。请注意,源目录或目标目录都可以位于远程主机上。

现在让我们假设昨天我们使用了rsync来同步两个目录。今天我们想要重新同步它们,但是我们已经从源目录中删除了一些文件。rsync通常的做法是将所有新的或更改的文件复制到目标位置,并将删除的文件留在目标位置。这可能是您想要的行为,但是如果您希望从源中删除的文件也从目标(即备份)中删除,您可以添加- delete 选项来实现这一点。

另一个有趣的选项是- link-dest 选项,这也是我个人最喜欢的选项,因为它极大地增强了 rsync 的功能和灵活性。- link-dest 选项使用硬链接,67来创建一系列每日备份,这些备份每天占用的额外空间非常少,创建时间也非常短。

使用该选项指定前一天的目标目录,并为今天指定一个新目录。然后,rsync命令创建今天的新目录,在今天的目录中创建昨天目录中每个文件的硬链接。所以我们现在在今天的目录中有一堆指向昨天文件的硬链接。没有创建或复制新文件。只创建了一些昨天文件的硬链接。使用这组指向昨天的目标目录的硬链接创建今天的目标目录后,rsync照常执行其同步,但是当在文件中检测到更改时,目标硬链接被昨天的文件的副本替换,然后文件的更改从源复制到目标。

所以现在我们的命令看起来像代码样本 21-3 中的那样。

代码示例 21-3

这个版本的 rsync 命令首先在今天的备份目录中为昨天的备份目录中的每个文件创建硬链接。然后,将源目录(正在备份的目录)中的文件与刚刚创建的硬链接进行比较。如果源目录中的文件没有更改,则不会采取进一步的操作。

rsync -aH --delete --link-dest=yesterdaystargetdir sourcedir todaystargetdir

如果对源目录中的文件进行了更改,rsync 会删除指向昨天备份目录中的文件的硬链接,并从昨天的备份中制作文件的精确副本。然后,它将对文件所做的更改从源目录复制到今天的目标备份目录。

rsync还删除目标驱动器或目录上已从源目录中删除的文件。

有时也希望将某些目录或文件排除在同步之外。我们通常不关心缓存目录的备份,因为它们可能包含大量数据,与其他数据目录相比,备份它们所需的时间可能会很长。为此,有- exclude 选项。对要排除的文件或目录使用该选项和模式。您可能希望排除浏览器缓存文件,这样您的新命令将类似于代码示例 21-4。

代码示例 21-4

rsync -aH --delete --exclude Cache --link-dest=yesterdaystargetdir sourcedir todaystargetdir

请注意,要排除的每个文件模式都必须有单独的排除选项。

rsync命令可以与作为源或目标的远程主机同步文件。对于下一个示例,我们假设源目录位于主机名为 remote1 的远程计算机上,目标目录位于本地主机上。尽管 ssh 是在与远程主机之间传输数据时使用的默认通信协议,但我总是添加 SSH 选项。该命令现在看起来像这样。

代码示例 21-5

在这段代码中,源目录位于远程主机 remote1 上。

rsync -aH -e ssh --delete --exclude Cache --link-dest=yesterdaystargetdir remote1:sourcedir todaystargetdir

此命令将数据从远程主机上的目录备份到本地主机。

The rsync命令有大量的选项,您可以用来定制同步过程。在很大程度上,我在这里描述的相对简单的命令非常适合我的个人需求。请务必阅读 rsync 的大量手册页,以了解它的更多功能以及这里讨论的选项的详细信息。

执行备份

我自动化了我的备份,因为“自动化一切”我编写了一个 bash 脚本rsbu,它处理使用rsync创建一系列每日备份的细节。这包括确保安装了备份介质,为昨天和今天的备份目录生成名称,在备份介质上创建适当的目录结构(如果它们还不在那里),执行实际的备份,以及卸载介质。

我在脚本中使用rsync命令的方法的最终结果是,我得到了网络中每个主机的备份的日期序列。备份驱动器的结构类似于图 21-1 所示。这使得查找可能需要恢复的特定文件变得容易。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 21-1

我的备份数据磁盘的目录结构

因此,从 1 月 1 日的一个空磁盘开始,rsbu脚本对我在配置文件中指定的所有文件和目录的每个主机进行了完整的备份。如果您像我一样有很多数据,第一次备份可能需要几个小时。

1 月 2 日,rsync命令使用–link-dest =选项创建一个与 1 月 1 日完全相同的新目录结构,然后它在源目录中查找已更改的文件。如果有任何更改,将在 1 月 2 日的目录中制作 1 月 1 日的原始文件的副本,然后从原始文件更新文件中已更改的部分。

第一次备份到空驱动器后,备份只需很少的时间,因为首先会创建硬链接,然后只需要对自上次备份以来发生更改的文件进行进一步的操作。

图 21-1 还显示了一个文件/home/student/file1.txt 在 1 月 1 日、2 日和 3 日的 host2 系列备份的更多细节。1 月 2 日,该文件自 1 月 1 日以来一直未更改。在这种情况下,rsync 备份不会复制 1 月 1 日以后的原始数据。它只是创建一个目录条目,其中包含 1 月 2 日目录到 1 月 1 日目录的硬链接,这是一个非常快速的过程。我们现在有两个目录条目指向硬盘上的相同数据。1 月 3 日,文件已经更改。在这种情况下,数据…/2018-01-02/home/student/file 1 . txt 复制到新目录,…/2018-01-03/home/student/file 1 . txt 以及任何已更改的数据块都将复制到 1 月 3 日的备份文件中。这些策略是使用rsync程序的功能实现的,允许备份大量数据,同时节省磁盘空间和复制完全相同的数据文件所需的大量时间。

我的一个过程是每天从一个 cron 作业运行两次备份脚本。第一个迭代执行到内部 4TB 硬盘驱动器的备份。这是始终可用的备份,并且始终是我所有数据的最新版本。如果发生了一些事情,我需要恢复一个文件或所有文件,我可能会失去的最多是几个小时的工作。

第二次备份是在一个旋转的 4TB 外部 USB 硬盘系列中进行的。我每周至少一次从最近的地方开车到我在银行的保险箱。如果我的家庭办公室被破坏,我在那里维护的备份也随之被破坏,我只需从银行获得外部硬盘驱动器,我最多会丢失一周的数据。这种损失很容易恢复。

我用于备份的驱动器,不仅仅是内部硬盘,还有我每周轮换的外部 USB 硬盘,从来没有填满过。这是因为我编写的rsbu脚本会在进行新备份之前检查每个驱动器上备份的天数。如果驱动器上有任何超过指定天数的备份,它们将被删除。该脚本使用find命令来定位这些备份。天数在 rsbu.conf 配置文件中指定。

当然,在一场彻底的灾难之后,我首先必须为我和我的妻子找到一个有办公空间的新住处,购买部件并构建新的计算机,从剩余的备份中恢复,然后重新创建任何丢失的数据。

我的脚本 rsbu 及其配置文件 rsbu.conf 和一个 READ 是可用的。ME 文件来自 https://github.com/Apress/linux-philo-sysadmins/tree/master/Ch21

恢复测试

没有测试,任何备份方案都是不完整的。您应该定期测试随机文件或整个目录结构的恢复,以确保不仅备份正常工作,而且备份中的数据可以在灾难发生后恢复使用。我见过太多这样的例子,由于这样或那样的原因,备份无法恢复,并且由于缺乏测试而无法发现问题,导致宝贵的数据丢失。

只需选择一个文件或目录进行测试,并将其恢复到测试位置,如/tmp,这样就不会覆盖自备份执行以来可能已更新的文件。验证文件的内容是否符合您的预期。从使用上述 rsync 命令创建的备份中恢复文件,只需从备份中找到要恢复的文件,然后将其复制到要恢复的位置。

我遇到过几次不得不恢复单个文件的情况,有时还需要恢复完整的目录结构。正如我在本章前面所讨论的,有几次我不得不恢复硬盘的全部内容。大多数时候,这是我自己造成的,因为我不小心删除了一个文件或目录。至少有几次是因为硬盘崩溃。所以这些备份确实派上了用场。

异地备份

创建良好的备份是备份策略中重要的第一步。将生成的备份介质与原始数据保存在同一物理位置是错误的。

我们已经看到,将所有备份存储在内部驱动器上的计算机被盗会导致重要数据完全丢失且无法恢复。如果原始数据和备份数据存储在同一位置,火灾和其他灾难也会导致原始数据和备份数据丢失。防火保险箱是一种选择,可以减少盗窃和火灾等灾难的威胁。这种保险箱通常在规定的温度下以分钟为单位进行评级,以保护其内容物。我想我个人关心的是,我不知道火会燃烧多长时间或多热。也许保险箱能支撑足够长的时间,但如果不能呢?

我更喜欢像大公司那样为自己做备份。我保留当前的异地备份。对我来说,这个在我银行的保险箱里。对于其他人来说,这可能是“在云”的某个地方。我喜欢我的保险箱解决方案提供的端到端控制。我知道它被保护得很好。如果我小小的家庭办公室被摧毁,银行可能离得足够远,不会受到任何灾难的影响。

对于大公司来说,有一些服务可以将你的备份存储在一个远程、高安全性的地方,有气候控制的保险库。这些服务中的大多数甚至会派装甲卡车到您的设备处,来取走和运输您的备份介质。有些提供高速网络连接,以便可以在远程位置将备份直接备份到自己的存储介质上。

如今,许多人和组织都在进行云备份。我对所谓的“云”持严重保留意见。首先,“云”只是别人电脑的另一种说法。第二,考虑到我读到的所谓安全计算设施遭到黑客攻击的数量,我不太可能将我的数据交给任何维护可从互联网访问的在线备份的外部组织。我更希望我的远程备份数据离线,直到我需要它。

我对云的担心是,除了提供商放在他们网站上的营销信息,我没有办法知道他们的安全措施是否比我自己做的更好。也许他们可以,但作为一名系统管理员,我希望得到一些证明。我毫不怀疑,与许多企业和个人相比,很大一部分云提供商能够更好地管理委托给他们的数据的安全性。我怎么知道那些是什么?请记住,我们谈论的是基于云的备份解决方案,而不是应用或 web 存在解决方案。

我想我可以有一定把握地说,在安全性方面,成熟和公认的云提供商,如 Amazon、Azure、Google 等,肯定比许多中小型组织更值得信赖。我在考虑那些没有全职系统管理员的公司,或者把它外包给那些不太知名的小型本地公司。我还认为,在当今不断遭受网络攻击的世界里,许多经验不足的系统管理员还没有准备好应对互联网所需的高安全级别。

因此,对于许多组织来说,云可能是一个可行的选择。对于其他人来说,一个经验丰富、知识渊博的系统管理员可能是最好的选择。与许多 IT 决策一样,这是一个权衡风险因素并确定您愿意接受多少风险的问题。

灾难恢复服务

更进一步说,我工作过的一些地方与一个或多个灾难恢复服务有合同。这种类型的服务是为了维护一个完整的计算机和网络环境,可以在接到通知后立即替换您自己的计算机和网络环境。这通常包括从大型机到基于 Intel 的服务器和工作站的一切。当然,这还不包括在异地备份存储中保存大量数据。

在我工作的一个地方,我们对灾难恢复计划进行季度评估。我们通过英特尔服务器关闭了主机上的所有计算机。我们通知灾难恢复公司我们正在进行一项测试,他们准备了他们的站点,提供了我们恢复和运行所需的各种计算机。我们让备份存储服务将最新的备份介质从他们的安全设施运送到费城的恢复站点。

我们办公室的一群人来到恢复站点,从我们的备份介质中恢复了所有数据,使所有东西都上线,并进行测试以确保一切都正常工作。

总是有问题。一直都是。但这是整个练习的目的——发现我们的策略和程序中的问题。然后修复它们。

其他选项

不是每个人都需要灾难恢复服务或大量备份数据存储。对于一些只有一台电脑的个人和非常小的企业来说,几个 USB 拇指驱动器和手动备份到其中一个驱动器就足够了。对于其他人来说,一个相对较小的外部 USB 硬盘驱动器就可以了。

这都是你在你的环境中所需要的。

“经常”的部分呢?

这很简单。每天至少做一次备份。无论如何?如果一些文件特别重要,而你刚刚创建或修改了它们,那么马上备份它们。

rsbu脚本会很快完成这项工作,因为它只会备份已经更改的文件。它以一种仍然允许你在你的计算机上继续工作的方式做到这一点。

摘要

作为系统管理员,备份是我们工作中极其重要的一部分。我经历过许多这样的例子:备份实现了我工作过的地方以及我自己的业务和个人数据的快速操作恢复。

执行和维护数据备份有许多选项。我做对我有用的事情,从来没有丢失超过几个小时的数据。

像其他任何东西一样,备份是关于你需要什么的。无论你做什么——做点什么!计算一下,如果你失去了一切——数据、电脑、硬拷贝记录——一切,你会有多痛苦。这一难题包括更换硬件的成本,以及恢复已备份数据和未备份数据所需的时间成本。然后相应地计划和实施您的备份系统和程序。

您的主机必须安装并运行邮件传输代理(MTA),如 SendMail。/etc/aliases 文件必须包含一个条目,以便将 root 用户的电子邮件发送到您的电子邮件地址。

2

了解智能报告https://lime-technology.com/wiki/Understanding_SMART_Reports

3

维基百科,【smart】https://en.wikipedia.org/wiki/SMART

4

BackBlaze 网站,“智能统计告诉我们关于硬盘的什么”, https://www.backblaze.com/blog/what-smart-stats-indicate-hard-drive-failures/

5

维基百科,【rsync】https://en.wikipedia.org/wiki/Rsync

6

维基百科,【硬链接】https://en.wikipedia.org/wiki/Hard_link

7

大卫,Linux 数据手册,使用 Linux 文件系统中的硬链接和软链接http://www.linux-databook.info/?page_id=5087

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值