在 GitHub 上对编程语言与软件质量的一个大规模研究

![](/data/attachment/album/201806/28/180042t9ilo3tizks3qtpp.jpg)

编程语言对软件质量的影响是什么？这个问题在很长一段时间内成为一个引起了大量辩论的主题。在这项研究中，我们从 GitHub 上收集了大量的数据（728 个项目，6300 万行源代码，29000 位作者，150 万个提交，17 种编程语言），尝试在这个问题上提供一些实证。这个还算比较大的样本数量允许我们去使用一个混合的方法，结合多种可视化的回归模型和文本分析，去研究语言特性的影响，比如，在软件质量上，静态与动态类型和允许混淆与不允许混淆的类型。通过从不同的方法作三角测量研究（LCTT 译注：一种测量研究的方法），并且去控制引起混淆的因素，比如，团队大小、项目大小和项目历史，我们的报告显示，语言设计确实（对很多方面）有很大的影响，但是，在软件质量方面，语言的影响是非常有限的。最明显的似乎是，不允许混淆的类型比允许混淆的类型要稍微好一些，并且，在函数式语言中，静态类型也比动态类型好一些。值得注意的是，这些由语言设计所引起的轻微影响，绝大多数是由过程因素所主导的，比如，项目大小、团队大小和提交数量。但是，我们需要提示读者，即便是这些不起眼的轻微影响，也是由其它的无形的过程因素所造成的，例如，对某些函数类型、以及不允许类型混淆的静态语言的偏爱。

### 1 序言

在给定的编程语言是否是“适合这个工作的正确工具”的讨论期间，紧接着又发生了多种辩论。虽然一些辩论出现了带有宗教般狂热的色彩，但是大部分人都一致认为，编程语言的选择能够对编码过程和由此生成的结果都有影响。

主张强静态类型的人，倾向于认为静态方法能够在早期捕获到缺陷；他们认为，一点点的预防胜过大量的矫正。动态类型拥护者主张，保守的静态类型检查无论怎样都是非常浪费开发者资源的，并且，最好是依赖强动态类型检查来捕获错误类型。然而，这些辩论，大多数都是“纸上谈兵”，只靠“传说中”的证据去支持。

这些“传说”也许并不是没有道理的；考虑到影响软件工程结果的大量其它因素，获取这种经验性的证据支持是一项极具挑战性的任务，比如，代码质量、语言特征，以及应用领域。比如软件质量，考虑到它有大量的众所周知的影响因素，比如，代码数量，    6  团队大小，    2  和年龄/熟练程度。    9

受控实验是检验语言选择在面对如此令人气馁的混淆影响时的一种方法，然而，由于成本的原因，这种研究通常会引入一种它们自己的混淆，也就是说，限制了范围。在这种研究中，完整的任务是必须要受限制的，并且不能去模拟 *真实的世界* 中的开发。这里有几个最近的这种大学本科生使用的研究，或者，通过一个实验因素去比较静态或动态类型的语言。   7  ，   12  ，   15

幸运的是，现在我们可以基于大量的真实世界中的软件项目去研究这些问题。GitHub 包含了多种语言的大量的项目，并且在大小、年龄、和开发者数量上有很大的差别。每个项目的仓库都提供一个详细的记录，包含贡献历史、项目大小、作者身份以及缺陷修复。然后，我们使用多种工具去研究语言特性对缺陷发生的影响。对我们的研究方法的最佳描述应该是“混合方法”，或者是三角测量法；    5  我们使用文本分析、聚簇和可视化去证实和支持量化回归研究的结果。这个以经验为根据的方法，帮助我们去了解编程语言对软件质量的具体影响，因为，他们是被开发者非正式使用的。

### 2 方法

我们的方法是软件工程中典型的大范围观察研究法。我们首先大量的使用自动化方法，从几种数据源采集数据。然后使用预构建的统计分析模型对数据进行过滤和清洗。过滤器的选择是由一系列的因素共同驱动的，这些因素包括我们研究的问题的本质、数据质量和认为最适合这项统计分析研究的数据。尤其是，GitHub 包含了由大量的编程语言所写的非常多的项目。对于这项研究，我们花费大量的精力专注于收集那些用大多数的主流编程语言写的流行项目的数据。我们选择合适的方法来评估计数数据上的影响因素。

#### 2.1 数据收集

我们选择了 GitHub 上的排名前 19 的编程语言。剔除了 CSS、Shell 脚本、和 Vim 脚本，因为它们不是通用的编程语言。我们包含了 Typescript，它是 JavaScript 的超集。然后，对每个被研究的编程语言，我们检索出以它为主要编程语言的前 50 个项目。我们总共分析了 17 种不同的语言，共计 850 个项目。

我们的编程语言和项目的数据是从 *GitHub Archive* 中提取的，这是一个记录所有活跃的公共 GitHub 项目的数据库。它记录了 18 种不同的 GitHub 事件，包括新提交、fork 事件、PR（拉取请求）、开发者信息和以每小时为基础的所有开源 GitHub 项目的问题跟踪。打包后的数据上传到 Google BigQuery 提供的交互式数据分析接口上。

**识别编程语言排名榜单**

我们基于它们的主要编程语言分类合计项目。然后，我们选择大多数的项目进行进一步分析，如 [表 1](http://deliveryimages.acm.org/10.1145/3130000/3126905/t1.jpg) 所示。一个项目可能使用多种编程语言；将它确定成单一的编程语言是很困难的。Github Archive 保存的信息是从 GitHub Linguist 上采集的，它使用项目仓库中源文件的扩展名来确定项目的发布语言是什么。源文件中使用数量最多的编程语言被确定为这个项目的 *主要编程语言*。

[![t1.jpg](/data/attachment/album/201806/28/180106xsjhkjzgnjdjjjue.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/t1.jpg)

*表 1 每个编程语言排名前三的项目*

**检索流行的项目**

对于每个选定的编程语言，我们先根据项目所使用的主要编程语言来选出项目，然后根据每个项目的相关 *星* 的数量排出项目的流行度。 *星* 的数量表示了有多少人主动表达对这个项目感兴趣，并且它是流行度的一个合适的代表指标。因此，在 C 语言中排名前三的项目是 linux、git、php-src；而对于 C++，它们则是 node-webkit、phantomjs、mongo ；对于 Java，它们则是 storm、elasticsearch、ActionBarSherlock 。每个编程语言，我们各选了 50 个项目。

为确保每个项目有足够长的开发历史，我们剔除了少于 28 个提交的项目（28 是候选项目的第一个四分位值数）。这样我们还剩下 728 个项目。[表 1](http://deliveryimages.acm.org/10.1145/3130000/3126905/t1.jpg) 展示了每个编程语言的前三个项目。

**检索项目演进历史**

对于 728 个项目中的每一个项目，我们下载了它们的非合并提交、提交记录、作者数据、作者使用 *git* 的名字。我们从每个文件的添加和删除的行数中计算代码改动和每个提交的修改文件数量。我们以每个提交中修改的文件的扩展名所代表的编程语言，来检索出所使用的编程语言（一个提交可能有多个编程语言标签）。对于每个提交，我们通过它的提交日期减去这个项目的第一个提交的日期，来计算它的 *提交年龄* 。我们也计算其它的项目相关的统计数据，包括项目的最大提交年龄和开发者总数，用于我们的回归分析模型的控制变量，它在第三节中会讨论到。我们通过在提交记录中搜索与错误相关的关键字，比如，`error`、`bug`、`fix`、`issue`、`mistake`、`incorrect`、`fault`、`defect`、`flaw`，来识别 bug 修复提交。这一点与以前的研究类似。   18

[表 2](http://deliveryimages.acm.org/10.1145/3130000/3126905/t2.jpg) 汇总了我们的数据集。因为一个项目可能使用多个编程语言，表的第二列展示了使用某种编程语言的项目的总数量。我们进一步排除了项目中该编程语言少于 20 个提交的那些编程语言。因为 20 是每个编程语言的每个项目的提交总数的第一个四分位值。例如，我们在 C 语言中共找到 220 项目的提交数量多于 20 个。这确保了每个“编程语言 – 项目”对有足够的活跃度。

[![t2.jpg](/data/attachment/album/201806/28/180107wg3hku3eddoc0dok.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/t2.jpg)

*表 2 研究主题*

总而言之，我们研究了最近 18 年以来，用了 17 种编程语言开发的，总共 728 个项目。总共包括了 29,000 个不同的开发者，157 万个提交，和 564,625 个 bug 修复提交。

#### 2.2 语言分类

我们基于影响语言质量的几种编程语言特性定义了语言类别，   7  ，   8  ，   12  ，如 [表 3](http://deliveryimages.acm.org/10.1145/3130000/3126905/t3.jpg) 所示。

<ruby> 编程范式 <rt>  Programming Paradigm </rt></ruby> 表示项目是以命令方式、脚本方式、还是函数语言所写的。在本文的下面部分，我们分别使用 <ruby> 命令 <rt>  procedural </rt></ruby> 和 <ruby> 脚本 <rt>  scripting </rt></ruby> 这两个术语去代表命令方式和脚本方式。

[![t3.jpg](/data/attachment/album/201806/28/180108w33j5t5jf8bktj88.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/t3.jpg)

*表 3. 语言分类的不同类型*

<ruby> 类型检查 <rt>  Type Checking </rt></ruby> 代表静态或者动态类型。在静态类型语言中，在编译时进行类型检查，并且变量名是绑定到一个值和一个类型的。另外，（包含变量的）表达式是根据运行时，它们可能产生的值所符合的类型来分类的。在动态类型语言中，类型检查发生在运行时。因此，在动态类型语言中，它可能出现在同一个程序中，一个变量名可能会绑定到不同类型的对象上的情形。

<ruby> 隐式类型转换 <rt>  Implicit Type Conversion </rt></ruby> 允许一个类型为 T1 的操作数，作为另一个不同的类型 T2 来访问，而无需进行显式的类型转换。这样的隐式类型转换在一些情况下可能会带来类型混淆，尤其是当它表示一个明确的 T1 类型的操作数时，把它再作为另一个不同的 T2 类型的情况下。因为，并不是所有的隐式类型转换都会立即出现问题，通过我们识别出的允许进行隐式类型转换的所有编程语言中，可能发生隐式类型转换混淆的例子来展示我们的定义。例如，在像 Perl、 JavaScript、CoffeeScript 这样的编程语言中，一个字符和一个数字相加是允许的（比如，`"5" + 2` 结果是 `"52"`）。但是在 Php 中，相同的操作，结果是 `7`。像这种操作在一些编程语言中是不允许的，比如 Java 和 Python，因为，它们不允许隐式转换。在强数据类型的 C 和 C++ 中，这种操作的结果是不可预料的，例如，`int x; float y; y=3.5; x=y`；是合法的 C 代码，并且对于 `x` 和 `y` 其结果是不同的值，具体是哪一个值，取决于含义，这可能在后面会产生问题。   a  在 `Objective-C` 中，数据类型 *id* 是一个通用对象指针，它可以被用于任何数据类型的对象，而不管分类是什么。   b  像这种通用数据类型提供了很好的灵活性，它可能导致隐式的类型转换，并且也会出现不可预料的结果。      c    因此，我们根据它的编译器是否 *允许* 或者 *不允许* 如上所述的隐式类型转换，对编程语言进行分类；而不允许隐式类型转换的编程语言，会显式检测类型混淆，并报告类型不匹配的错误。

不允许隐式类型转换的编程语言，使用一个类型判断算法，比如，Hindley    10  和 Milner，   17  或者，在运行时上使用一个动态类型检查器，可以在一个编译器（比如，使用 Java）中判断静态类型的结果。相比之下，一个类型混淆可能会悄无声息地发生，因为，它可能因为没有检测到，也可能是没有报告出来。无论是哪种方式，允许隐式类型转换在提供了灵活性的同时，最终也可能会出现很难确定原因的错误。为了简单起见，我们将用 <ruby> 隐含 <rt>  implicit </rt></ruby> 代表允许隐式类型转换的编程语言，而不允许隐式类型转换的语言，我们用 <ruby> 明确 <rt>  explicit </rt></ruby> 代表。

<ruby> 内存分类 <rt>  Memory Class </rt></ruby> 表示是否要求开发者去管理内存。尽管 Objective-C 遵循了一个混合模式，我们仍将它放在非管理的分类中来对待，因为，我们在它的代码库中观察到很多的内存错误，在第 3 节的 RQ4 中会讨论到。

请注意，我们之所以使用这种方式对编程语言来分类和研究，是因为，这种方式在一个“真实的世界”中被大量的开发人员非正式使用。例如，TypeScript 被有意地分到静态编程语言的分类中，它不允许隐式类型转换。然而，在实践中，我们注意到，开发者经常（有 50% 的变量，并且跨 TypeScript —— 在我们的数据集中使用的项目）使用 `any` 类型，这是一个笼统的联合类型，并且，因此在实践中，TypeScript 允许动态地、隐式类型转换。为减少混淆，我们从我们的编程语言分类和相关的模型中排除了 TypeScript（查看 [表 3](http://deliveryimages.acm.org/10.1145/3130000/3126905/t3.jpg) 和 [7](http://deliveryimages.acm.org/10.1145/3130000/3126905/t7.jpg)）。

#### 2.3 识别项目领域

我们基于编程语言的特性和功能，使用一个自动加手动的混合技术，将研究的项目分类到不同的领域。在 GitHub 上，项目使用 `project descriptions` 和 `README` 文件来描述它们的特性。我们使用一种文档主题生成模型（Latent Dirichlet Allocation，缩写为：LDA）    3  去分析这些文本。提供一组文档给它，LDA 将生成不同的关键字，然后来识别可能的主题。对于每个文档，LDA 也估算每个主题分配的文档的概率。

我们检测到 30 个不同的领域（换句话说，就是主题），并且评估了每个项目从属于每个领域的概率。因为，这些自动检测的领域包含了几个具体项目的关键字，例如，facebook，很难去界定它的底层的常用功能。为了给每个领域分配一个有意义的名字，我们手动检查了 30 个与项目名字无关的用于识别领域的领域识别关键字。我们手动重命名了所有的 30 个自动检测的领域，并且找出了以下六个领域的大多数的项目：应用程序、数据库、代码分析、中间件、库，和框架。我们也找出了不符合以上任何一个领域的一些项目，因此，我们把这个领域笼统地标记为 *其它* 。随后，我们研究组的另一名成员检查和确认了这种项目领域分类的方式。[表 4](http://deliveryimages.acm.org/10.1145/3130000/3126905/t4.jpg) 汇总了这个过程识别到的领域结果。

[![t4.jpg](/data/attachment/album/201806/28/180109zc0l4om8c944b4s9.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/t4.jpg)

*表 4 领域特征*

#### 2.4 bug 分类

尽管修复软件 bug 时，开发者经常会在提交日志中留下关于这个 bug 的原始的重要信息；例如，为什么会产生 bug，以及怎么去修复 bug。我们利用很多信息去分类 bug，与 Tan 的 *et al* 类似。    13  ，   24

首先，我们基于 bug 的 <ruby> 原因 <rt>  Cause </rt></ruby> 和 <ruby> 影响 <rt>  Impact </rt></ruby> 进行分类。\_ 原因 \_ 进一步分解为不相关的错误子类：算法方面的、并发方面的、内存方面的、普通编程错误，和未知的。bug 的 *影响* 也分成四个不相关的子类：安全、性能、失败、和其它的未知类。因此，每个 bug 修复提交也包含原因和影响的类型。[表 5](http://deliveryimages.acm.org/10.1145/3130000/3126905/t5.jpg) 展示了描述的每个 bug 分类。这个类别分别在两个阶段中被执行：

[![t5.jpg](/data/attachment/album/201806/28/180109an4zzypzor8n4won.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/t5.jpg)

*表 5 bug 分类和在整个数据集中的描述*

**(1) 关键字搜索** 我们随机选择了 10% 的 bug 修复信息，并且使用一个基于关键字的搜索技术去对它们进行自动化分类，作为可能的 bug 类型。我们对这两种类型（原因和影响）分别使用这个注释。我们选择了一个限定的关键字和习惯用语集，如 [表 5](http://deliveryimages.acm.org/10.1145/3130000/3126905/t5.jpg) 所展示的。像这种限定的关键字和习惯用语集可以帮我们降低误报。

**(2) 监督分类** 我们使用前面步骤中的有注释的 bug 修复日志作为训练数据，为监督学习分类技术，通过测试数据来矫正，去对剩余的 bug 修复信息进行分类。我们首先转换每个 bug 修复信息为一个词袋（LCTT 译注：bag-of-words，一种信息检索模型）。然后，删除在所有的 bug 修复信息中仅出现过一次的词。这样减少了具体项目的关键字。我们也使用标准的自然语言处理技术来解决这个问题。最终，我们使用支持向量机（LCTT 译注：Support Vector Machine，缩写为 SVM，在机器学习领域中，一种有监督的学习算法）去对测试数据进行分类。

为精确评估 bug 分类器，我们手动注释了 180 个随机选择的 bug 修复，平均分布在所有的分类中。然后，我们比较手动注释的数据集在自动分类器中的结果。最终处理后的，表现出的精确度是可接受的，性能方面的精确度最低，是 70%，并发错误方面的精确度最高，是 100%，平均是 84%。再次运行，精确度从低到高是 69% 到 91%，平均精确度还是 84%。

我们的 bug 分类的结果展示在 [表 5](http://deliveryimages.acm.org/10.1145/3130000/3126905/t5.jpg) 中。大多数缺陷的原因都与普通编程错误相关。这个结果并不意外，因为，在这个分类中涉及了大量的编程错误，比如，类型错误、输入错误、编写错误、等等。我们的技术并不能将在任何（原因或影响）分类中占比为 1.4% 的 bug 修复信息再次进行分类；我们将它归类为未知。

#### 2.5 统计方法

我们使用回归模型对软件项目相关的其它因素中的有缺陷的提交数量进行了建模。所有的模型使用<ruby> 负二项回归 <rt>  negative binomial regression </rt></ruby>（缩写为 NBR）（LCTT 译注：一种回归分析模型） 去对项目属性计数进行建模，比如，提交数量。NBR 是一个广义的线性模型，用于对非负整数进行响应建模。   4

在我们的模型中，我们对每个项目的编程语言，控制几个可能影响最终结果的因素。因此，在我们的回归分析中，每个（语言/项目）对是一个行，并且可以视为来自流行的开源项目中的样本。我们依据变量计数进行对象转换，以使变量保持稳定，并且提升了模型的适用度。   4  我们通过使用 AIC 和 Vuong 对非嵌套模型的测试比较来验证它们。

去检查那些过度的多重共线性（LCTT 译注：多重共线性是指，在线性回归模型中解释变量之间由于存在精确相关关系或高度相关关系而使模型估计失真或难以估计准确。）并不是一个问题，我们在所有的模型中使用一个保守的最大值 5，去计算每个依赖的变量的膨胀因子的方差。   4  我们通过对每个模型的残差和杠杆图进行视觉检查来移除高杠杆点，找出库克距离（LCTT 译注：一个统计学术语，用于诊断回归分析中是否存在异常数据）的分离值和最大值。

我们利用 *效果* ，或者 *差异* ，编码到我们的研究中，以提高编程语言回归系数的表现。   4  加权的效果代码允许我们将每种编程语言与所有编程语言的效果进行比较，同时弥补了跨项目使用编程语言的不均匀性。   23  去测试两种变量因素之间的联系，我们使用一个独立的卡方检验（LCTT 译注：Chi-square，一种统计学上的假设检验方法）测试。   14  在证实一个依赖之后，我们使用 Cramer 的 V，它是与一个 `r × c` 等价的正常数据的 `phi(φ)` 系数，去建立一个效果数据。

### 3 结果

我们从简单明了的问题开始，它非常直接地解决了人们坚信的一些核心问题，即：

#### 问题 1：一些编程语言相比其它语言来说更易于出现缺陷吗？

我们使用了回归分析模型，去比较每个编程语言对所有编程语言缺陷数量平均值的影响，以及对缺陷修复提交的影响（查看 [表 6](http://deliveryimages.acm.org/10.1145/3130000/3126905/t6.jpg)）。

[![t6.jpg](/data/attachment/album/201806/28/180110c9aauni19ij95h7j.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/t6.jpg)

*表 6. 一些语言的缺陷要少于其它语言*

我们包括了一些变量，作为对明确影响反应的控制因子。项目<ruby> 年龄 <rt>  age </rt></ruby>也包括在内，因为，越老的项目生成的缺陷修复数量越大。<ruby> 提交 <rt>  commits </rt></ruby>数量也会对项目反应有轻微的影响。另外，从事该项目的<ruby> 开发人员 <rt>  dev </rt></ruby>的数量和项目的原始<ruby> 大小 <rt>  size </rt></ruby>，都会随着项目的活跃而增长。

上述模型中估算系数的大小和符号（LCTT 译注：指 “+”或者“-”）与结果的预测因子有关。初始的四种变量是控制变量，并且，我们对这些变量对最终结果的影响不感兴趣，只是说它们都是积极的和有意义的。语言变量是指示变量，是每个项目的变化因子，该因子将每种编程语言与所有项目的编程语言的加权平均值进行比较。编程语言系数可以大体上分为三类。第一类是，那些在统计学上无关紧要的系数，并且在建模过程中这些系数不能从 0 中区分出来。这些编程语言的表现与平均值相似，或者它们也可能有更大的方差。剩余的系数是非常明显的，要么是正的，要么是负的。对于那些正的系数，我们猜测可能与这个编程语言有大量的缺陷修复相关。这些语言包括 C、C++、Objective-C、Php，以及 Python。所有的有一个负的系数的编程语言，比如 Clojure、Haskell、Ruby，和 Scala，暗示这些语言的缺陷修复提交可能小于平均值。

应该注意的是，虽然，从统计学的角度观察到编程语言与缺陷之间有明显的联系，但是，大家不要过高估计编程语言对于缺陷的影响，因为，这种影响效应是非常小的。异常分析的结果显示，这种影响小于总异常的 1%。

[![ut1.jpg](/data/attachment/album/201806/28/180111w61eo2zgbts86s6s.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/ut1.jpg)

我们可以这样去理解模型的系数，它代表一个预测因子在所有其它预测因子保持不变的情况下，这个预测因子一个<ruby> 单位 <rt>  unit </rt></ruby>的变化，所反应出的预期的响应的对数变化；换句话说，对于一个系数 β i ，在 β i 中一个单位的变化，产生一个预期的 e β   i  响应的变化。对于可变因子，这个预期的变化是与所有编程语言的平均值进行比较。因此，如果对于一定数量的提交，用一个处于平均值的编程语言开发的特定项目有四个缺陷提交，那么，如果选择使用 C++ 来开发，意味着我们预计应该有一个额外的（LCTT 译注：相对于平均值 4，多 1 个）缺陷提交，因为 e 0.18 × 4 = 4.79。对于相同的项目，如果选择使用 Haskell 来开发，意味着我们预计应该少一个（LCTT 译注：同上，相对于平均值 4）缺陷提交。因为， e −0.26 × 4 = 3.08。预测的精确度取决于剩余的其它因子都保持不变，除了那些微不足道的项目之外，所有的这些都是一个极具挑战性的命题。所有观察性研究都面临类似的局限性；我们将在第 5 节中详细解决这些事情。

**结论 1：一些编程语言相比其它编程语言有更高的缺陷相关度，不过，影响非常小。**

在这篇文章的剩余部分，我们会在基本结论的基础上详细阐述，通过考虑不同种类的应用程序、缺陷、和编程语言，可以进一步深入了解编程语言和缺陷倾向之间的关系。

软件 bug 通常落进两种宽泛的分类中：

1. *特定领域的 bug* ：特定于项目功能，并且不依赖于底层编程语言。
2. *普通 bug* ：大多数的普通 bug 是天生的，并且与项目功能无关，比如，输入错误，并发错误、等等。

因此，在一个项目中，应用程序领域和编程语言相互作用可能会影响缺陷的数量，这一结论被认为是合理的。因为一些编程语言被认为在一些任务上相比其它语言表现更突出，例如，C 对于低级别的（底层）工作，或者，Java 对于用户应用程序，对于编程语言的一个不合适的选择，可能会带来更多的缺陷。为研究这种情况，我们将理想化地忽略领域特定的 bug，因为，普通 bug 更依赖于编程语言的特性。但是，因为一个领域特定的 bug 也可能出现在一个普通的编程错误中，这两者是很难区分的。一个可能的变通办法是在控制领域的同时去研究编程语言。从统计的角度来看，虽然，使用 17 种编程语言跨 7 个领域，在给定的样本数量中，理解大量的术语将是一个极大的挑战。

鉴于这种情况，我们首先考虑在一个项目中测试领域和编程语言使用之间的依赖关系，独立使用一个<ruby> 卡方检验 <rt>  Chi-square </rt></ruby>测试。在 119 个单元中，是 46 个，也就是说是 39%，它在我们设定的保守值 5 以上，它太高了。这个数字不能超过 20%，应该低于 5。   14  我们在这里包含了完整有值；    d  但是，通过 Cramer 的 V 测试的值是 0.191，是低相关度的，表明任何编程语言和领域之间的相关度是非常小的，并且，在回归模型中包含领域并不会产生有意义的结果。

去解决这种情况的一个选择是，去移除编程语言，或者混合领域，但是，我们现有的数据没有进行完全挑选。或者，我们混合编程语言；这个选择导致一个相关但略有不同的问题。

#### 问题 2： 哪些编程语言特性与缺陷相关？

我们按编程语言类别聚合它们，而不是考虑单独的编程语言，正如在第 2.2 节所描述的那样，然后去分析与缺陷的关系。总体上说，这些属性中的每一个都将编程语言按照在上下文中经常讨论的错误、用户辩论驱动、或者按以前工作主题来划分的。因此，单独的属性是高度相关的，我们创建了六个模型因子，将所有的单独因子综合到我们的研究中。然后，我们对六个不同的因子对缺陷数量的影响进行建模，同时控制我们在 *问题 1* 节中使用的模型中的相同的基本协变量（LCTT 译注：协变量是指在实验中不能被人为操纵的独立变量）。

关于使用的编程语言（在前面的 [表 6](http://deliveryimages.acm.org/10.1145/3130000/3126905/t6.jpg)中），我们使用跨所有语言类的平均反应来比较编程语言 *类* 。这个模型在 [表 7](http://deliveryimages.acm.org/10.1145/3130000/3126905/t7.jpg) 中表达了出来。很明显，`Script-Dynamic-Explicit-Managed` 类有最小的量级系数。这个系数是微不足道的，换句话说，对这个系数的 <ruby> Z 校验 <rt>  z-test </rt></ruby>（LCTT 译注：统计学上的一种平均值差异校验的方法） 并不能把它从 0 中区分出来。鉴于标准错误的量级，我们可以假设，在这个类别中的编程语言的行为是非常接近于所有编程语言行为的平均值。我们可以通过使用 `Proc-Static-Implicit-Unmanaged` 作为基本级并用于处理，或者使用基本级来虚假编码比较每个语言类，来证明这一点。在这种情况下，`Script-Dynamic-Explicit-Managed` 是明显不同于 *p* = 0.00044 的。注意，虽然我们在这是选择了不同的编码方法，影响了系数和 Z 值，这个方法和所有其它的方面都是一样的。当我们改变了编码，我们调整系数去反应我们希望生成的对比。   4  将其它类的编程语言与总体平均数进行比较，`Proc-Static-Implicit-Unmanaged` 类编程语言更容易引起缺陷。这意味着与其它过程类编程语言相比，隐式类型转换或者管理内存会导致更多的缺陷倾向。

[![t7.jpg](/data/attachment/album/201806/28/180111irj5bffc5vfsjjr7.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/t7.jpg)

*表 7. 函数式语言与缺陷的关联度和其它类语言相比要低，而过程类语言则大于或接近于平均值。*

在脚本类编程语言中，我们观察到类似于允许与不允许隐式类型转换的编程语言之间的关系，它们提供的一些证据表明，隐式类型转换（与显式类型转换相比）才是导致这种差异的原因，而不是内存管理。鉴于各种因素之间的相关性，我们并不能得出这个结论。但是，当它们与平均值进行比较时，作为一个组，那些不允许隐式类型转换的编程语言出现错误的倾向更低一些，而那些出现错误倾向更高的编程语言，出现错误的机率则相对更高。在函数式编程语言中静态和动态类型之间的差异也很明显。

函数式语言作为一个组展示了与平均值的很明显的差异。静态类型语言的系数要小很多，但是函数式语言类都有同样的标准错误。函数式静态编程语言出现错误的倾向要小于函数式动态编程语言，这是一个强有力的证据，尽管如此，Z 校验仅仅是检验系数是否能从 0 中区分出来。为了强化这个推论，我们使用处理编码，重新编码了上面的模型，并且观察到，`Functional-Static-Explicit-Managed` 编程语言类的错误倾向是明显小于 `Functional-Dynamic-Explicit-Managed` 编程语言类的 *p* = 0.034。

[![ut2.jpg](/data/attachment/album/201806/28/180111mhyp9sn0bssgs9yn.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/ut2.jpg)

与编程语言和缺陷一样，编程语言类与缺陷之间关系的影响是非常小的。解释类编程语言的这种差异也是相似的，虽然很小，解释类编程语言的这种差异小于 1%。

我们现在重新回到应用领域这个问题。应用领域是否与语言类相互影响？怎么选择？例如，一个函数化编程语言，对特定的领域有一定的优势？与上面一样，对于这些因素和项目领域之间的关系做一个卡方检验，它的值是 99.05， *df* = 30， *p* = 2.622e–09，我们拒绝无意义假设，Cramer 的 V 产生的值是 0.133，表示一个弱关联。因此，虽然领域和编程语言之间有一些关联，但在这里应用领域和编程语言类之间仅仅是一个非常弱的关联。

**结论 2：在编程语言类与缺陷之间有一个很小但是很明显的关系。函数式语言与过程式或者脚本式语言相比，缺陷要少。**

这个结论有些不太令人满意的地方，因为，我们并没有一个强有力的证据去证明，在一个项目中编程语言或者语言类和应用领域之间的关联性。一个替代方法是，基于全部的编程语言和应用领域，忽略项目和缺陷总数，而去查看相同的数据。因为，这样不再产生不相关的样本，我们没有从统计学的角度去尝试分析它，而是使用一个描述式的、基于可视化的方法。

我们定义了 <ruby> 缺陷倾向 <rt>  Defect Proneness </rt></ruby> 作为 bug 修复提交与每语言每领域总提交的比率。[图 1](http://deliveryimages.acm.org/10.1145/3130000/3126905/f1.jpg) 使用了一个热力图展示了应用领域与编程语言之间的相互作用，从亮到暗表示缺陷倾向在增加。我们研究了哪些编程语言因素影响了跨多种语言写的项目的缺陷修复提交。它引出了下面的研究问题：

[![f1.jpg](/data/attachment/album/201806/28/180112r6atbblu48h6vz2a.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/f1.jpg)

*图 1. 编程语言的缺陷倾向与应用领域之间的相互作用。对于一个给定的领域（列底部），热力图中的每个格子表示了一个编程语言的缺陷倾向（行头部）。“整体”列表示一个编程语言基于所有领域的缺陷倾向。用白色十字线标记的格子代表一个 null 值，换句话说，就是在那个格子里没有符合的提交。*

#### 问题 3： 编程语言的错误倾向是否取决于应用领域？

为了回答这个问题，我们首先在我们的回归模型中，以高杠杆点过滤掉认为是异常的项目，这种方法在这里是必要的，尽管这是一个非统计学的方法，一些关系可能影响可视化。例如，我们找到一个简单的项目，Google 的 v8，一个 JavaScript 项目，负责中间件中的所有错误。这对我们来说是一个惊喜，因为 JavaScript 通常不用于中间件。这个模式一直在其它应用领域中不停地重复着，因此，我们过滤出的项目的缺陷度都低于 10% 和高于 90%。这个结果在 [图 1](http://deliveryimages.acm.org/10.1145/3130000/3126905/f1.jpg) 中。

我们看到在这个热力图中仅有一个很小的差异，正如在问题 1 节中看到的那样，这个结果仅表示编程语言固有的错误倾向。为验证这个推论，我们测量了编程语言对每个应用领域和对全部应用领域的缺陷倾向。对于除了数据库以外的全部领域，关联性都是正向的，并且 p 值是有意义的（<0.01）。因此，关于缺陷倾向，在每个领域的语言排序与全部领域的语言排序是基本相同的。

[![ut3.jpg](/data/attachment/album/201806/28/180112csq9bb5fh23g9gb5.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/ut3.jpg)

**结论 3： 应用领域和编程语言缺陷倾向之间总体上没有联系。**

我们证明了不同的语言产生了大量的缺陷，并且，这个关系不仅与特定的语言相关，也适用于一般的语言类；然而，我们发现，项目类型并不能在一定程度上协调这种关系。现在，我们转变我们的注意力到反应分类上，我想去了解，编程语言与特定种类的缺陷之间有什么联系，以及这种关系怎么与我们观察到的更普通的关系去比较。我们将缺陷分为不同的类别，如 [表 5](http://deliveryimages.acm.org/10.1145/3130000/3126905/t5.jpg) 所描述的那样，然后提出以下的问题：

#### 问题 4：编程语言与 bug 分类之间有什么关系？

我们使用了一个类似于问题 3 中所用的方法，去了解编程语言与 bug 分类之间的关系。首先，我们研究了 bug 分类和编程语言类之间的关系。一个热力图（[图 2](http://deliveryimages.acm.org/10.1145/3130000/3126905/f2.jpg)）展示了在编程语言类和 bug 类型之上的总缺陷数。去理解 bug 分类和语言之间的相互作用，我们对每个类别使用一个 NBR 回归模型。对于每个模型，我们使用了与问题 1 中相同的控制因素，以及使用加权效应编码后的语言，去预测缺陷修复提交。

[![f2.jpg](/data/attachment/album/201806/28/180113amngy444l6qlgnlc.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/f2.jpg)

*图 2. bug 类别与编程语言类之间的关系。每个格子表示每语言类（行头部）每 bug 类别（列底部）的 bug 修复提交占全部 bug 修复提交的百分比。这个值是按列规范化的。*

结果和编程语言的方差分析值展示在 [表 8](http://deliveryimages.acm.org/10.1145/3130000/3126905/t8.jpg) 中。每个模型的整体异常是非常小的，并且对于特定的缺陷类型，通过语言所展示的比例在大多数类别中的量级是类似的。我们解释这种关系为，编程语言对于特定的 bug 类别的影响要大于总体的影响。尽管我们结论概括了全部的类别，但是，在接下来的一节中，我们对 [表 5](http://deliveryimages.acm.org/10.1145/3130000/3126905/t5.jpg) 中反应出来的 bug 数较多的 bug 类别做进一步研究。

[![t8.jpg](/data/attachment/album/201806/28/180113aee8ue0w5mjjpp4z.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/t8.jpg)

*表 8. 虽然编程语言对缺陷的影响因缺陷类别而不同，但是，编程语言对特定的类别的影响要大于一般的类别。*

**编程错误** 普通的编程错误占所有 bug 修复提交的 88.53% 左右，并且在所有的编程语言类中都有。因此，回归分析给出了一个与问题 1 中类似的结论（查看 [表 6](http://deliveryimages.acm.org/10.1145/3130000/3126905/t6.jpg)）。所有的编程语言都会导致这种编程错误，比如，处理错误、定义错误、输入错误、等等。

**内存错误** 内存错误占所有 bug 修复提交的 5.44%。热力图 [图 2](http://deliveryimages.acm.org/10.1145/3130000/3126905/f2.jpg) 证明了在 `Proc-Static-Implicit-Unmanaged` 类和内存错误之间存在着非常紧密的联系。非管理内存的编程语言出现内存 bug，这是预料之中的。[表 8](http://deliveryimages.acm.org/10.1145/3130000/3126905/t8.jpg) 也证明了这一点，例如，C、C++、和 Objective-C 引发了很多的内存错误。在管理内存的语言中 Java 引发了更多的内存错误，尽管它少于非管理内存的编程语言。虽然 Java 自己做内存回收，但是，它出现内存泄露一点也不奇怪，因为对象引用经常阻止内存回收。   11  在我们的数据中，Java 的所有内存错误中，28.89% 是内存泄漏造成的。就数量而言，编程语言中内存缺陷相比其它类别的 *原因* 造成的影响要大很多。

**并发错误** 在总的 bug 修复提交中，并发错误相关的修复提交占 1.99%。热力图显示，`Proc-Static-Implicit-Unmanaged` 是主要的错误类型。在这种错误中，C 和 C++ 分别占 19.15% 和 7.89%，并且它们分布在各个项目中。

[![ut4.jpg](/data/attachment/album/201806/28/180114lyk49rfqneb7zqb9.jpg)](http://deliveryimages.acm.org/10.1145/3130000/3126905/ut4.jpg)

属于 `Static-Strong-Managed` 语言类的编程语言都被证实处于热力图中的暗区中，普通的静态语言相比其它语言产生更多的并发错误。在动态语言中，仅仅有 Erlang 有更多的并发错误倾向，或许与使用这种语言开发的并发应用程序非常多有关系。同样地，在 [表 8](http://deliveryimages.acm.org/10.1145/3130000/3126905/t8.jpg) 中的负的系数表明，用诸如 Ruby 和 `Php 这样的动态编程语言写的项目，并发错误要少一些。请注意，某些语言，如 JavaScript、CoffeeScript 和 TypeScript 是不支持并发的，在传统的惯例中，虽然 Php 具有有限的并发支持（取决于它的实现）。这些语言在我们的数据中引入了虚假的 0，因此，在 [表 8](http://deliveryimages.acm.org/10.1145/3130000/3126905/t8.jpg) 中这些语言的并发模型的系数，不能像其它的语言那样去解释。因为存在这些虚假的 0，所以在这个模型中所有语言的平均数非常小，它可能影响系数的大小，因此，她们给 w.r.t. 一个平均数，但是，这并不影响他们之间的相对关系，因为我们只关注它们的相对关系。

基于 bug 修复消息中高频词的文本分析表明，大多数的并发错误发生在一个条件争用、死锁、或者不正确的同步上，正如上面表中所示。遍历所有语言，条件争用是并发错误出现最多的原因，例如，在 Go 中占 92%。在 Go 中条件争用错误的改进，或许是因为使用了一个争用检测工具帮助开发者去定位争用。同步错误主要与消息传递接口（MPI）或者共享内存操作（SHM）相关。Erlang 和 Go 对线程间通讯使用 MPI    e  ，这就是为什么这两种语言没有发生任何 SHM 相关的错误的原因，比如共享锁、互斥锁等等。相比之下，为线程间通讯使用早期的 SHM 技术的语言写的项目，就可能存在锁相关的错误。

**安全和其它冲突错误** 在所有的 bug 修复提交中，与<ruby> 冲突 <rt>  Impact </rt></ruby>错误相关的提交占了 7.33% 左右。其中，Erlang、C++、Python 与安全相关的错误要高于平均值（[表 8](http://deliveryimages.acm.org/10.1145/3130000/3126905/t8.jpg)）。Clojure 项目相关的安全错误较少（[图 2](http://deliveryimages.acm.org/10.1145/3130000/3126905/f2.jpg)）。从热力图上我们也可以看出来，静态语言一般更易于发生失败和性能错误，紧随其后的是 `Functional-Dynamic-Explicit-Managed` 语言，比如 Erlang。对异常结果的分析表明，编程语言与冲突失败密切相关。虽然安全错误在这个类别中是弱相关的，与残差相比，解释类语言的差异仍然比较大。

**结论 4： 缺陷类型与编程语言强相关；一些缺陷类型比如内存错误和并发错误也取决于早期的语言（所使用的技术）。对于特定类别，编程语言所引起的错误比整体更多。**

### 4. 相关工作

在编程语言比较之前做的工作分为以下三类：

#### (1) 受控实验

对于一个给定的任务，开发者使用不同的语言进行编程时受到监视。研究然后比较结果，比如，开发成果和代码质量。Hanenberg    7  通过开发一个解析程序，对 48 位程序员监视了 27 小时，去比较了静态与动态类型。他发现这两者在代码质量方面没有显著的差异，但是，基于动态类型的语言花费了更短的开发时间。他们的研究是在一个实验室中，使用本科学生，设置了定制的语言和 IDE 来进行的。我们的研究正好相反，是一个实际的流行软件应用的研究。虽然我们只能够通过使用回归模型间接（和 *事后* ）控制混杂因素，我们的优势是样本数量大，并且更真实、使用更广泛的软件。我们发现在相同的条件下，静态化类型的语言比动态化类型的语言更少出现错误倾向，并且不允许隐式类型转换的语言要好于允许隐式类型转换的语言，其对结果的影响是非常小的。这是合理的，因为样本量非常大，所以这种非常小的影响在这个研究中可以看得到。

Harrison et al.    8  比较了 C++ 与 SML，一个是过程化编程语言，一个是函数化编程语言，在总的错误数量上没有找到显著的差异，不过 SML 相比 C++ 有更高的缺陷密集度。SML 在我们的数据中并没有体现出来，不过，认为函数式编程语言相比过程化编程语言更少出现缺陷。另一个重点工作是比较跨不同语言的开发工作。   12  ，   20  不过，他们并不分析编程语言的缺陷倾向。

#### (2) 调查

Meyerovich 和 Rabkin    16  调查了开发者对编程语言的观点，去研究为什么一些编程语言比其它的语言更流行。他们的报告指出，非编程语言的因素影响非常大：先前的编程语言技能、可用的开源工具、以及现有的老式系统。我们的研究也证明，可利用的外部工具也影响软件质量；例如，在 Go 中的并发 bug（请查看问题 4 节内容）。

#### (3) 对软件仓库的挖掘

Bhattacharya 和 Neamtiu    1  研究了用 C 和 C++ 开发的四个项目，并且发现在 C++ 中开发的组件一般比在 C 中开发的组件更可靠。我们发现 C 和 C++ 的错误倾向要高于全部编程语言的平均值。但是，对于某些 bug 类型，像并发错误，C 的缺陷倾向要高于 C++（请查看第 3 节中的问题 4）。

### 5. 有效的风险

我们认为，我们的报告的结论几乎没有风险。首先，在识别 bug 修复提交方面，我们依赖的关键字是开发者经常用于表示 bug 修复的关键字。我们的选择是经过认真考虑的。在一个持续的开发过程中，我们去捕获那些开发者一直面对的问题，而不是他们报告的 bug。不过，这种选择存在过高估计的风险。我们对领域分类是为了去解释缺陷的倾向，而且，我们研究组中另外的成员验证过分类。此外，我们花费精力去对 bug 修复提交进行分类，也可能有被最初选择的关键字所污染的风险。每个项目在提交日志的描述上也不相同。为了缓解这些风险，我们像 2.4 节中描述的那样，利用手工注释评估了我们的类别。

我们判断文件所属的编程语言是基于文件的扩展名。如果使用不同的编程语言写的文件使用了我们研究的通用的编程语言文件的扩展名，这种情况下也容易出现错误倾向。为减少这种错误，我们使用一个随机样本文件集手工验证了我们的语言分类。

根据我们的数据集所显示的情形，2.2 节中的解释类编程语言，我们依据编程语言属性的主要用途作了一些假设。例如，我们将 Objective-C 分到非管理内存类型中，而不是混合类型。同样，我们将 Scala 注释为函数式编程语言，将 C# 作为过程化的编程语言，虽然，它们在设计的选择上两者都支持。    19  ，   21  在这项研究工作中，我们没有从过程化编程语言中分离出面向对象的编程语言（OOP），因为，它们没有清晰的区别，主要差异在于编程类型。我们将 C++ 分到允许隐式类型转换的类别中是因为，某些类型的内存区域可以通过使用指针操作被进行不同的处理，    22  并且我们注意到大多数 C++ 编译器可以在编译时检测类型错误。

最后，我们将缺陷修复提交关联到编程语言属性上，它们可以反应出报告的风格或者其它开发者的属性。可用的外部工具或者<ruby> 库 <rt>  library </rt></ruby>也可以影响一个相关的编程语言的 bug 数量。

### 6. 总结

我们对编程语言和使用进行了大规模的研究，因为它涉及到软件质量。我们使用的 Github 上的数据，具有很高的复杂性和多个维度上的差异的特性。我们的样本数量允许我们对编程语言效果以及在控制一些混杂因素的情况下，对编程语言、应用领域和缺陷类型之间的相互作用，进行一个混合方法的研究。研究数据显示，函数式语言是好于过程化语言的；不允许隐式类型转换的语言是好于允许隐式类型转换的语言的；静态类型的语言是好于动态类型的语言的；管理内存的语言是好于非管理的语言的。进一步讲，编程语言的缺陷倾向与软件应用领域并没有关联。另外，每个编程语言更多是与特定的 bug 类别有关联，而不是与全部 bug。

另一方面，即便是很大规模的数据集，它们被多种方法同时进行分割后，也变得很小且不全面。因此，随着依赖的变量越来越多，很难去回答某个变量所产生的影响有多大这种问题，尤其是在变量之间相互作用的情况下。因此，我们无法去量化编程语言在使用中的特定的效果。其它的方法，比如调查，可能对此有帮助。我们将在以后的工作中来解决这些挑战。

### 致谢

这个材料是在美国国家科学基金会（NSF）以及美国空军科学研究办公室（AFOSR）的授权和支持下完成的。授权号 1445079, 1247280, 1414172，1446683，FA955-11-1-0246。

### 参考资料

1. Bhattacharya, P., Neamtiu, I. Assessing programming language impact on development and maintenance: A study on C and C++. In *Proceedings of the 33rd International Conference on Software Engineering, ICSE'11* (New York, NY USA, 2011). ACM, 171–180.
2. Bird, C., Nagappan, N., Murphy, B., Gall, H., Devanbu, P. Don't touch my code! Examining the effects of ownership on software quality. In *Proceedings of the 19th ACM SIGSOFT Symposium and the 13th European Conference on Foundations of Software Engineering* (2011). ACM, 4–14.
3. Blei, D.M. Probabilistic topic models. *Commun. ACM 55* , 4 (2012), 77–84.
4. Cohen, J. *Applied Multiple Regression/Correlation Analysis for the Behavioral Sciences.* Lawrence Erlbaum, 2003.
5. Easterbrook, S., Singer, J., Storey, M.-A., Damian, D. Selecting empirical methods for software engineering research. In *Guide to Advanced Empirical Software Engineering* (2008). Springer, 285–311.
6. El Emam, K., Benlarbi, S., Goel, N., Rai, S.N. The confounding effect of class size on the validity of object-oriented metrics. *IEEE Trans. Softw. Eng. 27* , 7 (2001), 630–650.
7. Hanenberg, S. An experiment about static and dynamic type systems: Doubts about the positive impact of static type systems on development time. In *Proceedings of the ACM International Conference on Object Oriented Programming Systems Languages and Applications, OOPSLA'10* (New York, NY, USA, 2010). ACM, 22–35.
8. Harrison, R., Smaraweera, L., Dobie, M., Lewis, P. Comparing programming paradigms: An evaluation of functional and object-oriented programs. *Softw. Eng. J. 11* , 4 (1996), 247–254.
9. Harter, D.E., Krishnan, M.S., Slaughter, S.A. Effects of process maturity on quality, cycle time, and effort in software product development. *Manage. Sci. 46* 4 (2000), 451–466.
10. Hindley, R. The principal type-scheme of an object in combinatory logic. *Trans. Am. Math. Soc.* (1969), 29–60.
11. Jump, M., McKinley, K.S. Cork: Dynamic memory leak detection for garbage-collected languages. In *ACM SIGPLAN Notices* , Volume 42 (2007). ACM, 31–38.
12. Kleinschmager, S., Hanenberg, S., Robbes, R., Tanter, É., Stefik, A. Do static type systems improve the maintainability of software systems? An empirical study. In *2012 IEEE 20th International Conference on Program Comprehension (ICPC)* (2012). IEEE, 153–162.
13. Li, Z., Tan, L., Wang, X., Lu, S., Zhou, Y., Zhai, C. Have things changed now? An empirical study of bug characteristics in modern open source software. In *ASID'06: Proceedings of the 1st Workshop on Architectural and System Support for Improving Software Dependability* (October 2006).
14. Marques De Sá, J.P. *Applied Statistics Using SPSS, Statistica and Matlab* , 2003.
15. Mayer, C., Hanenberg, S., Robbes, R., Tanter, É., Stefik, A. An empirical study of the influence of static type systems on the usability of undocumented software. In *ACM SIGPLAN Notices* , Volume 47 (2012). ACM, 683–702.
16. Meyerovich, L.A., Rabkin, A.S. Empirical analysis of programming language adoption. In *Proceedings of the 2013 ACM SIGPLAN International Conference on Object Oriented Programming Systems Languages & Applications* (2013). ACM, 1–18.
17. Milner, R. A theory of type polymorphism in programming. *J. Comput. Syst. Sci. 17* , 3 (1978), 348–375.
18. Mockus, A., Votta, L.G. Identifying reasons for software changes using historic databases. In *ICSM'00. Proceedings of the International Conference on Software Maintenance* (2000). IEEE Computer Society, 120.
19. Odersky, M., Spoon, L., Venners, B. *Programming in Scala.* Artima Inc, 2008.
20. Pankratius, V., Schmidt, F., Garretón, G. Combining functional and imperative programming for multicore software: An empirical study evaluating scala and java. In *Proceedings of the 2012 International Conference on Software Engineering* (2012). IEEE Press, 123–133.
21. Petricek, T., Skeet, J. *Real World Functional Programming: With Examples in F# and C#.* Manning Publications Co., 2009.
22. Pierce, B.C. *Types and Programming Languages.* MIT Press, 2002.
23. Posnett, D., Bird, C., Dévanbu, P. An empirical study on the influence of pattern roles on change-proneness. *Emp. Softw. Eng. 16* , 3 (2011), 396–423.
24. Tan, L., Liu, C., Li, Z., Wang, X., Zhou, Y., Zhai, C. Bug characteristics in open source software. *Emp. Softw. Eng.* (2013).

### 作者

**Baishakhi Ray** ([rayb@virginia.edu](mailto:rayb@virginia.edu)), Department of Computer Science, University of Virginia, Charlottesville, VA.

**Daryl Posnett** ([dpposnett@ucdavis.edu](mailto:dpposnett@ucdavis.edu)), Department of Computer Science, University of California, Davis, CA.

**Premkumar Devanbu** ([devanbu@cs.ucdavis.edu](mailto:devanbu@cs.ucdavis.edu)), Department of Computer Science, University of California, Davis, CA.

**Vladimir Filkov** ([filkov@cs.ucdavis.edu](mailto:filkov@cs.ucdavis.edu)), Department of Computer Science, University of California, Davis, CA.

### 脚注

* a. Wikipedia's article on type conversion, <https://en.wikipedia.org/wiki/Type_conversion>, has more examples of unintended behavior in C.
* b. This Apple developer article describes the usage of "id" <http://tinyurl.com/jkl7cby>.
* c. Some examples can be found here <http://dobegin.com/objc-id-type/> and here <http://tinyurl.com/hxv8kvg>.
* d. Chi-squared value of 243.6 with 96 df. and p = 8.394e–15
* e. MPI does not require locking of shared resources.

---

via: <https://cacm.acm.org/magazines/2017/10/221326-a-large-scale-study-of-programming-languages-and-code-quality-in-github/fulltext?imm_mid=0f7103&cmp=em-prog-na-na-newsltr_20171007>

作者：[Baishakhi Ray](http://delivery.acm.org/10.1145/3130000/3126905/mailto:rayb@virginia.edu), [Daryl Posnett](http://delivery.acm.org/10.1145/3130000/3126905/mailto:dpposnett@ucdavis.edu), [Premkumar Devanbu](http://delivery.acm.org/10.1145/3130000/3126905/mailto:devanbu@cs.ucdavis.edu), [Vladimir Filkov](http://delivery.acm.org/10.1145/3130000/3126905/mailto:filkov@cs.ucdavis.edu)  译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

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


10 条加速 Ubuntu Linux 的杀手级技巧

Summary: 一些实用的加速 Ubuntu Linux 的技巧。 这里的技巧对于大多数版本的 Ubuntu 是有效的，也可以应用于 Linux Mint 以及其他的基于 Ubuntu 的发行版。

>
> 一些实际的**加速 Ubuntu Linux 的技巧**。 这里的技巧对于大多数版本的 Ubuntu 是有效的，也可以应用于 Linux Mint 以及其他的基于 Ubuntu 的发行版。
>
>
>

也许你经历过使用 Ubuntu 一段时间后系统开始运行缓慢的情况。 在这篇文章里，我们将看到几项调整以及**使 Ubuntu 运行更快的窍门**。

在我们了解如何提升 Ubuntu 的总体系统表现之前，首先让我们思考为什么系统逐渐变慢。这个问题可能有很多原因。也许你有一台只有基础配置的简陋的电脑；也许你安装了一些在启动时即耗尽资源的应用。事实上原因无穷无尽。

这里我列出一些能够帮助你稍微加速 Ubuntu 的小调整。也有一些你能够采用以获取一个更流畅、有所提升的系统表现的经验。你可以选择遵循全部或部分的建议。将各项调整一点一点的结合就能给你一个更流畅、更迅捷快速的 Ubuntu。

### 使 Ubuntu 更快的技巧

![Tips to speed up Ubuntu](/data/attachment/album/201806/29/084505zm0hjcwjlkpa0w8m.jpg)

我在一个较老版本的 Ubuntu 上使用了这些调整，但是我相信其他的 Ubuntu 版本以及其他的例如 Linux Mint、 Elementary OS Luna 等基 Ubuntu 的 Linux 版本也是同样适用的。

#### 1、 减少默认的 grub 载入时间

Grub 给你 10 秒的时间以让你在多系统启动项或恢复模式之间改变。对我而言，它是多余的。它也意味着你将不得不坐在电脑旁，敲下回车键以尽可能快的启动进入 Ubuntu。这花了一点时间，不是吗？ 第一个技巧便是改变这个启动时间。如果你使用图形工具更舒适，阅读这篇文章来[使用 Grub 定制器改变 grub 时间以及启动顺序](https://itsfoss.com/windows-default-os-dual-boot-ubuntu-1304-easy/ "Make Windows Default OS In Dual Boot With Ubuntu 13.04: The Easy Way")。

如果更倾向于命令行，你可以简单地使用以下的命令来打开 grub 配置：

```
sudo gedit /etc/default/grub &

```

并且将 `GRUB_TIMEOUT=10` 改为 `GRUB_TIMEOUT=2`。这将改变启动时间为 2 秒。最好不要将这里改为 0，因为这样你将会失去在操作系统及恢复选项之间切换的机会。一旦你更改了 grub 配置，使用以下命令来使更改生效：

```
sudo update-grub

```

#### 2、 管理开机启动的应用

渐渐地你开始安装各种应用。 如果你是我们的老读者, 你也许从 [App of the week](https://itsfoss.com/tag/app-of-the-week/) 系列安装了许多应用。

这些应用中的一些在每次开机时都会启动，当然资源运行这些应用也会陷入繁忙。结果：一台电脑因为每次启动时的持续时间而变得缓慢。进入 Unity Dash 寻找 “Startup Applications”：

![](/data/attachment/album/201806/29/084506eygjgge7y2fkz3f1.jpg)

在这里，看看哪些应用在开机时被载入。现在考虑在你每次启动 Ubuntu 时是否有不需要启动的应用。尽管移除它们：

![](/data/attachment/album/201806/29/084507t1djdzl1odfo9f1d.jpg)

但是要是你不想从启动里移除它们怎么办？举个例子，如果你安装了 [Ubuntu 最好的指示器程序](https://itsfoss.com/best-indicator-applets-ubuntu/ "7 Best Indicator Applets For Ubuntu 13.10")之一， 你将想要它们在每次开机时自动地启动。

这里你所能做的就是延迟一些程序的启动时间。这样你将能够释放开机启动时的资源，并且一段时间后你的应用将被自动启动。在上一张图片里点击 Edit 并使用 sleep 选项来更改运行命令。

例如，如果你想要延迟 Dropbox 指示器的运行，我们指定时间 20 秒，你只需要在已有的命令里像这样**加入一个命令**：

```
sleep 20;

```

所以，命令 `dropbox start -i` 变为 `sleep 20; drobox start -i` 。这意味着现在 Dropbox 将延迟 20 秒启动。你可以通过相似的方法来改变另一个开机启动应用的启动时间。

![](/data/attachment/album/201806/29/084508jo9ggga5v58ivirv.jpg)

#### 3、 安装 preload 来加速应用载入时间：

Preload 是一个后台运行的守护进程，它分析用户行为和频繁运行的应用。打开终端，使用如下的命令来安装 preload：

```
sudo apt-get install preload

```

安装后，重启你的电脑就不用管它了。它将在后台工作。[阅读更多关于preload](https://itsfoss.com/improve-application-startup-speed-with-preload-in-ubuntu/ "Improve Application Startup Speed With Preload in Ubuntu")

#### 4、 选择最好的软件更新镜像

验证你更新软件是否正在使用最好的镜像是很好的做法。Ubuntu 的软件仓库镜像跨过全球，使用离你最近的一个是相当明智的。随着从服务器获取包的时间减少，这将形成更快的系统更新。

在 “Software & Updates->Ubuntu Software tab->Download From” 里选择 “Other” 紧接着点击 “Select Best Server”：

![](/data/attachment/album/201806/29/084509kg7widyxoadhf4ix.jpg)

它将运行测试来告知你那个是最好的镜像。正常地，最好的镜像已经被设置，但是我说过，验证它没什么坏处。并且，如果仓库缓存的最近的镜像没有频繁更新的话，这将引起获取更新时的一些延迟。这对于网速相对慢的人们是有用的。你可以使用这些技巧来[加速 Ubuntu 的 wifi](https://itsfoss.com/speed-up-slow-wifi-connection-ubuntu/ "Speed Up Slow WiFi Connection In Ubuntu 13.04")。

#### 5、 为了更快的更新，使用 apt-fast 而不是 apt-get

`apt-fast` 是 `apt-get` 的一个 shell 脚本包装器，通过从多连接同时下载包来提升更新及包下载速度。 如果你经常使用终端以及 `apt-get` 来安装和更新包，你也许会想要试一试 `apt-fast`。使用下面的命令来通过官方 PPA 安装 `apt-fast`：

```
sudo add-apt-repository ppa:apt-fast/stable
sudo apt-get update
sudo apt-get install apt-fast

```

#### 6、 从 apt-get 更新移除语言相关的 ign

你曾经注意过 `sudo apt-get` 更新的输出吗？其中有三种行，`hit`、`ign` 和 `get`。 你可以在[这里](http://ubuntuforums.org/showthread.php?t=231300)阅读它们的意义。如果你看到 IGN 行，你会发现它们中的大多数都与语言翻译有关。如果你使用所有的英文应用及包，你将完全不需要英文向英文的包数据库的翻译。

如果你从 `apt-get` 制止语言相关的更新，它将略微地增加 `apt-get` 的更新速度。为了那样，打开如下的文件：

```
sudo gedit /etc/apt/apt.conf.d/00aptitude

```

然后在文件末尾添加如下行：

```
Acquire::Languages "none";

```

[![speed up apt get update in Ubuntu](/data/attachment/album/201806/29/084510yvpjukzlj60vlvvs.jpg)](https://itsfoss.com/wp-content/uploads/2014/01/ign_language-apt_get_update.jpeg)

#### 7、 减少过热

现在过热是电脑普遍的问题。一台过热的电脑运行相当缓慢。当你的 CPU 风扇转得像 [尤塞恩·博尔特](http://en.wikipedia.org/wiki/Usain_Bolt) 一样快，打开一个程序将花费很长的时间。有两个工具你可以用来减少过热，使 Ubuntu 获得更好的系统表现，即 TLP 和 CPUFREQ。

在终端里使用以下命令来安装 TLP：

```
sudo add-apt-repository ppa:linrunner/tlp
sudo apt-get update
sudo apt-get install tlp tlp-rdw
sudo tlp start

```

安装完 TLP 后你不需要做任何事。它在后台工作。

使用如下命令来安装 CPUFREQ 指示器：

```
sudo apt-get install indicator-cpufreq

```

重启你的电脑并使用 Powersave 模式：

![](/data/attachment/album/201806/29/084512r2mx2tshm58qq6cs.jpg)

#### 8、 调整 LibreOffice 来使它更快

如果你是频繁使用 office 产品的用户，那么你会想要稍微调整默认的 LibreOffice 使它更快。这里你将调整内存选项。打开 Open LibreOffice，进入 “Tools->Options”。在那里，从左边的侧栏选择“Memory”并启用 “Systray Quickstarter” 以及增加内存分配。

![](/data/attachment/album/201806/29/084513kjtttzwfaatsspxf.jpg)

你可以阅读更多关于[如何提速 LibreOffice](https://itsfoss.com/speed-libre-office-simple-trick/ "Speed Up LibreOffice With This Simple Trick") 的细节。

#### 9、 使用轻量级的桌面环境 (如果你可以)

如果你选择安装默认的 Unity of GNOME 桌面环境, 你也许会选择一个轻量级的桌面环境像 [Xfce](https://xfce.org/) 或 [LXDE](https://lxde.org/)。

这些桌面环境使用更少的内存，消耗更少的 CPU。它们也自带轻量应用集来更深入地帮助更快地使用 Ubuntu。你可以参考这篇详细指南来学习[如何在 Ubuntu 上安装 Xfce](https://itsfoss.com/install-xfce-desktop-xubuntu/)。

当然，桌面也许没有 Unity 或 GNOME 看起来现代化。那是你必须做出的妥协。

#### 10、 使用不同应用的更轻量可选

这不仅仅是建议和喜好。一些默认的或者流行的应用是耗资源的且可能不适合低端的电脑。你能做的就是使用这些应用的一些替代品。例如，使用 [AppGrid](https://itsfoss.com/app-grid-lighter-alternative-ubuntu-software-center/ "App Grid: Lighter Alternative Of Ubuntu Software Center") 而不是 Ubuntu 软件中心。使用 [Gdebi](https://itsfoss.com/install-deb-files-easily-and-quickly-in-ubuntu-12-10-quick-tip/ "Install .deb Files Easily And Quickly In Ubuntu 12.10 [Quick Tip]") 来安装包。使用 AbiWord 而不是 LibreOffice Writer 等。

可以断定这些技巧的汇总使 Ubuntu 14.04，16.04 以及其他版本更快。我确定这些技巧会提供一个总体上更好的系统表现。

对于加速 Ubuntu 你也有妙计吗？这些技巧也帮到你了吗？分享你的观点。 问题，建议总是受欢迎的。请在评论区里提出来。

---

via: <https://itsfoss.com/speed-up-ubuntu-1310/>

作者：[Abhishek Prakash](https://itsfoss.com/author/abhishek/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[darsh8](https://github.com/darsh8) 校对：[wxy](https://github.com/wxy)

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


如何暂时禁用 iptables 防火墙

这个操作的话、禁用就是把iptables规则全部清空吗？

iptables的服务不是还在吗、如果我新安装一个软件需要用到防火墙、岂不是这个软件会自动添加iptables规则?

iptables和firewall能共存吗？  我看测试优先权还是iptables生效

有没有办法能够完全让firewall替换iptables呢？

谢谢

>
> 了解如何在 Linux 中暂时禁用 iptables 防火墙来进行故障排除。还要学习如何保存策略以及如何在启用防火墙时恢复它们。
>
>
>

![How to disable iptables firewall temporarily](/data/attachment/album/201806/30/090750t2j8purzppwjj5bq.png)

有时你需要关闭 iptables 防火墙来做一些连接故障排除，然后你需要重新打开它。在执行此操作时，你还需要保存所有[防火墙策略](https://kerneltalks.com/networking/configuration-of-iptables-policies/)。在本文中，我们将引导你了解如何保存防火墙策略以及如何禁用/启用 iptables 防火墙。有关 iptables 防火墙和策略的更多详细信息[请阅读我们的文章](https://kerneltalks.com/networking/basics-of-iptables-linux-firewall/)。

### 保存 iptables 策略

临时禁用 iptables 防火墙的第一步是保存现有的防火墙规则/策略。`iptables-save` 命令列出你可以保存到服务器中的所有现有策略。

```
root@kerneltalks # # iptables-save
# Generated by iptables-save v1.4.21 on Tue Jun 19 09:54:36 2018
*nat
:PREROUTING ACCEPT [1:52]
:INPUT ACCEPT [1:52]
:OUTPUT ACCEPT [15:1140]
:POSTROUTING ACCEPT [15:1140]
:DOCKER - [0:0]
---- output trucated----

root@kerneltalks # iptables-save > /root/firewall_rules.backup

```

因此，iptables-save 是可以用来备份 iptables 策略的命令。

### 停止/禁用 iptables 防火墙

对于较老的 Linux 内核，你可以选择使用 `service iptables stop` 停止 iptables 服务，但是如果你在用新内核，则只需清除所有策略并允许所有流量通过防火墙。这和你停止防火墙效果一样。

使用下面的命令列表来做到这一点。

```
root@kerneltalks # iptables -F
root@kerneltalks # iptables -X
root@kerneltalks # iptables -P INPUT ACCEPT
root@kerneltalks # iptables -P OUTPUT ACCEPT
root@kerneltalks # iptables -P FORWARD ACCEPT

```

这里 –

* `-F`：删除所有策略链
* `-X`：删除用户定义的链
* `-P INPUT/OUTPUT/FORWARD` ：接受指定的流量

完成后，检查当前的防火墙策略。它应该看起来像下面这样接受所有流量（和禁用/停止防火墙一样）

```
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

```

### 恢复防火墙策略

故障排除后，你想要重新打开 iptables 的所有配置。你需要先从我们在第一步中执行的备份中恢复策略。

```
root@kerneltalks # iptables-restore </root/firewall_rules.backup

```

### 启动 iptables 防火墙

然后启动 iptables 服务，以防止你在上一步中使用 `service iptables start` 停止了它。如果你已经停止服务，那么只有恢复策略才能有用。检查所有策略是否恢复到 iptables 配置中：

```
#  iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy DROP)
target     prot opt source               destination
DOCKER-USER  all  --  anywhere             anywhere
DOCKER-ISOLATION-STAGE-1  all  --  anywhere             anywhere
-----output truncated-----

```

就是这些了！你已成功禁用并启用了防火墙，而不会丢失你的策略规则。

---

via: <https://kerneltalks.com/howto/how-to-disable-iptables-firewall-temporarily/>

作者：[kerneltalks](https://kerneltalks.com) 选题：[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: 学习密码学背后的基本概念，主要是保密性、完整性和身份认证。

学习密码学背后的基本概念，主要是保密性、完整性和身份认证。

>
> 学习密码学背后的基本概念，主要是保密性、完整性和身份认证。
>
>
>

![](/data/attachment/album/201806/30/093605tjvejilcrs5qlc40.jpg)

安全通信正快速成为当今互联网的规范。从 2018 年 7 月起，Google Chrome 将对**全部**使用 HTTP 传输（而不是 HTTPS 传输）的站点[开始显示“不安全”警告](https://security.googleblog.com/2018/02/a-secure-web-is-here-to-stay.html)。虽然密码学已经逐渐变广为人知，但其本身并没有变得更容易理解。[Let's Encrypt](https://letsencrypt.org/) 设计并实现了一套令人惊叹的解决方案，可以提供免费安全证书和周期性续签；但如果不了解底层概念和缺陷，你也不过是加入了类似“[<ruby> 货物崇拜 <rt>  cargo cult </rt></ruby>](https://en.wikipedia.org/wiki/Cargo_cult_programming)”的技术崇拜的程序员大军。

### 安全通信的特性

密码学最直观明显的目标是<ruby> 保密性 <rt>  confidentiality </rt></ruby>：<ruby> 消息 <rt>  message </rt></ruby>传输过程中不会被窥探内容。为了保密性，我们对消息进行加密：对于给定消息，我们结合一个<ruby> 密钥 <rt>  key </rt></ruby>生成一个无意义的乱码，只有通过相同的密钥逆转加密过程（即解密过程）才能将其转换为可读的消息。假设我们有两个朋友 [Alice 和 Bob](https://en.wikipedia.org/wiki/Alice_and_Bob)，以及他们的<ruby> 八卦 <rt>  nosy </rt></ruby>邻居 Eve。Alice 加密类似 "Eve 很讨厌" 的消息，将其发送给 Bob，期间不用担心 Eve 会窥探到这条消息的内容。

对于真正的安全通信，保密性是不够的。假如 Eve 收集了足够多 Alice 和 Bob 之间的消息，发现单词 “Eve” 被加密为 "Xyzzy"。除此之外，Eve 还知道 Alice 和 Bob 正在准备一个派对，Alice 会将访客名单发送给 Bob。如果 Eve 拦截了消息并将 “Xyzzy” 加到访客列表的末尾，那么她已经成功的破坏了这个派对。因此，Alice 和 Bob 需要他们之间的通信可以提供<ruby> 完整性 <rt>  integrity </rt></ruby>：消息应该不会被篡改。

而且我们还有一个问题有待解决。假如 Eve 观察到 Bob 打开了标记为“来自 Alice”的信封，信封中包含一条来自 Alice 的消息“再买一加仑冰淇淋”。Eve 看到 Bob 外出，回家时带着冰淇淋，这样虽然 Eve 并不知道消息的完整内容，但她对消息有了大致的了解。Bob 将上述消息丢弃，但 Eve 找出了它并在下一周中的每一天都向 Bob 的邮箱中投递一封标记为“来自 Alice”的信封，内容拷贝自之前 Bob 丢弃的那封信。这样到了派对的时候，冰淇淋严重超量；派对当晚结束后，Bob 分发剩余的冰淇淋，Eve 带着免费的冰淇淋回到家。消息是加密的，完整性也没问题，但 Bob 被误导了，没有认出发信人的真实身份。<ruby> 身份认证 <rt>  Authentication </rt></ruby>这个特性用于保证你正在通信的人的确是其声称的那样。

信息安全还有[其它特性](https://en.wikipedia.org/wiki/Information_security#Availability)，但保密性、完整性和身份验证是你必须了解的三大特性。

### 加密和加密算法

加密都包含哪些部分呢？首先，需要一条消息，我们称之为<ruby> 明文 <rt>  plaintext </rt></ruby>。接着，需要对明文做一些格式上的初始化，以便用于后续的加密过程（例如，假如我们使用<ruby> 分组加密算法 <rt>  block cipher </rt></ruby>，需要在明文尾部填充使其达到特定长度）。下一步，需要一个保密的比特序列，我们称之为<ruby> 密钥 <rt>  key </rt></ruby>。然后，基于私钥，使用一种加密算法将明文转换为<ruby> 密文 <rt>  ciphertext </rt></ruby>。密文看上去像是随机噪声，只有通过相同的加密算法和相同的密钥（在后面提到的非对称加密算法情况下，是另一个数学上相关的密钥）才能恢复为明文。

（LCTT 译注：cipher 一般被翻译为密码，但其具体表达的意思是加密算法，这里采用加密算法的翻译）

加密算法使用密钥加密明文。考虑到希望能够解密密文，我们用到的加密算法也必须是<ruby> 可逆的 <rt>  reversible </rt></ruby>。作为简单示例，我们可以使用 [XOR](https://en.wikipedia.org/wiki/XOR_cipher)。该算子可逆，而且逆算子就是本身（`P ^ K = C; C ^ K = P`），故可同时用于加密和解密。该算子的平凡应用可以是<ruby> 一次性密码本 <rt>  one-time pad </rt></ruby>，不过一般而言并不[可行](https://en.wikipedia.org/wiki/One-time_pad#Problems)。但可以将 XOR 与一个基于单个密钥生成<ruby> 任意随机数据流 <rt>  arbitrary stream of random data </rt></ruby>的函数结合起来。现代加密算法 AES 和 Chacha20 就是这么设计的。

我们把加密和解密使用同一个密钥的加密算法称为<ruby> 对称加密算法 <rt>  symmetric cipher </rt></ruby>。对称加密算法分为<ruby> 流加密算法 <rt>  stream ciphers </rt></ruby>和分组加密算法两类。流加密算法依次对明文中的每个比特或字节进行加密。例如，我们上面提到的 XOR 加密算法就是一个流加密算法。流加密算法适用于明文长度未知的情形，例如数据从管道或 socket 传入。[RC4](https://en.wikipedia.org/wiki/RC4) 是最为人知的流加密算法，但在多种不同的攻击面前比较脆弱，以至于最新版本 （1.3）的 TLS （“HTTPS” 中的 “S”）已经不再支持该加密算法。[Efforts](https://en.wikipedia.org/wiki/ESTREAM) 正着手创建新的加密算法，候选算法 [ChaCha20](https://en.wikipedia.org/wiki/Salsa20) 已经被 TLS 支持。

分组加密算法对固定长度的分组，使用固定长度的密钥加密。在分组加密算法领域，排行第一的是 [<ruby> 先进加密标准 <rt>  Advanced Encryption Standard </rt></ruby>](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)（AES），使用的分组长度为 128 比特。分组包含的数据并不多，因而分组加密算法包含一个[工作模式](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation)，用于描述如何对任意长度的明文执行分组加密。最简单的工作模式是 [<ruby> 电子密码本 <rt>  Electronic Code Book </rt></ruby>](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#/media/File:ECB_encryption.svg)（ECB），将明文按分组大小划分成多个分组（在必要情况下，填充最后一个分组），使用密钥独立的加密各个分组。

![](/data/attachment/album/201806/30/093247tak4vjkmqkfu09aj.png)

这里我们留意到一个问题：如果相同的分组在明文中出现多次（例如互联网流量中的 `GET / HTTP/1.1` 词组），由于我们使用相同的密钥加密分组，我们会得到相同的加密结果。我们的安全通信中会出现一种<ruby> 模式规律 <rt>  pattern </rt></ruby>，容易受到攻击。

因此还有很多高级的工作模式，例如 [<ruby> 密码分组链接 <rt>  Cipher Block Chaining </rt></ruby>](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#/media/File:CBC_encryption.svg)（CBC），其中每个分组的明文在加密前会与前一个分组的密文进行 XOR 操作，而第一个分组的明文与一个随机数构成的初始化向量进行 XOR 操作。还有其它一些工作模式，在安全性和执行速度方面各有优缺点。甚至还有 Counter (CTR) 这种工作模式，可以将分组加密算法转换为流加密算法。

![](/data/attachment/album/201806/30/093247zvbrrskvtvzsptrz.png)

除了对称加密算法，还有<ruby> 非对称加密算法 <rt>  asymmetric ciphers </rt></ruby>，也被称为<ruby> 公钥密码学 <rt>  public-key cryptography </rt></ruby>。这类加密算法使用两个密钥：一个<ruby> 公钥 <rt>  public key </rt></ruby>，一个<ruby> 私钥 <rt>  private key </rt></ruby>。公钥和私钥在数学上有一定关联，但可以区分二者。经过公钥加密的密文只能通过私钥解密，经过私钥加密的密文可以通过公钥解密。公钥可以大范围分发出去，但私钥必须对外不可见。如果你希望和一个给定的人通信，你可以使用对方的公钥加密消息，这样只有他们的私钥可以解密出消息。在非对称加密算法领域，目前 [RSA](https://en.wikipedia.org/wiki/RSA_(cryptosystem)) 最具有影响力。

非对称加密算法最主要的缺陷是，它们是<ruby> 计算密集型 <rt>  computationally expensive </rt></ruby>的。那么使用对称加密算法可以让身份验证更快吗？如果你只与一个人共享密钥，答案是肯定的。但这种方式很快就会失效。假如一群人希望使用对称加密算法进行两两通信，如果对每对成员通信都采用单独的密钥，一个 20 人的群体将有 190 对成员通信，即每个成员要维护 19 个密钥并确认其安全性。如果使用非对称加密算法，每个成员仅需确保自己的私钥安全并维护一个公钥列表即可。

非对称加密算法也有加密[数据长度](https://security.stackexchange.com/questions/33434/rsa-maximum-bytes-to-encrypt-comparison-to-aes-in-terms-of-security)限制。类似于分组加密算法，你需要将长消息进行划分。但实际应用中，非对称加密算法通常用于建立<ruby> 机密 <rt>  confidential </rt></ruby>、<ruby> 已认证 <rt>  authenticated </rt></ruby>的<ruby> 通道 <rt>  channel </rt></ruby>，利用该通道交换对称加密算法的共享密钥。考虑到速度优势，对称加密算法用于后续的通信。TLS 就是严格按照这种方式运行的。

### 基础

安全通信的核心在于随机数。随机数用于生成密钥并为<ruby> 确定性过程 <rt>  deterministic processes </rt></ruby>提供不可预测性。如果我们使用的密钥是可预测的，那我们从一开始就可能受到攻击。计算机被设计成按固定规则操作，因此生成随机数是比较困难的。计算机可以收集鼠标移动或<ruby> 键盘计时 <rt>  keyboard timings </rt></ruby>这类随机数据。但收集随机性（也叫<ruby> 信息熵 <rt>  entropy </rt></ruby>）需要花费不少时间，而且涉及额外处理以确保<ruby> 均匀分布 <rt>  uniform distribution </rt></ruby>。甚至可以使用专用硬件，例如[<ruby> 熔岩灯 <rt>  lava lamps </rt></ruby>墙](https://www.youtube.com/watch?v=1cUUfMeOijg)等。一般而言，一旦有了一个真正的随机数值，我们可以将其用作<ruby> 种子 <rt>  seed </rt></ruby>，使用<ruby> 密码安全的伪随机数生成器 <rt>  cryptographically secure pseudorandom number generator </rt></ruby>生成随机数。使用相同的种子，同一个随机数生成器生成的随机数序列保持不变，但重要的是随机数序列是无规律的。在 Linux 内核中，[/dev/random 和 /dev/urandom](https://www.2uo.de/myths-about-urandom/) 工作方式如下：从多个来源收集信息熵，进行<ruby> 无偏处理 <rt>  remove biases </rt></ruby>，生成种子，然后生成随机数，该随机数可用于 RSA 密钥生成等。

### 其它密码学组件

我们已经实现了保密性，但还没有考虑完整性和身份验证。对于后两者，我们需要使用一些额外的技术。

首先是<ruby> 密码散列函数 <rt>  crytographic hash function </rt></ruby>，该函数接受任意长度的输入并给出固定长度的输出（一般称为<ruby> 摘要 <rt>  digest </rt></ruby>）。如果我们找到两条消息，其摘要相同，我们称之为<ruby> 碰撞 <rt>  collision </rt></ruby>，对应的散列函数就不适合用于密码学。这里需要强调一下“找到”：考虑到消息的条数是无限的而摘要的长度是固定的，那么总是会存在碰撞；但如果无需海量的计算资源，我们总是能找到发生碰撞的消息对，那就令人比较担心了。更严重的情况是，对于每一个给定的消息，都能找到与之碰撞的另一条消息。

另外，哈希函数必须是<ruby> 单向的 <rt>  one-way </rt></ruby>：给定一个摘要，反向计算对应的消息在计算上不可行。相应的，这类[条件](https://crypto.stackexchange.com/a/1174)被称为<ruby> 碰撞阻力 <rt>  collision resistance </rt></ruby>、<ruby> 第二原象抗性 <rt>  second preimage resistance </rt></ruby>和<ruby> 原象抗性 <rt>  preimage resistance </rt></ruby>。如果满足这些条件，摘要可以用作消息的指纹。[理论上](https://www.telegraph.co.uk/science/2016/03/14/why-your-fingerprints-may-not-be-unique/)不存在具有相同指纹的两个人，而且你无法使用指纹反向找到其对应的人。

如果我们同时发送消息及其摘要，接收者可以使用相同的哈希函数独立计算摘要。如果两个摘要相同，可以认为消息没有被篡改。考虑到 [SHA-1](https://en.wikipedia.org/wiki/SHA-1) 已经变得[有些过时](https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html)，目前最流行的密码散列函数是 [SHA-256](https://en.wikipedia.org/wiki/SHA-2)。

散列函数看起来不错，但如果有人可以同时篡改消息及其摘要，那么消息发送仍然是不安全的。我们需要将哈希与加密算法结合起来。在对称加密算法领域，我们有<ruby> 消息认证码 <rt>  message authentication codes </rt></ruby>（MAC）技术。MAC 有多种形式，但<ruby> 哈希消息认证码 <rt>  hash message authentication codes </rt></ruby>（HMAC） 这类是基于哈希的。[HMAC](https://en.wikipedia.org/wiki/HMAC) 使用哈希函数 H 处理密钥 K、消息 M，公式为 `H(K + H(K + M))`，其中 `+` 代表<ruby> 连接 <rt>  concatenation </rt></ruby>。公式的独特之处并不在本文讨论范围内，大致来说与保护 HMAC 自身的完整性有关。发送加密消息的同时也发送 MAC。Eve 可以任意篡改消息，但一旦 Bob 独立计算 MAC 并与接收到的 MAC 做比较，就会发现消息已经被篡改。

在非对称加密算法领域，我们有<ruby> 数字签名 <rt>  digital signatures </rt></ruby>技术。如果使用 RSA，使用公钥加密的内容只能通过私钥解密，反过来也是如此；这种机制可用于创建一种签名。如果只有我持有私钥并用其加密文档，那么只有我的公钥可以用于解密，那么大家潜在的承认文档是我写的：这是一种身份验证。事实上，我们无需加密整个文档。如果生成文档的摘要，只要对这个指纹加密即可。对摘要签名比对整个文档签名要快得多，而且可以解决非对称加密存在的消息长度限制问题。接收者解密出摘要信息，独立计算消息的摘要并进行比对，可以确保消息的完整性。对于不同的非对称加密算法，数字签名的方法也各不相同；但核心都是使用公钥来检验已有签名。

### 汇总

现在，我们已经有了全部的主体组件，可以用其实现一个我们期待的、具有全部三个特性的[<ruby> 体系 <rt>  system </rt></ruby>](https://en.wikipedia.org/wiki/Hybrid_cryptosystem)。Alice 选取一个保密的对称加密密钥并使用 Bob 的公钥进行加密。接着，她对得到的密文进行哈希并使用其私钥对摘要进行签名。Bob 接收到密文和签名，一方面独立计算密文的摘要，另一方面使用 Alice 的公钥解密签名中的摘要；如果两个摘要相同，他可以确信对称加密密钥没有被篡改且通过了身份验证。Bob 使用私钥解密密文得到对称加密密钥，接着使用该密钥及 HMAC 与 Alice 进行保密通信，这样每一条消息的完整性都得到保障。但该体系没有办法抵御消息重放攻击（我们在 Eve 造成的冰淇淋灾难中见过这种攻击）。要解决重放攻击，我们需要使用某种类型的“<ruby> 握手 <rt>  handshake </rt></ruby>”建立随机、短期的<ruby> 会话标识符 <rt>  session identifier </rt></ruby>。

密码学的世界博大精深，我希望这篇文章能让你对密码学的核心目标及其组件有一个大致的了解。这些概念为你打下坚实的基础，让你可以继续深入学习。

感谢 Hubert Kario、Florian Weimer 和 Mike Bursell 在本文写作过程中提供的帮助。

---

via: <https://opensource.com/article/18/5/cryptography-pki>

作者：[Alex Wood](https://opensource.com/users/awood) 选题：[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/) 荣誉推出


用这样的 Vi 配置来保存和组织你的笔记

Summary: Vimwiki 和 GitLab 是记录笔记的强大组合。

>
> Vimwiki 和 GitLab 是记录笔记的强大组合。
>
>
>

![](/data/attachment/album/201806/30/103554uu5o5uue9zchu394.png)

用 vi 来管理 wiki 来记录你的笔记，这听起来不像是一个符合常规的主意，但当你的日常工作都会用到 vi , 那它是有意义的。

作为一个软件开发人员，使用同编码一样的工具来写笔记会更加简单。我想将我的笔记变成一种编辑器命令，无论我在哪里，都能够用管理我代码的方法来管理我的笔记。这便是我创建一个基于 vi 的环境来搭建我自己的知识库的原因。简单概括起来，我在笔记本电脑上用 vi 插件 [Viwiki](https://vimwiki.github.io/) 来本地管理我的 wiki。用 Git 来进行版本控制（以保留一个中心化的更新版本），并用 GitLab 来进行在线修改（例如在我的手机上）。

### 为什么用 wiki 来进行笔记保存是有意义

我尝试过许多不同的工具来持续的记录我的笔记，笔记里保存着我的灵感以及需要记住的任务安排。这包括线下的笔记本 （没错，纸质的）、特殊的记录笔记的软件，以及思维导图软件。

但每种方案都有不好一面，没有一个能够满足我所有的需求。例如[思维导图](https://opensource.com/article/17/8/mind-maps-creative-dashboard)，能够很好的形象化你的想法（因而得名），但是这种工具的搜索功能很差（和纸质笔记本一样）。此外，当一段时间过去，思维导图会变得很难阅读，所以思维导图不适合长时间保存的笔记。

我为一个合作项目配置了 [DokuWiki](https://vimwiki.github.io/)，我发现这个 wiki 模型符合了我大多数的需求。在 wiki 上，你能够创建一个笔记（和你在文本编辑器中所作的一样），并在笔记间创建链接。如果一个链接指向一个不存在的页面（你想让本页面添加一条还没有创建的信息）， wiki 会为你建立这个页面。这个特性使得 wiki 很好的适应了那些需要快速写下心中所想的人的需求，而仍将你的笔记保持在能够容易浏览和搜索关键字的页面结构中。

这看起来很有希望，并且配置 DokuWiki 也很容易，但我发现只是为了记个笔记而配置整个 wiki 需要花费太多工作。在一番搜索后，我发现了 Vimwiki，这是一个我想要的 vi 插件。因为我每天使用 vi，记录笔记就行编辑代码一样。甚至在 vimwiki 创建一个页面比 Dokuwiki 更简单。你只需要对光标下的单词按下回车键就行。如果没有文件是这个名字，vimwiki 会为你创建一个。

为了更一步的实现用每天都会使用的工具来做笔记的计划，我不仅用这个我最爱的 IDE 来写笔记，而且用 Git 和 GitLab —— 我最爱的代码管理工具 —— 在我的各个机器间分发我的笔记，以便我可以在线访问它们。我也是在 Gitlab 的在线 markdown 工具上用 markdown 语法来写的这篇文章。

### 配置 vimwiki

用你已有的插件管理工具来安装 vimwiki 很简单，只需要添加 vimwiki/vimwiki 到你的插件。对于我的喜爱的插件管理器 Vundle 来说，你只需要在 `/.vimrc` 中添加 `plugin vimwiki/vimwiki` 这一行，然后执行 `:source ~/.vimrc | PluginInstall` 就行。

下面是我的文件 `.vimrc` 的一部分，展示了一些 vimwiki 配置。你能在 [vimwiki](https://vimwiki.github.io/) 页面学到更多的配置和使用的的信息。

```
let wiki_1 = {}
let wiki_1.path = '~/vimwiki_work_md/'
let wiki_1.syntax = 'markdown'
let wiki_1.ext = '.md'

let wiki_2 = {}
let wiki_2.path = '~/vimwiki_personal_md/'
let wiki_2.syntax = 'markdown'
let wiki_2.ext = '.md'

let g:vimwiki_list = [wiki_1, wiki_2]
let g:vimwiki_ext2syntax = {'.md': 'markdown', '.markdown': 'markdown', '.mdown': 'markdown'}

```

如你在上述配置中所见，我的配置还有一个优点。你能简单的区分个人和工作相关的笔记，而不用切换笔记软件。我想让我的个人笔记可以随时随地访问，而不想我的工作笔记同步到我私人的 GitLab 和计算机中。在 vimwiki 这样配置要比我试过的其他软件都要简单。

这个配置告诉 vimwiki 有两个不同 Wiki，都使用 markdown 语法（再一次，因为我的日常工作中天天都在用 markdown 语法）。我也告诉 Vimwiki 在哪个文件夹存储 wiki 页面。

如果你进入存储 wiki 页面的文件夹，你会找到你的 wiki 的普通的 markdown 页面文件，而没有其他特殊的 Vimwiki 相关内容，这使得很容易的初始化 Git 仓库和同步你的 wiki 到中心仓库。

### 同步你的 wiki 到 GitLab

这一步检出一个 GitLab 项目到本地的 VimWiki 文件夹，这步操作和你操作任何 GitHub 的仓库相同，只不过因为我更喜欢保存我的笔记到我的私人 GitLab 仓库，所以我运行了一个 GitLab 实例用于我个人的项目。

GitLab 的 wiki 功能可以用来为你的项目创建 wiki 页面。这些 wiki 就是 Git 仓库本身。它们使用 markdown 语法，你懂得。

只需要初始化你需要的 wiki ，让它与你为笔记而创建的项目的 wiki 同步即可。

```
cd ~/vimwiki_personal_md/
git init
git remote add origin git@your.gitlab.com:your_user/vimwiki_personal_md.wiki
git add .
git commit -m "Initial commit"
git push -u origin master

```

在 GitLab 创建一个新的项目后，你就可以从页面上复制这些步骤的代码。唯一的改变是仓库地址结尾是 .wiki（而不是 .git）。 这会告诉 Git 克隆 wiki 仓库而不是项目本身。

就是这样！现在你能够通过 Git 来管理你的笔记，通过 GitLab wiki 用户界面来修改笔记。

你可能（像我一样）不想手动的为每个添加到笔记本的笔记创建一个提交。为了解决这个问题，我使用了 Vim 插件 [chazy/dirsetting](https://github.com/chazy/dirsettings)。我添加一个 `.vimaddr` 文件，已经下面的内容：

```
:cd %:p:h
silent! !git pull > /dev/null
:e!
autocmd! BufWritePost * silent! !git add .;git commit -m "vim autocommit" > /dev/null; git push > /dev/null&

```

每当我打开 Wiki 文件按下 `:w` 发布我的修改时，它就会更新到最新的版本。这样做会使你的本地文件与中心仓库保持同步。如果你有合并冲突，通常你需要解决它们。

目前，这就是以我的知识来互动的方法，我很喜欢这方法；请告诉我你对于这个方法的想法，可以在评论区分享你如何追踪笔记的方法。

---

via: <https://opensource.com/article/18/6/vimwiki-gitlab-notes>

作者：[Manuel Dewald](https://opensource.com/users/ntlx) 选题：[lujun9972](https://github.com/lujun9972) 译者：[octopus](https://github.com/singledo) 校对：[wxy](https://github.com/wxy)

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


如何在 Linux 中的特定时间运行命令

![](/data/attachment/album/201806/30/215416vzcnu0r888rr0hra.png)

有一天，我使用 `rsync` 将大文件传输到局域网上的另一个系统。由于它是非常大的文件，大约需要 20 分钟才能完成。我不想再等了，我也不想按 `CTRL+C` 来终止这个过程。我只是想知道在类 Unix 操作系统中是否有简单的方法可以在特定的时间运行一个命令，并且一旦超时就自动杀死它 —— 因此有了这篇文章。请继续阅读。

### 在 Linux 中在特定时间运行命令

我们可以用两种方法做到这一点。

#### 方法 1 - 使用 timeout 命令

最常用的方法是使用 `timeout` 命令。对于那些不知道的人来说，`timeout` 命令会有效地限制一个进程的绝对执行时间。`timeout` 命令是 GNU coreutils 包的一部分，因此它预装在所有 GNU/Linux 系统中。

假设你只想运行一个命令 5 秒钟，然后杀死它。为此，我们使用：

```
$ timeout <time-limit-interval> <command>

```

例如，以下命令将在 10 秒后终止。

```
$ timeout 10s tail -f /var/log/pacman.log

```

![](/data/attachment/album/201806/30/215418ewnz5ikwitiiu9jt.gif)

你也可以不用在秒数后加后缀 `s`。以下命令与上面的相同。

```
$ timeout 10 tail -f /var/log/pacman.log

```

其他可用的后缀有：

* `m` 代表分钟。
* `h` 代表小时。
* `d` 代表天。

如果你运行这个 `tail -f /var/log/pacman.log` 命令，它将继续运行，直到你按 `CTRL+C` 手动结束它。但是，如果你使用 `timeout` 命令运行它，它将在给定的时间间隔后自动终止。如果该命令在超时后仍在运行，则可以发送 `kill` 信号，如下所示。

```
$ timeout -k 20 10 tail -f /var/log/pacman.log

```

在这种情况下，如果 `tail` 命令在 10 秒后仍然运行，`timeout` 命令将在 20 秒后发送一个 kill 信号并结束。

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

```
$ man timeout

```

有时，某个特定程序可能需要很长时间才能完成并最终冻结你的系统。在这种情况下，你可以使用此技巧在特定时间后自动结束该进程。

另外，可以考虑使用 `cpulimit`，一个简单的限制进程的 CPU 使用率的程序。有关更多详细信息，请查看下面的链接。

* [如何在 Linux 中限制一个进程的使用的 CPU](https://www.ostechnix.com/how-to-limit-cpu-usage-of-a-process-in-linux/)

#### 方法 2 - 使用 timelimit 程序

`timelimit` 使用提供的参数执行给定的命令，并在给定的时间后使用给定的信号终止进程。首先，它会发送警告信号，然后在超时后发送 kill 信号。

与 `timeout` 不同，`timelimit` 有更多选项。你可以传递参数数量，如 `killsig`、`warnsig`、`killtime`、`warntime` 等。它存在于基于 Debian 的系统的默认仓库中。所以，你可以使用命令来安装它：

```
$ sudo apt-get install timelimit

```

对于基于 Arch 的系统，它在 AUR 中存在。因此，你可以使用任何 AUR 助手进行安装，例如 [Pacaur](https://www.ostechnix.com/install-pacaur-arch-linux/)、[Packer](https://www.ostechnix.com/install-packer-arch-linux-2/)、[Yay](https://www.ostechnix.com/yay-found-yet-another-reliable-aur-helper/)、[Yaourt](https://www.ostechnix.com/install-yaourt-arch-linux/) 等。

对于其他发行版，请[在这里](http://devel.ringlet.net/sysutils/timelimit/#download)下载源码并手动安装。安装 `timelimit` 后，运行下面的命令执行一段特定的时间，例如 10 秒钟：

```
$ timelimit -t10 tail -f /var/log/pacman.log

```

如果不带任何参数运行 `timelimit`，它将使用默认值：`warntime=3600` 秒、`warnsig=15` 秒、`killtime=120` 秒、`killsig=9`。有关更多详细信息，请参阅本指南最后给出的手册页和项目网站。

```
$ man timelimit

```

今天就是这些。我希望对你有用。还有更好的东西。敬请关注！

干杯！

### 资源

* [timelimit 网站](http://devel.ringlet.net/sysutils/timelimit/)

---

via: <https://www.ostechnix.com/run-command-specific-time-linux/>

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

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


让我们从 GitHub 中迁移出来

Summary: 有些人可能认为微软的收购没有问题，但对我来说，这是压垮骆驼的最后一根稻草。

有些人可能认为微软的收购没有问题，但对我来说，这是压垮骆驼的最后一根稻草。

>
> 编者按：本文不代表本站观点，而且微软收购 GitHub 的后果目前尚未显现出来，本文只是提供了一个选择。
>
>
>

![](/data/attachment/album/201806/30/222202ocm5ue55pm29u3g4.jpg)

正如你们之前听到的那样，[微软收购了 GitHub](https://www.bloomberg.com/news/articles/2018-06-03/microsoft-is-said-to-have-agreed-to-acquire-coding-site-github)。这对 GitHub 的未来意味着什么尚不清楚，但 [Gitlab 的人](https://about.gitlab.com/2018/06/03/microsoft-acquires-github/)认为微软的最终目标是将 GitHub 整合到他们的 Azure 帝国。对我来说，这很有道理。

尽管我仍然不情愿地将 GitHub 用于某些项目，但我前一段时间将所有个人仓库迁移到了 Gitlab 中。现在是时候让你做同样的事情，并抛弃 GitHub。

有些人可能认为微软的收购没有问题，但对我来说，这是压垮骆驼的最后一根稻草。几年来，微软一直在开展一场关于他们如何热爱 Linux 的大型营销活动，并突然决定用它所有的形式拥抱自由软件。更像 MS BS 给我的。

让我们花点时间提醒自己：

* Windows 仍然是一个巨大的专有怪物，数十亿人每天都在丧失他们的隐私和权利。
* 微软公司（曾经）以传播自由软件的“危害”闻名，以防止政府和学校放弃 Windows，转而支持 FOSS。
* 为了确保他们的垄断地位，微软通过向全世界的小学颁发“免费”许可证来吸引孩子使用 Windows。毒品经销商使用相同的策略并提供免费样品来获取新客户。
* 微软的 Azure 平台 - 即使它可以运行 Linux 虚拟机 - 它仍然是一个巨大的专有管理程序。

我知道移动 git 仓库看起来像是一件痛苦的事情，但是 Gitlab 的员工正在趁着人们离开 GitHub 的浪潮，通过[提供 GitHub 导入器](https://docs.gitlab.com/ee/user/project/import/github.html)使迁移变得容易。

如果你不想使用 Gitlab 的主实例（[gitlab.org](https://gitlab.org)），下面是另外两个你可以用于自由软件项目的备选实例：

* [Debian Gitlab 实例](https://salsa.debian.org)适用于每个 FOSS 项目，它不仅适用于与 Debian 相关的项目。只要项目符合 [Debian 自由软件指南](https://en.wikipedia.org/wiki/Debian_Free_Software_Guidelines)，你就可以使用该实例及其 CI。
* Riseup 为名为 [0xacab](https://0xacab.org) 的激进项目维护了一个 Gitlab 实例。如果你的[理念与 Riseup 的一致](https://riseup.net/en/about-us/politics)，他们很乐意在那里托管你的项目。

朋友不要再让人使用 Github 了。

---

via: <https://veronneau.org/lets-migrate-away-from-github.html>

作者：[Louis-Philippe Véronneau](https://veronneau.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: 你的路由器是你与互联网之间的第一个联系点。它给你带来了多少风险？

你的路由器是你与互联网之间的第一个联系点。它给你带来了多少风险？

>
> 你的路由器是你与互联网之间的第一个联系点。它给你带来了多少风险？
>
>
>

![](/data/attachment/album/201806/30/224955u4nthdcim0dk8nmc.jpg)

我一直对写在 T 恤上的“127.0.0.1 是独一无二的地方” 这句话持有异议。我知道你可能会认为应该将它看作是“家”，但是对于我来说，我认为应该是“本地主机是独一无二的地方”，就像世界上没有完全相同的二个戒指一样。在本文中，我想去讨论一些宽泛的问题：家庭网络的入口，对大多数人来说它是线缆或宽带路由器。 1 英国和美国政府刚公布了“俄罗斯”  2 攻击路由器的通告。我估计这次的攻击主要针对的是机构，而不是家庭，（看我以前的文章 “[国家行为者是什么，我们应该注意些什么？](https://aliceevebob.com/2018/03/13/whats-a-state-actor-and-should-i-care/)”），但这对我们所有人都是一个警示。

### 路由器有什么用？

路由器很重要。它用于将一个网络（在本文中，是我们的家庭网络）与另外一个网络（在本文中，是指互联网，通过我们的互联网服务提供商的网络）连接。事实上，对于大多数人来说，我们所谓的“路由器”  3 这个小盒子能够做的事情远比我们想到的要多。“路由” 一个比特就就如其听起来的意思一样：它让网络中的计算机能够找到一条向外部网络中计算机发送数据的路径 —— 当你接收数据时，反之。

在路由器的其它功能中，大多数时候也作为一台调制解调器来使用。我们中的大部分人  4 到互联网的连接是通过电话线来实现的 —— 无论是电缆还是标准电话线 —— 尽管现在的最新趋势是通过移动互联网连接到家庭中。当你通过电话线连接时，我们所使用的互联网信号必须转换成其它的一些东西，（从另一端来的）返回信号也是如此。对于那些还记得过去的“拨号上网”时代的人来说，它就是你的电脑边上那个用于上网的发出刺耳声音的小盒子。

但是路由器能做的事情很多，有时候很多的事情，包括流量记录、作为一个无线接入点、提供 VPN 功能以便于从外部访问你的内网、儿童上网控制、防火墙等等。

现在的家用路由器越来越复杂；虽然国家行为者也行不会想着攻破它，但是其它人也许会。

你会问，这很重要吗？如果其它人可以进入你的系统，他们可以很容易地攻击你的笔记本电脑、电话、网络设备等等。他们可以访问和删除未被保护的个人数据。他们可以假装是你。他们使用你的网络去寄存非法数据或者用于攻击其它人。基本上所有的坏事都可以做。

幸运的是，现在的路由器趋向于由互联网提供商来做设置，言外之意是你可以忘了它的存在，他们将保证它是运行良好和安全的。

### 因此，我们是安全的吗？

不幸的是，事实并非如此。

第一个问题是，互联网提供商是在有限的预算范围内做这些事情，而使用便宜的设备来做这些事可以让他们的利益最大化。互联网提供商的路由器质量越来越差。它是恶意攻击者的首选目标：如果他们知道特定型号的路由器被安装在几百万个家庭使用，那么很容易找到攻击的动机，因为攻击那个型号的路由器对他们来说是非常有价值的。

产生的其它问题还包括：

* 修复 bug 或者漏洞的过程很缓慢。升级固件可能会让互联网提供商产生较高的成本，因此，修复过程可能非常缓慢（如果他们打算修复的话）。
* 非常容易获得或者默认的管理员密码，这意味着攻击者甚至都不需要去找到真实的漏洞 —— 他们就可以登入到路由器中。

### 对策

对于进入互联网第一跳的路由器，如何才能提升它的安全性，这里给你提供一个快速应对的清单。我是按从简单到复杂的顺序来列出它们的。在你对路由器做任何改变之前，需要先保存配置数据，以便于你需要的时候回滚它们。

1. **密码：** 一定，一定，一定要改变你的路由器的管理员密码。你可能很少会用到它，所以你一定要把密码记录在某个地方。它用的次数很少，你可以考虑将密码粘贴到路由器上，因为路由器一般都放置在仅供授权的人（你和你的家人  5 ）才可以接触到的地方。
2. **仅允许管理员从内部进行访问：** 除非你有足够好的理由和你知道如何去做，否则不要允许任何机器从外部的互联网上管理你的路由器。在你的路由器上有一个这样的设置。
3. **WiFi 密码：** 一旦你做到了第 2 点，也要确保在你的网络上的那个 WiFi 密码 —— 无论是设置为你的路由器管理密码还是别的 —— 一定要是强密码。为了简单，为连接你的网络的访客设置一个“友好的”简单密码，但是，如果附近一个恶意的人猜到了密码，他做的第一件事情就是查找网络中的路由器。由于他在内部网络，他是可以访问路由器的（因此，第 1 点很重要）。
4. **仅打开你知道的并且是你需要的功能：** 正如我在上面所提到的，现代的路由器有各种很酷的选项。不要使用它们。除非你真的需要它们，并且你真正理解了它们是做什么的，以及打开它们后有什么危险。否则，将增加你的路由器被攻击的风险。
5. **购买你自己的路由器：** 用一个更好的路由器替换掉互联网提供商给你的路由器。去到你本地的电脑商店，让他们给你一些建议。你可能会花很多钱，但是也可能会遇到一些非常便宜的设备，而且比你现在拥有的更好、性能更强、更安全。你也可以只买一个调制解调器。一般设置调制解调器和路由器都很简单，并且，你可以从你的互联网提供商给你的设备中复制配置，它一般就能“正常工作”。
6. **更新固件：** 我喜欢使用最新的功能，但是通常这并不容易。有时，你的路由器上会出现固件更新的提示。大多数的路由器会自动检查并且提示你在下次登入的时候去更新它。问题是如果更新失败则会产生灾难性后果  6 或者丢失配置数据，那就需要你重新输入。但是你真的需要考虑去持续关注修复安全问题的固件更新，并更新它们。
7. **转向开源：** 有一些非常好的开源路由器项目，可以让你用在现有的路由器上，用开源的软件去替换它的固件/软件。你可以在 [Wikipedia](https://en.wikipedia.org/wiki/List_of_router_firmware_projects) 上找到许多这样的项目，以及在 [Opensource.com 上搜索 “router”](https://opensource.com/sitewide-search?search_api_views_fulltext=router)，你将看到很多非常好的东西。对于谨慎的人来说要小心，这将会让你的路由器失去保修，但是如果你想真正控制你的路由器，开源永远是最好的选择。

### 其它问题

一旦你提升了你的路由器的安全性，你的家庭网络将变的很好——这是假像，事实并不是如此。你家里的物联网设备（Alexa、Nest、门铃、智能灯泡、等等）安全性如何？连接到其它网络的 VPN 安全性如何？通过 WiFi 的恶意主机、你的孩子手机上的恶意应用程序 …？

不，你永远不会有绝对的安全。但是正如我们前面讨论的，虽然没有绝对的“安全”这种事情，但是并不意味着我们不需要去提升安全标准，以让坏人干坏事更困难。

### 脚注

1. 我写的很简单 — 但请继续读下去，我们将达到目的。
2. “俄罗斯政府赞助的信息技术国家行为者”
3. 或者，以我父母的例子来说，我猜叫做 “互联网盒子”。
4. 这里还有一种这样的情况，我不希望在评论区告诉我，你是直接以 1TB/s 的带宽连接到本地骨干网络的。非常感谢！
5. 或许并没有包含整个家庭。
6. 你的路由器现在是一块“砖”，并且你不能访问互联网了。

这篇文章最初发表在 [Alice, Eve, 和 Bob – 安全博客](https://aliceevebob.com/2018/04/17/defending-our-homes/) 并授权重发布。

---

via: <https://opensource.com/article/18/5/how-insecure-your-router>

作者：[Mike Bursell](https://opensource.com/users/mikecamel) 选题：[lujun9972](https://github.com/lujun9972) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

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


Python 调试技巧

Summary: 这里有一些我最喜欢的 Python 调试技巧。

这里有一些我最喜欢的 Python 调试技巧。

![](/data/attachment/album/201807/02/085733el0oe0s9adjsdz2j.jpg)

当进行调试时，你有很多选择，但是很难给出一直有效的通用建议（除了“你试过关闭再打开么？”以外）。

这里有一些我最喜欢的 Python 调试技巧。

### 建立一个分支

请相信我。即使你从来没有打算将修改提交回上游，你也会很乐意将你的实验被包含在它们自己的分支中。

不说别的，它会使清理更容易！

### 安装 pdb++

认真地说，如果你使用命令行，它会让你的生活更轻松。

pdb++ 所做的一切就是用更好的模块替换标准的 pdb 模块。以下是你在 `pip install pdbpp` 会看到的：

* 彩色提示！
* 制表符补全！（非常适合探索！）
* 支持切分！

好的，也许最后一个是有点多余……但是非常认真地说，安装 pdb++ 非常值得。

### 探索

有时候最好的办法就是胡乱试试，然后看看会发生什么。在“明显”的位置放置一个断点并确保它被命中。在代码中加入 `print()` 和/或 `logging.debug()` 语句，并查看代码执行的位置。

检查传递给你的函数的参数，检查库的版本（如果你已经非常绝望了）。

### 一次只能改变一件事

在你在探索了一下后，你将会对你可以做的事情有所了解。但在你开始摆弄代码之前，先退一步，考虑一下你可以改变什么，然后只改变一件事。

做出改变后，然后测试一下，看看你是否接近解决问题。如果没有，请将它改回来，然后尝试其他方法。

只更改一件事就可以让你知道什可以工作，哪些不工作。另外，一旦可以工作后，你的新提交将会小得多（因为将有更少的变化）。

这几乎是<ruby> 科学过程 <rt>  Scientific Process </rt></ruby>中所做的事情：一次只更改一个变量。通过让自己看到并衡量一次更改的结果，你可以节省你的理智，并更快地找到解决方案。

### 不要假设，提出问题

偶尔一个开发人员（当然不是你咯！）会匆忙提交一些有问题的代码。当你去调试这段代码时，你需要停下来，并确保你明白它想要完成什么。

不要做任何假设。仅仅因为代码在 `model.py` 文件中并不意味着它不会尝试渲染一些 HTML。

同样，在做任何破坏性的事情之前，仔细检查你的所有外部关联。要删除一些配置数据？**请确保你没有连接到你的生产系统。**

### 聪明，但不要聪明过头

有时候我们编写的代码神奇般地奏效，不知道它是如何做的。

当我们发布代码时，我们可能会觉得自己很聪明，但当代码崩溃时，我们往往会感到愚蠢，我们必须记住它是如何工作的，以便弄清楚它为什么不起作用。

留意任何看起来过于复杂、冗长或极短的代码段。这些可能是隐藏复杂并导致错误的地方。

---

via: <https://pythondebugging.com/articles/python-debugging-tips>

作者：[PythonDebugging.com](https://pythondebugging.com) 选题：[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 文件系统详解

Summary: 这篇教程将帮你快速了解 Linux 文件系统。

超级棒的文章，已看完+收藏！！

漏了一个`

/dev 目录的条目中漏了一个 ’

这篇教程将帮你快速了解 Linux 文件系统。

>
> 这篇教程将帮你快速了解 Linux 文件系统。
>
>
>

![](/data/attachment/album/201807/02/094427lwhfavghagdqfcqm.jpg)

早在 1996 年，在真正理解文件系统的结构之前，我就学会了如何在我崭新的 Linux 上安装软件。这是一个问题，但对程序来说不是大问题，因为即使我不知道实际的可执行文件在哪里，它们也会神奇地工作。问题在于文档。

你知道，那时候，Linux 不是像今天这样直观、用户友好的系统。你必须读很多东西。你必须知道你的 CRT 显示器的扫描频率以及拨号调制解调器的噪音来龙去脉，以及其他数以百计的事情。 我很快就意识到我需要花一些时间来掌握目录的组织方式以及 `/etc`（不是用于“其它”文件），`/usr`（不是用于“用户”文件）和 `/bin` （不是“垃圾桶”）的意思。

本教程将帮助你比我当时更快地了解这些。

### 结构

从终端窗口探索 Linux 文件系统是有道理的，这并不是因为作者是一个脾气暴躁的老人，并且对新孩子和他们漂亮的图形工具不以为然（尽管某些事实如此），而是因为终端，尽管只是文本界面，才是更好地显示 Linux 目录树结构的工具。

事实上，帮助你了解这一切的、应该首先安装的第一个工具的名为：`tree`。如果你正在使用 Ubuntu 或 Debian ，你可以：

```
sudo apt install tree

```

在 Red Hat 或 Fedora :

```
sudo dnf install tree

```

对于 SUSE/openSUSE 可以使用 `zypper`：

```
sudo zypper install tree

```

对于使用 Arch （Manjaro，Antergos，等等）使用：

```
sudo pacman -S tree

```

……等等。

一旦安装好，在终端窗口运行 `tree` 命令：

```
tree /

```

上述指令中的 `/` 指的是根目录。系统中的其他目录都是从根目录分支而出，当你运行 `tree` 命令，并且告诉它从根目录开始，那么你就可以看到整个目录树，系统中的所有目录及其子目录，还有它们的文件。

如果你已经使用你的系统有一段时间了，这可能需要一段时间，因为即使你自己还没有生成很多文件，Linux 系统及其应用程序总是在记录、缓存和存储各种临时文件。文件系统中的条目数量会快速增长。

不过，不要感到不知所措。 相反，试试这个：

```
tree -L 1 /

```

你应该看到如图 1 所示。

![](/data/attachment/album/201807/02/094428icyf0yr7rud5cb6y.png)

*tree*

上面的指令可以翻译为“只显示以 `/`（根目录） 开头的目录树的第一级”。 `-L` 选项告诉树你想看到多少层目录。

大多数 Linux 发行版都会向你显示与你在上图中看到的相同或非常类似的结构。 这意味着，即使你现在感到困惑，掌握这一点，你将掌握大部分（如果不是全部的话）全世界的 Linux 文件系统。

为了让你开始走上掌控之路，让我们看看每个目录的用途。 当我们查看每一个目录的时候，你可以使用 `ls` 来查看他们的内容。

### 目录

从上到下，你所看到的目录如下

#### /bin

`/bin` 目录是包含一些二进制文件的目录，即可以运行的一些应用程序。 你会在这个目录中找到上面提到的 `ls` 程序，以及用于新建和删除文件和目录、移动它们基本工具。还有其它一些程序，等等。文件系统树的其他部分有更多的 *bin* 目录，但我们将在一会儿讨论这些目录。

#### /boot

`/boot` 目录包含启动系统所需的文件。我必须要说吗？ 好吧，我会说：**不要动它**！ 如果你在这里弄乱了其中一个文件，你可能无法运行你的 Linux，修复被破坏的系统是非常痛苦的一件事。 另一方面，不要太担心无意中破坏系统：你必须拥有超级用户权限才能执行此操作。

#### /dev

`/dev` 目录包含设备文件。 其中许多是在启动时或甚至在运行时生成的。 例如，如果你将新的网络摄像头或 USB 随身碟连接到你的机器中，则会自动弹出一个新的设备条目。

#### /etc

`/etc` 的目录名称会让人变得非常的困惑。`/etc` 得名于最早的 Unix 系统们，它的字面意思是 “etcetera”（诸如此类） ，因为它是系统文件管理员不确定在哪里放置的文件的垃圾场。

现在，说 `/etc` 是“<ruby> 要配置的所有内容 <rt>  Everything To Configure </rt></ruby>”更为恰当，因为它包含大部分（如果不是全部的话）的系统配置文件。 例如，包含系统名称、用户及其密码、网络上计算机名称以及硬盘上分区的安装位置和时间的文件都在这里。 再说一遍，如果你是 Linux 的新手，最好是不要在这里接触太多，直到你对系统的工作有更好的理解。

#### /home

`/home` 是你可以找到用户个人目录的地方。在我的情况下，`/home` 下有两个目录：`/home/paul`，其中包含我所有的东西；另外一个目录是 `/home/guest` 目录，以防有客人需要使用我的电脑。

#### /lib

`/lib` 是库文件所在的地方。库是包含应用程序可以使用的代码文件。它们包含应用程序用于在桌面上绘制窗口、控制外围设备或将文件发送到硬盘的代码片段。

在文件系统周围散布着更多的 `lib` 目录，但是这个直接挂载在 `/` 的 `/lib` 目录是特殊的，除此之外，它包含了所有重要的内核模块。 内核模块是使你的显卡、声卡、WiFi、打印机等工作的驱动程序。

#### /media

在 `/media` 目录中，当你插入外部存储器试图访问它时，将自动挂载它。与此列表中的大多数其他项目不同，`/media` 并不追溯到 1970 年代，主要是因为当计算机正在运行而动态地插入和检测存储（U 盘、USB 硬盘、SD 卡、外部 SSD 等)，这是近些年才发生的事。

#### /mnt

然而，`/mnt` 目录是一些过去的残余。这是你手动挂载存储设备或分区的地方。现在不常用了。

#### /opt

`/opt` 目录通常是你编译软件（即，你从源代码构建，并不是从你的系统的软件库中安装软件）的地方。应用程序最终会出现在 `/opt/bin` 目录，库会在 `/opt/lib` 目录中出现。

稍微的题外话：应用程序和库的另一个地方是 `/usr/local`，在这里安装软件时，也会有 `/usr/local/bin` 和 `/usr/local/lib` 目录。开发人员如何配置文件来控制编译和安装过程，这就决定了软件安装到哪个地方。

#### /proc

`/proc`，就像 `/dev` 是一个虚拟目录。它包含有关你的计算机的信息，例如关于你的 CPU 和你的 Linux 系统正在运行的内核的信息。与 `/dev` 一样，文件和目录是在计算机启动或运行时生成的，因为你的系统正在运行且会发生变化。

#### /root

`/root` 是系统的超级用户（也称为“管理员”）的主目录。 它与其他用户的主目录是分开的，**因为你不应该动它**。 所以把自己的东西放在你自己的目录中，伙计们。

#### /run

`/run` 是另一个新出现的目录。系统进程出于自己不可告人的原因使用它来存储临时数据。这是另一个**不要动它**的文件夹。

#### /sbin

`/sbin` 与 `/bin` 类似，但它包含的应用程序只有超级用户（即首字母的 `s` ）才需要。你可以使用 `sudo` 命令使用这些应用程序，该命令暂时允许你在许多 Linux 发行版上拥有超级用户权限。`/sbin` 目录通常包含可以安装、删除和格式化各种东西的工具。你可以想象，如果你使用不当，这些指令中有一些是致命的，所以要小心处理。

#### /usr

`/usr` 目录是在 UNIX 早期用户的主目录所处的地方。然而，正如我们上面看到的，现在 `/home` 是用户保存他们的东西的地方。如今，`/usr` 包含了大量目录，而这些目录又包含了应用程序、库、文档、壁纸、图标和许多其他需要应用程序和服务共享的内容。

你还可以在 `/usr` 目录下找到 `bin`，`sbin`，`lib` 目录，它们与挂载到根目录下的那些有什么区别呢？现在的区别不是很大。在早期，`/bin` 目录（挂载在根目录下的）只会包含一些基本的命令，例如 `ls`、`mv` 和 `rm` ；这是一些在安装系统的时候就会预装的一些命令，用于维护系统的一个基本的命令。 而 `/usr/bin` 目录则包含了用户自己安装和用于工作的软件，例如文字处理器，浏览器和一些其他的软件。

但是许多现代的 Linux 发行版只是把所有的东西都放到 `/usr/bin` 中，并让 `/bin` 指向 `/usr/bin`，以防彻底删除它会破坏某些东西。因此，Debian、Ubuntu 和 Mint 仍然保持 `/bin` 和 `/usr/bin` （和 `/sbin` 和 `/usr/sbin` ）分离；其他的，比如 Arch 和它衍生版，只是有一个“真实”存储二进制程序的目录，`/usr/bin`，其余的任何 `bin` 目录是指向 `/usr/`bin` 的“假”目录。

#### /srv

`/srv` 目录包含服务器的数据。如果你正在 Linux 机器上运行 Web 服务器，你网站的 HTML文件将放到 `/srv/http`（或 `/srv/www`）。 如果你正在运行 FTP 服务器，则你的文件将放到 `/srv/ftp`。

#### /sys

`/sys` 是另一个类似 `/proc` 和 `/dev` 的虚拟目录，它还包含连接到计算机的设备的信息。

在某些情况下，你还可以操纵这些设备。 例如，我可以通过修改存储在 `/sys/devices/pci0000:00/0000:00:02.0/drm/card1/card1-eDP-1/intel_backlight/brightness` 中的值来更改笔记本电脑屏幕的亮度（在你的机器上你可能会有不同的文件）。但要做到这一点，你必须成为超级用户。原因是，与许多其它虚拟目录一样，在 `/sys` 中打乱内容和文件可能是危险的，你可能会破坏系统。直到你确信你知道你在做什么。否则不要动它。

#### /tmp

`/tmp` 包含临时文件，通常由正在运行的应用程序放置。文件和目录通常（并非总是）包含应用程序现在不需要但以后可能需要的数据。

你还可以使用 `/tmp` 来存储你自己的临时文件 —— `/tmp` 是少数挂载到根目录下而你可以在不成为超级用户的情况下与它进行实际交互的目录之一。

#### /var

`/var` 最初被如此命名是因为它的内容被认为是<ruby> 可变的 <rt>  variable </rt></ruby>，因为它经常变化。今天，它有点用词不当，因为还有许多其他目录也包含频繁更改的数据，特别是我们上面看到的虚拟目录。

不管怎样，`/var` 目录包含了放在 `/var/log` 子目录的日志文件之类。日志是记录系统中发生的事件的文件。如果内核中出现了什么问题，它将被记录到 `/var/log` 下的文件中；如果有人试图从外部侵入你的计算机，你的防火墙也将记录尝试。它还包含用于任务的假脱机程序。这些“任务”可以是你发送给共享打印机必须等待执行的任务，因为另一个用户正在打印一个长文档，或者是等待递交给系统上的用户的邮件。

你的系统可能还有一些我们上面没有提到的目录。例如，在屏幕截图中，有一个 `/snap` 目录。这是因为这张截图是在 Ubuntu 系统上截取的。Ubuntu 最近将 [snap](https://www.ubuntu.com/desktop/snappy) 包作为一种分发软件的方式。`/snap` 目录包含所有文件和从 snaps 安装的软件。

### 更深入的研究

这里仅仅谈了根目录，但是许多子目录都指向它们自己的一组文件和子目录。图 2 给出了基本文件系统的总体概念（图片是在 Paul Gardner 的 CC BY-SA 许可下提供的），[Wikipedia 对每个目录的用途进行了总结](https://en.wikipedia.org/wiki/Unix_filesystem#Conventional_directory_layout)。

![filesystem](/data/attachment/album/201807/02/094428ko2tc8saplc2apto.png "filesystem")

*图 2：标准 Unix 文件系统*

要自行探索文件系统，请使用 `cd` 命令：`cd`将带你到你所选择的目录（ `cd` 代表更改目录）。

如果你不知道你在哪儿，`pwd`会告诉你，你到底在哪里，（ `pwd` 代表打印工作目录 ），同时 `cd`命令在没有任何选项或者参数的时候，将会直接带你到你自己的主目录，这是一个安全舒适的地方。

最后，`cd ..`将会带你到上一层目录，会使你更加接近根目录，如果你在 `/usr/share/wallpapers` 目录，然后你执行 `cd ..` 命令，你将会跳转到 `/usr/share` 目录

要查看目录里有什么内容，使用 `ls` 或这简单的使用 `l` 列出你所在目录的内容。

当然，你总是可以使用 `tree` 来获得目录中内容的概述。在 `/usr/share` 上试试——里面有很多有趣的东西。

### 总结

尽管 Linux 发行版之间存在细微差别，但它们的文件系统的布局非常相似。 你可以这么说：一旦你了解一个，你就会都了解了。 了解文件系统的最好方法就是探索它。 因此，伴随 `tree` ，`ls` 和 `cd` 进入未知的领域吧。

你不会只是因为查看文件系统就破坏了文件系统，因此请从一个目录移动到另一个目录并进行浏览。 很快你就会发现 Linux 文件系统及其布局的确很有意义，并且你会直观地知道在哪里可以找到应用程序，文档和其他资源。

通过 Linux 基金会和 edX 免费的 “[Linux入门](https://training.linuxfoundation.org/linux-courses/system-administration-training/introduction-to-linux)” 课程了解更多有关 Linux 的信息。

---

via: <https://www.linux.com/blog/learn/intro-to-linux/2018/4/linux-filesystem-explained>

作者：[PAUL BROWN](https://www.linux.com/users/bro66) 选题：[lujun9972](https://github.com/lujun9972) 译者：[amwps290](https://github.com/amwps290) 校对：[wxy](https://github.com/wxy)

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


容器基础知识：你需要知道的术语

Summary: 让我们来探讨 Docker 容器世界中使用的一些基本术语吧。

让我们来探讨 Docker 容器世界中使用的一些基本术语吧。

![](/data/attachment/album/201807/02/100402fcve3ll4fvzo9aev.jpg)

[在前一篇文章中](/article-9468-1.html)，我们谈到了<ruby> 容器 <rt>  container </rt></ruby>是什么以及它是如何培育创新并助力企业快速发展的。在以后的文章中，我们将讨论如何使用容器。然而，在深入探讨这个话题之前，我们需要了解关于容器的一些术语和命令。掌握了这些术语，才不至于产生混淆。

让我们来探讨 [Docker](https://www.docker.com/) 容器世界中使用的一些基本术语吧。

<ruby> 容器 <rt>  Container </rt></ruby>：到底什么是容器呢？它是一个 Docker <ruby> 镜像 <rt>  image </rt></ruby>的运行实例。它包含一个 Docker 镜像、执行环境和说明。它与系统完全隔离，所以可以在系统上运行多个容器，并且完全无视对方的存在。你可以从同一镜像中复制出多个容器，并在需求较高时扩展服务，在需求低时对这些容器进行缩减。

Docker <ruby> 镜像 <rt>  Image </rt></ruby>：这与你下载的 Linux 发行版的镜像别无二致。它是一个安装包，包含了用于创建、部署和执行容器的一系列依赖关系和信息。你可以在几秒钟内创建任意数量的完全相同的容器。镜像是分层叠加的。一旦镜像被创建出来，是不能更改的。如果你想对容器进行更改，则只需创建一个新的镜像并从该镜像部署新的容器即可。

<ruby> 仓库 <rt>  Repository </rt></ruby>（repo）：Linux 的用户对于仓库这个术语一定不陌生吧。它是一个软件库，存储了可下载并安装在系统中的软件包。在 Docker 容器中，唯一的区别是它管理的是通过标签分类的 Docker 镜像。你可以找到同一个应用程序的不同版本或不同变体，他们都有适当的标记。

<ruby> 镜像管理服务 <rt>  Registry </rt></ruby>：可以将其想象成 GitHub。这是一个在线服务，管理并提供了对 Docker 镜像仓库的访问，例如默认的公共镜像仓库——DockerHub。供应商可以将他们的镜像库上传到 DockerHub 上，以便他们的客户下载和使用官方镜像。一些公司为他们的镜像提供自己的服务。镜像管理服务不必由第三方机构来运行和管理。组织机构可以使用预置的服务来管理内部范围的镜像库访问。

<ruby> 标签 <rt>  Tag </rt></ruby>：当你创建 Docker 镜像时，可以给它添加一个合适的标签，以便轻松识别不同的变体或版本。这与你在任何软件包中看到的并无区别。Docker 镜像在添加到镜像仓库时被标记。

现在你已经掌握了基本知识，下一个阶段是理解实际使用 Docker 容器时用到的术语。

**Dockerfile** ：这是一个文本文件，包含为了为构建 Docker 镜像需手动执行的命令。Docker 使用这些指令自动构建镜像。

<ruby> 构建 <rt>  Build </rt></ruby>：这是从 Dockerfile 创建成镜像的过程。

<ruby> 推送 <rt>  Push </rt></ruby>：一旦镜像创建完成，“push” 是将镜像发布到仓库的过程。该术语也是我们下一篇文章要学习的命令之一。

<ruby> 拉取 <rt>  Pull </rt></ruby>：用户可以通过 “pull” 过程从仓库检索该镜像。

<ruby> 编组 <rt>  Compose </rt></ruby>：复杂的应用程序会包含多个容器。docker-compose 是一个用于运行多容器应用程序的命令行工具。它允许你用单条命令运行一个多容器的应用程序，简化了多容器带来的问题。

### 总结

容器术语的范围很广泛，这里是经常遇到的一些基本术语。下一次当你看到这些术语时，你会确切地知道它们的含义。在下一篇文章中，我们将开始使用 Docker 容器。

---

via: <https://www.linux.com/blog/intro-to-linux/2017/12/container-basics-terms-you-need-know>

作者：[Swapnil Bhartiya](https://www.linux.com/users/arnieswap) 译者：[jessie-pang](https://github.com/jessie-pang) 校对：[wxy](https://github.com/wxy)

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


协同编辑器的历史性清单

Summary: 按时间顺序快速列出主要协同编辑器的演变。

按时间顺序快速列出主要协同编辑器的演变。

![](/data/attachment/album/201807/03/104256fa6lfmmmexxmmxl9.jpg)

按时间顺序快速列出主要协同编辑器的演变。

正如任何这样的清单一样，它必定会在一开始便提到被誉为“<ruby>   所有演示之母  <rt>  the mother of all demos </rt></ruby>”，在这个演示里<ruby>   道格·恩格尔巴特  <rt>  Doug Engelbart </rt></ruby>早在 1968 年就描述了几乎所有软件的详尽清单。这不仅包括协同编辑器，还包括图形、编程和数学编辑器。

一切都始于那个演示，只不过软件的实现跟不上硬件的发展罢了。

>
> 软件发展的速度比硬件提升的速度慢。——沃斯定律
>
>
>

闲话少说，这里是我找到的可圈可点的协同编辑器的清单。我说“可圈可点”的意思是它们具有可圈可点的特征或实现细节。

| 项目 | 日期 | 平台 | 说明 |
| --- | --- | --- | --- |
| [SubEthaEdit](https://www.codingmonkeys.de/subethaedit/) | 2003-2015？ | 仅 Mac | 我能找到的首个协同的、实时的、多光标的编辑器， [有个在 Emacs 上的逆向工程的尝试](https://www.emacswiki.org/emacs/SubEthaEmacs)却没有什么结果。 |
| [DocSynch](http://docsynch.sourceforge.net/) | 2004-2007 | ？ | 建立于 IRC 之上！ |
| [Gobby](https://gobby.github.io/) | 2005 至今 | C，多平台 | 首个开源、稳固可靠的实现。 仍然存在！众所周知 [libinfinoted](http://infinote.0x539.de/libinfinity/API/libinfinity/) 协议很难移植到其他编辑器中（例如： [Rudel](https://www.emacswiki.org/emacs/Rudel) 不能在 Emacs 上实现此协议）。 2017 年 1 月发行的 0.7 版本添加了也许可以改善这种状况的 Python 绑定。 值得注意的插件: 自动保存到磁盘。 |
| [Ethercalc](https://ethercalc.net/) | 2005 至今 | Web，JavaScript | 首个电子表格，随同 [Google Docs](https://en.wikipedia.org/wiki/Google_docs)。 |
| [moonedit](https://web.archive.org/web/20060423192346/http://www.moonedit.com:80/) | 2005-2008？ | ？ | 原网站已关闭。其他用户的光标可见并且会模仿击键的声音。 包括一个计算器和音乐定序器。 |
| [synchroedit](http://www.synchroedit.com/) | 2006-2007 | ？ | 首个 Web 应用。 |
| [Inkscape](http://wiki.inkscape.org/wiki/index.php/WhiteBoard) | 2007-2011 | C++ | 首个具备协同功能的图形编辑器，其背后的“whiteboard” 插件构建于 Jabber 之上，现已停摆。 |
| [Abiword](https://en.wikipedia.org/wiki/AbiWord) | 2008 至今 | C++ | 首个文字处理器。 |
| [Etherpad](http://etherpad.org/) | 2008 至今 | Web | 首款稳定的 Web 应用。 最初在 2008 年被开发时是一款大型 Java 应用，在 2009 年被谷歌收购并开源，然后在 2011 年被用 Node.JS 重写。使用广泛。 |
| [Wave](https://en.wikipedia.org/wiki/Apache_Wave) | 2009-2010 | Web， Java | 在大一统协议的尝试上失败。 |
| [CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) | 2011 | 特定平台 | 在不同电脑间可靠地复制一个文件的数据结构的标准。 |
| [Operational transform](http://operational-transformation.github.io/) | 2013 | 特定平台 | 与 CRDT 类似，然而确切地说，两者是不同的。 |
| [Floobits](https://floobits.com/) | 2013 至今 | ？ | 商业软件，但有对各种编辑器的开源插件。 |
| [LibreOffice Online](https://wiki.documentfoundation.org/Development/LibreOffice_Online) | 2015至今 | Web | 免费的 Google docs 替代品，现已集成到 [Nextcloud](https://nextcloud.com/collaboraonline/) |
| [HackMD](https://hackmd.io/) | 2015 至今 | ？ | 商业软件，[开源](https://github.com/hackmdio/hackmd)。灵感来自于（已被 Dropbox 收购的) hackpad。 |
| [Cryptpad](https://cryptpad.fr/) | 2016 至今 | Web ? | Xwiki 的副产品。服务器端的加密的、“零知识” 产品。 |
| [Prosemirror](https://prosemirror.net/) | 2016 至今 | Web， Node.JS | “试图架起消除 Markdown 文本编辑和传统的所见即所得编辑器之间隔阂的桥梁。”不是完全意义上的编辑器，而是一种可以用来构建编辑器的工具。 |
| [Qill](https://quilljs.com/) | 2013 至今 | Web， Node.JS | 富文本编辑器，同时也是 JavaScript 编辑器。不确定是否是协同式的。 |
| [Teletype](https://teletype.atom.io/) | 2017 至今 | WebRTC， Node.JS | 为 GitHub 的 [Atom 编辑器](https://atom.io) 引入了“门户”的思路 ，使得访客可以夸多个文档跟踪主人的操作。访问介绍服务器后使用实时通讯的点对点技术（P2P），基于 CRDT。 |
| [Tandem](http://typeintandem.com/) | 2018 至今 | Node.JS？ | Atom、 Vim、Neovim、 Sublime 等的插件。 使用中继来设置基于 CRDT 的 P2P 连接。多亏 Debian 开发者的参与，[可疑证书问题](https://github.com/typeintandem/tandem/issues/131)已被解决，这使它成为很有希望在未来被遵循的标准。 |

### 其他清单

* [Emacs 维基](https://www.emacswiki.org/emacs/CollaborativeEditing)
* [维基百科](https://en.wikipedia.org/wiki/Collaborative_real-time_editor)

---

via: <https://anarc.at/blog/2018-06-26-collaborative-editors-history/>

作者：[Anacr](https://anarc.at) 选题：[lujun9972](https://github.com/lujun9972) 译者：[ZenMoore](https://github.com/ZenMoore) 校对：[wxy](https://github.com/wxy)

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


在开源项目中做出你的第一个贡献

Summary: 这是许多事情的第一步

>
> 这是许多事情的第一步
>
>
>

![women programming](/data/attachment/album/201807/03/110953dg5cui5c31a3uisg.png "women programming")

有一个普遍的误解，那就是对开源做出贡献是一件很难的事。你可能会想，“有时我甚至不能理解我自己的代码；那我怎么可能理解别人的？”

放轻松。直到去年，我都以为是这样。阅读和理解他人的代码，然后在他们的基础上写上你自己的代码，这是一件令人气馁的任务；但如果有合适的资源，这不像你想象的那么糟。

第一步要做的是选择一个项目。这个决定是可能是一个菜鸟转变成一个老练的开源贡献者的关键一步。

许多对开源感兴趣的业余程序员都被建议从 [Git](https://git-scm.com/) 入手，但这并不是最好的开始方式。Git 是由许多有着多年软件开发经验的超级极客维护的。它是寻找可以做贡献的开源项目的好地方，但对新手并不友好。大多数对 Git 做出贡献的开发者都有足够的经验，他们不需要参考各类资源或文档。在这篇文章里，我将提供一个对新手友好的特性的列表，并且给出一些建议，希望可以使你更轻松地对开源做出贡献。

### 理解产品

在开始贡献之前，你需要理解项目是怎么工作的。为了理解这一点，你需要自己来尝试。如果你发现这个产品很有趣并且有用，它就值得你来做贡献。

初学者常常选择参与贡献那些他们没有使用过的软件。他们会失望，并且最终放弃贡献。如果你没有用过这个软件，你不会理解它是怎么工作的。如果你不理解它是怎么工作的，你怎么能解决 bug 或添加新特性呢？

要记住：尝试它，才能改变它。

### 确认产品的状况

这个项目有多活跃？

如果你向一个暂停维护的项目提交一个<ruby> 拉取请求 <rt>  pull request </rt></ruby>，你的请求可能永远不会被讨论或合并。找找那些活跃的项目，这样你的代码可以得到即时的反馈，你的贡献也就不会被浪费。

这里介绍了怎么确认一个项目是否还是活跃的：

* **贡献者数量：** 一个增加的贡献者数量表明开发者社区乐于接受新的贡献者。
* **<ruby> 提交 <rt>  commit </rt></ruby>频率：** 查看最近的提交时间。如果是一周之内，甚至是一两个月内，这个项目应该是定期维护的。
* **维护者数量：** 维护者的数量越多，你越可能得到指导。
* **聊天室或 IRC 活跃度：** 一个繁忙的聊天室意味着你的问题可以更快得到回复。

### 新手资源

Coala 是一个开源项目的例子。它有自己的教程和文档，让你可以使用它（每一个类和方法）的 API。这个网站还设计了一个有吸引力的界面，让你有阅读的兴趣。

**文档：** 不管哪种水平的开发者都需要可靠的、被很好地维护的文档，来理解项目的细节。找找在 [GitHub](https://github.com/)（或者放在其它位置）或者类似于 [Read the Docs](https://readthedocs.org/) 之类的独立站点上提供了完善文档的项目，这样可以帮助你深入了解代码。

![Coala Newcomers' Guide screen](/data/attachment/album/201807/03/110953yxuc3h11gqm8gum2.png "Coala Newcomers' Guide screen")

**教程：** 教程会给新手解释如何在项目里添加特性 （然而，你不是在每个项目中都能找到它）。例如，Coala 提供了 [小熊编写指南](http://api.coala.io/en/latest/Developers/Writing_Linter_Bears.html) （进行代码分析的<ruby> 代码格式化 <rt>  linting </rt></ruby>工具的 Python 包装器）。

![Coala UI](/data/attachment/album/201807/03/110954moyj3tbp02nchhn4.png "Coala User Interface screenshot")

**分类的<ruby> 讨论点 <rt>  issue </rt></ruby>：** 对刚刚想明白如何选择第一个项目的初学者来说，选择一个讨论点是一个更加困难的任务。标签被设为“难度/低”、“难度/新手”、“利于初学者”，以及“<ruby> 触手可及 <rt>  low-hanging fruit </rt></ruby>”都表明是对新手友好的。

![Coala labeled issues](/data/attachment/album/201807/03/110955gi5xzt0goiz5i7ve.png "Coala labeled issues")

### 其他因素

![CI user pipeline log](/data/attachment/album/201807/03/110956p1rrnbrur1a1y72b.png "CI user pipeline log")

* **维护者对新的贡献者的态度：** 从我的经验来看，大部分开源贡献者都很乐于帮助他们项目里的新手。然而，当你问问题时，你也有可能遇到一些不太友好的人（甚至可能有点粗鲁）。不要因为这些人失去信心。他们只是因为在比他们经验更丰富的人那儿得不到发泄的机会而已。还有很多其他人愿意提供帮助。
* **审阅过程/机制：** 你的拉取请求将经历几遍你的同伴和有经验的开发者的查看和更改——这就是你学习软件开发最主要的方式。一个具有严格审阅过程的项目使您在编写生产级代码的过程中成长。
* **一个稳健的<ruby> 持续集成 <rt>  continuous integration </rt></ruby>管道：** 开源项目会向新手们介绍持续集成和部署服务。一个稳健的 CI 管道将帮助你学习阅读和理解 CI 日志。它也将带给你处理失败的测试用例和代码覆盖率问题的经验。
* **参加编程项目（例如 [Google Summer Of Code](https://en.wikipedia.org/wiki/Google_Summer_of_Code)）：** 参加组织证明了你乐于对一个项目的长期发展做贡献。他们也会给新手提供一个机会来获得现实世界中的开发经验，从而获得报酬。大多数参加这些项目的组织都欢迎新人加入。

### 7 对新手友好的组织

* [coala (Python)](https://github.com/coala/coala)
* [oppia (Python, Django)](https://github.com/oppia/oppia)
* [DuckDuckGo (Perl, JavaScript)](https://github.com/duckduckgo/)
* [OpenGenus (JavaScript)](https://github.com/OpenGenus/)
* [Kinto (Python, JavaScript)](https://github.com/kinto)
* [FOSSASIA (Python, JavaScript)](https://github.com/fossasia/)
* [Kubernetes (Go)](https://github.com/kubernetes)

### 关于作者

Palash Nigam - 我是一个印度计算机科学专业本科生，十分乐于参与开源软件的开发，我在 GitHub 上花费了大部分的时间。我现在的兴趣包括 web 后端开发，区块链，和 All things python。

---

via: <https://opensource.com/article/18/4/get-started-open-source-project>

作者：[Palash Nigam](https://opensource.com/users/palash25) 译者：[lonaparte](https://github.com/lonaparte) 校对：[wxy](https://github.com/wxy)

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


如何记录你在终端中执行的所有操作

![](/data/attachment/album/201807/03/111838fnoqyctynxwzxdx4.jpg)

几天前，我们发布了一个解释如何[保存终端中的命令并按需使用](https://www.ostechnix.com/save-commands-terminal-use-demand/)的指南。对于那些不想记忆冗长的 Linux 命令的人来说，这非常有用。今天，在本指南中，我们将看到如何使用 `script` 命令记录你在终端中执行的所有操作。你可能已经在终端中运行了一个命令，或创建了一个目录，或者安装了一个程序。`script` 命令会保存你在终端中执行的任何操作。如果你想知道你几小时或几天前做了什么，那么你可以查看它们。我知道我知道，我们可以使用上/下箭头或 `history` 命令查看以前运行的命令。但是，你无法查看这些命令的输出。而 `script` 命令记录并显示完整的终端会话活动。

`script` 命令会在终端中创建你所做的所有事件的记录。无论你是安装程序，创建目录/文件还是删除文件夹，一切都会被记录下来，包括命令和相应的输出。这个命令对那些想要一份交互式会话拷贝作为作业证明的人有用。无论是学生还是导师，你都可以将所有在终端中执行的操作和所有输出复制一份。

### 在 Linux 中使用 script 命令记录终端中的所有内容

`script` 命令预先安装在大多数现代 Linux 操作系统上。所以，我们不用担心安装。

让我们继续看看如何实时使用它。

运行以下命令启动终端会话记录。

```
$ script -a my_terminal_activities

```

其中，`-a` 标志用于将输出追加到文件（记录）中，并保留以前的内容。上述命令会记录你在终端中执行的所有操作，并将输出追加到名为 `my_terminal_activities` 的文件中，并将其保存在当前工作目录中。

示例输出：

```
Script started, file is my_terminal_activities

```

现在，在终端中运行一些随机的 Linux 命令。

```
$ mkdir ostechnix
$ cd ostechnix/
$ touch hello_world.txt
$ cd ..
$ uname -r

```

运行所有命令后，使用以下命令结束 `script` 命令的会话：

```
$ exit

```

示例输出：

```
exit
Script done, file is my_terminal_activities

```

如你所见，终端活动已存储在名为 `my_terminal_activities` 的文件中，并将其保存在当前工作目录中。

要查看你的终端活动，只需在任何编辑器中打开此文件，或者使用 `cat` 命令直接显示它。

```
$ cat my_terminal_activities

```

示例输出：

```
Script started on Thu 09 Mar 2017 03:33:44 PM IST
[sk@sk]: ~>$ mkdir ostechnix
[sk@sk]: ~>$ cd ostechnix/
[sk@sk]: ~/ostechnix>$ touch hello_world.txt
[sk@sk]: ~/ostechnix>$ cd ..
[sk@sk]: ~>$ uname -r
4.9.11-1-ARCH
[sk@sk]: ~>$ exit
exit

Script done on Thu 09 Mar 2017 03:37:49 PM IST
```

正如你在上面的输出中看到的，`script` 命令记录了我所有的终端活动，包括 `script` 命令的开始和结束时间。真棒，不是吗？使用 `script` 命令的原因不仅仅是记录命令，还有命令的输出。简单地说，脚本命令将记录你在终端上执行的所有操作。

### 结论

就像我说的那样，脚本命令对于想要保留其终端活动记录的学生，教师和 Linux 用户非常有用。尽管有很多 CLI 和 GUI 可用来执行此操作，但 `script` 命令是记录终端会话活动的最简单快捷的方式。

就是这些。希望这有帮助。如果你发现本指南有用，请在你的社交，专业网络上分享，并支持我们。

干杯!

---

via: <https://www.ostechnix.com/record-everything-terminal/>

作者：[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/) 荣誉推出


不要再手动合并你的拉取请求（PR）

![](/data/attachment/album/201807/03/215624d3a6b26agw2wj6nj.png)

如果有什么我讨厌的东西，那就是当我知道我可以自动化它们时，但我手动进行了操作。只有我有这种情况么？我觉得不是。

尽管如此，他们每天都有数千名使用 [GitHub](https://github.com) 的开发人员一遍又一遍地做同样的事情：他们点击这个按钮：

![Screen-Shot-2018-06-19-at-18.12.39](/data/attachment/album/201807/03/215624yzviftyoyeofiwbc.png)

这没有任何意义。

不要误解我的意思。合并拉取请求是有意义的。只是每次点击这个该死的按钮是没有意义的。

这样做没有意义因为世界上的每个开发团队在合并拉取请求之前都有一个已知的先决条件列表。这些要求几乎总是相同的，而且这些要求也是如此：

* 是否通过测试？
* 文档是否更新了？
* 这是否遵循我们的代码风格指南？
* 是否有若干位开发人员对此进行审查？

随着此列表变长，合并过程变得更容易出错。 “糟糕，在没有足够的开发人员审查补丁时 John 就点了合并按钮。” 要发出警报么？

在我的团队中，我们就像外面的每一支队伍。我们知道我们将一些代码合并到我们仓库的标准是什么。这就是为什么我们建立一个持续集成系统，每次有人创建一个拉取请求时运行我们的测试。我们还要求代码在获得批准之前由团队的 2 名成员进行审查。

当这些条件全部设定好时，我希望代码被合并。

而不用点击一个按钮。

这正是启动 [Mergify](https://mergify.io) 的原因。

![github-branching-1](/data/attachment/album/201807/03/215625ejbuubqzvjdkxwbm.png)

[Mergify](https://mergify.io) 是一个为你按下合并按钮的服务。你可以在仓库的 `.mergify.yml` 中定义规则，当规则满足时，Mergify 将合并该请求。

无需按任何按钮。

随机抽取一个请求，就像这样：

![Screen-Shot-2018-06-20-at-17.12.11](/data/attachment/album/201807/03/215625cero7rr47q4serzz.png)

这来自一个小型项目，没有很多持续集成服务，只有 Travis。在这个拉取请求中，一切都是绿色的：其中一个所有者审查了代码，并且测试通过。因此，该代码应该被合并：但是它还在那里挂起这，等待某人有一天按下合并按钮。

使用 [Mergify](https://mergify.io) 后，你只需将 `.mergify.yml` 放在仓库的根目录即可：

```
rules:
  default:
    protection:
      required_status_checks:
        contexts:
          - continuous-integration/travis-ci
      required_pull_request_reviews:
        required_approving_review_count: 1

```

通过这样的配置，[Mergify](https://mergify.io) 可以实现所需的限制，即 Travis 通过，并且至少有一个项目成员审阅了代码。只要这些条件是肯定的，拉取请求就会自动合并。

我们将 [Mergify](https://mergify.io) 构建为 **一个对开源项目免费的服务**。[提供服务的引擎](https://github.com/mergifyio/mergify-engine)也是开源的。

现在去[尝试它](https://mergify.io)，不要让这些拉取请求再挂起哪怕一秒钟。合并它们！

如果你有任何问题，请随时在下面向我们提问或写下评论！并且敬请期待 - 因为 Mergify 还提供了其他一些我迫不及待想要介绍的功能！

---

via: <https://julien.danjou.info/stop-merging-your-pull-request-manually/>

作者：[Julien Danjou](https://julien.danjou.info/author/jd/) 选题：[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/) 荣誉推出


Intel 和 AMD 透露新的处理器设计

>
> Whiskey Lake U 系列和 Amber Lake Y 系列的酷睿芯片将会在今年秋季开始出现在超过 70 款笔记本以及 2 合 1 机型中。
>
>
>

![](/data/attachment/album/201807/04/094930tdxid7b0jpejb8j8.jpg)

根据最近的台北国际电脑展 (Computex 2018) 以及最近其它的消息，处理器成为科技新闻圈中最前沿的话题。Intel 发布了一些公告涉及从新的酷睿处理器到延长电池续航的尖端技术。与此同时，AMD 亮相了第二代 32 核心的高端游戏处理器线程撕裂者（Threadripper）以及一些适合嵌入式的新型号锐龙 Ryzen 处理器。

以上是对 Intel 和 AMD 主要公布产品的快速浏览，针对那些对嵌入式 Linux 开发者最感兴趣的处理器。

### Intel 最新的第八代 CPU 家族

在四月份，Intel 已经宣布量产 10nm 制程的 Cannon Lake 系列酷睿处理器将会延期到 2019 年，这件事引起了人们对摩尔定律最终走上正轨的议论。然而，在 Intel 的 [Computex 展区](https://newsroom.intel.com/editorials/pc-personal-contribution-platform-pushing-boundaries-modern-computers-computex/) 中有着众多让人欣慰的消息。Intel 展示了两款节能的第八代 14nm 酷睿家族产品，同时也是 Intel 首款 5GHz 的设计。

Whiskey Lake U 系列和 Amber Lake Y 系列的酷睿芯片将会在今年秋季开始出现在超过 70 款笔记本以及 2 合 1 机型中。Intel 表示，这些芯片相较于第七代的 Kaby Lake 酷睿系列处理器会带来两倍的性能提升。新的产品家族将会相比于目前出现的搭载 [Coffee Lake](https://www.linux.com/news/elc-openiot/2018/3/hot-chips-face-mwc-and-embedded-world) 芯片的产品更加节能 。

Whiskey Lake 和 Amber Lake 两者将会配备 Intel 高性能千兆 WiFi (Intel 9560 AC)，该网卡同样出现在 [Gemini Lake](http://linuxgizmos.com/intel-launches-gemini-lake-socs-with-gigabit-wifi/) 架构的奔腾银牌和赛扬处理器，随之出现在 Apollo Lake 一代。千兆 WiFi 本质上就是 Intel 将 2×2 MU-MIMO 和 160MHz 信道技术与 802.11ac 结合。

Intel 的 Whiskey Lake 将作为第七代和第八代 Skylake U 系列处理器的继任者，它们现在已经流行于嵌入式设备。Intel 透漏了少量细节，但是 Whiskey Lake 想必将会提供同样的，相对较低的 15W TDP。这与 [Coffee Lake U 系列芯片](http://linuxgizmos.com/intel-coffee-lake-h-series-debuts-in-congatec-and-seco-modules) 也很像，它将会被用于四核以及 Kaby Lake 和 Skylake U 系列的双核芯片。

[PC World](https://www.pcworld.com/article/3278091/components-processors/intel-computex-news-a-28-core-chip-a-5ghz-8086-two-new-architectures-and-more.html) 报导称，Amber Lake Y 系列芯片主要目标定位是 2 合 1 机型。就像双核的 [Kaby Lake Y 系列](http://linuxgizmos.com/more-kaby-lake-chips-arrive-plus-four-nuc-mini-pcs/) 芯片，Amber Lake 将会支持 4.5W TDP。

为了庆祝 Intel 即将到来的 50 周年庆典， 同样也是作为世界上第一款 8086 处理器的 40 周年庆典，Intel 将启动一个限量版，带有一个时钟频率 4GHz 的第八代 [酷睿 i7-8086K](https://newsroom.intel.com/wp-content/uploads/sites/11/2018/06/intel-i7-8086k-launch-fact-sheet.pdf) CPU。 这款 64 位限量版产品将会是第一块拥有 5GHz, 单核睿频加速，并且是首款带有集成显卡的 6 核，12 线程处理器。Intel 将会于 6 月 7 日开始 [赠送](https://game.intel.com/8086sweepstakes/) 8086 块超频酷睿 i7-8086K 芯片。

Intel 也展示了计划于今年年底启动新的高端 Core X 系列拥有高核心和线程数。[AnandTech 预测](https://www.anandtech.com/show/12878/intel-discuss-whiskey-lake-amber-lake-and-cascade-lake) 可能会使用类似于 Xeon 的 Cascade Lake 架构。今年晚些时候，Intel 将会公布新的酷睿 S系列型号，AnandTech 预测它可能会是八核心的 Coffee Lake 芯片。

Intel 也表示第一款疾速傲腾 SSD —— 一个 M.2 接口产品被称作 [905P](https://www.intel.com/content/www/us/en/products/memory-storage/solid-state-drives/gaming-enthusiast-ssds/optane-905p-series.htm) —— 终于可用了。今年来迟的是 Intel XMM 800 系列调制解调器，它支持 Sprint 的 5G 蜂窝技术。Intel 表示 可用于 PC 的 5G 将会在 2019 年出现。

### Intel 承诺笔记本全天候的电池寿命

另一则消息，Intel 表示将会尽快启动一项 Intel 低功耗显示技术，它将会为笔记本设备提供一整天的电池续航。合作开发伙伴 Sharp 和 Innolux 正在使用这项技术在 2018 年晚期开始生产 1W 显示面板，这能节省 LCD 一半的电量消耗。

### AMD 继续翻身

在展会中，AMD 亮相了第二代拥有 32 核 64 线程的线程撕裂者（Threadripper） CPU。为了走在 Intel 尚未命名的 28 核怪兽之前，这款高端游戏处理器将会在第三季度推出。根据 [Engadget](https://www.engadget.com/2018/06/05/amd-threadripper-32-cores/) 的消息，新的线程撕裂者同样采用了被用在锐龙 Ryzen 芯片的 12nm Zen+ 架构。

[WCCFTech](https://wccftech.com/amd-demos-worlds-first-7nm-gpu/) 报导，AMD 也表示它选自 7nm Vega Instinct GPU（为拥有 32GB 昂贵的 HBM2 显存而不是 GDDR5X 或 GDDR6 的显卡而设计）。这款 Vega Instinct 将提供相比现今 14nm Vega GPU 高出 35% 的性能和两倍的功效效率。新的渲染能力将会帮助它同 Nvidia 启用 CUDA 技术的 GPU 在光线追踪中竞争。

一些新的 Ryzen 2000 系列处理器近期出现在一个 ASRock CPU 聊天室，它将拥有比主流的 Ryzen 芯片更低的功耗。[AnandTech](https://www.anandtech.com/show/12841/amd-preps-new-ryzen-2000series-cpus-45w-ryzen-7-2700e-ryzen-5-2600e) 详细介绍了，2.8GHz，8 核心，16 线程的 Ryzen 7 2700E 和 3.4GHz/3.9GHz，六核，12 线程 Ryzen 5 2600E 都将拥有 45W TDP。这比 12-54W TDP 的 [Ryzen Embedded V1000](https://www.linux.com/news/elc-openiot/2018/3/hot-chips-face-mwc-and-embedded-world) 处理器更高，但低于 65W 甚至更高的主流 Ryzen 芯片。新的 Ryzen-E 型号是针对 SFF (外形小巧，small form factor) 和无风扇系统。

在 [开源峰会 + 欧洲嵌入式 Linux 会议](https://events.linuxfoundation.org/events/elc-openiot-europe-2018/) 加入我们，关于 Linux，云，容器，AI，社区等 100 多场会议，爱丁堡，英国，2018 年 10 月 22-24 日。

---

via: <https://www.linux.com/blog/2018/6/intel-amd-and-arm-reveal-new-processor-designs>

作者：[Eric Brown](https://www.linux.com/users/ericstephenbrown) 选题：[lujun9972](https://github.com/lujun9972) 译者：[softpaopao](https://github.com/softpaopao) 校对：[wxy](https://github.com/wxy)

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


云计算的成本

Summary: 两个开发团队的一天

>
> 两个开发团队的一天
>
>
>

![](/data/attachment/album/201807/04/114335fta5toze5dxqztpq.png)

几个月以前，我与一些人讨论过关于公共云服务成本与传统专用基础设施价格比较的话题。为给你提供一些见解，我们来跟踪一下一个企业中的两个开发团队  —  并比较他们构建类似服务的方式。

第一个团队将使用传统的专用基础设施来部署他们的应用，而第二个团队将使用 AWS 提供的公共云服务。

这两个团队被要求为一家全球化企业开发一个新的服务，该企业目前为全球数百万消费者提供服务。要开发的这项新服务需要满足以下基本需求：

1. 能够随时**扩展**以满足弹性需求
2. 具备应对数据中心故障的**弹性**
3. 确保数据**安全**以及数据受到保护
4. 为排错提供深入的**调试**功能
5. 项目必须能**迅速分发**
6. 服务构建和维护的**性价比**要高

就新服务来说，这看起来是非常标准的需求 — 从本质上看传统专用基础设备上没有什么东西可以超越公共云了。

![](/data/attachment/album/201807/04/114336t95y1zy6daululbc.png)

#### 1 — 扩展以满足客户需求

当说到可扩展性时，这个新服务需要去满足客户变化无常的需求。我们构建的服务不可以拒绝任何请求，以防让公司遭受损失或者声誉受到影响。

**传统团队**

使用的是专用基础设施，架构体系的计算能力需要与峰值数据需求相匹配。对于负载变化无常的服务来说，大量昂贵的计算能力在低利用率时被浪费掉。

这是一种很浪费的方法  —  并且大量的资本支出会侵蚀掉你的利润。另外，这些未充分利用的庞大的服务器资源的维护也是一项很大的运营成本。这是一项你无法忽略的成本  —  我不得不再强调一下，为支持一个单一服务去维护一机柜的服务器是多么的浪费时间和金钱。

**云团队**

使用的是基于云的自动伸缩解决方案，应用会按需要进行自动扩展和收缩。也就是说你只需要支付你所消费的计算资源的费用。

一个架构良好的基于云的应用可以实现无缝地伸缩 —  并且还是自动进行的。开发团队只需要定义好自动伸缩的资源组即可，即当你的应用 CPU 利用率达到某个高位、或者每秒有多大请求数时启动多少实例，并且你可以根据你的意愿去定制这些规则。

#### 2 — 应对故障的弹性

当说到弹性时，将托管服务的基础设施放在同一个房间里并不是一个好的选择。如果你的应用托管在一个单一的数据中心  —  （不是如果）发生某些失败时（LCTT 译注：指坍塌、地震、洪灾等），你的所有的东西都被埋了。

**传统团队**

满足这种基本需求的标准解决方案是，为实现局部弹性建立至少两个服务器  —  在地理上冗余的数据中心之间实施秒级复制。

开发团队需要一个负载均衡解决方案，以便于在发生饱和或者故障等事件时将流量转向到另一个节点  —  并且还要确保镜像节点之间，整个栈是持续完全同步的。

**云团队**

在 AWS 全球 50 个地区中，他们都提供多个*可用区*。每个区域由多个容错数据中心组成  —  通过自动故障切换功能，AWS 可以将服务无缝地转移到该地区的其它区中。

在一个 `CloudFormation` 模板中定义你的*基础设施即代码*，确保你的基础设施在自动伸缩事件中跨区保持一致 — 而对于流量的流向管理，AWS 负载均衡服务仅需要做很少的配置即可。

#### 3 — 安全和数据保护

安全是一个组织中任何一个系统的基本要求。我想你肯定不想成为那些不幸遭遇安全问题的公司之一的。

**传统团队**

为保证运行他们服务的基础服务器安全，他们不得不持续投入成本。这意味着将需要投资一个团队，以监视和识别安全威胁，并用来自不同数据源的跨多个供应商解决方案打上补丁。

**云团队**

使用公共云并不能免除来自安全方面的责任。云团队仍然需要提高警惕，但是并不需要去担心为底层基础设施打补丁的问题。AWS 将积极地对付各种零日漏洞 — 最近的一次是 Spectre 和 Meltdown。

利用来自 AWS 的身份管理和加密安全服务，可以让云团队专注于他们的应用 —  而不是无差别的安全管理。使用 CloudTrail 对 API 到 AWS 服务的调用做全面审计，可以实现透明地监视。

#### 4 — 监视和日志

任何基础设施和部署为服务的应用都需要严密监视实时数据。团队应该有一个可以访问的仪表板，当超过指标阈值时仪表板会显示警报，并能够在排错时提供与事件相关的日志。

**传统团队**

对于传统基础设施，将不得不在跨不同供应商和“雪花状”的解决方案上配置监视和报告解决方案。配置这些“见鬼的”解决方案将花费你大量的时间和精力 —  并且能够正确地实现你的目的是相当困难的。

对于大多数部署在专用基础设施上的应用来说，为了搞清楚你的应用为什么崩溃，你可以通过搜索保存在你的服务器文件系统上的日志文件来找到答案。为此你的团队需要通过 SSH 进入服务器，导航到日志文件所在的目录，然后浪费大量的时间，通过 `grep` 在成百上千的日志文件中寻找。如果你在一个横跨 60 台服务器上部署的应用中这么做  —  我能负责任地告诉你，这是一个极差的解决方案。

**云团队**

利用原生的 AWS 服务，如 CloudWatch 和 CloudTrail，来做云应用程序的监视是非常容易。不需要很多的配置，开发团队就可以监视部署的服务上的各种指标  —  问题的排除过程也不再是个恶梦了。

对于传统的基础设施，团队需要构建自己的解决方案，配置他们的 REST API 或者服务去推送日志到一个聚合器。而得到这个“开箱即用”的解决方案将对生产力有极大的提升。

#### 5 — 加速开发进程

现在的商业环境中，快速上市的能力越来越重要。由于实施的延误所失去的机会成本，可能成为影响最终利润的一个主要因素。

**传统团队**

对于大多数组织，他们需要在新项目所需要的硬件采购、配置和部署上花费很长的时间 — 并且由于预测能力差，提前获得的额外的性能将造成大量的浪费。

而且还有可能的是，传统的开发团队在无数的“筒仓”中穿梭以及在移交创建的服务上花费数月的时间。项目的每一步都会在数据库、系统、安全、以及网络管理方面需要一个独立工作。

**云团队**

而云团队开发新特性时，拥有大量的随时可投入生产系统的服务套件供你使用。这是开发者的天堂。每个 AWS 服务一般都有非常好的文档并且可以通过你选择的语言以编程的方式去访问。

使用新的云架构，例如无服务器，开发团队可以在最小化冲突的前提下构建和部署一个可扩展的解决方案。比如，只需要几天时间就可以建立一个 [Imgur 的无服务器克隆](https://read.acloud.guru/building-an-imgur-clone-part-2-image-rekognition-and-a-dynamodb-backend-abc9af300123)，它具有图像识别的特性，内置一个产品级的监视/日志解决方案，并且它的弹性极好。

![](/data/attachment/album/201807/04/114337yg4k4uqvqxkjgbcu.png)

*如何建立一个 Imgur 的无服务器克隆*

如果必须要我亲自去设计弹性和可伸缩性，我可以向你保证，我会陷在这个项目的开发里 — 而且最终的产品将远不如目前的这个好。

从我实践的情况来看，使用无服务器架构的交付时间远小于在大多数公司中提供硬件所花费的时间。我只是简单地将一系列 AWS 服务与 Lambda 功能 — 以及 ta-da 耦合到一起而已！我只专注于开发解决方案，而无差别的可伸缩性和弹性是由 AWS 为我处理的。

#### 关于云计算成本的结论

就弹性而言，云计算团队的按需扩展是当之无愧的赢家 — 因为他们仅为需要的计算能力埋单。而不需要为维护和底层的物理基础设施打补丁付出相应的资源。

云计算也为开发团队提供一个可使用多个可用区的弹性架构、为每个服务构建的安全特性、持续的日志和监视工具、随用随付的服务、以及低成本的加速分发实践。

大多数情况下，云计算的成本要远低于为你的应用运行所需要的购买、支持、维护和设计的按需基础架构的成本 —  并且云计算的麻烦事更少。

通过利用云计算，我们可以更少的先期投入而使业务快速开展。整体而言，当你开发和部署业务服务时，云计算的经济性可以让你的工作更赞。

也有一些云计算比传统基础设施更昂贵的例子，一些情况是在周末忘记关闭运行的一些极其昂贵的测试机器。

[Dropbox 在决定推出自己的基础设施并减少对 AWS 服务的依赖之后，在两年的时间内节省近 7500 万美元的费用，Dropbox…——www.geekwire.com](https://www.geekwire.com/2018/dropbox-saved-almost-75-million-two-years-building-tech-infrastructure/)

即便如此，这样的案例仍然是非常少见的。更不用说当初 Dropbox 也是从 AWS 上开始它的业务的  —  并且当它的业务达到一个临界点时，才决定离开这个平台。即便到现在，他们也已经进入到云计算的领域了，并且还在 AWS 和 GCP 上保留了 40% 的基础设施。

将云服务与基于单一“成本”指标（LCTT 译注：此处的“成本”仅指物理基础设施的购置成本）的传统基础设施比较的想法是极其幼稚的  —  公然无视云为开发团队和你的业务带来的一些主要的优势。

在极少数的情况下，云服务比传统基础设施产生更多的绝对成本  —  但它在开发团队的生产力、速度和创新方面仍然贡献着更好的价值。

![](/data/attachment/album/201807/04/114337lk1jkge1gzxzex6x.png)

*客户才不在乎你的数据中心呢*

*我非常乐意倾听你在云中开发的真实成本相关的经验和反馈！请在下面的评论区、Twitter [@Elliot\_F](https://twitter.com/Elliot_F) 上、或者直接在 [LinkedIn](https://www.linkedin.com/in/elliotforbes/) 上联系我。*

---

via: <https://read.acloud.guru/the-true-cost-of-cloud-a-comparison-of-two-development-teams-edc77d3dc6dc>

作者：[Elliot Forbes](https://read.acloud.guru/@elliot_f?source=post_header_lockup) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

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


命令行中的世界杯

![](/data/attachment/album/201807/04/214729riril1v7egmtxklg.jpg)

足球始终在我们身边。即使我们国家的队伍已经出局（LCTT 译注：显然这不是指我们国家，因为我们根本没有入局……），我还是想知道球赛比分。目前， 国际足联世界杯是世界上最大的足球锦标赛，2018 届是由俄罗斯主办的。每届世界杯都有一些足球强国未能取得参赛资格（LCTT 译注：我要吐槽么？）。意大利和荷兰就无缘本次世界杯。但是即使在未参加比赛的国家，追踪关注最新比分也成为了一种仪式。我希望能及时了解这个世界级的重大赛事最新比分的变化，而不用去搜索不同的网站。

如果你很喜欢命令行，那么有更好的方法用一个小型命令行程序追踪最新的世界杯比分和排名。让我们看一看最热门的可用的球赛趋势分析程序之一，它叫作 football-cli。

football-cli 不是一个开创性的应用程序。这几年，有许多命令行工具可以让你了解到最新的球赛比分和赛事排名。例如，我是 soccer-cli （Python 写的）和 App-football （Perl 写的）的重度用户。但我总是在寻找新的趋势分析应用，而 football-cli 在某些方面脱颖而出。

football-cli 是 JavaScript 开发的，由 Manraj Singh 编写，它是开源的软件。基于 MIT 许可证发布，用 npm（JavaScript 包管理器）安装十分简单。那么，让我们直接行动吧！

该应用程序提供了命令以获取过去及现在的赛事得分、查看联赛和球队之前和将要进行的赛事。它也会显示某一特定联赛的排名。有一条指令可以列出程序所支持的不同赛事。我们不妨从最后一个条指令开始。

在 shell 提示符下：

```
luke@ganges:~$ football lists

```

![球赛列表](/data/attachment/album/201807/04/214730tre5a4elb5bzrmg5.png)

世界杯被列在最下方，我错过了昨天的比赛，所以为了了解比分，我在 shell 提示下输入：

```
luke@ganges:~$ football scores

```

![football-wc-22](/data/attachment/album/201807/04/214731nup5t510qb5z0pyy.png)

现在，我想看看目前的世界杯小组排名。很简单：

```
luke@ganges:~$ football standings -l WC

```

下面是输出的一个片段：

![football-wc-biaoge](/data/attachment/album/201807/04/214735xuhewryhhrs0haar.png)

你们当中眼尖的可能会注意到这里有一个错误。比如比利时看上去领先于 G 组，但这是不正确的，比利时和英格兰（截稿前）在得分上打平。在这种情况下，纪律好的队伍排名更高。英格兰收到两张黄牌，而比利时收到三张，因此，英格兰应当名列榜首。

假设我想知道利物浦 90 天前英超联赛的结果，那么:

```
luke@ganges:~$ football fixtures -l PL -d 90 -t "Liverpool"

```

![足球-利物浦](/data/attachment/album/201807/04/214737tpi7rp2pvk7vs2s7.png)

我发现这个程序非常方便。它用一种清晰、整洁而有吸引力的方式显示分数和排名。当欧洲联赛再次开始时，它就更有用了。（事实上 2018-19 冠军联赛已经在进行中）！

这几个示例让大家对 football-cli 的实用性有了更深的体会。想要了解更多，请转至开发者的 [GitHub 页面](https://github.com/ManrajGrover/football-cli)。足球 ＋ 命令行 ＝ football-cli。

如同许多类似的工具一样，该软件从 football-data.org 获取相关数据。这项服务以机器可读的方式为所有欧洲主要联赛提供数据，包括比赛、球队、球员、结果等等。所有这些信息都是以 JOSN 形式通过一个易于使用的 RESTful API 提供的。

---

via: <https://www.linuxlinks.com/football-cli-world-cup-football-on-the-command-line/>

作者：[Luke Baker](https://www.linuxlinks.com/author/luke-baker/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[ZenMoore](https://github.com/ZenMoore) 校对：[wxy](https://github.com/wxy)

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


3 个 Python 命令行工具

Summary: 用 Click、Docopt 和 Fire 库写你自己的命令行应用。

用 Click、Docopt 和 Fire 库写你自己的命令行应用。

>
> 用 Click、Docopt 和 Fire 库写你自己的命令行应用。
>
>
>

![](/data/attachment/album/201807/04/221255bso5csppf0437x8r.jpg)

有时对于某项工作来说一个命令行工具就足以胜任。命令行工具是一种从你的 shell 或者终端之类的地方交互或运行的程序。[Git](https://git-scm.com/) 和 [Curl](https://curl.haxx.se/) 就是两个你也许已经很熟悉的命令行工具。

当你有一小段代码需要在一行中执行多次或者经常性地被执行，命令行工具就会很有用。Django 开发者执行 `./manage.py runserver` 命令来启动他们的网络服务器；Docker 开发者执行 `docker-compose up` 来启动他们的容器。你想要写一个命令行工具的原因可能和你一开始想写代码的原因有很大不同。

对于这个月的 Python 专栏，我们有 3 个库想介绍给希望为自己编写命令行工具的 Python 使用者。

### Click

[Click](http://click.pocoo.org/5/) 是我们最爱的用来开发命令行工具的 Python 包。其：

* 有一个富含例子的出色文档
* 包含说明如何将命令行工具打包成一个更加易于执行的 Python 应用程序
* 自动生成实用的帮助文本
* 使你能够叠加使用可选和必要参数，甚至是 [多个命令](http://click.pocoo.org/5/commands/)
* 有一个 Django 版本（ [`django-click`](https://github.com/GaretJax/django-click) ）用来编写管理命令

Click 使用 `@click.command()` 去声明一个函数作为命令，同时可以指定必要和可选参数。

```
# hello.py
import click

@click.command()
@click.option('--name', default='', help='Your name')
def say_hello(name):
    click.echo("Hello {}!".format(name))

if __name__ == '__main__':
    say_hello()

```

`@click.option()` 修饰器声明了一个 [可选参数](http://click.pocoo.org/5/options/) ，而 `@click.argument()` 修饰器声明了一个 [必要参数](http://click.pocoo.org/5/arguments/)。你可以通过叠加修饰器来组合可选和必要参数。`echo()` 方法将结果打印到控制台。

```
$ python hello.py --name='Lacey'
Hello Lacey!

```

### Docopt

[Docopt](http://docopt.org/) 是一个命令行工具的解析器，类似于命令行工具的 Markdown。如果你喜欢流畅地编写应用文档，在本文推荐的库中 Docopt 有着最好的格式化帮助文本。它不是我们最爱的命令行工具开发包的原因是它的文档犹如把人扔进深渊，使你开始使用时会有一些小困难。然而，它仍是一个轻量级的、广受欢迎的库，特别是当一个漂亮的说明文档对你来说很重要的时候。

Docopt 对于如何格式化文章开头的 docstring 是很特别的。在工具名称后面的 docsring 中，顶部元素必须是 `Usage:` 并且需要列出你希望命令被调用的方式（比如：自身调用，使用参数等等）。`Usage:` 需要包含 `help` 和 `version` 参数。

docstring 中的第二个元素是 `Options:`，对于在 `Usages:` 中提及的可选项和参数，它应当提供更多的信息。你的 docstring 的内容变成了你帮助文本的内容。

```
"""HELLO CLI

Usage:
    hello.py
    hello.py <name>
    hello.py -h|--help
    hello.py -v|--version

Options:
    <name>  Optional name argument.
    -h --help  Show this screen.
    -v --version  Show version.
"""

from docopt import docopt

def say_hello(name):
    return("Hello {}!".format(name))

if __name__ == '__main__':
    arguments = docopt(__doc__, version='DEMO 1.0')
    if arguments['<name>']:
        print(say_hello(arguments['<name>']))
    else:
        print(arguments)

```

在最基本的层面，Docopt 被设计用来返回你的参数键值对。如果我不指定上述的 `name` 调用上面的命令，我会得到一个字典的返回值：

```
$ python hello.py
{'--help': False,
 '--version': False,
 '<name>': None}

```

这里可看到我没有输入 `help` 和 `version` 标记并且 `name` 参数是 `None`。

但是如果我带着一个 `name` 参数调用，`say_hello` 函数就会执行了。

```
$ python hello.py Jeff
Hello Jeff!

```

Docopt 允许同时指定必要和可选参数，且各自有着不同的语法约定。必要参数需要在 `ALLCAPS` 和 `<carets>` 中展示，而可选参数需要单双横杠显示，就像 `--like`。更多内容可以阅读 Docopt 有关 [patterns](https://github.com/docopt/docopt#usage-pattern-format) 的文档。

### Fire

[Fire](https://github.com/google/python-fire) 是谷歌的一个命令行工具开发库。尤其令人喜欢的是当你的命令需要更多复杂参数或者处理 Python 对象时，它会聪明地尝试解析你的参数类型。

Fire 的 [文档](https://github.com/google/python-fire/blob/master/docs/guide.md) 包括了海量的样例，但是我希望这些文档能被更好地组织。Fire 能够处理 [同一个文件中的多条命令](https://github.com/google/python-fire/blob/master/docs/guide.md#exposing-multiple-commands)、使用 [对象](https://github.com/google/python-fire/blob/master/docs/guide.md#version-3-firefireobject) 的方法作为命令和 [分组](https://github.com/google/python-fire/blob/master/docs/guide.md#grouping-commands) 命令。

它的弱点在于输出到控制台的文档。命令行中的 docstring 不会出现在帮助文本中，并且帮助文本也不一定标识出参数。

```
import fire

def say_hello(name=''):
    return 'Hello {}!'.format(name)

if __name__ == '__main__':
    fire.Fire()

```

参数是必要还是可选取决于你是否在函数或者方法定义中为其指定了一个默认值。要调用命令，你必须指定文件名和函数名，比较类似 Click 的语法：

```
$ python hello.py say_hello Rikki
Hello Rikki!

```

你还可以像标记一样传参，比如 `--name=Rikki`。

### 额外赠送：打包！

Click 包含了使用 `setuptools` [打包](http://click.pocoo.org/5/setuptools/) 命令行工具的使用说明（强烈推荐按照说明操作）。

要打包我们第一个例子中的命令行工具，将以下内容加到你的 `setup.py` 文件里：

```
from setuptools import setup

setup(
    name='hello',
    version='0.1',
    py_modules=['hello'],
    install_requires=[
        'Click',
    ],
    entry_points='''
        [console_scripts]
        hello=hello:say_hello
    ''',
)

```

任何你看见 `hello` 的地方，使用你自己的模块名称替换掉，但是要记得忽略 `.py` 后缀名。将 `say_hello` 替换成你的函数名称。

然后，执行 `pip install --editable` 来使你的命令在命令行中可用。

现在你可以调用你的命令，就像这样：

```
$ hello --name='Jeff'
Hello Jeff!

```

通过打包你的命令，你可以省掉在控制台键入 `python hello.py --name='Jeff'` 这种额外的步骤以减少键盘敲击。这些指令也很可能可在我们提到的其他库中使用。

---

via: <https://opensource.com/article/18/5/3-python-command-line-tools>

作者：[Jeff Triplett](https://opensource.com/users/laceynwilliams)，[Lacey Williams Hensche](https://opensource.com/users/laceynwilliams) 选题：[lujun9972](https://github.com/lujun9972) 译者：[hoppipolla-](https://github.com/hoppipolla-) 校对：[wxy](https://github.com/wxy)

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