程序员的学习之路

# 程序员的学习之路

*2016 年 10 月，当我从微软离职时，我已经在微软工作了近 21 年，在业界也快 35 年了。我花了一些时间反思我这些年来学到的东西，这些文字是那篇帖子稍加修改后得到。请见谅，文章有一点长。*

<img src="content_image/album_201805_23_110510l6r65l555zlgc3ld.jpg" alt="" title="">

要成为一名专业的程序员，你需要知道的事情多得令人吃惊：语言的细节、API、算法、数据结构、系统和工具。这些东西一直在随着时间变化——新的语言和编程环境不断出现，似乎总有一些“每个人”都在使用的热门的新工具或新语言。紧跟潮流，保持专业，这很重要。木匠需要知道如何为工作选择合适的锤子和钉子，并且要有能力笔直精准地钉入钉子。

与此同时，我也发现有一些理论和方法有着广泛的应用场景，它们能使用几十年。底层设备的性能和容量在这几十年来增长了几个数量级，但系统设计的思考方式还是互相有关联的，这些思考方式比具体的实现更根本。理解这些重复出现的主题对分析与设计我们所负责的系统大有帮助。

### 谦卑和自我

这不仅仅局限于编程，但在编程这个持续发展的领域，一个人需要在谦卑和自我中保持平衡。总有新的东西需要学习，并且总有人能帮助你学习——如果你愿意学习的话。一个人即需要保持谦卑，认识到自己不懂并承认它，也要保持自我，相信自己能掌握一个新的领域，并且能运用你已经掌握的知识。我见过的最大的挑战就是一些人在某个领域深入专研了很长时间，“忘记”了自己擅长学习新的东西。最好的学习来自放手去做，建造一些东西，即便只是一个原型或者 hack。我知道的最好的程序员对技术有广泛的认识，但同时他们对某个技术深入研究，成为了专家。而深入的学习来自努力解决真正困难的问题。

### 端到端观点

1981 年，Jerry Saltzer、 Dave Reed 和 Dave Clark 在做因特网和分布式系统的早期工作，他们提出了<ruby> 端到端 <rt>  end to end </rt></ruby>观点，并作出了[经典的阐述](http://web.mit.edu/Saltzer/www/publications/endtoend/endtoend.pdf)。网络上的文章有许多误传，所以更应该阅读论文本身。论文的作者很谦虚，没有声称这是他们自己的创造——从他们的角度看，这只是一个常见的工程策略，不只在通讯领域中，在其他领域中也有运用。他们只是将其写下来并收集了一些例子。下面是文章的一个小片段：

>
> 当我们设计系统的一个功能时，仅依靠端点的知识和端点的参与，就能正确地完整地实现这个功能。在一些情况下，系统的内部模块局部实现这个功能，可能会对性能有重要的提升。
>
>
>

该论文称这是一个“观点”，虽然在维基百科和其他地方它已经被上升成“原则”。实际上，还是把它看作一个观点比较好，正如作者们所说，系统设计者面临的最难的问题之一就是如何在系统组件之间划分责任，这会引发不断的讨论：怎样在划分功能时权衡利弊，怎样隔离复杂性，怎样设计一个灵活的高性能系统来满足不断变化的需求。没有简单的原则可以直接遵循。

互联网上的大部分讨论集中在通信系统上，但端到端观点的适用范围其实更广泛。分布式系统中的“<ruby> 最终一致性 <rt>  eventual consistency </rt></ruby>”就是一个例子。一个满足“最终一致性”的系统，可以让系统中的元素暂时进入不一致的状态，从而简化系统，优化性能，因为有一个更大的端到端过程来解决不一致的状态。我喜欢横向拓展的订购系统的例子（例如亚马逊），它不要求每个请求都通过中央库存的控制点。缺少中央控制点可能允许两个终端出售相同的最后一本书，所以系统需要用某种方法来解决这个问题，如通知客户该书会延期交货。不论怎样设计，想购买的最后一本书在订单完成前都有可能被仓库中的叉车运出厍（LCTT 译注：比如被其他人下单购买）。一旦你意识到你需要一个端到端的解决方案，并实现了这个方案，那系统内部的设计就可以被优化以利用这个解决方案。

事实上，这种设计上的灵活性可以优化系统的性能，或者提供其他的系统功能，从而使得端到端的方法变得如此强大。端到端的思考往往允许内部进行灵活的操作，使整个系统更加健壮，并且能适应每个组件特性的变化。这些都让端到端的方法变得健壮，并能适应变化。

端到端方法意味着，添加会牺牲整体性能灵活性的抽象层和功能时要非常小心（也可能是其他的灵活性，但性能，特别是延迟，往往是特殊的）。如果你展示出底层的原始性能（LCTT 译注：performance，也可能指操作，下同），端到端的方法可以根据这个性能（操作）来优化，实现特定的需求。如果你破坏了底层性能（操作），即使你实现了重要的有附加价值的功能，你也牺牲了设计灵活性。

如果系统足够庞大而且足够复杂，需要把整个开发团队分配给系统内部的组件，那么端到端观点可以和团队组织相结合。这些团队自然要扩展这些组件的功能，他们通常从牺牲设计上的灵活性开始，尝试在组件上实现端到端的功能。

应用端到端方法面临的挑战之一是确定端点在哪里。 俗话说，“大跳蚤上有小跳蚤，小跳蚤上有更少的跳蚤……等等”。

### 关注复杂性

编程是一门精确的艺术，每一行代码都要确保程序的正确执行。但这是带有误导的。编程的复杂性不在于各个部分的整合，也不在于各个部分之间如何相互交互。最健壮的程序会将复杂性隔离开，让最重要的部分变的简单直接，通过简单的方式与其他部分交互。虽然隐藏复杂性和信息隐藏、数据抽象等其他设计方法一样，但我仍然觉得，如果你真的要定位出系统的复杂所在，并将其隔离开，那你需要对设计特别敏锐。

在我的[文章](https://medium.com/@terrycrowley/model-view-controller-and-loose-coupling-6370f76e9cde#.o4gnupqzq)中反复提到的例子是早期的终端编辑器 VI 和 Emacs 中使用的屏幕重绘算法。早期的视频终端实现了控制序列，来控制绘制字符核心操作，也实现了附加的显示功能，来优化重新绘制屏幕，如向上向下滚动当前行，或者插入新行，或在当前行中移动字符。这些命令都具有不同的开销，并且这些开销在不同制造商的设备中也是不同的。（参见[TERMCAP](https://en.wikipedia.org/wiki/Termcap) 以获取代码和更完整的历史记录的链接。）像文本编辑器这样的全屏应用程序希望尽快更新屏幕，因此需要优化使用这些控制序列来从一个状态到另一个状态屏幕转换。

这些程序在设计上隐藏了底层的复杂性。系统中修改文本缓冲区的部分（功能上大多数创新都在这里）完全忽略了这些改变如何被转换成屏幕更新命令。这是可以接受的，因为针对*任何*内容的改变计算最佳命令所消耗的性能代价，远不及被终端本身实际执行这些更新命令的性能代价。在确定如何隐藏复杂性，以及隐藏哪些复杂性时，性能分析扮演着重要的角色，这一点在系统设计中非常常见。屏幕的更新与底层文本缓冲区的更改是异步的，并且可以独立于缓冲区的实际历史变化顺序。缓冲区是*怎样*改变的并不重要，重要的是改变了*什么*。异步耦合，在组件交互时消除组件对历史路径依赖的组合，以及用自然的交互方式以有效地将组件组合在一起是隐藏耦合复杂度的常见特征。

隐藏复杂性的成功不是由隐藏复杂性的组件决定的，而是由使用该模块的使用者决定的。这就是为什么组件的提供者至少要为组件的某些端到端过程负责。他们需要清晰的知道系统的其他部分如何与组件相互作用，复杂性是如何泄漏出来的（以及是否泄漏出来）。这常常表现为“这个组件很难使用”这样的反馈——这通常意味着它不能有效地隐藏内部复杂性，或者没有选择一个隐藏复杂性的功能边界。

### 分层与组件化

系统设计人员的一个基本工作是确定如何将系统分解成组件和层；决定自己要开发什么，以及从别的地方获取什么。开源项目在决定自己开发组件还是购买服务时，大多会选择自己开发，但组件之间交互的过程是一样的。在大规模工程中，理解这些决策将如何随着时间的推移而发挥作用是非常重要的。从根本上说，变化是程序员所做的一切的基础，所以这些设计决定不仅要在当下评估，还要随着产品的不断发展而在未来几年得到评估。

以下是关于系统分解的一些事情，它们最终会占用大量的时间，因此往往需要更长的时间来学习和欣赏。

* **层泄漏。**层（或抽象）[基本上是泄漏的](https://medium.com/@terrycrowley/leaky-by-design-7b423142ece0#.x67udeg0a)。这些泄漏会立即产生后果，也会随着时间的推移而产生两方面的后果。其中一方面就是该抽象层的特性渗透到了系统的其他部分，渗透的程度比你意识到得更深入。这些渗透可能是关于具体的性能特征的假设，以及抽象层的文档中没有明确的指出的行为发生的顺序。这意味着假如内部组件的行为发生变化，你的系统会比想象中更加脆弱。第二方面是你比表面上看起来更依赖组件内部的行为，所以如果你考虑改变这个抽象层，后果和挑战可能超出你的想象。
* **层具有太多功能。**您所采用的组件具有比实际需要更多的功能，这几乎是一个真理。在某些情况下，你决定采用这个组件是因为你想在将来使用那些尚未用到的功能。有时，你采用组件是想“上快车”，利用组件完成正在进行的工作。在功能强大的抽象层上开发会带来一些后果。
	1. 组件往往会根据你并不需要的功能作出取舍。
	2. 为了实现那些你并不没有用到的功能，组件引入了复杂性和约束，这些约束将阻碍该组件的未来的演变。
	3. 层泄漏的范围更大。一些泄漏是由于真正的“抽象泄漏”，另一些是由于明显的，逐渐增加的对组件全部功能的依赖（但这些依赖通常都没有处理好）。Office 软件太大了，我们发现，对于我们建立的任何抽象层，我们最终都在系统的某个部分完全运用了它的功能。虽然这看起来是积极的（我们完全地利用了这个组件），但并不是所用的使用都有同样的价值。所以，我们最终要付出巨大的代价才能从一个抽象层往另一个抽象层迁移，这种“长尾”没什么价值，并且对使用场景认识不足。
	4. 附加的功能会增加复杂性，并增加功能滥用的可能。如果将验证 XML 的 API 指定为 XML 树的一部分，那这个 API 可以选择动态下载 XML 的模式定义。这在我们的基本文件解析代码中被错误地执行，导致 w3c.org 服务器上的大量性能下降以及（无意）分布式拒绝服务攻击。（这些被通俗地称为“地雷”API）。
* **抽象层被更换。**需求在进化，系统在进化，组件被放弃。您最终需要更换该抽象层或组件。不管是对外部组件的依赖还是对内部组件的依赖都是如此。这意味着上述问题将变得重要起来。
* **自己构建还是购买的决定将会改变。**这是上面几方面的必然结果。这并不意味着自己构建还是购买的决定在当时是错误的。一开始时往往没有合适的组件，一段时间之后才有合适的组件出现。或者，也可能你使用了一个组件，但最终发现它不符合您不断变化的要求，而且你的要求非常窄、很好理解，或者对你的价值体系来说是非常重要的，以至于拥有自己的模块是有意义的。这意味着你像关心自己构造的模块一样，关心购买的模块，关心它们是怎样泄漏并深入你的系统中的。
* **抽象层会变臃肿。**一旦你定义了一个抽象层，它就开始增加功能。层是对使用模式优化的自然分界点。臃肿的层的困难在于，它往往会降低您利用底层的不断创新的能力。从某种意义上说，这就是操作系统公司憎恨构建在其核心功能之上的臃肿的层的原因——采用创新的速度放缓了。避免这种情况的一种比较规矩的方法是禁止在适配器层中进行任何额外的状态存储。微软基础类在 Win32 上采用这个一般方法。在短期内，将功能集成到现有层（最终会导致上述所有问题）而不是重构和重新推导是不可避免的。理解这一点的系统设计人员寻找分解和简化组件的方法，而不是在其中增加越来越多的功能。

### 爱因斯坦宇宙

几十年来，我一直在设计异步分布式系统，但是在微软内部的一次演讲中，SQL 架构师 Pat Helland 的一句话震惊了我。 “我们生活在爱因斯坦的宇宙中，没有同时性这种东西。”在构建分布式系统时（基本上我们构建的都是分布式系统），你无法隐藏系统的分布式特性。这是物理的。我一直感到远程过程调用在根本上错误的，这是一个原因，尤其是那些“透明的”远程过程调用，它们就是想隐藏分布式的交互本质。你需要拥抱系统的分布式特性，因为这些意义几乎总是需要通过系统设计和用户体验来完成。

拥抱分布式系统的本质则要遵循以下几个方面：

* 一开始就要思考设计对用户体验的影响，而不是试图在处理错误，取消请求和报告状态上打补丁。
* 使用异步技术来耦合组件。同步耦合是*不可能*的。如果某些行为看起来是同步的，是因为某些内部层尝试隐藏异步，这样做会遮蔽（但绝对不隐藏）系统运行时的基本行为特征。
* 认识到并且明确设计了交互状态机，这些状态表示长期的可靠的内部系统状态（而不是由深度调用堆栈中的变量值编码的临时，短暂和不可发现的状态）。
* 认识到失败是在所难免的。要保证能检测出分布式系统中的失败，唯一的办法就是直接看你的等待时间是否“太长”。这自然意味着[取消的等级最高](https://medium.com/@terrycrowley/how-to-think-about-cancellation-3516fc342ae#.3pfjc5b54)。系统的某一层（可能直接通向用户）需要决定等待时间是否过长，并取消操作。取消只是为了重建局部状态，回收局部的资源——没有办法在系统内广泛使用取消机制。有时用一种低成本，不可靠的方法广泛使用取消机制对优化性能可能有用。
* 认识到取消不是回滚，因为它只是回收本地资源和状态。如果回滚是必要的，它必须实现成一个端到端的功能。
* 承认永远不会真正知道分布式组件的状态。只要你发现一个状态，它可能就已经改变了。当你发送一个操作时，请求可能在传输过程中丢失，也可能被处理了但是返回的响应丢失了，或者请求需要一定的时间来处理，这样远程状态最终会在未来的某个任意的时间转换。这需要像幂等操作这样的方法，并且要能够稳健有效地重新发现远程状态，而不是期望可靠地跟踪分布式组件的状态。“[最终一致性](http://queue.acm.org/detail.cfm?id=2462076)”的概念简洁地捕捉了这其中大多数想法。

我喜欢说你应该“陶醉在异步”。与其试图隐藏异步，不如接受异步，为异步而设计。当你看到像幂等性或不变性这样的技术时，你就认识到它们是拥抱宇宙本质的方法，而不仅仅是工具箱中的一个设计工具。

### 性能

我确信 Don Knuth 会对人们怎样误解他的名言“过早的优化是一切罪恶的根源”而感到震惊。事实上，性能，及性能的持续超过 60 年的指数增长（或超过 10 年，取决于您是否愿意将晶体管，真空管和机电继电器的发展算入其中），为所有行业内的惊人创新和影响经济的“软件吃掉全世界”的变化打下了基础。

要认识到这种指数变化的一个关键是，虽然系统的所有组件正在经历指数级变化，但这些指数是不同的。硬盘容量的增长速度与内存容量的增长速度不同，与 CPU 的增长速度不同，与内存 CPU 之间的延迟的性能改善速度也不用。即使性能发展的趋势是由相同的基础技术驱动的，增长的指数也会有分歧。[延迟的改进跟不上带宽改善](http://www.ll.mit.edu/HPEC/agendas/proc04/invited/patterson_keynote.pdf)。指数变化在近距离或者短期内看起来是线性的，但随着时间的推移可能是压倒性的。系统不同组件的性能的增长不同，会出现压倒性的变化，并迫使对设计决策定期进行重新评估。

这样做的结果是，几年后，一度有意义的设计决策就不再有意义了。或者在某些情况下，二十年前有意义的方法又开始变成一个好的决策。现代内存映射的特点看起来更像是早期分时的进程切换，而不像分页那样。 （这样做有时会让我这样的老人说“这就是我们在 1975 年时用的方法”——忽略了这种方法在 40 年都没有意义，但现在又重新成为好的方法，因为两个组件之间的关系——可能是闪存和 NAND 而不是磁盘和核心内存——已经变得像以前一样了）。

当这些指数超越人自身的限制时，重要的转变就发生了。你能从 2 的 16 次方个字符（一个人可以在几个小时打这么多字）过渡到 2 的 32 次方个字符（远超出了一个人打字的范围）。你可以捕捉比人眼能感知的分辨率更高的数字图像。或者你可以将整个音乐专辑存在小巧的磁盘上，放在口袋里。或者你可以将数字化视频录制存储在硬盘上。再通过实时流式传输的能力，可以在一个地方集中存储一次，不需要在数千个本地硬盘上重复记录。

但有的东西仍然是根本的限制条件，那就是空间的三维和光速。我们又回到了爱因斯坦的宇宙。内存的分级结构将始终存在——它是物理定律的基础。稳定的存储和 IO、内存、计算和通信也都将一直存在。这些模块的相对容量，延迟和带宽将会改变，但是系统始终要考虑这些元素如何组合在一起，以及它们之间的平衡和折衷。Jim Gary 是这方面的大师。

空间和光速的根本限制造成的另一个后果是，性能分析主要是关于三件事：<ruby> 局部化 <rt>  locality </rt></ruby>、<ruby> 局部化 <rt>  locality </rt></ruby>、<ruby> 局部化 <rt>  locality </rt></ruby>。无论是将数据打包在磁盘上，管理处理器缓存的层次结构，还是将数据合并到通信数据包中，数据如何打包在一起，如何在一段时间内从局部获取数据，数据如何在组件之间传输数据是性能的基础。把重点放在减少管理数据的代码上，增加空间和时间上的局部性，是消除噪声的好办法。

Jon Devaan 曾经说过：“设计数据，而不是设计代码”。这也通常意味着当查看系统结构时，我不太关心代码如何交互——我想看看数据如何交互和流动。如果有人试图通过描述代码结构来解释一个系统，而不理解数据流的速率和数量，他们就不了解这个系统。

内存的层级结构也意味着缓存将会一直存在——即使某些系统层正在试图隐藏它。缓存是根本的，但也是危险的。缓存试图利用代码的运行时行为，来改变系统中不同组件之间的交互模式。它们需要对运行时行为进行建模，即使模型填充缓存并使缓存失效，并测试缓存命中。如果模型由于行为改变而变差或变得不佳，缓存将无法按预期运行。一个简单的指导方针是，缓存必须被检测——由于应用程序行为的改变，事物不断变化的性质和组件之间性能的平衡，缓存的行为将随着时间的推移而退化。每一个老程序员都有缓存变糟的经历。

我很幸运，我的早期职业生涯是在互联网的发源地之一 BBN 度过的。 我们很自然地将将异步组件之间的通信视为系统连接的自然方式。流量控制和队列理论是通信系统的基础，更是任何异步系统运行的方式。流量控制本质上是资源管理（管理通道的容量），但资源管理是更根本的关注点。流量控制本质上也应该由端到端的应用负责，所以用端到端的方式思考异步系统是自然的。[缓冲区膨胀](https://en.wikipedia.org/wiki/Bufferbloat)的故事在这种情况下值得研究，因为它展示了当对端到端行为的动态性以及技术“改进”（路由器中更大的缓冲区）缺乏理解时，在整个网络基础设施中导致的长久的问题。

我发现“<ruby> 光速 <rt>  light speed </rt></ruby>”的概念在分析任何系统时都非常有用。光速分析并不是从当前的性能开始分析，而是问“这个设计理论上能达到的最佳性能是多少？”真正传递的信息是什么，以什么样的速度变化？组件之间的底层延迟和带宽是多少？光速分析迫使设计师深入思考他们的方法能否达到性能目标，或者否需要重新考虑设计的基本方法。它也迫使人们更深入地了解性能在哪里损耗，以及损耗是由固有的，还是由于一些不当行为产生的。从构建的角度来看，它迫使系统设计人员了解其构建的模块的真实性能特征，而不是关注其他功能特性。

我的职业生涯大多花费在构建图形应用程序上。用户坐在系统的一端，定义关键的常量和约束。人类的视觉和神经系统没有经历过指数性的变化。它们固有地受到限制，这意味着系统设计者可以利用（必须利用）这些限制，例如，通过虚拟化（限制底层数据模型需要映射到视图数据结构中的数量），或者通过将屏幕更新的速率限制到人类视觉系统的感知限制。

### 复杂性的本质

我的整个职业生涯都在与复杂性做斗争。为什么系统和应用变得复杂呢？为什么在一个应用领域内进行开发并没有随着时间变得简单，而基础设施却没有变得更复杂，反而变得更强大了？事实上，管理复杂性的一个关键方法就是“走开”然后重新开始。通常新的工具或语言迫使我们从头开始，这意味着开发人员将工具的优点与从新开始的优点结合起来。从新开始是重要的。这并不是说新工具，新平台，或新语言可能不好，但我保证它们不能解决复杂性增长的问题。控制复杂性的最简单的方法就是用更少的程序员，建立一个更小的系统。

当然，很多情况下“走开”并不是一个选择——Office 软件建立在有巨大的价值的复杂的资源上。通过 OneNote， Office 从 Word 的复杂性上“走开”，从而在另一个维度上进行创新。Sway 是另一个例子， Office 决定从限制中跳出来，利用关键的环境变化，抓住机会从底层上采取全新的设计方案。我们有 Word、Excel、PowerPoint 这些应用，它们的数据结构非常有价值，我们并不能完全放弃这些数据结构，它们成为了开发中持续的显著的限制条件。

我受到 Fred Brook 讨论软件开发中的意外和本质的文章[《没有银弹》](http://worrydream.com/refs/Brooks-NoSilverBullet.pdf)的影响，他希望用两个趋势来尽可能地推动程序员的生产力：一是在选择自己开发还是购买时，更多地关注购买——这预示了开源社区和云架构的改变；二是从单纯的构建方法转型到更“有机”或者“生态”的增量开发方法。现代的读者可以认为是向敏捷开发和持续开发的转型。但那篇文章可是写于 1986 年！

我很欣赏 Stuart Kauffman 的在复杂性的基本性上的研究工作。Kauffman 从一个简单的布尔网络模型（“[NK 模型](https://en.wikipedia.org/wiki/NK_model)”）开始建立起来，然后探索这个基本的数学结构在相互作用的分子，基因网络，生态系统，经济系统，计算机系统（以有限的方式）等系统中的应用，来理解紧急有序行为的数学基础及其与混沌行为的关系。在一个高度连接的系统中，你固有地有一个相互冲突的约束系统，使得它（在数学上）很难向前发展（这被看作是在崎岖景观上的优化问题）。控制这种复杂性的基本方法是将系统分成独立元素并限制元素之间的相互连接（实质上减少 NK 模型中的“N”和“K”）。当然对那些使用复杂隐藏，信息隐藏和数据抽象，并且使用松散异步耦合来限制组件之间的交互的技术的系统设计者来说，这是很自然的。

我们一直面临的一个挑战是，我们想到的许多拓展系统的方法，都跨越了所有的方面。实时共同编辑是 Office 应用程序最近的一个非常具体的（也是最复杂的）例子。

我们的数据模型的复杂性往往等同于“能力”。设计用户体验的固有挑战是我们需要将有限的一组手势，映射到底层数据模型状态空间的转换。增加状态空间的维度不可避免地在用户手势中产生模糊性。这是“[纯数学](https://medium.com/@terrycrowley/the-math-of-easy-to-use-14645f819201#.untmk9eq7)”，这意味着确保系统保持“易于使用”的最基本的方式常常是约束底层的数据模型。

### 管理

我从高中开始担任一些领导角色（学生会主席！），对承担更多的责任感到理所当然。同时，我一直为自己在每个管理阶段都坚持担任全职程序员而感到自豪。但 Office 软件的开发副总裁最终还是让我从事管理，离开了日常的编程工作。当我在去年离开那份工作时，我很享受重返编程——这是一个出奇地充满创造力的充实的活动（当修完“最后”的 bug 时，也许也会有一点令人沮丧）。

尽管在我加入微软前已经做了十多年的“主管”，但是到了 1996 年我加入微软才真正了解到管理。微软强调“工程领导是技术领导”。这与我的观点一致，帮助我接受并承担更大的管理责任。

主管的工作是设计项目并透明地推进项目。透明并不简单，它不是自动的，也不仅仅是有好的意愿就行。透明需要被设计进系统中去。透明工作的最好方式是能够记录每个工程师每天活动的产出，以此来追踪项目进度（完成任务，发现 bug 并修复，完成一个情景）。留意主观上的红/绿/黄，点赞或踩的仪表板。

我过去说我的工作是设计反馈回路。独立工程师，经理，行政人员，每一个项目的参与者都能通过分析记录的项目数据，推进项目，产出结果，了解自己在整个项目中扮演的角色。最终，透明化最终成为增强能力的一个很好的工具——管理者可以将更多的局部控制权给予那些最接近问题的人，因为他们对所取得的进展有信心。这样的话，合作自然就会出现。

关键需要确定目标框架（包括关键资源的约束，如发布的时间表）。如果决策需要在管理链上下不断流动，那说明管理层对目标和约束的框架不好。

当我在 Beyond Software 工作时，我真正理解了一个项目拥有一个唯一领导的重要性。原来的项目经理离职了（后来从 FrontPage 团队雇佣了我）。我们四个主管在是否接任这个岗位上都有所犹豫，这不仅仅由于我们都不知道要在这家公司坚持多久。我们都技术高超，并且相处融洽，所以我们决定以同级的身份一起来领导这个项目。然而这槽糕透了。有一个显而易见的问题，我们没有相应的战略用来在原有的组织之间分配资源——这应当是管理者的首要职责之一！当你知道你是唯一的负责人时，你会有很深的责任感，但在这个例子中，这种责任感缺失了。我们没有真正的领导来负责统一目标和界定约束。

我有清晰地记得，我第一次充分认识到*倾听*对一个领导者的重要性。那时我刚刚担任了 Word、OneNote、Publisher 和 Text Services 团队的开发经理。关于我们如何组织文本服务团队，我们有一个很大的争议，我走到了每个关键参与者身边，听他们想说的话，然后整合起来，写下了我所听到的一切。当我向其中一位主要参与者展示我写下的东西时，他的反应是“哇，你真的听了我想说的话”！作为一名管理人员，我所经历的所有最大的问题（例如，跨平台和转型持续工程）涉及到仔细倾听所有的参与者。倾听是一个积极的过程，它包括：尝试以别人的角度去理解，然后写出我学到的东西，并对其进行测试，以验证我的理解。当一个关键的艰难决定需要发生的时候，在最终决定前，每个人都知道他们的想法都已经被听到并理解（不论他们是否同意最后的决定）。

在 FrontPage 团队担任开发经理的工作，让我理解了在只有部分信息的情况下做决定的“操作困境”。你等待的时间越长，你就会有更多的信息做出决定。但是等待的时间越长，实际执行的灵活性就越低。在某个时候，你仅需要做出决定。

设计一个组织涉及类似的两难情形。您希望增加资源领域，以便可以在更大的一组资源上应用一致的优先级划分框架。但资源领域越大，越难获得作出决定所需要的所有信息。组织设计就是要平衡这两个因素。软件复杂化，因为软件的特点可以在任意维度切入设计。Office 软件部门已经使用[共享团队](https://medium.com/@terrycrowley/breaking-conways-law-a0fdf8500413#.gqaqf1c5k)来解决这两个问题（优先次序和资源），让跨领域的团队能与需要产品的团队分享工作（增加资源）。

随着管理阶梯的提升，你会懂一个小秘密：你和你的新同事不会因为你现在承担更多的责任，就突然变得更聪明。这强调了整个组织比顶层领导者更聪明。赋予每个级别在一致框架下拥有自己的决定是实现这一目标的关键方法。听取并使自己对组织负责，阐明和解释决策背后的原因是另一个关键策略。令人惊讶的是，害怕做出一个愚蠢的决定可能是一个有用的激励因素，以确保你清楚地阐明你的推理，并确保你听取所有的信息。

### 结语

我离开大学寻找第一份工作时，面试官在最后一轮面试时问我对做“系统”和做“应用”哪一个更感兴趣。我当时并没有真正理解这个问题。在软件技术栈的每一个层面都会有趣的难题，我很高兴深入研究这些问题。保持学习。

---

via: <https://hackernoon.com/education-of-a-programmer-aaecf2d35312>

作者：[Terry Crowley](https://hackernoon.com/@terrycrowley) 译者：[explosic4](https://github.com/explosic4) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


开始 Vagrant 之旅

Summary: 用管理虚拟机和容器的工具 Vagrant 清理你的开发环境和依赖。

用管理虚拟机和容器的工具 Vagrant 清理你的开发环境和依赖。

# 开始 Vagrant 之旅

>
> 用管理虚拟机和容器的工具 Vagrant 清理你的开发环境和依赖。
>
>
>

<img src="content_image/album_201805_23_114315cw6iy7ak7yikb9w9.jpg" alt="" title="">

如果你和我一样，你可能在某一个地方有一个“沙盒”，你可以在那里进行你正在做的任何项目。随着时间的推移，沙盒会变得杂乱无章，充斥着各种想法、工具链元素、你不使用的代码模块，以及其他你不需要的东西。当你完成某件事情时，这会使你的部署变得复杂，因为你可能不确定项目的实际依赖关系 —— 随着时间推移你在沙盒中已经有了一些工具，但是你忘了必须安装它。你需要一个干净的环境，将所有的依赖关系放在一个地方，以便以后更方便。

或者你可能工作在 DevOps 中，你所服务的开发人员用模糊的依赖关系来编写代码，这使得测试变得更加困难。你需要一种方法来获得一个干净的盒子，将代码放入其中，并通过它运行代码，而且你希望这些环境是一次性的和可重复的。

那么选择 [Vagrant](https://vagrantup.com) 吧。由 HashiCorp 在 [MIT 许可证](https://opensource.org/licenses/MIT)下创建，Vagrant 可充当 VirtualBox、Microsoft Hyper-V 或 Docker 容器的包装器和前端，并且可以通过[许多其他供应商](https://github.com/hashicorp/vagrant/wiki/Available-Vagrant-Plugins#providers)的插件进行扩展。你可以配置 Vagrant 以提供可重复的、干净的环境，并且已安装需要的基础架构。配置脚本是可移植的，因此，如果你的仓库和 Vagrant 配置脚本位于基于云存储上，那么你只需要很少的限制就可以启动并在多台机器机器上工作。让我们来看一看。

### 安装

对于本次安装，我的环境是 Linux Mint 桌面，版本是 18.3 Cinnamon 64 位，在其他大多数 Debian 派生系统上安装非常类似。在大多数发行版中，对于基于 RPM 的系统也有类似的安装程序。Vagrant 的[安装页面](https://www.vagrantup.com/downloads.html)为 Debian、 Windows、 CentOS、 MacOS 和 Arch Linux 都提供下载，但是我在我的软件包管理器中找到了它，所以我在那进行了安装。

最简单的安装使用了 VirtualBox 作为虚拟化提供者，所以我需要安装它：

```
sudo apt-get install virtualbox vagrant

```

安装程序将会获取依赖项 —— 主要是 Ruby 的一些东西，安装它们。

### 建立一个项目

在设置你的项目之前，你需要了解一些你想要运行它的环境。你可以在 [Vagrant Boxes 仓库](https://app.vagrantup.com/boxes/search)中找到为许多虚拟化供应商提供的大量预配置的<ruby> 系统 <rt>  box </rt></ruby>。许多会预先配置一些你可能需要的核心基础设置，比如 PHP、 MySQL 和 Apache，但是对于本次测试，我将安装一个 Debian 8 64 位 “Jessie” 裸机沙盒并手动安装一些东西，这样你就可以看到具体过程了。

```
mkdir ~/myproject
cd ~/myproject
vagrant init debian/contrib-jessie64
vagrant up

```

最后一条命令将根据需要从仓库中获取或更新 VirtualBox 镜像，然后运行启动器，你的系统上会出现一个运行的系统！下次启动这个项目时，除非镜像已经在仓库中更新，否则不会花费太长时间。

要访问该沙盒，只需要输入 `vagrant ssh`，你将进入虚拟机的全功能 SSH 会话中，你将会是 `vagrant` 用户，但也是 `sudo` 组的成员，所以你可以切换到 root，并在这里做你想做的任何事情。

你会在沙盒中看到一个名为 `/vagrant` 目录，对这个目录小心点，因为它与你主机上的 `~/myproject` 文件夹保持同步。在虚拟机 `/vagrant` 下建立一个文件它会立即复制到主机上，反之亦然。注意，有些沙盒并没有安装 VirtualBox 的附加功能，所以拷贝只能在启动时才起作用。有一些用于手动同步的命令行工具，这可能是测试环境中非常有用的特性。我倾向于坚持使用那些有附加功能的沙盒，所以这个目录可以正常工作，不必考虑它。

这个方案的好处很快显现出来了： 如果你在主机上有一个代码编辑工具链，并处于某种原因不希望它出现在虚拟机上，那么这不是问题 —— 在主机上进行编辑，虚拟机会立刻更改。快速更改虚拟机，它也将其同步到主机上的“官方”副本 。

让我们关闭这个系统，这样我们就可以在这个系统里提供一些我们需要的东西：

```
vagrant halt

```

### 在虚拟机上安装额外的软件

对于这个例子，我将使用 [Apache](https://httpd.apache.org/)、 [PostgreSQL](https://postgresql.org) 和 Perl 的 [Dancer](https://perldancer.org) web 框架进行项目开发。我将修改Vagrant 配置脚本，以便我需要的东西已经安装。 为了使之稍后更容易保持更新，我将在项目根目录下创建一个脚本`~/myproject/Vagrantfile`：

```
$provision_script = <<SCRIPT
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get -y install \
  apache2 \
  postgresql-client-9.4 \
  postgresql-9.4 \
  libdbd-pg-perl \
  libapache2-mod-fastcgi \
  libdata-validate-email-perl  \
  libexception-class-perl \
  libexception-class-trycatch-perl \
  libtemplate-perl \
  libtemplate-plugin-json-escape-perl \
  libdbix-class-perl \
  libyaml-tiny-perl \
  libcrypt-saltedhash-perl \
  libdancer2-perl \
  libtemplate-plugin-gravatar-perl  \
  libtext-csv-perl \
  libstring-tokenizer-perl \
  cpanminus
cpanm -f -n \
  Dancer2::Session::Cookie \
  Dancer2::Plugin::DBIC \
  Dancer2::Plugin::Auth::Extensible::Provider::DBIC \
  Dancer2::Plugin::Locale \
  Dancer2::Plugin::Growler
sudo a2enmod rewrite fastcgi
sudo apache2ctl restart
SCRIPT

```

在 Vagrantfile 结尾附近，你会发现一行 `config.vm.provision` 变量，正如你在示例中看到的那样，你可以在此处以内联方式进行操作，只需通过取消注释以下行：

```
  # config.vm.provision "shell", inline: <<-SHELL
  #   sudo apt-get update
  #   sudo apt-get install -y apache2
  # SHELL

```

相反，将那四行替换为使用你在文件顶部定义为变量的配置脚本：

```
config.vm.provision "shell", inline: $provision_script

```

你可能还希望将转发的端口设置为从主机访问虚拟机上的 Apache。寻找包含 `forwarded_port` 的行并取消注释它。如果你愿意，也可以将端口从 8080 更改为其他内容。我通常使用端口 5000，并在我的浏览器浏览 http://localhost:5000 就可以访问我虚拟机上的 Apache 服务器。

这里有一个设置提示：如果你的仓库位于云存储上，为了在多台机器上使用 Vagrant，你可能希望将不同机器上的 `VAGRANT_HOME` 环境变量设置为不同的东西。以 VirtualBox 的工作方式，你需要分别为这些系统存储状态信息，确保你的版本控制系统忽略了用于此的目录 —— 我将 `.vagrant.d*` 添加到仓库的 `.gitignore` 文件中。不过，我确实让 Vagrantfile 成为仓库的一部分！

### 好了！

我输入 `vagrant up`，我准备开始写代码了。一旦你做了一两次，你可能会想到你可以循环利用很多的 Vagrantfile 模板文件（就像我刚刚那样），这就是 Vagrant 的优势之一。你可以更快地完成实际的编码工作，并将很少的时间花在基础设施上！

你可以使用 Vagrant 做更多事情。配置工具存在于许多工具链中，因此，无论你需要复制什么环境，它都是快速而简单的。

---

via: <https://opensource.com/article/18/4/getting-started-vagrant>

作者：[Ruth Holloway](https://opensource.com/users/druthb) 选题：[lujun9972](https://github.com/lujun9972) 译者：[MjSeven](https://github.com/MjSeven) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


LikeCoin，一种给开放式许可的内容创作者的加密货币

Summary: 在共创协议下授权作品和挣钱这二者不再是一种争议。

在共创协议下授权作品和挣钱这二者不再是一种争议。

# LikeCoin，一种给开放式许可的内容创作者的加密货币

>
> 在共创协议下授权作品和挣钱这二者不再是一种争议。
>
>
>

<img src="content_image/album_201805_24_084556u7k0oguuuiu07ovh.jpg" alt="" title="">

传统观点认为，作家、摄影师、艺术家和其他创作者在<ruby> 共创协议 <rt>  Creative Commons </rt></ruby>和其他开放许可下免费共享内容的不会得到报酬。这意味着大多数独立创作者无法通过在互联网上发布他们的作品来赚钱。而现在有了 [LikeCoin](https://like.co/)：一个新的开源项目，旨在使这个让艺术家们经常为了贡献而不得不妥协或牺牲的常识成为过去。

LikeCoin 协议旨在通过创意内容获利，以便创作者可以专注于创造出色的内容而不是出售它。

该协议同样基于去中心化技术，它可以跟踪何时使用内容，并使用 LikeCoin 这种 [以太坊 ERC-20](https://en.wikipedia.org/wiki/ERC20) 加密货币通证来奖励其创作者。它通过“<ruby> 创造性共识 <rt>  Proof of Creativity </rt></ruby>”算法进行操作，该算法一部分根据作品收到多少个“喜欢”，一部分根据有多少作品衍生自它而分配 LikeCoin。由于开放式授权的内容有更多机会被重复使用并获得 LikeCoin 令牌，因此系统鼓励内容创作者在<ruby> 共创协议 <rt>  Creative Commons </rt></ruby>许可下发布。

### 如何运作的

当通过 LikeCoin 协议上传创意片段时，内容创作者也将包括作品的元数据，包括作者信息及其 InterPlanetary 关联数据（[IPLD](https://ipld.io/)）。这些数据构成了衍生作品的家族图谱；我们称作品与其衍生品之间的关系为“内容足迹”。这种结构使得内容的继承树可以很容易地追溯到原始作品。

LikeCoin 通证会使用作品的衍生历史记录的信息来将其分发给创作者。由于所有创意作品都包含作者钱包的元数据，因此相应的 LikeCoin 份额可以通过算法计算并分发。

LikeCoin 可以通过两种方式获得奖励：要么由想要通过支付给内容创建者来表示赞赏的个人直接给予，或通过 Creators Pool 收集观众的“赞”的并根据内容的 LikeRank 分配 LikeCoin。基于在 LikeCoin 协议中的内容追踪，LikeRank 衡量作品重要性（或者我们在这个场景下定义的创造性）。一般来说，一副作品有越多的衍生作品，创意内容的创新就越多，内容就会有更高的 LikeRank。 LikeRank 是内容创新性的量化者。

### 如何参与？

LikeCoin 仍然非常新，我们期望在 2018 年晚些时候推出我们的第一个去中心化程序来奖励<ruby> 共创协议 <rt>  Creative Commons </rt></ruby>的内容，并与更大的社区无缝连接。

LikeCoin 的大部分代码都可以在 [LikeCoin GitHub](https://github.com/likecoin) 仓库中通过 [GPL 3.0 许可证](https://www.gnu.org/licenses/gpl-3.0.en.html)访问。由于它仍处于积极开发阶段，一些实验代码尚未公开，但我们会尽快完成。

我们欢迎功能请求、拉取请求、复刻和星标。请参与我们在 Github 上的开发，并加入我们在 [Telegram](https://t.me/likecoin) 的讨论组。我们同样在 [Medium](http://medium.com/likecoin)、[Facebook](http://fb.com/likecoin.foundation)、[Twitter](https://twitter.com/likecoin_fdn) 和我们的网站 [like.co](https://like.co/) 发布关于我们进展的最新消息。

---

via: <https://opensource.com/article/18/5/likecoin>

作者：[Kin Ko](https://opensource.com/users/ckxpress) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Linux 局域网路由新手指南：第 2 部分

Summary: 今天我们继续学习更精彩的内容：局域网路由器。

今天我们继续学习更精彩的内容：局域网路由器。

# Linux 局域网路由新手指南：第 2 部分

<img src="content_image/album_201805_24_090919jy9w2h6eyysowa3s.jpg" alt="" title="">

上周 [我们学习了 IPv4 地址](/article-9657-1.html) 和如何使用管理员不可或缺的工具 —— `ipcalc`，今天我们继续学习更精彩的内容：局域网路由器。

VirtualBox 和 KVM 是测试路由的好工具，在本文中的所有示例都是在 KVM 中执行的。如果你喜欢使用物理硬件去做测试，那么你需要三台计算机：一台用作路由器，另外两台用于表示两个不同的网络。你也需要两台以太网交换机和相应的线缆。

我们假设这个示例是一个有线以太局域网，为了更符合真实使用场景，我们将假设有一些桥接的无线接入点，当然我并不会使用这些无线接入点做任何事情。（我也不会去尝试所有的无线路由器，以及使用一个移动宽带设备连接到以太网的局域网口进行混合组网，因为它们需要进一步的安装和设置）

### 网段

最简单的网段是两台计算机连接在同一个交换机上的相同地址空间中。这样两台计算机不需要路由器就可以相互通讯。这就是我们常说的术语 —— “广播域”，它表示所有在相同的网络中的一组主机。它们可能连接到一台单个的以太网交换机上，也可能是连接到多台交换机上。一个广播域可以包括通过以太网桥连接的两个不同的网络，通过网桥可以让两个网络像一个单个网络一样运转。无线访问点一般是桥接到有线以太网上。

一个广播域仅当在它们通过一台网络路由器连接的情况下，才可以与不同的广播域进行通讯。

### 简单的网络

以下示例的命令并不是永久生效的，重启之后你所做的改变将会消失。

一个广播域需要一台路由器才可以与其它广播域通讯。我们使用两台计算机和 `ip` 命令来解释这些。我们的两台计算机是 192.168.110.125 和 192.168.110.126，它们都插入到同一台以太网交换机上。在 VirtualBox 或 KVM 中，当你配置一个新网络的时候会自动创建一个虚拟交换机，因此，当你分配一个网络到虚拟虚拟机上时，就像是插入一个交换机一样。使用 `ip addr show` 去查看你的地址和网络接口名字。现在，这两台主机可以互 ping 成功。

现在，给其中一台主机添加一个不同网络的地址：

```
# ip addr add 192.168.120.125/24 dev ens3

```

你可以指定一个网络接口名字，在示例中它的名字是 `ens3`。这不需要去添加一个网络前缀，在本案例中，它是 `/24`，但是显式地添加它并没有什么坏处。你可以使用 `ip` 命令去检查你的配置。下面的示例输出为了清晰其见进行了删减：

```
$ ip addr show
ens3:
 inet 192.168.110.125/24 brd 192.168.110.255 scope global dynamic ens3
 valid_lft 875sec preferred_lft 875sec
 inet 192.168.120.125/24 scope global ens3
 valid_lft forever preferred_lft forever

```

主机在 192.168.120.125 上可以 ping 它自己（`ping 192.168.120.125`），这是对你的配置是否正确的一个基本校验，这个时候第二台计算机就已经不能 ping 通那个地址了。

现在我们需要做一些网络变更。添加第三台主机作为路由器。它需要两个虚拟网络接口并添加第二个虚拟网络。在现实中，你的路由器必须使用一个静态 IP 地址，但是现在，我们可以让 KVM 的 DHCP 服务器去为它分配地址，所以，你仅需要两个虚拟网络：

* 第一个网络：192.168.110.0/24
* 第二个网络：192.168.120.0/24

接下来你的路由器必须配置去转发数据包。数据包转发默认是禁用的，你可以使用 `sysctl` 命令去检查它的配置：

```
$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0

```

`0` 意味着禁用，使用如下的命令去启用它：

```
# echo 1 > /proc/sys/net/ipv4/ip_forward

```

接下来配置你的另一台主机做为第二个网络的一部分，你可以通过将原来在 192.168.110.0/24 的网络中的一台主机分配到 192.168.120.0/24 虚拟网络中，然后重新启动两个 “连网的” 主机，注意不是路由器。（或者重启动主机上的网络服务；我年龄大了还有点懒，我记不住那些重启服务的奇怪命令，还不如重启主机来得干脆。）重启后各台机器的地址应该如下所示：

* 主机 1： 192.168.110.125
* 主机 2： 192.168.120.135
* 路由器： 192.168.110.126 和 192.168.120.136

现在可以去随意 ping 它们，可以从任何一台计算机上 ping 到任何一台其它计算机上。使用虚拟机和各种 Linux 发行版做这些事时，可能会产生一些意想不到的问题，因此，有时候 ping 的通，有时候 ping 不通。不成功也是一件好事，这意味着你需要动手去创建一条静态路由。首先，查看已经存在的路由表。主机 1 和主机 2 的路由表如下所示：

```
$ ip route show
default via 192.168.110.1 dev ens3 proto static metric 100
192.168.110.0/24 dev ens3 proto kernel scope link src 192.168.110.164 metric 100

$ ip route show
default via 192.168.110.1 dev ens3 proto static metric 100
default via 192.168.120.1 dev ens3 proto static metric 101
169.254.0.0/16 dev ens3 scope link metric 1000
192.168.110.0/24 dev ens3 proto kernel scope link
 src 192.168.110.126 metric 100
192.168.120.0/24 dev ens9 proto kernel scope link
 src 192.168.120.136 metric 100

```

这显示了我们使用的由 KVM 分配的缺省路由。169.\* 地址是自动链接的本地地址，我们不去管它。接下来我们看两条路由，这两条路由指向到我们的路由器。你可以有多条路由，在这个示例中我们将展示如何在主机 1 上添加一个非默认路由：

```
# ip route add 192.168.120.0/24 via 192.168.110.126 dev ens3

```

这意味着主机 1 可以通过路由器接口 192.168.110.126 去访问 192.168.110.0/24 网络。看一下它们是如何工作的？主机 1 和路由器需要连接到相同的地址空间，然后路由器转发到其它的网络。

以下的命令去删除一条路由：

```
# ip route del 192.168.120.0/24

```

在真实的案例中，你不需要像这样手动配置一台路由器，而是使用一个路由器守护程序，并通过 DHCP 做路由器通告，但是理解基本原理很重要。接下来我们将学习如何去配置一个易于使用的路由器守护程序来为你做这些事情。

通过来自 Linux 基金会和 edX 的免费课程 [“Linux 入门”](https://training.linuxfoundation.org/linux-courses/system-administration-training/introduction-to-linux) 来学习更多 Linux 的知识。

---

via: <https://www.linux.com/learn/intro-to-linux/2018/3/linux-lan-routing-beginners-part-2>

作者：[CARLA SCHRODER](https://www.linux.com/users/cschroder) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


在 Linux 中轻松搜索和安装 Google Web 字体

Summary: Font Finder 是旧的 Typecatcher 的 Rust 实现，用于从 Google 的字体存档中轻松搜索和安装 Google Web 字体。

# 在 Linux 中轻松搜索和安装 Google Web 字体

Font Finder 是旧的 [Typecatcher](https://www.ostechnix.com/install-google-web-fonts-ubuntu/) 的 Rust 实现，用于从 [Google 的字体存档](https://fonts.google.com/)中轻松搜索和安装 Google Web 字体。它可以帮助你在 Linux 桌面上安装数百种免费和开源字体。如果你正在为你的 Web 项目和应用以及其他任何地方寻找好看的字体，Font Finder 可以轻松地为你提供。它是用 Rust 编程语言编写的自由、开源的 GTK3 应用程序。与使用 Python 编写的 Typecatcher 不同，Font Finder 可以按类别过滤字体，没有 Python 运行时依赖关系，并且有更好的性能和更低的资源消耗。

在这个简短的教程中，我们将看到如何在 Linux 中安装和使用 Font Finder。

### 安装 Font Finder

由于 Fond Finder 是使用 Rust 语言编写的，因此你需要向下面描述的那样在系统中安装 Rust。

安装 Rust 后，运行以下命令安装 Font Finder：

```
$ cargo install fontfinder

```

Font Finder 也可以从 [flatpak app](https://flathub.org/apps/details/io.github.mmstick.FontFinder) 安装。首先在你的系统中安装 Flatpak，如下面的链接所述。

然后，使用命令安装 Font Finder：

```
$ flatpak install flathub io.github.mmstick.FontFinder

```

### 在 Linux 中使用 Font Finder 搜索和安装 Google Web 字体

你可以从程序启动器启动 Font Finder，也可以运行以下命令启动它。

```
$ flatpak run io.github.mmstick.FontFinder

```

这是 Font Finder 默认界面的样子。

正如你所看到的，Font Finder 的用户界面非常简单。所有 Google Web 字体都列在左侧窗格中，相应字体的预览显示在右侧窗格中。你可以在预览框中输入任何单词以查看单词在所选字体中的外观。在左上方还有一个搜索框，可以让你快速搜索你选择的字体。

默认情况下，Font Finder 显示所有类型的字体。但是，你可以通过搜索框上方的类别下拉框中的分类显示字体。

要安装字体，只需选择它并点击顶部的 “Install” 按钮即可。

你可以在任何文本处理程序中测试新安装的字体。

同样，要删除字体，只需从 Font Finder 面板中选择它并单击 “Uninstall” 按钮。就这么简单！

左上角的设置按钮（齿轮按钮）提供了切换到暗色预览的选项。

如你所见，Font Finder 非常简单，完全可以像在主页上宣传的那样完成工作。如果你正在寻找安装 Google Web 字体的程序，Font Finder 就是这样的一个程序。

今天就是这些。希望这有帮助。还有更好的东西。敬请关注！

干杯!

---

via: <https://www.ostechnix.com/font-finder-easily-search-and-install-google-web-fonts-in-linux/>

作者：[SK](https://www.ostechnix.com/author/sk/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


使用树莓派构建一个婴儿监视器

Summary: 比一般的视频监控还要好，这种 DIY 型号还有婴儿房间的自动室温控制功能。

比一般的视频监控还要好，这种 DIY 型号还有婴儿房间的自动室温控制功能。

# 使用树莓派构建一个婴儿监视器

>
> 比一般的视频监控还要好，这种 DIY 型号还有婴儿房间的自动室温控制功能。
>
>
>

香港很湿热，即便是晚上，许多人为了更舒适，在家里也使用空调。当我的大儿子还是一个小婴儿的时候，他卧室的空调还是需要手动控制的，没有温度自动调节的功能。它的控制器只有开或者关，让空调整个晚上持续运行会导致房间过冷，并且也浪费能源和钱。

我决定使用一个基于 [树莓派](https://opensource.com/tags/raspberry-pi) 的 [物联网](https://opensource.com/tags/internet-things) 解决方案去修复这个问题。后来我进一步为它添加了一个[婴儿监视器](https://opensource.com/article/17/9/gonimo)插件。在这篇文章中，我将解释我是如何做的，它的代码在 [我的 GitHub](https://github.com/jervine/rpi-temp-humid-monitor) 页面上。

### 设计空调控制器

解决我的问题的第一个部分是使用了一个 Orvibo S20 [可通过 WiFi 连接的智能插头](https://www.amazon.co.uk/marsboy-S20-Automation-Control-Smartphone/dp/B01LXKPUDK/ref=sr_1_1/258-6082934-2585109?ie=UTF8&qid=1520578769&sr=8-1&keywords=orvibo+s20)和智能手机应用程序。虽然这样可以让我通过远程来控制空调，但是它还是手动处理的，而我希望尝试让它自动化。我在 [Instructables](http://www.instructables.com/id/Raspberry-Pi-Temperature-Humidity-Network-Monitor/) 上找到了一个满足我的需求的项目：他使用树莓派从一个 [AM2302 传感器](https://www.adafruit.com/product/393) 上测量附近的温度和湿度，并将它们记录到一个 MySQL 数据库中。

使用压接头将温度/湿度传感器连接到树莓派的相应 GPIO 针脚上。幸运的是，AM2302 传感器有一个用于读取的 [开源软件](https://github.com/adafruit/Adafruit_Python_DHT)，并且同时提供了 [Python](https://opensource.com/tags/python) 示例。

与我的项目放在一起的用于 [AM2302 传感器](https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/tree/legacy/Adafruit_DHT_Driver_Python) 接口的软件已经更新了，并且我使用的原始代码现在应该已经过时了，停止维护了。这个代码是由一个小的二进制组成，用于连接到传感器以及解释读取并返回正确值的 Python 脚本。

*树莓派、传感器、以及用于构建温度/湿度监视器的 Python 代码。*

将传感器连接到树莓派，这些 Python 代码能够正确地返回温度和湿度读数。将 Python 连接到 MySQL 数据库很简单，并且也有大量的使用 python-mysql 绑定的代码示例。因为我需要持续地监视温度和湿度，所以我写软件来实现这些。

事实上，最终我用了两个解决方案，一是作为一个持续运行的进程，周期性（一般是间隔一分钟）地获取传感器数据，另一种是让 Python 脚本运行一次然后退出。我决定使用第二种方法，并使用 cron 去每分钟调用一次这个脚本。之所以选择这种方法的主要原因是，（通过循环实现的）持续的脚本偶尔会不返回读数，这将导致尝试读取传感器的进程出现堆积，最终可能会导致系统挂起而缺乏可用资源。

我也找到了可以用程序来控制我的智能插头的一个 [Perl 脚本](https://github.com/franc-carter/bauhn-wifi)。它是解决这种问题所需的一部分，因此当某些温度/湿度达到触发条件，将触发这个 Perl 脚本。在做了一些测试之后，我决定去设计一个独立的 `checking` 脚本，从 MySQL 去拉取最新的数据，然后根据返回的值去设置智能开关为开或关。将插头控制逻辑与传感器读取脚本分开，意味着它们是各自独立运行的，就算是传感器读取脚本写的有问题也没事。

配置一个打开/关闭空调的温度值是很有意义的，因此，我将这些值转移到控制脚本读取的配置文件中。我也发现，虽然传感器的值一般都很准确，但是，偶尔也会出现返回不正确读数的情况。这个传感器脚本被修改为不向 MySQL 数据库中写入与前值差异非常大的值。同样也在配置文件中写入了连续读取的温度/湿度之间允许的最大差异值，如果读取的值处于这些限制值以外，这些值写不会提交到数据库中。

虽然，做这个自动调节器似乎花费了很多努力，但是，这意味着，记录到 MySQL 数据库的数据是有效的、可用于进一步去分析识别用户使用模式的有用数据。可以用多种图形方式去展示来自 MySQL 数据库中的数据，而我决定使用 [Google Chart](https://developers.google.com/chart/) 在一个 Web 页面上显示数据。

*过去六小时内测量到的温度和湿度*

### 添加一个婴儿监视摄像头

树莓派开放的性能意味着我可以不断地为它增加功能 —— 并且我有大量的未使用的可用 GPIO 针脚。我的下一个创意是去添加一个摄像头模块，将它放在孩子的卧室中，配置它去监视婴儿。

我需要一个能够在黑暗环境中工作的摄像头，而 [Pi Noir](https://www.raspberrypi.org/products/pi-noir-camera-v2/) 摄像头模块是非常适合这种条件的。Pi Noir 除了没有红外过滤之外，同样也是树莓派常用的摄像头模块。这意味着它在白天时的图像可能有点偏紫色，但是它可以在黑暗中借助红外灯来显示图像。

现在我需要一个红外光源。由于树莓派非常流行，并且进入门槛很低，因此它有大量的外围配件和插件。也有适合它的各种红外光源，我注意到的其中一个是 [Bright Pi](https://www.pi-supply.com/product/bright-pi-bright-white-ir-camera-light-raspberry-pi/)。它可以从树莓派上供电，并且它很适合为树莓派的摄像头模块提供红外光和普通光。它唯一的缺点是太考验我的焊接技能了。

我的焊接技能还是不错的，但是可能花费的时间比其他人更长。我成功地连接了外壳上所有的红外 LEDs，并将它们连接到树莓派的 GPIO 针脚上。这意味着树莓派能够编程控制红外 LED 是否点亮，以及它的亮度。

通过一个 Web 流去公开捕获的视频也很有意义，因为这样我就可以从 Web 页面上查看温度和湿度的读数图表。进一步研究之后，我选择了一个使用 M-JPEG 捕获器的 [流软件](https://elinux.org/RPi-Cam-Web-Interface)。通过 Web 页面公开 JPG 源，我可以在我的智能手机上去连接摄像头查看程序，去查看摄像头的输出。

### 做最后的修饰

没有哪个树莓派项目都已经完成了还没有为它选择一个合适的外壳，并且它有各种零件。在大量搜索和比较之后，有了一个显然的 [赢家](https://smarticase.com/collections/all/products/smartipi-kit-3)：SmartPi 的乐高积木式外壳。乐高的兼容性可以让我去安装温度/湿度传感器和摄像头。下面是最终的成果图：

在这以后，我对我的这个作品作了一些改变和更新：

* 我将它从树莓派 2 Model B 升级到了 [树莓派 3](https://opensource.com/article/18/3/raspberry-pi-3b-model-news)，这意味着我可以使用 USB WiFi 模块。
* 我用一个 [TP-Link HS110](https://www.tp-link.com/uk/products/details/cat-5258_HS110.html) 智能插头替换了 Orvibo S20。
* 我也将树莓派插到了一个智能插头上，这样我就可以远程重启/重置它了。
* 我从树莓派上将 MySQL 数据库移走了，它现在运行在一个 NAS 设备上的容器中。
* 我增加了一个非常 [灵活的三角夹](https://www.amazon.com/Flexpod-Flexible-Tripod-Discontinued-Manufacturer/dp/B000JC8WYA)，这样我就可以调整到最佳角度。
* 我重新编译了 USB WiFi 模块，禁用了板载 LED 指示灯，这就是升级到树莓派 3 的其中一个好处。
* 我因此为我的第二个孩子设计了另外一个监视器。
* 因为没有时间去折腾，我为我的第三个孩子购买了夜用摄像头。

想学习更多的东西吗？所有的代码都在 [我的 GitHub](https://github.com/jervine/rpi-temp-humid-monitor) 页面上。

想分享你的树莓派项目吗？[将你的故事和创意发送给我们](http://opensource.com/story)。

---

via: <https://opensource.com/article/18/3/build-baby-monitor-raspberry-pi>

作者：[Jonathan Ervine](https://opensource.com/users/jervine) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


You-Get：支持 80 多个网站的命令行多媒体下载器

# You-Get：支持 80 多个网站的命令行多媒体下载器

你们大多数人可能用过或听说过 **Youtube-dl**，这个命令行程序可以从包括 Youtube 在内的 100+ 网站下载视频。我偶然发现了一个类似的工具，名字叫做 **You-Get**。这是一个 Python 编写的命令行下载器，可以让你从 Youtube、Facebook、Twitter 等很多热门网站下载图片，音频和视频（LCTT 译注：首先，它们得是存在的网站）。目前该下载器支持 80+ 站点，点击[这里](https://you-get.org/#supported-sites)查看所有支持的网站。

You-Get 不仅仅是一个下载器，它还可以将在线视频导流至你的视频播放器。更进一步，它还允许你在 Google 上搜索视频，只要给出搜索项，You-Get 使用 Google 搜索并下载相关度最高的视频。另外值得一提的特性是，它允许你暂停和恢复下载过程。它是一个完全自由、开源及跨平台的应用，适用于 Linux、MacOS 及 Windows。

### 安装 You-Get

确保你已经安装如下依赖项：

* Python 3
* FFmpeg (强烈推荐) 或 Libav
* (可选) RTMPDump

有多种方式安装 You-Get，其中官方推荐采用 pip 包管理器安装。如果你还没有安装 pip，可以参考如下链接：

* [如何使用 pip 管理 Python 软件包](https://www.ostechnix.com/manage-python-packages-using-pip/)

需要注意的是，你需要安装 Python 3 版本的 `pip`。

接下来，运行如下命令安装 You-Get：

```
$ pip3 install you-get

```

可以使用命令升级 You-Get 至最新版本：

```
$ pip3 install --upgrade you-get

```

### 开始使用 You-Get

使用方式与 Youtube-dl 工具基本一致。

#### 下载视频

下载视频，只需运行：

```
$ you-get https://www.youtube.com/watch?v=HXaglTFJLMc

```

输出示例：

```
site: YouTube
title: The Last of The Mohicans by Alexandro Querevalú
stream:
 - itag: 22
 container: mp4
 quality: hd720
 size: 56.9 MiB (59654303 bytes)
 # download-with: you-get --itag=22 [URL]

Downloading The Last of The Mohicans by Alexandro Querevalú.mp4 ...
 100% ( 56.9/ 56.9MB) ├███████████████████████████████████████████████████████┤[1/1] 752 kB/s

```

下载视频前，你可能希望查看视频的细节信息。You-Get 提供了 `–info` 或 `-i` 参数，使用该参数可以获得给定视频所有可用的分辨率和格式。

```
$ you-get -i https://www.youtube.com/watch?v=HXaglTFJLMc

```

或者

```
$ you-get --info https://www.youtube.com/watch?v=HXaglTFJLMc

```

输出示例如下：

```
site: YouTube
title: The Last of The Mohicans by Alexandro Querevalú
streams: # Available quality and codecs
 [ DASH ] ____________________________________
 - itag: 137
 container: mp4
 quality: 1920x1080
 size: 101.9 MiB (106816582 bytes)
 # download-with: you-get --itag=137 [URL]

- itag: 248
 container: webm
 quality: 1920x1080
 size: 90.3 MiB (94640185 bytes)
 # download-with: you-get --itag=248 [URL]

- itag: 136
 container: mp4
 quality: 1280x720
 size: 56.9 MiB (59672392 bytes)
 # download-with: you-get --itag=136 [URL]

- itag: 247
 container: webm
 quality: 1280x720
 size: 52.6 MiB (55170859 bytes)
 # download-with: you-get --itag=247 [URL]

- itag: 135
 container: mp4
 quality: 854x480
 size: 32.2 MiB (33757856 bytes)
 # download-with: you-get --itag=135 [URL]

- itag: 244
 container: webm
 quality: 854x480
 size: 28.0 MiB (29369484 bytes)
 # download-with: you-get --itag=244 [URL]

[ DEFAULT ] _________________________________
 - itag: 22
 container: mp4
 quality: hd720
 size: 56.9 MiB (59654303 bytes)
 # download-with: you-get --itag=22 [URL]

```

默认情况下，You-Get 会下载标记为 “DEFAULT” 的格式。如果你对格式或分辨率不满意，可以选择你喜欢的格式，使用格式对应的 itag 值即可。

```
$ you-get --itag=244 https://www.youtube.com/watch?v=HXaglTFJLMc

```

#### 下载音频

执行下面的命令，可以从 soundcloud 网站下载音频：

```
$ you-get 'https://soundcloud.com/uiceheidd/all-girls-are-same-999-prod-nick-mira'
Site: SoundCloud.com
Title: ALL GIRLS ARE THE SAME (PROD. NICK MIRA)
Type: MP3 (audio/mpeg)
Size: 2.58 MiB (2710046 Bytes)

Downloading ALL GIRLS ARE THE SAME (PROD. NICK MIRA).mp3 ...
 100% ( 2.6/ 2.6MB) ├███████████████████████████████████████████████████████┤[1/1] 983 kB/s

```

查看音频文件细节，使用 `-i` 参数：

```
$ you-get -i 'https://soundcloud.com/uiceheidd/all-girls-are-same-999-prod-nick-mira'

```

#### 下载图片

运行如下命令下载图片：

```
$ you-get https://pixabay.com/en/mountain-crumpled-cyanus-montanus-3393209/

```

You-Get 也可以下载网页中的全部图片：

```
$ you-get https://www.ostechnix.com/pacvim-a-cli-game-to-learn-vim-commands/

```

#### 搜索视频

你只需向 You-Get 传递一个任意的搜索项，而无需给出有效的 URL；You-Get 会使用 Google 搜索并下载与你给出搜索项最相关的视频。(LCTT 译注：Google 的机器人检测机制可能导致 503 报错导致该功能无法使用）。

```
$ you-get 'Micheal Jackson'
Google Videos search:
Best matched result:
site: YouTube
title: Michael Jackson - Beat It (Official Video)
stream:
 - itag: 43
 container: webm
 quality: medium
 size: 29.4 MiB (30792050 bytes)
 # download-with: you-get --itag=43 [URL]

Downloading Michael Jackson - Beat It (Official Video).webm ...
 100% ( 29.4/ 29.4MB) ├███████████████████████████████████████████████████████┤[1/1] 2 MB/s

```

#### 观看视频

You-Get 可以将在线视频导流至你的视频播放器或浏览器，跳过广告和评论部分。（LCTT 译注：使用 `-p` 参数需要对应的 vlc/chrominum 命令可以调用，一般适用于具有图形化界面的操作系统）。

以 VLC 视频播放器为例，使用如下命令在其中观看视频：

```
$ you-get -p vlc https://www.youtube.com/watch?v=HXaglTFJLMc

```

或者

```
$ you-get --player vlc https://www.youtube.com/watch?v=HXaglTFJLMc

```

类似地，将视频导流至以 chromium 为例的浏览器中，使用如下命令：

```
$ you-get -p chromium https://www.youtube.com/watch?v=HXaglTFJLMc

```

在上述屏幕截图中，可以看到并没有广告和评论部分，只是一个包含视频的简单页面。

#### 设置下载视频的路径及文件名

默认情况下，使用视频标题作为默认文件名，下载至当前工作目录。当然，你可以按照你的喜好进行更改，使用 `–output-dir` 或 `-o` 参数可以指定路径，使用 `–output-filename` 或 `-O` 参数可以指定下载文件的文件名。

```
$ you-get -o ~/Videos -O output.mp4 https://www.youtube.com/watch?v=HXaglTFJLMc

```

#### 暂停和恢复下载

按 `CTRL+C` 可以取消下载。一个以 `.download` 为扩展名的临时文件会保存至输出路径。下次你使用相同的参数下载时，下载过程将延续上一次的过程。

当文件下载完成后，以 `.download` 为扩展名的临时文件会自动消失。如果这时你使用同样参数下载，You-Get 会跳过下载；如果你想强制重新下载，可以使用 `–force` 或 `-f` 参数。

查看命令的帮助部分可以获取更多细节，命令如下：

```
$ you-get --help

```

这次的分享到此结束，后续还会介绍更多的优秀工具，敬请期待！

感谢各位阅读！

---

via: <https://www.ostechnix.com/you-get-a-cli-downloader-to-download-media-from-80-websites/>

作者：[SK](https://www.ostechnix.com/author/sk/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[pinewall](https://github.com/pinewall) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在 Ubuntu 上安装和优化 Apache

# 如何在 Ubuntu 上安装和优化 Apache

这是我们的 LAMP 系列教程的开始：如何在 Ubuntu 上安装 Apache web 服务器。

这些说明适用于任何基于 Ubuntu 的发行版，包括 Ubuntu 14.04、 Ubuntu 16.04、 [Ubuntu 18.04](https://thishosting.rocks/ubuntu-18-04-new-features-release-date/)，甚至非 LTS 的 Ubuntu 发行版，例如 Ubuntu 17.10。这些说明经过测试并为 Ubuntu 16.04 编写。

Apache (又名 httpd) 是最受欢迎和使用最广泛的 web 服务器，所以这应该对每个人都有用。

### 开始安装 Apache 之前

在我们开始之前，这里有一些要求和说明：

* Apache 可能已经在你的服务器上安装了，所以开始之前首先检查一下。你可以使用 `apachectl -V` 命令来显示你正在使用的 Apache 的版本和一些其他信息。
* 你需要一个 Ubuntu 服务器。你可以从 [Vultr](https://thishosting.rocks/go/vultr/) 购买一个，它们是[最便宜的云托管服务商](https://thishosting.rocks/cheap-cloud-hosting-providers-comparison/)之一。它们的服务器价格每月 2.5 美元起。（LCTT 译注：广告 ≤\_≤ ）
* 你需要有 root 用户或具有 sudo 访问权限的用户。下面的所有命令都由 root 用户执行，所以我们不必为每个命令都添加 `sudo`。
* 如果你使用 Ubuntu，则需要[启用 SSH](https://thishosting.rocks/how-to-enable-ssh-on-ubuntu/)，如果你使用 Windows，则应该使用类似 [MobaXterm](https://mobaxterm.mobatek.net/) 的 SSH 客户端。

这就是全部要求和注释了，让我们进入安装过程。

### 在 Ubuntu 上安装 Apache

你需要做的第一件事就是更新 Ubuntu，这是在你做任何事情之前都应该做的。你可以运行：

```
apt-get update && apt-get upgrade

```

接下来，安装 Apache，运行以下命令：

```
apt-get install apache2

```

如果你愿意，你也可以安装 Apache 文档和一些 Apache 实用程序。对于我们稍后将要安装的一些模块，你将需要一些 Apache 实用程序。

```
apt-get install apache2-doc apache2-utils

```

\**就是这样。你已经成功安装了 Apache \**

你仍然需要配置它。

### 在 Ubuntu 上配置和优化 Apache

你可以在 Apache 上做各种各样的配置，但是主要的和最常见的配置将在下面做出解释。

#### 检查 Apache 是否正在运行

默认情况下，Apache 设置为在机器启动时自动启动，因此你不必手动启用它。你可以使用以下命令检查它是否正在运行以及其他相关信息：

```
systemctl status apache2

```

[](https://thishosting.rocks/wp-content/uploads/2018/01/apache-running.jpg)

并且你可以检查你正在使用的版本：

```
apachectl -V

```

一种更简单的检查方法时访问服务器的 IP 地址，如果你得到默认的 Apache 页面，那么一切都正常。

#### 更新你的防火墙

如果你使用防火墙（你应该使用它），则可能需要更新防火墙规则并允许访问默认端口。Ubuntu 上最常用的防火墙是 UFW，因此以下说明使用于 UFW。

要允许通过 80（http）和 443（https）端口的流量，运行以下命令：

```
ufw allow 'Apache Full'

```

#### 安装常见的 Apache 模块

一些模块经常被建议使用，所以你应该安装它们。我们将包含最常见模块的说明：

##### 使用 PageSpeed 加速你的网站

PageSpeed 模块将自动优化并加速你的 Apache 服务器。

首先，进入 [PageSpeed 下载页](https://www.modpagespeed.com/doc/download)并选择你需要的的文件。我们使用的是 64 位 Ubuntu 服务器，所以我们安装最新的稳定版本。使用 `wget` 下载它：

```
wget https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_amd64.deb

```

然后，使用以下命令安装它：

```
dpkg -i mod-pagespeed-stable_current_amd64.deb
apt-get -f install

```

重启 Apache 以使更改生效：

```
systemctl restart apache2

```

##### 使用 mod\_rewrite 模块启动重写/重定向

顾名思义，该模块用于重写（重定向）。如果你使用 WordPress 或任何其他 CMS 来处理此问题，你就需要它。要安装它，只需运行：

```
a2enmod rewrite

```

然后再次重新启动 Apache。你可能需要一些额外的配置，具体取决于你使用的 CMS，如果有的话。为你的设置 Google 一下得到它的具体说明。

##### 使用 ModSecurity 模块保护你的 Apache

顾名思义，ModSecurity 是一个用于安全性的模块，它基本上起着防火墙的作用，它可以监控你的流量。要安装它，运行以下命令：

```
apt-get install libapache2-modsecurity

```

再次重启 Apache：

```
systemctl restart apache2

```

ModSecurity 自带了一个默认的设置，但如果你想扩展它，你可以使用 [OWASP 规则集](https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project)。

##### 使用 mod\_evasive 模块抵御 DDoS 攻击

尽管 mod\_evasive 在防止攻击方面有多大用处值得商榷，但是你可以使用它来阻止和防止服务器上的 DDoS 攻击。要安装它，使用以下命令：

```
apt-get install libapache2-mod-evasive

```

默认情况下，mod\_evasive 是禁用的，要启用它，编辑以下文件：

```
nano /etc/apache2/mods-enabled/evasive.conf

```

取消注释所有行（即删除 `#`），根据你的要求进行配置。如果你不知道要编辑什么，你可以保持原样。

[](https://thishosting.rocks/wp-content/uploads/2018/01/mod_evasive.jpg)

创建一个日志文件：

```
mkdir /var/log/mod_evasive
chown -R www-data:www-data /var/log/mod_evasive

```

就是这样。现在重启 Apache 以使更改生效。

```
systemctl restart apache2

```

你可以安装和配置[附加模块](https://httpd.apache.org/docs/2.4/mod/)，但完全取决于你和你使用的软件。它们通常不是必需的。甚至我们上面包含的 4 个模块也不是必需的。如果特定应用需要模块，那么它们可能会注意到这一点。

#### 用 Apache2Buddy 脚本优化 Apache

Apache2Buddy 是一个可以自动调整 Apache 配置的脚本。你唯一需要做的就是运行下面的命令，脚本会自动完成剩下的工作：

```
curl -sL https://raw.githubusercontent.com/richardforth/apache2buddy/master/apache2buddy.pl | perl

```

如果你没有安装 `curl`，那么你可能需要安装它。使用以下命令来安装 `curl`：

```
apt-get install curl

```

#### 额外配置

用 Apache 还可以做一些额外的东西，但我们会留下它们作为另一个教程。像启用 http/2 支持，关闭（或打开） KeepAlive，调整你的 Apache 甚至更多。这些东西你现在不需要做，但是如果你在网上找到了教程，并且如果你等不及我们的教程，那就去做吧。

### 使用 Apache 创建你的第一个网站

现在我们已经完成了所有的调优工作，让我们开始创建一个实际的网站。按照我们的指示创建一个简单的 HTML 页面和一个在 Apache 上运行的虚拟主机。

你需要做的第一件事是为你的网站创建一个新的目录。运行以下命令来执行此操作：

```
mkdir -p /var/www/example.com/public_html

```

当然，将 `example.com` 替换为你所需的域名。你可以从 [Namecheap](https://thishosting.rocks/neamcheap-review-cheap-domains-cool-names) 获得一个便宜的域名。

不要忘记在下面的所有命令中替换 `example.com`。

接下来，创建一个简单的静态网页。创建 HTML 文件：

```
nano /var/www/example.com/public_html/index.html

```

粘贴这些：

```
<html>
     <head>
       <title>Simple Page</title>
     </head>
     <body>
       <p>If you're seeing this in your browser then everything works.</p>
     </body>
</html>

```

保存并关闭文件。

配置目录的权限：

```
chown -R www-data:www-data /var/www/example.com
chmod -R og-r /var/www/example.com

```

为你的网站创建一个新的虚拟主机：

```
nano /etc/apache2/sites-available/example.com.conf

```

粘贴以下内容：

```
<VirtualHost *:80>
     ServerAdmin admin@example.com
     ServerName example.com
     ServerAlias www.example.com

     DocumentRoot /var/www/example.com/public_html

     ErrorLog ${APACHE_LOG_DIR}/error.log
     CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

```

这是一个基础的虚拟主机。根据你的设置，你可能需要更高级的 `.conf` 文件。

在更新所有内容后保存并关闭文件。

现在，使用以下命令启用虚拟主机：

```
a2ensite example.com.conf

```

最后，重启 Apache 以使更改生效：

```
systemctl restart apache2

```

这就是全部了，你做完了。现在你可以访问 example.com 并查看你的页面。

---

via: <https://thishosting.rocks/how-to-install-optimize-apache-ubuntu/>

作者：[ThisHosting](https://thishosting.rocks) 译者：[MjSeven](https://github.com/MjSeven) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


在 CentOS 6 系统上安装最新版 Python3 软件包的 3 种方法

# 在 CentOS 6 系统上安装最新版 Python3 软件包的 3 种方法

<img src="content_image/album_201805_26_071008ki1rmqmar1q9yhyr.png" alt="" title="">

CentOS 克隆自 RHEL，无需付费即可使用。CentOS 是一个企业级标准的、前沿的操作系统，被超过 90% 的网络主机托管商采用，因为它提供了技术领先的服务器控制面板 cPanel/WHM。

该控制面板使得用户无需进入命令行即可通过其管理一切。

众所周知，RHEL 提供长期支持，出于稳定性考虑，不提供最新版本的软件包。

如果你想安装的最新版本软件包不在默认源中，你需要手动编译源码安装。但手动编译安装的方式有不小的风险，即如果出现新版本，无法升级手动安装的软件包；你不得不重新手动安装。

那么在这种情况下，安装最新版软件包的推荐方法和方案是什么呢？是的，可以通过为系统添加所需的第三方源来达到目的。

可供企业级 Linux 使用的第三方源有很多，但只有几个是 CentOS 社区推荐使用的，它们在很大程度上不修改基础软件包。

这几个推荐的源维护的很好，为 CentOS 提供大量补充软件包。

在本教程中，我们将向你展示，如何在 CentOS 6 操作系统上安装最新版本的 Python 3 软件包。

### 方法 1：使用 Software Collections 源 （SCL）

SCL 源目前由 CentOS SIG 维护，除了重新编译构建 Red Hat 的 Software Collections 外，还额外提供一些它们自己的软件包。

该源中包含不少程序的更高版本，可以在不改变原有旧版本程序包的情况下安装，使用时需要通过 `scl` 命令调用。

运行如下命令可以在 CentOS 上安装 SCL 源：

```
# yum install centos-release-scl

```

检查可用的 Python 3 版本：

```
# yum info rh-python35
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
 * epel: ewr.edge.kernel.org
 * remi-safe: mirror.team-cymru.com
Available Packages
Name        : rh-python35
Arch        : x86_64
Version     : 2.0
Release     : 2.el6
Size        : 0.0
Repo        : installed
From repo   : centos-sclo-rh
Summary     : Package that installs rh-python35
License     : GPLv2+
Description : This is the main package for rh-python35 Software Collection.

```

运行如下命令从 `scl` 源安装可用的最新版 python 3：

```
# yum install rh-python35

```

运行如下特殊的 `scl` 命令，在当前 shell 中启用安装的软件包：

```
# scl enable rh-python35 bash

```

运行如下命令检查安装的 python3 版本：

```
# python --version
Python 3.5.1

```

运行如下命令获取系统已安装的 SCL 软件包列表：

```
# scl -l
rh-python35

```

### 方法 2：使用 EPEL 源 (Extra Packages for Enterprise Linux）

EPEL 是 Extra Packages for Enterprise Linux 的缩写，该源由 Fedora SIG （Special Interest Group）维护。

该 SIG 为企业级 Linux 创建、维护并管理了一系列高品质补充软件包，受益的企业级 Linux 发行版包括但不限于红帽企业级 Linux （RHEL）、 CentOS、 Scientific Linux （SL） 和 Oracle Linux （OL）等。

EPEL 通常基于 Fedora 对应代码提供软件包，不会与企业级 Linux 发行版中的基础软件包冲突或替换其中的软件包。

**推荐阅读：** [在 RHEL, CentOS, Oracle Linux 或 Scientific Linux 上安装启用 EPEL 源](https://www.2daygeek.com/install-enable-epel-repository-on-rhel-centos-scientific-linux-oracle-linux/)

EPEL 软件包位于 CentOS 的 Extra 源中，已经默认启用，故我们只需运行如下命令即可：

```
# yum install epel-release

```

检查可用的 python 3 版本：

```
# yum --disablerepo="*" --enablerepo="epel" info python34
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
 * epel: ewr.edge.kernel.org
Available Packages
Name        : python34
Arch        : x86_64
Version     : 3.4.5
Release     : 4.el6
Size        : 50 k
Repo        : epel
Summary     : Version 3 of the Python programming language aka Python 3000
URL         : http://www.python.org/
License     : Python
Description : Python 3 is a new version of the language that is incompatible with the 2.x
            : line of releases. The language is mostly the same, but many details, especially
            : how built-in objects like dictionaries and strings work, have changed
            : considerably, and a lot of deprecated features have finally been removed.

```

运行如下命令从 EPEL 源安装可用的最新版 python 3 软件包：

```
# yum --disablerepo="*" --enablerepo="epel" install python34

```

默认情况下并不会安装 `pip` 和 `setuptools`，我们需要运行如下命令手动安装：

```
# curl -O https://bootstrap.pypa.io/get-pip.py
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1603k  100 1603k    0     0  2633k      0 --:--:-- --:--:-- --:--:-- 4816k

# /usr/bin/python3.4 get-pip.py
Collecting pip
  Using cached https://files.pythonhosted.org/packages/0f/74/ecd13431bcc456ed390b44c8a6e917c1820365cbebcb6a8974d1cd045ab4/pip-10.0.1-py2.py3-none-any.whl
Collecting setuptools
  Downloading https://files.pythonhosted.org/packages/8c/10/79282747f9169f21c053c562a0baa21815a8c7879be97abd930dbcf862e8/setuptools-39.1.0-py2.py3-none-any.whl (566kB)
    100% |████████████████████████████████| 573kB 4.0MB/s
Collecting wheel
  Downloading https://files.pythonhosted.org/packages/1b/d2/22cde5ea9af055f81814f9f2545f5ed8a053eb749c08d186b369959189a8/wheel-0.31.0-py2.py3-none-any.whl (41kB)
    100% |████████████████████████████████| 51kB 8.0MB/s
Installing collected packages: pip, setuptools, wheel
Successfully installed pip-10.0.1 setuptools-39.1.0 wheel-0.31.0

```

运行如下命令检查已安装的 python3 版本：

```
# python3 --version
Python 3.4.5

```

### 方法 3：使用 IUS 社区源

IUS 社区是 CentOS 社区批准的第三方 RPM 源，为企业级 Linux （RHEL 和 CentOS） 5、 6 和 7 版本提供最新上游版本的 PHP、 Python、 MySQL 等软件包。

IUS 社区源依赖于 EPEL 源，故我们需要先安装 EPEL 源，然后再安装 IUS 社区源。按照下面的步骤安装启用 EPEL 源和 IUS 社区源，利用该 RPM 系统安装软件包。

**推荐阅读：** [在 RHEL 或 CentOS 上安装启用 IUS 社区源](https://www.2daygeek.com/install-enable-ius-community-repository-on-rhel-centos/)

EPEL 软件包位于 CentOS 的 Extra 源中，已经默认启用，故我们只需运行如下命令即可：

```
# yum install epel-release

```

下载 IUS 社区源安装脚本：

```
# curl 'https://setup.ius.io/' -o setup-ius.sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  1914  100  1914    0     0   6563      0 --:--:-- --:--:-- --:--:--  133k

```

安装启用 IUS 社区源：

```
# sh setup-ius.sh

```

检查可用的 python 3 版本：

```
# yum --enablerepo=ius info python36u
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
 * epel: ewr.edge.kernel.org
 * ius: mirror.team-cymru.com
 * remi-safe: mirror.team-cymru.com
Available Packages
Name        : python36u
Arch        : x86_64
Version     : 3.6.5
Release     : 1.ius.centos6
Size        : 55 k
Repo        : ius
Summary     : Interpreter of the Python programming language
URL         : https://www.python.org/
License     : Python
Description : Python is an accessible, high-level, dynamically typed, interpreted programming
            : language, designed with an emphasis on code readability.
            : It includes an extensive standard library, and has a vast ecosystem of
            : third-party libraries.
            :
            : The python36u package provides the "python3.6" executable: the reference
            : interpreter for the Python language, version 3.
            : The majority of its standard library is provided in the python36u-libs package,
            : which should be installed automatically along with python36u.
            : The remaining parts of the Python standard library are broken out into the
            : python36u-tkinter and python36u-test packages, which may need to be installed
            : separately.
            :
            : Documentation for Python is provided in the python36u-docs package.
            :
            : Packages containing additional libraries for Python are generally named with
            : the "python36u-" prefix.

```

运行如下命令从 IUS 源安装最新可用版本的 python 3 软件包：

```
# yum --enablerepo=ius install python36u

```

运行如下命令检查已安装的 python3 版本：

```
# python3.6 --version
Python 3.6.5

```

---

via: <https://www.2daygeek.com/3-methods-to-install-latest-python3-package-on-centos-6-system/>

作者：[PRAKASH SUBRAMANIAN](https://www.2daygeek.com/author/prakash/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[pinewall](https://github.com/pinewall) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


更深入的理解 Python 中的迭代

Summary: 在这篇文章中，我们将深入探讨 Python 的 for 循环来看看它们在底层如何工作，以及为什么它们会按照它们的方式工作。

# 更深入的理解 Python 中的迭代

>
> 深入探讨 Python 的 `for` 循环来看看它们在底层如何工作，以及为什么它们会按照它们的方式工作。
>
>
>

<img src="content_image/album_201805_26_080352ai3a6qticbnnbb8o.jpg" alt="" title="">

Python 的 `for` 循环不会像其他语言中的 `for` 循环那样工作。在这篇文章中，我们将深入探讨 Python 的 `for` 循环来看看它们在底层如何工作，以及为什么它们会按照它们的方式工作。

### 循环的问题

我们将通过看一些“陷阱”开始我们的旅程，在我们了解循环如何在 Python 中工作之后，我们将再次看看这些问题并解释发生了什么。

#### 问题 1：循环两次

假设我们有一个数字列表和一个生成器，生成器会返回这些数字的平方：

```
>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)

```

我们可以将生成器对象传递给 `tuple` 构造器，从而使其变为一个元组：

```
>>> tuple(squares)
(1, 4, 9, 25, 49)

```

如果我们使用相同的生成器对象并将其传给 `sum` 函数，我们可能会期望得到这些数的和，即 `88`。

```
>>> sum(squares)
0

```

但是我们得到了 `0`。

#### 问题 2：包含的检查

让我们使用相同的数字列表和相同的生成器对象：

```
>>> numbers = [1, 2, 3, 5, 7]

>>> squares = (n**2 for n in numbers)

```

如果我们询问 `9` 是否在 `squares` 生成器中，Python 将会告诉我们 9 在 `squares` 中。但是如果我们再次询问相同的问题，Python 会告诉我们 9 不在 `squares` 中。

```
>>> 9 in squares
True
>>> 9 in squares
False

```

我们询问相同的问题两次，Python 给了两个不同的答案。

#### 问题 3 ：拆包

这个字典有两个键值对：

```
>>> counts = {'apples': 2, 'oranges': 1}

```

让我们使用多个变量来对这个字典进行拆包：

```
>>> x, y = counts

```

你可能会期望当我们对这个字典进行拆包时，我们会得到键值对或者得到一个错误。

但是解包字典不会引发错误，也不会返回键值对。当你解包一个字典时，你会得到键：

```
>>> x
'apples'

```

### 回顾：Python 的 for 循环

在我们了解一些关于这些 Python 片段的逻辑之后，我们将回到这些问题。

Python 没有传统的 `for` 循环。为了解释我的意思，让我们看一看另一种编程语言的 `for` 循环。

这是一种传统 C 风格的 `for` 循环，用 JavaScript 编写：

```
let numbers = [1, 2, 3, 5, 7];
for (let i = 0; i < numbers.length; i += 1) {
    print(numbers[i])
}

```

JavaScript、 C、 C++、 Java、 PHP 和一大堆其他编程语言都有这种风格的 `for` 循环，但是 Python **确实没有**。

Python **确实没有** 传统 C 风格的 `for` 循环。在 Python 中确实有一些我们称之为 `for` 循环的东西，但是它的工作方式类似于 [foreach 循环](https://en.wikipedia.org/wiki/Foreach_loop)。

这是 Python 的 `for` 循环的风格：

```
numbers = [1, 2, 3, 5, 7]
for n in numbers:
    print(n)

```

与传统 C 风格的 `for` 循环不同，Python 的 `for` 循环没有索引变量，没有索引变量初始化，边界检查，或者索引递增。Python 的 `for` 循环完成了对我们的 `numbers` 列表进行遍历的所有工作。

因此，当我们在 Python 中确实有 `for` 循环时，我们没有传统 C 风格的 `for` 循环。我们称之为 for 循环的东西的工作机制与之相比有很大的不同。

### 定义：可迭代和序列

既然我们已经解决了 Python 世界中无索引的 `for` 循环，那么让我们在此之外来看一些定义。

**可迭代**是任何你可以用 Python 中的 `for` 循环遍历的东西。可迭代意味着可以遍历，任何可以遍历的东西都是可迭代的。

```
for item in some_iterable:
    print(item)

```

序列是一种非常常见的可迭代类型，列表，元组和字符串都是序列。

```
>>> numbers = [1, 2, 3, 5, 7]
>>> coordinates = (4, 5, 7)
>>> words = "hello there"

```

序列是可迭代的，它有一些特定的特征集。它们可以从 `0` 开始索引，以小于序列的长度结束，它们有一个长度并且它们可以被切分。列表，元组，字符串和其他所有序列都是这样工作的。

```
>>> numbers[0]
1
>>> coordinates[2]
7
>>> words[4]
'o'

```

Python 中很多东西都是可迭代的，但不是所有可迭代的东西都是序列。集合、字典、文件和生成器都是可迭代的，但是它们都不是序列。

```
>>> my_set = {1, 2, 3}
>>> my_dict = {'k1': 'v1', 'k2': 'v2'}
>>> my_file = open('some_file.txt')
>>> squares = (n**2 for n in my_set)

```

因此，任何可以用 `for` 循环遍历的东西都是可迭代的，序列只是一种可迭代的类型，但是 Python 也有许多其他种类的迭代器。

### Python 的 for 循环不使用索引

你可能认为，Python 的 `for` 循环在底层使用了索引进行循环。在这里我们使用 `while` 循环和索引手动遍历：

```
numbers = [1, 2, 3, 5, 7]
i = 0
while i < len(numbers):
    print(numbers[i])
    i += 1

```

这适用于列表，但它不会对所有东西都起作用。这种循环方式**只适用于序列**。

如果我们尝试用索引去手动遍历一个集合，我们会得到一个错误：

```
>>> fruits = {'lemon', 'apple', 'orange', 'watermelon'}
>>> i = 0
>>> while i < len(fruits):
...     print(fruits[i])
...     i += 1
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
TypeError: 'set' object does not support indexing

```

集合不是序列，所以它们不支持索引。

我们不能使用索引手动对 Python 中的每一个迭代对象进行遍历。对于那些不是序列的迭代器来说，这是行不通的。

### 迭代器驱动 for 循环

因此，我们已经看到，Python 的 `for` 循环在底层不使用索引。相反，Python 的 `for` 循环使用**迭代器**。

迭代器就是可以驱动可迭代对象的东西。你可以从任何可迭代对象中获得迭代器，你也可以使用迭代器来手动对它的迭代进行遍历。

让我们来看看它是如何工作的。

这里有三个可迭代对象：一个集合，一个元组和一个字符串。

```
>>> numbers = {1, 2, 3, 5, 7}
>>> coordinates = (4, 5, 7)
>>> words = "hello there"

```

我们可以使用 Python 的内置 `iter` 函数来访问这些迭代器，将一个迭代器传递给 `iter` 函数总会给我们返回一个迭代器，无论我们正在使用哪种类型的迭代器。

```
>>> iter(numbers)
<set_iterator object at 0x7f2b9271c860>
>>> iter(coordinates)
<tuple_iterator object at 0x7f2b9271ce80>
>>> iter(words)
<str_iterator object at 0x7f2b9271c860>

```

一旦我们有了迭代器，我们可以做的事情就是通过将它传递给内置的 `next` 函数来获取它的下一项。

```
>>> numbers = [1, 2, 3]
>>> my_iterator = iter(numbers)
>>> next(my_iterator)
1
>>> next(my_iterator)
2

```

迭代器是有状态的，这意味着一旦你从它们中消耗了一项，它就消失了。

如果你从迭代器中请求 `next` 项，但是其中没有更多的项了，你将得到一个 `StopIteration` 异常：

```
>>> next(my_iterator)
3
>>> next(my_iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

```

所以你可以从每个迭代中获得一个迭代器，迭代器唯一能做的事情就是用 `next` 函数请求它们的下一项。如果你将它们传递给 `next`，但它们没有下一项了，那么就会引发 `StopIteration` 异常。

你可以将迭代器想象成 Pez 分配器（LCTT 译注：Pez 是一个结合玩具的独特复合式糖果），不能重新分配。你可以把 Pez 拿出去，但是一旦 Pez 被移走，它就不能被放回去，一旦分配器空了，它就没用了。

### 没有 for 的循环

既然我们已经了解了迭代器和 `iter` 以及 `next` 函数，我们将尝试在不使用 `for` 循环的情况下手动遍历迭代器。

我们将通过尝试将这个 `for` 循环变为 `while` 循环：

```
def funky_for_loop(iterable, action_to_do):
    for item in iterable:
        action_to_do(item)

```

为了做到这点，我们需要：

1. 从给定的可迭代对象中获得迭代器
2. 反复从迭代器中获得下一项
3. 如果我们成功获得下一项，就执行 `for` 循环的主体
4. 如果我们在获得下一项时得到了一个 `StopIteration` 异常，那么就停止循环

```
def funky_for_loop(iterable, action_to_do):
    iterator = iter(iterable)
    done_looping = False
    while not done_looping:
        try:
            item = next(iterator)
        except StopIteration:
            done_looping = True
        else:
            action_to_do(item)

```

我们只是通过使用 `while` 循环和迭代器重新定义了 `for` 循环。

上面的代码基本上定义了 Python 在底层循环的工作方式。如果你理解内置的 `iter` 和 `next` 函数的遍历循环的工作方式，那么你就会理解 Python 的 `for` 循环是如何工作的。

事实上，你不仅仅会理解 `for` 循环在 Python 中是如何工作的，所有形式的遍历一个可迭代对象都是这样工作的。

<ruby> 迭代器协议 <rt>  iterator protocol </rt></ruby> 是一种很好表示 “在 Python 中遍历迭代器是如何工作的”的方式。它本质上是对 `iter` 和 `next` 函数在 Python 中是如何工作的定义。Python 中所有形式的迭代都是由迭代器协议驱动的。

迭代器协议被 `for` 循环使用（正如我们已经看到的那样）：

```
for n in numbers:
    print(n)

```

多重赋值也使用迭代器协议：

```
x, y, z = coordinates

```

星型表达式也是用迭代器协议：

```
a, b, *rest = numbers
print(*numbers)

```

许多内置函数依赖于迭代器协议：

```
unique_numbers = set(numbers)

```

在 Python 中任何与迭代器一起工作的东西都可能以某种方式使用迭代器协议。每当你在 Python 中遍历一个可迭代对象时，你将依赖于迭代器协议。

### 生成器是迭代器

所以你可能会想：迭代器看起来很酷，但它们看起来像一个实现细节，我们作为 Python 的使用者，可能不需要关心它们。

我有消息告诉你：在 Python 中直接使用迭代器是很常见的。

这里的 `squares` 对象是一个生成器：

```
>>> numbers = [1, 2, 3]
>>> squares = (n**2 for n in numbers)

```

生成器是迭代器，这意味着你可以在生成器上调用 `next` 来获得它的下一项：

```
>>> next(squares)
1
>>> next(squares)
4

```

但是如果你以前用过生成器，你可能也知道可以循环遍历生成器：

```
>>> squares = (n**2 for n in numbers)
>>> for n in squares:
...     print(n)
...
1
4
9

```

如果你可以在 Python 中循环遍历某些东西，那么它就是**可迭代的**。

所以**生成器是迭代器**，但是生成器也是可迭代的，这又是怎么回事呢？

### 我欺骗了你

所以在我之前解释迭代器如何工作时，我跳过了它们的某些重要的细节。

#### 生成器是可迭代的

我再说一遍：Python 中的每一个迭代器都是可迭代的，意味着你可以循环遍历迭代器。

因为迭代器也是可迭代的，所以你可以使用内置 `next` 函数从可迭代对象中获得迭代器：

```
>>> numbers = [1, 2, 3]
>>> iterator1 = iter(numbers)
>>> iterator2 = iter(iterator1)

```

请记住，当我们在可迭代对象上调用 `iter` 时，它会给我们返回一个迭代器。

当我们在迭代器上调用 `iter` 时，它会给我们返回它自己：

```
>>> iterator1 is iterator2
True

```

迭代器是可迭代的，所有的迭代器都是它们自己的迭代器。

```
def is_iterator(iterable):
    return iter(iterable) is iterable

```

迷惑了吗？

让我们回顾一些这些措辞。

* 一个**可迭代对象**是你可以迭代的东西
* 一个**迭代对象器**是一种实际上遍历可迭代对象的代理

此外，在 Python 中迭代器也是可迭代的，它们充当它们自己的迭代器。

所以迭代器是可迭代的，但是它们没有一些可迭代对象拥有的各种特性。

迭代器没有长度，它们不能被索引：

```
>>> numbers = [1, 2, 3, 5, 7]
>>> iterator = iter(numbers)
>>> len(iterator)
TypeError: object of type 'list_iterator' has no len()
>>> iterator[0]
TypeError: 'list_iterator' object is not subscriptable

```

从我们作为 Python 程序员的角度来看，你可以使用迭代器来做的唯一有用的事情是将其传递给内置的 `next` 函数，或者对其进行循环遍历：

```
>>> next(iterator)
1
>>> list(iterator)
[2, 3, 5, 7]

```

如果我们第二次循环遍历迭代器，我们将一无所获：

```
>>> list(iterator)
[]

```

你可以把迭代器看作是**惰性迭代器**，它们是**一次性使用**，这意味着它们只能循环遍历一次。

正如你在下面的真值表中所看到的，可迭代对象并不总是迭代器，但是迭代器总是可迭代的：

| 对象 | 可迭代？ | 迭代器？ |
| --- | --- | --- |
| 可迭代对象 | V | ? |
| 迭代器 | V | V |
| 生成器 | V | V |
| 列表 | V | X |

### 全部的迭代器协议

让我们从 Python 的角度来定义迭代器是如何工作的。

可迭代对象可以被传递给 `iter` 函数，以便为它们获得迭代器。

迭代器：

* 可以传递给 `next` 函数，它将给出下一项，如果没有下一项，那么它将会引发 `StopIteration` 异常
* 可以传递给 `iter` 函数，它会返回一个自身的迭代器

这些语句反过来也是正确的：

* 任何可以在不引发 `TypeError` 异常的情况下传递给 `iter` 的东西都是可迭代的
* 任何可以在不引发 `TypeError` 异常的情况下传递给 `next` 的东西都是一个迭代器
* 当传递给 `iter` 时，任何返回自身的东西都是一个迭代器

这就是 Python 中的迭代器协议。

#### 迭代器的惰性

迭代器允许我们一起工作，创建**惰性可迭代对象**，即在我们要求它们提供下一项之前，它们不做任何事情。因为可以创建惰性迭代器，所以我们可以创建无限长的迭代器。我们可以创建对系统资源比较保守的迭代器，可以节省我们的内存，节省 CPU 时间。

### 迭代器无处不在

你已经在 Python 中看到过许多迭代器，我也提到过生成器是迭代器。Python 的许多内置类型也是迭代器。例如，Python 的 `enumerate` 和 `reversed` 对象就是迭代器。

```
>>> letters = ['a', 'b', 'c']
>>> e = enumerate(letters)
>>> e
<enumerate object at 0x7f112b0e6510>
>>> next(e)
(0, 'a')

```

在 Python 3 中，`zip`, `map` 和 `filter` 也是迭代器。

```
>>> numbers = [1, 2, 3, 5, 7]
>>> letters = ['a', 'b', 'c']
>>> z = zip(numbers, letters)
>>> z
<zip object at 0x7f112cc6ce48>
>>> next(z)
(1, 'a')

```

Python 中的文件对象也是迭代器。

```
>>> next(open('hello.txt'))
'hello world\n'

```

在 Python 标准库和第三方库中内置了大量的迭代器。这些迭代器首先惰性迭代器一样，延迟工作直到你请求它们下一项。

### 创建你自己的迭代器

知道你已经在使用迭代器是很有用的，但是我希望你也知道，你可以创建自己的迭代器和你自己的惰性迭代器。

下面这个类构造了一个迭代器接受一个可迭代的数字，并在循环结束时提供每个数字的平方。

```
class square_all:
    def __init__(self, numbers):
        self.numbers = iter(numbers)
    def __next__(self):
        return next(self.numbers) * 2
    def __iter__(self):
        return self

```

但是在我们开始对该类的实例进行循环遍历之前，没有任何工作要做。

这里，我们有一个无限长的可迭代对象 `count`，你可以看到 `square_all` 接受 `count` 而不用完全循环遍历这个无限长的迭代：

```
>>> from itertools import count
>>> numbers = count(5)
>>> squares = square_all(numbers)
>>> next(squares)
25
>>> next(squares)
36

```

这个迭代器类是有效的，但我们通常不会这样做。通常，当我们想要做一个定制的迭代器时，我们会生成一个生成器函数：

```
def square_all(numbers):
    for n in numbers:
        yield n**2

```

这个生成器函数等价于我们上面所做的类，它的工作原理是一样的。

这种 `yield` 语句似乎很神奇，但它非常强大：`yield` 允许我们在调用 `next` 函数之间暂停生成器函数。`yield` 语句是将生成器函数与常规函数分离的东西。

另一种实现相同迭代器的方法是使用生成器表达式。

```
def square_all(numbers):
    return (n**2 for n in numbers)

```

这和我们的生成器函数确实是一样的，但是它使用的语法看起来[像是一个列表推导一样](http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/)。如果你需要在代码中使用惰性迭代，请考虑迭代器，并考虑使用生成器函数或生成器表达式。

### 迭代器如何改进你的代码

一旦你已经接受了在代码中使用惰性迭代器的想法，你就会发现有很多可能来发现或创建辅助函数，以此来帮助你循环遍历和处理数据。

#### 惰性求和

这是一个 `for` 循环，它对 Django queryset 中的所有工作时间求和：

```
hours_worked = 0
for event in events:
    if event.is_billable():
        hours_worked += event.duration

```

下面是使用生成器表达式进行惰性评估的代码：

```
billable_times = (
    event.duration
    for event in events
    if event.is_billable()
)

hours_worked = sum(billable_times)

```

请注意，我们代码的形状发生了巨大变化。

将我们的计算工作时间变成一个惰性迭代器允许我们能够命名以前未命名（`billable_times`）的东西。这也允许我们使用 `sum` 函数，我们以前不能使用 `sum` 函数是因为我们甚至没有一个可迭代对象传递给它。迭代器允许你从根本上改变你组织代码的方式。

#### 惰性和打破循环

这段代码打印出日志文件的前 10 行：

```
for i, line in enumerate(log_file):
    if i >= 10:
        break
    print(line)

```

这段代码做了同样的事情，但是我们使用的是 `itertools.islice` 函数来惰性地抓取文件中的前 10 行：

```
from itertools import islice
first_ten_lines = islice(log_file, 10)
for line in first_ten_lines:
    print(line)

```

我们定义的 `first_ten_lines` 变量是迭代器，同样，使用迭代器允许我们给以前未命名的东西命名(`first_ten_lines`)。命名事物可以使我们的代码更具描述性，更具可读性。

作为奖励，我们还消除了在循环中使用 `break` 语句的需要，因为 `islice` 实用函数为我们处理了中断。

你可以在标准库中的 [itertools](https://docs.python.org/3/library/itertools.html) 中找到更多的迭代辅助函数，以及诸如 [boltons](https://boltons.readthedocs.io) 和 [more-itertools](https://more-itertools.readthedocs.io) 之类的第三方库。

#### 创建自己的迭代辅助函数

你可以在标准库和第三方库中找到用于循环的辅助函数，但你也可以自己创建！

这段代码列出了序列中连续值之间的差值列表。

```
current = readings[0]
for next_item in readings[1:]:
    differences.append(next_item - current)
    current = next_item

```

请注意，这段代码中有一个额外的变量，我们每次循环时都要指定它。还要注意，这段代码只适用于我们可以切片的东西，比如序列。如果 `readings` 是一个生成器，一个 zip 对象或其他任何类型的迭代器，那么这段代码就会失败。

让我们编写一个辅助函数来修复代码。

这是一个生成器函数，它为给定的迭代中的每个项目提供了当前项和下一项：

```
def with_next(iterable):
    """Yield (current, next_item) tuples for each item in iterable."""
    iterator = iter(iterable)
    current = next(iterator)
    for next_item in iterator:
        yield current, next_item
        current = next_item

```

我们从可迭代对象中手动获取一个迭代器，在它上面调用 `next` 来获取第一项，然后循环遍历迭代器获取后续所有的项目，跟踪后一个项目。这个函数不仅适用于序列，而且适用于任何类型迭代。

这段代码和以前代码是一样的，但是我们使用的是辅助函数而不是手动跟踪 `next_item`：

```
differences = []
for current, next_item in with_next(readings):
    differences.append(next_item - current)

```

请注意，这段代码不会挂在我们循环周围的 `next_item` 上，`with_next` 生成器函数处理跟踪 `next_item` 的工作。

还要注意，这段代码已足够紧凑，如果我们愿意，我们甚至可以[将方法复制到列表推导中来](http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/)。

```
differences = [
    (next_item - current)
    for current, next_item in with_next(readings)
]

```

### 再次回顾循环问题

现在我们准备回到之前看到的那些奇怪的例子并试着找出到底发生了什么。

#### 问题 1：耗尽的迭代器

这里我们有一个生成器对象 `squares`：

```
>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)

```

如果我们把这个生成器传递给 `tuple` 构造函数，我们将会得到它的一个元组：

```
>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)
>>> tuple(squares)
(1, 4, 9, 25, 49)

```

如果我们试着计算这个生成器中数字的和，使用 `sum`,我们就会得到 `0`：

```
>>> sum(squares)
0

```

这个生成器现在是空的：我们已经把它耗尽了。如果我们试着再次创建一个元组，我们会得到一个空元组：

```
>>> tuple(squares)
()

```

生成器是迭代器，迭代器是一次性的。它们就像 Hello Kitty Pez 分配器那样不能重新加载。

#### 问题 2：部分消耗一个迭代器

再次使用那个生成器对象 `squares`：

```
>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)

```

如果我们询问 `9` 是否在 `squares` 生成器中，我们会得到 `True`：

```
>>> 9 in squares
True

```

但是我们再次询问相同的问题，我们会得到 `False`：

```
>>> 9 in squares
False

```

当我们询问 `9` 是否在迭代器中时，Python 必须对这个生成器进行循环遍历来找到 `9`。如果我们在检查了 `9` 之后继续循环遍历，我们只会得到最后两个数字，因为我们已经在找到 9 之前消耗了这些数字：

```
>>> numbers = [1, 2, 3, 5, 7]
>>> squares = (n**2 for n in numbers)
>>> 9 in squares
True
>>> list(squares)
[25, 49]

```

询问迭代器中是否包含某些东西将会部分地消耗迭代器。如果没有循环遍历迭代器，那么是没有办法知道某个东西是否在迭代器中。

#### 问题 3：拆包是迭代

当你在字典上循环时，你会得到键：

```
>>> counts = {'apples': 2, 'oranges': 1}
>>> for key in counts:
...     print(key)
...
apples
oranges

```

当你对一个字典进行拆包时，你也会得到键：

```
>>> x, y = counts
>>> x, y
('apples', 'oranges')

```

循环依赖于迭代器协议，可迭代对象拆包也依赖于有迭代器协议。拆包一个字典与在字典上循环遍历是一样的，两者都使用迭代器协议，所以在这两种情况下都得到相同的结果。

### 回顾

序列是迭代器，但是不是所有的迭代器都是序列。当有人说“迭代器”这个词时，你只能假设他们的意思是“你可以迭代的东西”。不要假设迭代器可以被循环遍历两次、询问它们的长度或者索引。

迭代器是 Python 中最基本的可迭代形式。如果你想在代码中做一个惰性迭代，请考虑迭代器，并考虑使用生成器函数或生成器表达式。

最后，请记住，Python 中的每一种迭代都依赖于迭代器协议，因此理解迭代器协议是理解 Python 中的循环的关键。

这里有一些我推荐的相关文章和视频：

* [Loop Like a Native](https://nedbatchelder.com/text/iter.html)， Ned Batchelder 在 PyCon 2013 的讲演
* [Loop Better](https://www.youtube.com/watch?v=V2PkkMS2Ack) ，这篇文章是基于这个讲演的
* [The Iterator Protocol: How For Loops Work](http://treyhunner.com/2016/12/python-iterator-protocol-how-for-loops-work/)，我写的关于迭代器协议的短文
* [Comprehensible Comprehensions](https://www.youtube.com/watch?v=5_cJIcgM7rw)，关于推导和迭代器表达器的讲演
* [Python: Range is Not an Iterator](http://treyhunner.com/2018/02/python-range-is-not-an-iterator/)，我关于范围和迭代器的文章
* [Looping Like a Pro in Python](https://www.youtube.com/watch?v=u8g9scXeAcI)，DB 的 PyCon 2017 讲演

本文是基于作者去年在 [DjangoCon AU](https://www.youtube.com/watch?v=JYuE8ZiDPl4)、 [PyGotham](https://www.youtube.com/watch?v=Wd7vcuiMhxU) 和 [North Bay Python](https://www.youtube.com/watch?v=V2PkkMS2Ack) 中发表的 Loop Better 演讲。有关更多内容，请参加将于 2018 年 5 月 9 日至 17 日在 Columbus， Ohio 举办的 [PYCON](https://us.pycon.org/2018/)。

---

via: <https://opensource.com/article/18/3/loop-better-deeper-look-iteration-python>

作者：[Trey Hunner](https://opensource.com/users/treyhunner) 译者：[MjSeven](https://github.com/MjSeven) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


DevOps 如何消除了 Ranger 社区的瓶颈

# DevOps 如何消除了 Ranger 社区的瓶颈

>
> 拥抱 DevOps 让 Ranger 成员们变得更灵活、快捷和成为开发者社区更有价值的成员。
>
>
>

<img src="content_image/album_201805_26_234527kiri747ofifrv6r3.png" alt="" title="">

Visual Studio 的<ruby> 应用周期管理 <rt>  Application Lifecycle Management </rt></ruby>（ALM）项目 —— [Ranger](https://aka.ms/vsaraboutus) 是一个志愿者社区，它提供专业的指导、实践经验、以及开发者社区的漏洞修补解决方案。它创建于 2006 年，作为微软内部社区去 “将产品组与大家相连接，并去除推广阻力”。 在 2009 时，社区已经有超过 200 位成员，这导致了协作和计划面临很大的挑战，在依赖和手工流程上产生了瓶颈，并导致了开发者社区不断增加的延迟和各种报怨。在 2010 时，计划进一步去扩充包括微软最有价值专家（MVP）在内的分布在全球的社区。

这个社区被分割成十几个活跃的团队。每个团队都致力于通过它的生命周期去设计、构建和支持一个指导或处理项目。在以前，团队的瓶颈在团队管理级别上，原因是严格的、瀑布式的流程和高度依赖一个或多个项目经理。在制作、发布和“为什么、做什么、和怎么做”驱动的决定上，项目经理都要介入其中。另外，缺乏一个实时的指标阻止了团队对他们的解决方案效率的监控，以及对来自社区的关于 bug 和常见问题的关注。

是时候去寻找一些做好这些事情的方法了，更好地实现开发者社区的价值。

### DevOps 去“灭火”

>
> “DevOps 是人员、流程和产品的结合，使我们的最终用户能够持续传递价值。” --[Donovan Brown](http://donovanbrown.com/post/what-is-devops)
>
>
>

为解决这些挑战，社区停止了所有对新项目的冲刺，去探索敏捷实践和新产品。致力于使社区重新活跃起来，为找到促进自治、掌控、和目标的方法，正如在 Daniel H. Pink 的书《[Drive](http://www.danpink.com/books/drive/)》中所说的那样，对僵化的流程和产品进行彻底的改革。

>
> “成熟的自组织、自管理和跨职能团队，在自治、掌控和目标上茁壮成长。" --Drive, Daniel H. Pink.
>
>
>

从文化开始 —— 人 —— 第一步是去拥抱 DevOps。社区实现了 [Scrum](http://www.scrumguides.org/scrum-guide.html) 框架，使用 [kanban](https://leankit.com/learn/kanban/what-is-kanban/) 去提升工程化流程，并且通过可视化去提升透明度、意识和最重要的东西 —— 信任。使用自组织团队后，传统的等级制度和指挥系统消失了。自管理促使团队去积极监视和设计它们自己的流程。

在 2010 年 4 月份，社区再次实施了另外的关键一步，切换并提交它们的文化、流程、以及产品到云上。虽然开放的“为社区而社区”的核心 [解决方案](https://aka.ms/vsarsolutions) 仍然是指导和补充，但是在开源解决方案（OSS）上大量增加投资去研究和共享 DevOps 转换的成就。

持续集成（CI）和持续交付（CD）使用自动化流水线代替了死板的人工流程。这使得团队在不受来自项目经理的干预的情况下为早期问题和早期应用者部署解决方案。增加遥测技术可以使团队关注他们的解决方案，以及在用户注意到它们之前，检测和处理未知的问题。

DevOps 转变是一个持续进化的过程，通过实验去探索和验证人、流程和产品的改革。最新的试验引入了流水线革新，它可以持续提升价值流。自动扫描组件、持续地以及静默地检查安全、协议和开源组件的品质。部署环和特性标志允许团队对所有或者特定用户进行更细粒度的控制。

在 2017 年 10 月，社区将大部分的私有版本控制仓库转移到 [GitHub](https://github.com/ALM-Rangers) 上。将所有仓库转移所有者和管理职责到 ALM DevOps Rangers 社区，给团队提供自治和机会，去激励更多的社区对开源解决方案作贡献。团队被授权向他们的最终用户交付质量和价值。

### 好处和成就

拥抱 DevOps 使 Ranger 社区变得更加敏捷，实现了对市场的快速反应和快速学习和反应的流程，减少了宝贵的时间投入，并宣布自治。

下面是从这个转变中观察到的一个列表，排列没有特定的顺序：

* 自治、掌控和目标是核心。
* 从可触摸的和可迭代的东西开始 —— 避免摊子铺的过大。
* 可触摸的和可操作的指标很重要 —— 确保不要掺杂其它东西。
* 人（文化）的转变是最具挑战的部分。
* 没有蓝图；任何一个组织和任何一个团队都是独一无二的。
* 转变是一个持续的过程。
* 透明和可视非常关键。
* 使用工程化流程去强化预期行为。

转换变化表：

|  | 过去 | 当前 | 想象 |
| --- | --- | --- | --- |
| 分支 | 服务于发布隔离 | 特性 | 主分支 |
| 构建 | 手动且易错 | 自动而一致 |  |
| 问题检测 | 来自用户 | 主动感知 |  |
| 问题解决 | 几天到几周 | 几分钟到几天 | 几分钟 |
| 计划 | 详细的设计 | 原型与故事板 |  |
| 流程管理 | 2 个流程经理（PM） | 0.25 个 PM | 0.125 个 PM |
| 发布终止 | 6 到 12 个月 | 3 到 5 周期 | 每个周期 |
| 发布 | 手动且易错 | 自动而一致 |  |
| 周期 | 1 个月 | 3 周 |  |
| 团队规模 | 10 到 15 | 2 到 5 |  |
| 构建时间 | 几小时 | 几秒钟 |  |
| 发布时间 | 几天 | 几分钟 |  |

但是，我们还没有做完，相反，我们就是一个令人兴奋的、持续不断的、几乎从不结束的转变的一部分。

如果你想去学习更多的关于我们的转变、有益的经验、以及想知道我们所经历的挑战，请查看 [转变到 DevOps 文化的记录](https://github.com/ALM-Rangers/Guidance/blob/master/src/Stories/our-journey-of-transforming-to-a-devops-culture.md)。"

---

via: <https://opensource.com/article/17/11/devops-rangers-transformation>

作者：[Willy Schaub](https://opensource.com/users/wpschaub) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在 Linux 中找到你的 IP 地址

Summary: 每个网站都有一个独有的公开 IP 地址，可供任何人从任何地方访问。

每个网站都有一个独有的公开 IP 地址，可供任何人从任何地方访问。

# 如何在 Linux 中找到你的 IP 地址

>
> 每个网站都有一个独有的公开 IP 地址，可供任何人从任何地方访问。
>
>
>

<ruby> 互联网协议 <rt>  Internet Protocol </rt></ruby>（IP）不需要介绍 —— 我们每天都在使用它。即使你不直接使用它，当你在浏览器上输入 website-name.com 时，它会查找该 URL 的 IP 地址，然后加载该网站。

我们将 IP 地址分为两类：私有和公共。私有 IP 地址是你的无线路由（和公司内网）提供的私有 IP 地址。它们的范围是 10.xxx、172.16.xx-172.31.xx 和 192.168.xx，其中 x=0 到 255。公有 IP 地址，顾名思义，是“公共”的，你可以在世界上任何地方访问它。每个网站都有一个唯一的 IP 地址，任何人可在任何地点访问，这可被视为公共 IP 地址。

此外，还有两种类型的 IP 地址：IPv4 和 IPv6。

IPv4 地址格式为 x.x.x.x，其中 x=0 到 255。有 2 32（大约 40 亿个）可能的 IPv4 地址。

IPv6 地址使用更复杂的十六进制。总的比特数是 128，这意味着有 2 128 （340 后面有 36 个零！）个可能的 IPv6 地址。IPv6 已经被引入解决了可预见的 IPv4 地址耗尽问题。

作为网络工程师，我建议不要与任何人共享你机器的公有 IP 地址。你的 WiFi 路由器有公共 IP，即 WAN（广域网）IP 地址，并且连接到该 WiFi 的任何设备都是相同的。连接到相同 WiFi 的所有设备都有上面所说的私有 IP 地址。例如，我的笔记本电脑的 IP 地址 192.168.0.5，而我的电话是 192.168.0.8。这些是私有 IP 地址，但两者都有相同的公有 IP 地址。

以下命令将列出IP地址列表，以查找你计算机的公有 IP 地址：

1. `ifconfig.me`
2. `curl -4/-6 icanhazip.com`
3. `curl ipinfo.io/ip`
4. `curl api.ipify.org`
5. `curl checkip.dyndns.org`
6. `dig +short myip.opendns.com @resolver1.opendns.com`
7. `host myip.opendns.com resolver1.opendns.com`
8. `curl ident.me`
9. `curl bot.whatismyipaddress.com`
10. `curl ipecho.net/plain`

以下命令将为你提供接口的私有 IP 地址：

1. `ifconfig -a`
2. `ip addr (ip a)`
3. `hostname -I | awk ‘{print $1}’`
4. `ip route get 1.2.3.4 | awk '{print $7}'`
5. `(Fedora) Wifi-Settings→ click the setting icon next to the Wifi name that you are connected to → Ipv4 and Ipv6 both can be seen`
6. `nmcli -p device show`

*注意：一些工具需要根据你正在使用的 Linux 发行版安装在你的系统上。另外，一些提到的命令使用第三方网站来获取 IP*

---

via: <https://opensource.com/article/18/5/how-find-ip-address-linux>

作者：[Archit Modi](https://opensource.com/users/architmodi) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


zzupdate：单条命令升级 Ubuntu 18.04 LTS

Summary: 我们可以只通过一条命令使用 zzupdate 工具中将 Ubuntu PC/Server 从一个版本升级到另一个版本。

# zzupdate：单条命令升级 Ubuntu 18.04 LTS

Ubuntu 18.04 版本已经发布，并得到各个社区的一致好评，因为 Ubuntu 18.04 可能是 Ubuntu 多年来最令人兴奋的版本。

通常情况下，Ubuntu 及其衍生版可以使用命令从一个版本升级到最新版本或者其它版本，这也是官方推荐的升级方式。

### Ubuntu 18.04 特性/亮点

这次更新包含大量改进和新功能，这里只列举的几个主要的。如果您想要更详细的更新信息，请访问 [Ubuntu 18.04 官方](https://wiki.ubuntu.com/BionicBeaver/ReleaseNotes) 页面。

* 使用 Linux 4.15 内核，提供了从上游继承的新功能
* 它具有最新版本的 GNOME 3.28
* 它提供了与 RHEL 相似的最简安装选项，该选项可安装只包含一个 web 浏览器和核心系统程序的基本桌面环境
* 对于新安装，交换文件将取代默认的交换分区
* 您可以启用 Livepatch 安装内核更新而无需重新启动
* 笔记本电脑在使用电池供电时会在无操作 20 分钟后自动待机
* 不再提供 32 位的 Ubuntu 桌面安装程序映像

注意：

1. 不要忘记备份重要数据。如果升级出现问题，我们将重新安装并恢复数据。
2. 安装所需时间取决于您的网络状况和安装的程序。

<img src="content_image/album_201805_27_071719gd2db2ddoddd2d2o.jpg" alt="" title="">

### zzupdate 是什么？

我们可以只通过一条命令使用 [zzupdate](https://github.com/TurboLabIt/zzupdate) 工具中将 Ubuntu PC/Server 从一个版本升级到另一个版本。它是一个自由开源工具，使用它不需要任何脚本知识，因为它只需要配置文件即可运行。

工具中提供两个默认 shell 文件。`setup.sh` 自动安装、更新代码，将脚本转换为一个简单的 zzupdate shell 命令。`zzupdate.sh` 将执行版本间的升级。

### 如何安装 zzupdate？

要安装 `zzupdate`，只需执行以下命令：

```
$ curl -s https://raw.githubusercontent.com/TurboLabIt/zzupdate/master/setup.sh | sudo sh
.
.
Installing...
-------------
Cloning into 'zzupdate'...
remote: Counting objects: 57, done.
remote: Total 57 (delta 0), reused 0 (delta 0), pack-reused 57
Unpacking objects: 100% (57/57), done.
Checking connectivity... done.
Already up-to-date.

Setup completed!
----------------
See https://github.com/TurboLabIt/zzupdate for the quickstart guide.

```

将 Ubuntu 系统从一个版本升级到另一个版本，您不需要输入很多命令，也不需要重新启动，只需要运行下面的 `zzupdate` 命令并坐下喝杯咖啡就可以了。

请注意，当您远程升级系统时，建议您使用以下的工具来帮助您在任何断开连接时重新连接会话。

建议阅读： [如何让一个进程/命令在 SSH 连接断开后继续运行](https://www.2daygeek.com/how-to-keep-a-process-command-running-after-disconnecting-ssh-session/)

### 如何配置 zzupdate（可选）

默认情况下，`zzupdate` 可以开箱即用，不需要配置任何东西。当然，如果您想要自己配置一些内容也是可以的。复制提供的示例配置文件 `zzupdate.default.conf` 到 `zzupdate.conf`，并在 `zzupdate.conf` 中配置您的首选项。

```
$ sudo cp /usr/local/turbolab.it/zzupdate/zzupdate.default.conf /etc/turbolab.it/zzupdate.conf

```

打开文件，默认配置如下。

```
$ sudo nano /etc/turbolab.it/zzupdate.conf

REBOOT=1
REBOOT_TIMEOUT=15
VERSION_UPGRADE=1
VERSION_UPGRADE_SILENT=0
COMPOSER_UPGRADE=1
SWITCH_PROMPT_TO_NORMAL=0

```

* `REBOOT=1`：系统在更新完成后自动重启
* `REBOOT_TIMEOUT=15`：重启的默认超时值
* `VERSION_UPGRADE=1`：执行从一个版本到另一个版本的版本升级
* `VERSION_UPGRADE_SILENT=0`：禁用自动升级
* `COMPOSER_UPGRADE=1`：自动升级
* `SWITCH_PROMPT_TO_NORMAL=0`：如果值为 `0`，将寻找相同种类的版本升级。例如您正在运行 LTS 的版本，那么将寻找 LTS 的版本升级，而不是用于正常版本升级。如果值为 `1`，那么无论您是运行 LTS 还是正常版本，都会查找最新版本

我现在正在使用 Ubuntu 17.10 ，查看一下详细信息。

```
$ cat /etc/*-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=17.10
DISTRIB_CODENAME=artful
DISTRIB_DESCRIPTION="Ubuntu 17.10"
NAME="Ubuntu"
VERSION="17.10 (Artful Aardvark)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 17.10"
VERSION_ID="17.10"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=artful
UBUNTU_CODENAME=artful

```

要升级 Ubuntu 到最新版本，只需要执行以下命令：

```
$ sudo zzupdate

O===========================================================O
 zzupdate - Wed May 2 17:31:16 IST 2018
O===========================================================O

Self-update and update of other zzScript
----------------------------------------
.
.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Updating...
----------
Already up-to-date.

Setup completed!
----------------
See https://github.com/TurboLabIt/zzupdate for the quickstart guide.

Channel switching is disabled: using pre-existing setting
---------------------------------------------------------

Cleanup local cache
-------------------

Update available packages informations
--------------------------------------
Hit:1 https://download.docker.com/linux/ubuntu artful InRelease
Ign:2 http://dl.google.com/linux/chrome/deb stable InRelease
Hit:3 http://security.ubuntu.com/ubuntu artful-security InRelease
Hit:4 http://in.archive.ubuntu.com/ubuntu artful InRelease
Hit:5 http://dl.google.com/linux/chrome/deb stable Release
Hit:6 http://in.archive.ubuntu.com/ubuntu artful-updates InRelease
Hit:7 http://in.archive.ubuntu.com/ubuntu artful-backports InRelease
Hit:9 http://ppa.launchpad.net/notepadqq-team/notepadqq/ubuntu artful InRelease
Hit:10 http://ppa.launchpad.net/papirus/papirus/ubuntu artful InRelease
Hit:11 http://ppa.launchpad.net/twodopeshaggy/jarun/ubuntu artful InRelease
.
.
UPGRADE PACKAGES
----------------
Reading package lists...
Building dependency tree...
Reading state information...
Calculating upgrade...
The following packages were automatically installed and are no longer required:
.
.
Interactively upgrade to a new release, if any
----------------------------------------------

Reading cache

Checking package manager
Reading package lists... Done
Building dependency tree
Reading state information... Done
Ign http://dl.google.com/linux/chrome/deb stable InRelease
Hit https://download.docker.com/linux/ubuntu artful InRelease
Hit http://security.ubuntu.com/ubuntu artful-security InRelease
Hit http://dl.google.com/linux/chrome/deb stable Release
Hit http://in.archive.ubuntu.com/ubuntu artful InRelease
Hit http://in.archive.ubuntu.com/ubuntu artful-updates InRelease
Hit http://in.archive.ubuntu.com/ubuntu artful-backports InRelease
Hit http://ppa.launchpad.net/notepadqq-team/notepadqq/ubuntu artful InRelease
Hit http://ppa.launchpad.net/papirus/papirus/ubuntu artful InRelease
Hit http://ppa.launchpad.net/twodopeshaggy/jarun/ubuntu artful InRelease
Fetched 0 B in 6s (0 B/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done

```

我们需要按下回车键禁用第三方仓库以继续升级。

```
Updating repository information

Third party sources disabled

Some third party entries in your sources.list were disabled. You can
re-enable them after the upgrade with the 'software-properties' tool
or your package manager.

To continue please press [ENTER]
.
.
Get:35 http://in.archive.ubuntu.com/ubuntu bionic-updates/universe i386 Packages [2,180 B]
Get:36 http://in.archive.ubuntu.com/ubuntu bionic-updates/universe Translation-en [1,644 B]
Fetched 38.2 MB in 6s (1,276 kB/s)

Checking package manager
Reading package lists... Done
Building dependency tree
Reading state information... Done

Calculating the changes

Calculating the changes

```

开始下载 `Ubuntu 18.04 LTS` 软件包，所需时间取决于您的网络状况，一般情况下这将需要几分钟。

```
Do you want to start the upgrade?

63 installed packages are no longer supported by Canonical. You can
still get support from the community.

4 packages are going to be removed. 175 new packages are going to be
installed. 1307 packages are going to be upgraded.

You have to download a total of 999 M. This download will take about
12 minutes with your connection.

Installing the upgrade can take several hours. Once the download has
finished, the process cannot be canceled.

Continue [yN] Details [d]y
Fetching
Get:1 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 base-files amd64 10.1ubuntu2 [58.2 kB]
Get:2 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 debianutils amd64 4.8.4 [85.7 kB]
Get:3 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 bash amd64 4.4.18-2ubuntu1 [614 kB]
Get:4 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 locales all 2.27-3ubuntu1 [3,612 kB]
.
.
Get:1477 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 liblouisutdml-bin amd64 2.7.0-1 [9,588 B]
Get:1478 http://in.archive.ubuntu.com/ubuntu bionic/universe amd64 libtbb2 amd64 2017~U7-8 [110 kB]
Get:1479 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 libyajl2 amd64 2.1.0-2build1 [20.0 kB]
Get:1480 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 usb-modeswitch amd64 2.5.2+repack0-2ubuntu1 [53.6 kB]
Get:1481 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 usb-modeswitch-data all 20170806-2 [30.7 kB]
Get:1482 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 xbrlapi amd64 5.5-4ubuntu2 [61.8 kB]
Fetched 999 MB in 6s (721 kB/s)

```

安装新软件包时，很少有服务需要重新启动。 点击 `Yes` 按钮，它会自动重启所需的服务。

```
Upgrading
Inhibiting until Ctrl+C is pressed...
Preconfiguring packages ...
Preconfiguring packages ...
Preconfiguring packages ...
Preconfiguring packages ...
(Reading database ... 441375 files and directories currently installed.)
Preparing to unpack .../base-files_10.1ubuntu2_amd64.deb ...
Warning: Stopping motd-news.service, but it can still be activated by:
 motd-news.timer
Unpacking base-files (10.1ubuntu2) over (9.6ubuntu102) ...
Setting up base-files (10.1ubuntu2) ...
Installing new version of config file /etc/debian_version ...
Installing new version of config file /etc/issue ...
Installing new version of config file /etc/issue.net ...
Installing new version of config file /etc/lsb-release ...
motd-news.service is a disabled or a static unit, not starting it.
(Reading database ... 441376 files and directories currently installed.)
.
.
Progress: [ 80%]

Progress: [ 85%]

Progress: [ 90%]

Progress: [ 95%]

```

现在删除旧版的、系统不再需要的包。点击 `y` 以删除。

```
Searching for obsolete software
 ing package lists... 97%
 ding package lists... 98%
Reading package lists... Done
Building dependency tree
Reading state information... Done
Reading state information... 23%
Reading state information... 47%
Reading state information... 71%
Reading state information... 94%
Reading state information... Done

Remove obsolete packages?

88 packages are going to be removed.

Continue [yN] Details [d]y
.
.
.
done
Removing perlmagick (8:6.9.7.4+dfsg-16ubuntu6) ...
Removing snapd-login-service (1.23-0ubuntu1) ...
Processing triggers for libc-bin (2.27-3ubuntu1) ...
Processing triggers for man-db (2.8.3-2) ...
Processing triggers for dbus (1.12.2-1ubuntu1) ...
Fetched 0 B in 0s (0 B/s)

```

升级成功，需要重启系统。点击 `y` 以重启系统。

```
System upgrade is complete.

Restart required

To finish the upgrade, a restart is required.
If you select 'y' the system will be restarted.

Continue [yN]y

```

注意： 少数情况下，会要求您确认配置文件替换以继续安装。

查看升级后的系统详情：

```
$ cat /etc/*-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04 LTS"
NAME="Ubuntu"
VERSION="18.04 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

```

---

via: <https://www.2daygeek.com/zzupdate-single-command-to-upgrade-ubuntu-18-04/>

作者：[PRAKASH SUBRAMANIAN](https://www.2daygeek.com/author/prakash/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[XiatianSummer](https://github.com/XiatianSummer) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


为什么 Oil States 案对于开源界是个好消息

# 为什么 Oil States 案对于开源界是个好消息

>
> 提要：对于面临滥用专利制度的实体发起诉讼威胁的技术公司和创新者来说，此案是一个重大胜利。
>
>
>

对于面临滥用专利制度的实体发起诉讼威胁的技术公司和创新者来说，日前美国最高法院对 [Oil States 诉 Greene’s Energy 案](https://www.supremecourt.gov/opinions/17pdf/16-712_87ad.pdf)做出的[裁决是一个重大胜利](http://www.scotusblog.com/wp-content/uploads/2017/11/16-712-bsac-Dell.pdf)。此类滥用专利制度的实体试图从创新者和创造就业机会的企业身上攫取价值。

在 Oil States 案中，美国最高法院对美国专利商标局（USPTO）一项旨在质疑此前授权但存在问题的专利是否有效的行政程序是否违宪做出了裁决。<ruby> 专利流氓 <rp>  （ </rp> <rt>  patent trolls </rt> <rp>  ） </rp></ruby>最喜欢利用此类专利发起诉讼。这一称为<ruby> “双方复审” <rp>  （ </rp> <rt>  inter partes review </rt> <rp>  ） </rp></ruby>的程序已经成为一种应对基于可能无效专利而轻率发起的侵权主张的重要工具，其成本远低于在联邦法院检验此类专利有效性的成本。

美国国会根据<ruby> 《2011年美国发明法案》 <rp>  （ </rp> <rt>  America Invents Act of 2011 </rt> <rp>  ） </rp></ruby>创建了这种双方复审程序，以清理美国国会所认为的大量被不当授权的专利。此类专利被专利流氓用于向创新者谋取费用。自 2012 年实施该程序以来，已经有 7000 多份申请被提交，主要是为了审查计算机和高科技领域的可疑专利，并且有超过 1300 项权利主张被裁定无效。

在 7 比 2 的多数意见中，托马斯法官表示：“授予专利的决定是涉及公共权利的事项。双方复审只是对该授权过程的再审，美国国会已经授权美国专利商标局实施此种再审。”法官认为双方复审程序并没有侵犯专利权人所拥有的案件需由联邦法院裁决（在联邦法院无效专利的成本更高，诉讼费用明显更为昂贵）的宪法权利。

与开发、销售或实施复杂技术的任何实体一样，开发或实施开源软件的成功企业是专利流氓诉讼威胁的颇有吸引力的目标。因此，确认双方复审程序的合宪性不仅提高了专利系统的质量，而且也保留了一种工具来保护开源技术免受那些滥用专利系统的实体的攻击。

美国专利商标局打算评估双方复审程序的实施情况，这可能会影响其作为防御工具的有效性。但科技行业人士应继续让政策制定者保留《2011 年美国发明法案》所带来的有益变革，以保护创新并阻止滥用专利的行为。

---

作者简介：Matt Krupnick 是<ruby> 红帽公司 <rp>  （ </rp> <rt>  Red Hat </rt> <rp>  ） </rp></ruby>公共政策总监，于 2017 年加入红帽公司，负责政策制定以及为一系列对红帽公司及开源技术有潜在影响的各种政策问题提供建议。

译者简介：薛亮，集慧智佳知识产权咨询公司高级咨询师，擅长专利检索、专利分析、竞争对手跟踪、FTO 分析、开源软件知识产权风险分析，致力于为互联网企业、高科技公司提供知识产权咨询服务。


# 在 Ubuntu Snap 应用商店上发现的加密货币 ‘恶意软件’ 是怎么回事？

最近，有发现称一些 Ubuntu Snap 应用商店上的应用包含加密货币挖矿程序。Canonical 公司迅速下架了这些违规的应用，但是留下了几个有待回答的问题。

### 在 Snap 应用商店上发现了加密矿工

5 月 11 号，一位名叫 [tarwirdur](https://github.com/tarwirdur) 的用户在 [snapcraft.io repository](https://github.com/canonical-websites/snapcraft.io/issues/651) 开了一个新的工单 ，他提到一个由 Nicolas Tomb 开发，叫做 2048buntu 的 snap 应用包含加密货币矿工。tarwirdur 询问，他怎样才能出于安全的原因而“投诉该应用” 。tarwirdur 后来发表说其它由 Nicolas Tomb 开发的 snap 应用也都包含加密货币矿工。

看起来该 snap 应用使用了 systemd 在系统启动时自动地运行代码，并在用户不知情的情况下在后台运行。

>
> 对那些不熟悉相关术语的人来说，<ruby> 加密货币 <rt>  cryptocurrency </rt></ruby><ruby> 矿工 <rt>  miner </rt></ruby>是一段使用计算机的主处理器或者图形处理器来“挖掘”数字货币的程序。“挖矿”通常涉及到解决一个数学等式。在这种情况下，如果你在运行 2048buntu 游戏，这个游戏将会额外使用处理器的计算能力去进行加密货币的挖掘。
>
>
>

Snapcraft 团队迅速地下架了所有由该违规者开发的应用来做出回应。他们同时也开展了调查。

### 隐匿者发声

5 月 13 号，一位同名为 Nicolas Tomb 的 Disqus 用户在 OMGUbuntu 的新闻报道上发表了[评论](https://disqus.com/home/discussion/omgubuntu/malware_found_on_the_ubuntu_snap_store/#comment-3899153046)，他在评论中称自己向 snap 应用中添加了加密货币矿工，从而获取收益。他为他的行为道歉，同时承诺将所有挖矿所得的收益送给 Ubuntu 基金会。

我们不能确认这个评论就是由 Nicolas Tomb 发表的，因为这个 Disqus 账户最近才被创建，也只有一条评论与之相关联。现在，我们假设他是。

### Canonical 公司发表了声明

5 月 15 号，Canonical 公司在这种情况下发表了一份声明。标题为 [“在 Snap 应用商店中的信任与安全”](https://blog.ubuntu.com/2018/05/15/trust-and-security-in-the-snap-store)，声明开头重申了当下的情况。他们也补充道[重新发布的 snap 应用中已经被删除了加密货币挖矿程序](https://forum.snapcraft.io/t/action-against-snap-store-malware/5417/8)。

Canonical 公司随后尝试调查 Nicolas Tomb 的动机。他们指出，他告诉他们说自己这样做是为了通过应用赚钱（如上所诉），而当面对质疑时就停止了。他们也指出“挖掘加密货币本身并非不合法和不道德的”。然而，他们仍对他没有在 snap 应用描述中披露加密矿工这件事表示了不满意。

随后 Canonical 公司将话题转移到审核软件上。根据这份申明，Snap 应用商店将会采用一种类似 iOS、Android、Windows 的质量控制系统，这个系统将有“自动化的检查点，安装包必须在被接受前通过检查，同时在特殊问题被标记时会进行人工审核”。

然后，Canonical 公司声称“对大规模的软件仓库来说，只接受每个单独文件都被仔细审核过的软件是不可能的”。因此，他们需要信任软件源而不是内容。毕竟，软件源是现在 Ubuntu 软件仓库系统的基础。

Canonical 公司紧接着谈到了 Snap 应用的未来。他们承认现在的系统是不完美的。他们也在不断地进行改善。他们“在开发非常有趣的安全功能，这些功能将会在改善系统安全性同时提升人们在服务器或桌面上进行软件开发的体验”。

其中一个他们正在开发的功能是查看一个软件发布者是否已通过验证。其他的改进包括：“将所有 AppArmor 内核补丁递交到上游”和其它底层修复。

### 一些关于“Snap 应用商店中的恶意软件”的想法

基于我所了解的所有内容，我产生了一些想法和问题。

#### 这种挖矿软件运行多久了？

首先，这些挖矿软件存在于 Snap 应用商店多久了？因为它们已经被下架了，我们没有这样的数据。我可以通过 Google 快照抓取一些 2048buntu 页面的图片，但这没有提供任何可用的信息。根据该软件运行时间，多少系统安装过，挖掘出了什么加密货币，我们能否知道违规者获取的是一点钱还是一笔钱。一个更长远的问题是：Canonical 公司将来有能力捕捉到这样的违规情况吗？

#### 这真的是一个恶意软件吗？

许多新闻网站将之报道为恶意软件感染。我想我甚至可以看到这个事件被称为 Linux 的第一个恶意软件。我不确定这个术语是否精确。Dictionary.com 这样定义 [恶意软件](http://www.dictionary.com/browse/malware?s=t)：“意图损害计算机、移动设备、计算机系统或者计算机网络，或者对其运作进行部分控制的软件”。

这个有问题的 snap 应用并没有损害或者控制涉及到的计算机。它同样没有感染其他计算机。它也不能这样做，因为所有的 snap 应用位于沙盒之中。它们最多利用了处理器的计算能力，就是这样。所以，我不会称之为恶意软件。

#### 无孔不入

Nicolas Tomb 使用的一个辩解是在他上传应用的时候 Snap 应用商店没有任何反对加密货币挖矿的规则。（我敢向你打赌他们正在纠正这个错误。）他们之所以没有这样的规则，原因很简单，之前没有人做过这种事。如果 Tomb 想正确地做事，他应该提前询问是否允许这种行为。而事实是他似乎没有指出他知道 Canonical 公司可能会拒绝的事实。至少，Canonical 公司会告诉他将这些写在软件的描述中。

#### 一看就不对劲

如我之前说的，我从 Google 快照获取了一个 2048buntu 的页面截图。仅仅看它就会感觉到一些危险的信号。首先，截图中几乎没有真实的描述。它是这样描述的“类似 2048 的游戏。这个游戏用 ubuntu 主题克隆了流行的游戏 2048。”哇，这将会引来容易上当受骗的人。当我读到类似空洞的描述时，我会多考虑下。

我注意到的另一件事是软件的大小。2048buntu 的 1.0 版本大小将近 140 MB。一个简单的游戏怎么需要这么多的空间？有用 Javascript 写的浏览器版本大概只用了不到它的四分之一。其他 snap 应用商店的 2048 游戏的大小没有一个达到了这个软件的一半。

然后，它有个许可证。这是一个使用了 Ubuntu 主题的流行游戏的克隆。它怎么能被认为是专有软件？我确信，其他合法的开发者会因为该内容而使用了 FOSS （自由开源软件）许可证来上传它。

单是这些因素就使得这个 snap 应用很特殊，应该进行审核。

#### 谁是 Nicolas Tomb？

当第一次了解到这些之后，我决定看看我能否找出造成这些混乱的人。当我搜索 Nicolas Tomb 的时候，我什么都没找到。所有我找到的只是一大堆关于加密货币挖矿 snap 应用的新闻和文章，以及去 tomb of St. Nicolas 旅游的信息。在 Twiter 和 Github 上都没有 Nicolas Tomb 的标志。看起来似乎是为了上传这些 snap 应用才创建的名称。

这同样引出了 Canonical 公司发表的申明中的一点，关于验证发布者。上一次我查看的时候，相当多的 snap 应用不是由应用的维护者发布的。这让我感到担忧。我更乐意相信由 Mozilla 基金会发布的 firefox 的 snap 应用，而不是 Leonard Borsch。如果对应用维护者来说关注应用的 snap 版本太耗费精力，应该有办法让维护者在他们软件的 snap 版本上贴上批准的标签。就像是 Firefox 的 snap 版本由 Fredrick 发布，经 Mozilla 基金会批准。这样才能让用户对下载的内容更放心。

#### 无疑 Snap 应用商店还有改善的空间

在我看来，Snap 应用商店团队应该实现的第一个特性是报告可疑应用的方式。tarwirdur 必须找到该网站的 Github 页面才行。而大多数用户不会想到这一点。如果 Snap 应用商店不能审核每一行代码，那么使用户能够报告问题是退而求其次的办法。即使评分系统也是一个不差的补充。我确信一定有部分人因为 2048buntu 使用了太多系统资源而给它很低的评分。

### 结论

从我所知道的情况来说，我认为这是某个人创建了一些简单的应用，在每个应用中嵌入了加密货币矿工，之后将这些应用上传到 Snap 应用商店，想着捞一笔钱。一旦他们被抓了，他们就声称这仅仅为了通过应用程序获利。如果真的是这样，他们应该在 snap 应用的描述中提到才对。隐藏加密矿工并不是什么[新鲜事](https://krebsonsecurity.com/2018/03/who-and-what-is-coinhive/)。他们通常是一种盗取计算能力的方法。

我希望 Canonical 公司已经具备了解决这个问题的功能，盼望这些功能能很快出来。

你对 Snap 应用商店的“恶意软件风波”有什么看法？你将如何改善这种情况？请在下面的评论中告诉我们。

如果你觉得这篇文章有趣，请花费一点时间将它分享到社交媒体上。

---

via: <https://itsfoss.com/snapstore-cryptocurrency-saga/>

作者：[John Paul](https://itsfoss.com/author/john/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[paperzhang](https://github.com/paperzhang) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Cron 任务入门指南

# Cron 任务入门指南

**Cron** 是您可以在任何类 Unix 操作系统中找到的最有用的实用程序之一。它用于安排命令在特定时间执行。这些预定的命令或任务被称为 “Cron 任务”。Cron 通常用于运行计划备份、监视磁盘空间、定期删除不再需要的文件（例如日志文件）、运行系统维护任务等等。在本简要指南中，我们将看到 Linux 中 Cron 任务的基本用法。

### Cron 任务入门指南

cron 任务的典型格式是：

```
分钟(0-59) 小时(0-24) 日(1-31) 月(1-12) 星期(0-6) 要执行的命令

```

只需记住 cron 任务的格式或打印下面的插图并将其放在你桌面上即可。

在上图中，星号表示特定的时间块。

要显示当前登录用户的 crontab 文件的内容：

```
$ crontab -l

```

要编辑当前用户的 cron 任务，请执行以下操作：

```
$ crontab -e

```

如果这是第一次编辑此文件，会询问你使用哪个编辑器来编辑此文件。

```
no crontab for sk - using an empty one

Select an editor. To change later, run 'select-editor'.
1. /bin/nano <---- easiest
2. /usr/bin/vim.basic
3. /usr/bin/vim.tiny
4. /bin/ed

Choose 1-4 [1]:

```

选择适合你的编辑器。这里是一个示例 crontab 文件的样子。

在这个文件中，你需要添加你的 cron 任务。

要编辑其他用户的 crontab，例如 ostechnix，请执行：

```
$ crontab -u ostechnix -e

```

让我们看看一些例子。

要 **每分钟** 执行一次 cron 任务，需使用如下格式。

```
* * * * * <command-to-execute>

```

要每 5 分钟运行一次 cron 任务，请在 crontab 文件中添加以下内容。

```
*/5 * * * * <command-to-execute>

```

要在每 1/4 个小时（每 15 分钟）运行一次 cron 任务，请添加以下内容：

```
*/15 * * * * <command-to-execute>

```

要每小时的第 30 分钟运行一次 cron 任务，请运行：

```
30 * * * * <command-to-execute>

```

您还可以使用逗号定义多个时间间隔。例如，以下 cron 任务每小时运行三次，分别在第 0、 5 和 10 分钟运行：

```
0,5,10 * * * * <command-to-execute>

```

每半小时运行一次 cron 任务：

```
*/30 * * * * <command-to-execute>

```

每小时运行一次：

```
0 * * * * <command-to-execute>

```

每 2 小时运行一次：

```
0 */2 * * * <command-to-execute>

```

每天运行一项（在 00:00 运行）：

```
0 0 * * * <command-to-execute>

```

每天凌晨 3 点运行：

```
0 3 * * * <command-to-execute>

```

每周日运行：

```
0 0 * * SUN <command-to-execute>

```

或使用，

```
0 0 * * 0 <command-to-execute>

```

它将在每周日的午夜 00:00 运行。

星期一至星期五每天运行一次，亦即每个工作日运行一次：

```
0 0 * * 1-5 <command-to-execute>

```

这项工作将于 00:00 开始。

每个月运行一次：

```
0 0 1 * * <command-to-execute>

```

于每月第 1 天的 16:15 运行：

```
15 16 1 * * <command-to-execute>

```

每季度运行一次，亦即每隔 3 个月的第 1 天运行：

```
0 0 1 */3 * <command-to-execute>

```

在特定月份的特定时间运行：

```
5 0 * 4 * <command-to-execute>

```

每个四月的 00:05 运行。

每 6 个月运行：

```
0 0 1 */6 * <command-to-execute>

```

这个定时任务将在每六个月的第一天的 00:00 运行。

每年运行：

```
0 0 1 1 * <command-to-execute>

```

这项 cron 任务将于 1 月份的第一天的 00:00 运行。

我们也可以使用以下字符串来定义任务。

`@reboot` 在每次启动时运行一次。 `@yearly` 每年运行一次。 `@annually`（和 `@yearly` 一样）。 `@monthly` 每月运行一次。 `@weekly` 每周运行一次。 `@daily` 每天运行一次。 `@midnight` （和 `@daily` 一样）。 `@hourly` 每小时运行一次。

例如，要在每次重新启动服务器时运行任务，请将此行添加到您的 crontab 文件中。

```
@reboot <command-to-execute>

```

要删除当前用户的所有 cron 任务：

```
$ crontab -r

```

还有一个名为 [crontab.guru](https://crontab.guru/) 的专业网站，用于学习 cron 任务示例。这个网站提供了很多 cron 任务的例子。

有关更多详细信息，请查看手册页。

```
$ man crontab

```

那么，就是这样。到此为止，您应该对 cron 任务以及如何实时使用它们有了一个基本的了解。后续还会介绍更多的优秀工具。敬请关注！！

干杯!

---

via: <https://www.ostechnix.com/a-beginners-guide-to-cron-jobs/>

作者：[SK](https://www.ostechnix.com/author/sk/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[KevinSJ](https://github.com/KevinSJ) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在 X 中启动用户软件

# 如何在 X 中启动用户软件

<img src="content_image/album_201805_28_215431k8cpcr4cpc4ckb9k.jpg" alt="" title="">

目前有很多方法可以在开启用户会话时启动软件。

这是一篇试图将所有方法聚集在一起的文章。某些部分可能不精确或不正确，但这是一个开始，如果我收到更正，我很乐意保持更新。

### x11-common

```
man xsession

```

* 由显示管理器启动，如 `/usr/share/lightdm/lightdm.conf.d/01_debian.conf` 或 `/etc/gdm3/Xsession`。
* Debian 特定。
* 在 `/etc/X11/Xsession.d/` 中运行脚本。
* `/etc/X11/Xsession.d/40x11-common_xsessionrc` 引用 `~/.xsessionrc` ，它能比设置环境变量能做的多一点，因为它在 X 会话启动时运行。
* 最后，它启动会话管理器（`gnome-session`、`xfce4-session` 等等）。

### systemd --user

* <https://wiki.archlinux.org/index.php/Systemd/User>
* 由 `pam_systemd` 启动，所以在环境变量中可能没有设置 `DISPLAY` 变量。
* 管理单元：
	+ `/usr/lib/systemd/user/` 由已安装的软件包提供的单元。
	+ `~/.local/share/systemd/user/` 由安装在家目录的软件包提供的单元。
	+ `/etc/systemd/user/` 由系统管理员提供的系统范围的用户的单元。
	+ `~/.config/systemd/user/` ，用户自己放置的单元。
* 当设置 X 会话和 `DISPLAY` 变量时，启动 systemd 用户单元的技巧是从 `.desktop` 自启动文件调用 `systemctl start`。

### dbus 激活

* <https://dbus.freedesktop.org/doc/system-activation.txt>
* 进行 dbus 请求的用户进程可以触发启动服务器程序。
* 对于系统调试，有没有一种方法可以监控哪些服务正在启动 dbus ？

### X 会话管理器

* <https://en.wikipedia.org/wiki/X_session_manager>
* 由 `x11-common` 的 `Xsession.d` 运行。
* 运行 freedesktop 自动启动的 `.desktop` 文件。
* 运行桌面环境特定的软件。

### xdg 自动启动

* <https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html>
* 由会话管理器运行。
* 如果存在 `/etc/xdg/autostart/foo.desktop` 和 `~/.config/autostart/foo.desktop` ，那么只会使用 `~/.config/autostart/foo.desktop`，因为 `~/.config/autostart/` 比 `/etc/xdg/autostart/` 更重要。
* 是顺序的还是并行？

### 其他启动注意事项

#### ~/.Xauthority

要连接到 X 服务器，客户端需要从 `~/.Xauthority` 发送一个令牌，这证明他们可以读取用户的隐私数据。

`~/.Xauthority` 包含显示管理器生成的一个令牌，并在启动时传递给 X。

要查看它的内容，请使用 `xauth -i -f ~/.Xauthority list`。

---

via: <http://www.enricozini.org/blog/2018/debian/starting-user-software/>

作者：[Enrico Zini](http://www.enricozini.org/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在终端中显示图片

Summary: Linux 上有许多 GUI 图片浏览器。但我尚未听说或使用过任何在终端中显示图片的应用程序。幸运的是，我刚刚发现了一个可用于在终端中显示图像的名叫 FIM 的图像查看器。

# 如何在终端中显示图片

Linux 上有许多 GUI 图片浏览器。但我尚未听说或使用过任何在终端中显示图片的应用程序。幸运的是，我刚刚发现了一个可用于在终端中显示图像的名叫 **FIM** 的图像查看器。FIM 引起了我的注意，是因为与大多数 GUI 图片浏览器相比，它非常轻巧。毫不迟疑，让我们来看看它能做什么。

### 使用 FIM 在终端中显示图像

**FIM** 意即 **F**bi **IM**proved。对于那些不知道的人，**Fbi** 是指 Linux 中的 **f**rame **b**uffer **i**mageviewer。它使用系统的帧缓冲器直接从命令行显示图像。默认情况下，它能用终端显示 bmp、gif、jpeg、PhotoCD、png、ppm、tiff 和 xwd。对于其他格式，它会尝试使用 ImageMagick 的转换。

FIM 基于 Fbi，它是一款高度可高度定制及脚本化的图像浏览器，非常适合使用 Vim 文本编辑器或 Mutt 等软件的用户。它会以全屏显示图像，并可通过键盘快捷键控制（如调整大小、翻转、缩放）。与 Fbi 不同的是，FIM 是通用的：它可以打开许多文件格式，并且可以在以下视频模式下显示图片：

* 使用 Linux framebuffer 设备，以图形方式呈现
* 在 X / Xorg 下，使用 SDL 库，以图形方式呈现
* 在 X / Xorg 下，使用 Imlib2 库，以图形方式呈现。
* 在任何文本控制台中使用 AAlib 库呈现为 ASCII 字符画

FIM 是完全免费且开源的。

### 安装 FIM

基于 DEB 的系统，如 Ubuntu、Linux Mint， 可从默认的仓库中获取 FIM 图像查看器。因此，你可以使用如下命令安装fbi：

```
$ sudo apt-get install fim

```

如果它在你使用的 Linux 发行版的仓库中不包含 FIM，则可以下载源代码进行编译和安装，如下所示。

```
wget http://download.savannah.nongnu.org/releases/fbi-improved/fim-0.6-trunk.tar.gz
wget http://download.savannah.nongnu.org/releases/fbi-improved/fim-0.6-trunk.tar.gz.sig
gpg --search 'dezperado autistici org'
＃按照屏幕上的说明，从密钥服务器导入密钥
gpg --verify fim-0.6-trunk.tar.gz.sig

```

```
tar xzf fim-0.6-trunk.tar.gz
cd fim-0.6-trunk
./configure --help=short
＃阅读./configure --help=short 的输出：你可以在 ./configure 中添加选项
./configure
make
su -c“make install”

```

### FIM用法

安装完成后，您可以使用以下命令以“自动缩放”显示的图像：

```
$ fim -a dog.jpg

```

这里是我的 Ubuntu 主机的示例输出。

正如你在上面的屏幕截图中看到的，FIM 没有使用任何外部 GUI 图片浏览器。相反，它使用我们系统的帧缓冲器来显示图像。

如果当前目录中有多个 .jpg 文件，可以使用通配符打开所有文件，如下所示。

```
$ fim -a * .jpg

```

要打开目录中的所有图像，例如 `Pictures`，请运行：

```
$ fim Pictures/

```

我们也可以在文件夹及其子文件夹中递归地打开图像，然后像下面那样对列表进行排序。

```
$ fim -R Pictures/ --sort

```

要以 ASCII 格式渲染图像，可以使用 `-t` 标志。

```
$ fim -t dog.jpg

```

要退出 Fim，请按 `ESC` 或 `q`。

#### 键盘快捷键

您可以使用各种键盘快捷键来管理图像。例如，要加载下一张图像和之前的图像，请按下 `PgUp` / `PgDown` 键。成倍放大或缩小，请使用 `+` / `-` 键。以下是用于在FIM中控制图像的常用按键。

* `PageUp` / `Down`：上一张/下一张图片
* `+` / `-` ：放大/缩小
* `a`：自动缩放
* `w`：自适应宽度
* `h`：自适应高度
* `j` / `k`：平移/向上
* `f` / `m`：翻转/镜像
* `r` / `R`：旋转（顺时针/逆时针）
* `ESC` / `q`：退出

有关完整详细信息，请参阅手册页。

```
$ man fim

```

那么，就是这样。希望这对你有所帮助。后续还会介绍更多的优秀工具。敬请关注！

干杯!

---

via: <https://www.ostechnix.com/how-to-display-images-in-the-terminal/>

作者：[SK](https://www.ostechnix.com/author/sk/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[KevinSJ](https://github.com/KevinSJ) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


DevOps 接下来会发生什么：要关注的 5 个趋势

Summary: 专家们权衡了 DevOps 团队最近会有何期望。

专家们权衡了 DevOps 团队最近会有何期望。

# DevOps 接下来会发生什么：要关注的 5 个趋势

>
> 专家们权衡了 DevOps 团队最近会有何期望。
>
>
>

<img src="content_image/album_201805_29_073014kx8s8oa778ii8i7i.png" alt="" title="">

“DevOps” 一词通常认为是来源于 [这篇 2008 年关于敏捷基础设施和运营的讲演中](http://www.jedi.be/presentations/agile-infrastructure-agile-2008.pdf)。现在的 IT 词汇中，它无处不在，这个“混搭”的词汇出现还不到 10 年：我们还在研究它在 IT 中更现代化的工作方法。

诚然，多年来一直在 “从事 DevOps” 的人积累了丰富的知识。但是大多数的 DevOps 环境 —— 人与 [文化](https://enterprisersproject.com/article/2017/9/5-ways-nurture-devops-culture) 、流程与方法、工具与技术的融合 —— 还远远没有成熟。

更多的变化即将到来。Robert Reeves 说 “DevOps 是一个过程，一种算法”，他是 [Datical](https://www.datical.com/) 的 CTO， “它的绝对目标就是随着时间进行改变和演进”，这就是重点。

那我们预计接下来会发生什么呢？这里有一些专家们观察到的重要趋势。

### 1、 预计 DevOps、容器、以及微服务之间的相互依赖会增强

驱动 DevOps 发展的文化本身可能会演进。当然，DevOps 仍然将在根本上摧毁传统的 IT 孤岛和瓶颈，但这样做的理由可能会变得更加急迫。证据 A & B： [对容器和微服务的兴趣](https://enterprisersproject.com/article/2017/9/microservices-and-containers-6-things-know-start-time) 与日俱增。这个技术组合很强大、可连续扩展、与规划和 [持续进行的管理](https://enterprisersproject.com/article/2017/10/microservices-and-containers-6-management-tips-long-haul)配合最佳。

Arvind Soni 说 “影响 DevOps 的其中一个主要因素是向微服务转变”，他是 [Netsil](https://netsil.com/) 的产品副总裁，补充道，容器和业务流程，使开发者打包和交付的速度越来越快。DevOps 团队的任务可能是帮助去加速打包并管理越来越复杂的微服务弹性架构。

### 2、 预计 ”安全网“ 更少

DevOps 使团队可以更快更敏捷地去构建软件，部署速度也更快更频繁、同时还能提升软件质量和稳定性。但是好的 IT 领导通常都不会忽视管理风险，因此，早期大量的 DevOps 迭代都是使用了安全防护 —— 从后备的不重要业务开始的。为了实现更快的速度和敏捷性，越来越多的团队将抛弃他们的 ”辅助轮“（LCTT 译注：意思说减少了安全防护措施）。

Nic Grange 说 “随着团队的成熟，他们决定不再需要一些早期增加的安全 ‘防护栏’ 了”，他是 [Retriever Communications](http://retrievercommunications.com/) 的 CTO。Grange 给出了一个阶段展示的服务器的示例：随着 DevOps 团队的成熟，他们决定不再需要阶段展示的服务器了，尤其是他们很少在试生产环境中发现问题。（Grange 指出，这一举措对于缺乏 DevOps 经验的团队来说，不可轻易效仿）

Grange 说 “这个团队可能在监视和发现并解决生产系统中出现的问题的能力上有足够的信心”，“部署过程和测试阶段，如果没有任何证据证明它的价值，那么它可能会把整个进度拖慢”。

### 3、 预计 DevOps 将在其它领域大面积展开

DevOps 将两个传统的 IT 部门（开发和运营）结合的更紧密。越来越多的公司看到了这种结合的好处，这种文化可能会传播开来。这种情况在一些组织中已经出现，在 “DevSecOps” 一词越来越多出现的情况下，它反映出了在软件开发周期中有意地、越来越早地包含了安全性。

Derek Weeks 说 “DevSecOps 不仅是一个工具，它是将安全思维更早地集成到开发实践中”，它是 [Sonatype](https://www.sonatype.com/) 的副总裁和 DevOps 拥挤者。

[Red Hat](https://www.redhat.com/en/) 的安全策略师 Kirsten Newcomer 说，这种做法并不是一个技术挑战，而是一个文化挑战。

Newcomer 说 “从历史来看，安全团队都是从开发团队中分离出来的 —— 每个团队在它们不同的 IT 领域中形成了各自的专长” ，“它并不需要这种方法。每个关心安全性的企业也关心他们通过软件快速交付业务价值的能力，这些企业正在寻找方法，将安全放进应用程序的开发周期中。它们采用 DevSecOps 通过 CI/CD 流水线去集成安全实践、工具和自动化。为了做的更好，他们整合他们的团队 —— 将安全专家整合到应用程序开发团队中，参与到从设计到产品部署的全过程中。这种做法使双方都看到了价值 —— 每个团队都扩充了它们的技能和知识，使他们成为更有价值的技术专家。DevOps 做对了 —— 或者说是 DevSecOps —— 提升了 IT 安全性。”

除了安全以外，还可以让 DevOps 扩展到其它领域，比如数据库团队、QA，甚至是 IT 以外的潜在领域。

Datical 的 Reeves 说 “这是一件非常 DevOps 化的事情：发现相互掣肘的地方并解决它们”，“对于以前采用 DevOps 的企业来说，安全和数据库是他们面临的最大瓶颈。”

### 4、 预计 ROI 将会增加

Eric Schabell 说，“由于公司深入推进他们的 DevOps 工作，IT 团队在方法、流程、容器和微服务方面的投资将得到更多的回报。” 他是 Red Hat 的全球技术传播总监，Schabell 说 “‘圣杯’将移动的更快、完成的更多、并且变得更灵活。由于这些组件找到了更宽阔的天地，组织在应用程序中更有归属感时，结果就会出现。”

“每当新兴技术获得我们的关注时，任何事都有一个令人兴奋的学习曲线，但当认识到它应用很困难的时候，同时也会经历一个从兴奋到幻灭的低谷。最终，我们将开始看到从低谷中爬出来，并收获到应用 DevOps、容器、和微服务的好处。”

### 5、 预计成功的指标将持续演进

Mike Kail 说 “我相信 DevOps 文化的两个核心原则 —— 自动化和可衡量是从来不会变的”，它是 [CYBRIC](https://www.cybric.io/) 的 CTO，也是 Yahoo 前 CIO。“总是有办法去自动化一个任务，或者提升一个已经自动化的解决方案，而随着时间的推移，重要的事情是测量可能的变化和扩展。这个成熟的过程是一个永不停步的旅行，而不是一个目的地或者已完成的任务。”

在 DevOps 的精神中，成熟和学习也与协作者和分享精神有关。Kail 认为，对于敏捷方法和 DevOps 文化来说，它仍然为时尚早，这意味着它们还有足够的增长空间。

Kail 说 “随着越来越多的成熟组织持续去测量可控指标，我相信（希望） —— 这些经验应该被广泛的分享，以便我们去学习并改善它们。”

作为 Red Hat 技术传播专家，[Gordon Haff](https://enterprisersproject.com/user/gordon-haff) 最近注意到，组织使用业务成果相关的因素去改善他们的 DevOps 指标的工作越来越困难。 [Haff 写道](https://enterprisersproject.com/article/2017/7/devops-metrics-are-you-measuring-what-matters) “你或许并不真正关心你的开发者写了多少行代码、服务器是否在一夜之间出现了硬件故障、或者你的测试覆盖面是否全面”。事实上，你可能并不直接关心你的网站的响应速度和更新快慢。但是你要注意的是，这些指标可能与消费者放弃购物车或者转到你的竞争对手那里有关。”

与业务成果相关的一些 DevOps 指标的例子包括，消费者交易金额（作为消费者花销统计的指标）和净推荐值（消费者推荐公司产品和服务的意愿）。关于这个主题更多的内容，请查看这篇完整的文章—— [DevOps 指标：你是否测量了重要的东西](https://enterprisersproject.com/article/2017/7/devops-metrics-are-you-measuring-what-matters)。

### 唯一不变的就是改变

顺利说一句，如果你希望这件事一蹴而就，那你就要倒霉了。

Reeves 说 “如果你认为今天发布非常快，那你就什么也没有看到”，“这就是为什么要让相关者包括数据库团队进入到 DevOps 中的重要原因。因为今天这两组人员的冲突会随着发布速度的提升而呈指数级增长。”

---

via: <https://enterprisersproject.com/article/2017/10/what-s-next-devops-5-trends-watch>

作者：[Kevin Casey](https://enterprisersproject.com/user/kevin-casey) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出