使用开源工具构建 DevOps 流水线的初学者指南

写的很棒！谢谢分享！

原来之前搭建的Jenkins 编译代码，就属于DevOps, 感谢分享！

# 使用开源工具构建 DevOps 流水线的初学者指南

>
> 如果你是 DevOps 新人，请查看这 5 个步骤来构建你的第一个 DevOps 流水线。
>
>
>

DevOps 已经成为解决软件开发过程中出现的缓慢、孤立或者其他故障的默认方式。但是当你刚接触 DevOps 并且不确定从哪开始时，就意义不大了。本文探索了什么是 DevOps 流水线并且提供了创建它的 5 个步骤。尽管这个教程并不全面，但可以给你以后上手和扩展打下基础。首先，插入一个小故事。

### 我的 DevOps 之旅

我曾经在花旗集团的云小组工作，开发<ruby> <rt>  Infrastructure as a Service </rt> 基础设施即服务</ruby>网页应用来管理花旗的云基础设施，但我经常对研究如何让开发流水线更加高效以及如何带给团队积极的文化感兴趣。我在 Greg Lavender 推荐的书中找到了答案。Greg Lavender 是花旗的云架构和基础设施工程（即 [Phoenix 项目](https://www.amazon.com/dp/B078Y98RG8/)）的 CTO。这本书尽管解释的是 DevOps 原理，但它读起来像一本小说。

书后面的一张表展示了不同公司部署在发布环境上的频率：

| 公司 | 部署频率 |
| --- | --- |
| Amazon | 23,000 次/天 |
| Google | 5,500 次/天 |
| Netflix | 500 次/天 |
| Facebook | 1 次/天 |
| Twitter | 3 次/周 |
| 典型企业 | 1 次/9 个月 |

Amazon、Google、Netflix 怎么能做到如此之频繁？那是因为这些公司弄清楚了如何去实现一个近乎完美的 DevOps 流水线。

但在花旗实施 DevOps 之前，情况并非如此。那时候，我的团队拥有不同<ruby> 构建阶段 <rt>  stage </rt></ruby>的环境，但是在开发服务器上的部署非常手工。所有的开发人员都只能访问一个基于 IBM WebSphere Application 社区版的开发环境服务器。问题是当多个用户同时尝试部署时，服务器就会宕机，因此开发人员在部署时就得互相通知，这一点相当痛苦。此外，还存在代码测试覆盖率低、手动部署过程繁琐以及无法根据定义的任务或用户需求跟踪代码部署的问题。

我意识到必须做些事情，同时也找到了一个有同样感受的同事。我们决定合作去构建一个初始的 DevOps 流水线 —— 他设置了一个虚拟机和一个 Tomcat 服务器，而我则架设了 Jenkins，集成了 Atlassian Jira、BitBucket 和代码覆盖率测试。这个业余项目非常成功：我们近乎全自动化了开发流水线，并在开发服务器上实现了几乎 100% 的正常运行，我们可以追踪并改进代码覆盖率测试，并且 Git 分支能够与部署任务和 jira 任务关联在一起。此外，大多数用来构建 DevOps 所使用的工具都是开源的。

现在我意识到了我们的 DevOps 流水线是多么的原始，因为我们没有利用像 Jenkins 文件或 Ansible 这样的高级设置。然而，这个简单的过程运作良好，这也许是因为 [Pareto](https://en.wikipedia.org/wiki/Pareto_principle) 原则（也被称作 80/20 法则）。

### DevOps 和 CI/CD 流水线的简要介绍

如果你问一些人，“什么是 DevOps？”，你或许会得到一些不同的回答。DevOps，就像敏捷，已经发展到涵盖着诸多不同的学科，但大多数人至少会同意这些：DevOps 是一个软件开发实践或一个<ruby> 软件开发生命周期 <rt>  software development lifecycle </rt></ruby>（SDLC），并且它的核心原则是一种文化上的变革 —— 开发人员与非开发人员呼吸着同一片天空的气息，之前手工的事情变得自动化；每个人做着自己擅长的事；同一时间的部署变得更加频繁；吞吐量提升；灵活度增加。

虽然拥有正确的软件工具并非实现 DevOps 环境所需的唯一东西，但一些工具却是必要的。最关键的一个便是持续集成和持续部署（CI/CD）。在流水线环境中，拥有不同的构建阶段（例如：DEV、INT、TST、QA、UAT、STG、PROD），手动的工作能实现自动化，开发人员可以实现高质量的代码，灵活而且大量部署。

这篇文章描述了一个构建 DevOps 流水线的五步方法，就像下图所展示的那样，使用开源的工具实现。

闲话少说，让我们开始吧。

### 第一步：CI/CD 框架

首先你需要的是一个 CI/CD 工具，Jenkins，是一个基于 Java 的 MIT 许可下的开源 CI/CD 工具，它是推广 DevOps 运动的工具，并已成为了<ruby> 事实标准 <rt>  de facto standard </rt> <ruby>  。 </ruby></ruby>

所以，什么是 Jenkins？想象它是一种神奇的万能遥控，能够和许多不同的服务器和工具打交道，并且能够将它们统一安排起来。就本身而言，像 Jenkins 这样的 CI/CD 工具本身是没有用的，但随着接入不同的工具与服务器时会变得非常强大。

Jenkins 仅是众多构建 DevOps 流水线的开源 CI/CD 工具之一。

| 名称 | 许可证 |
| --- | --- |
| [Jenkins](https://github.com/jenkinsci/jenkins) | Creative Commons 和 MIT |
| [Travis CI](https://github.com/travis-ci/travis-ci) | MIT |
| [CruiseControl](http://cruisecontrol.sourceforge.net) | BSD |
| [Buildbot](https://github.com/buildbot/buildbot) | GPL |
| [Apache Gump](https://gump.apache.org) | Apache 2.0 |
| [Cabie](http://cabie.tigris.org) | GNU |

下面就是使用 CI/CD 工具时 DevOps 看起来的样子。

你的 CI/CD 工具在本地主机上运行，但目前你还不能够做些别的。让我们紧随 DevOps 之旅的脚步。

### 第二步：源代码控制管理

验证 CI/CD 工具可以执行某些魔术的最佳（也可能是最简单）方法是与源代码控制管理（SCM）工具集成。为什么需要源代码控制？假设你在开发一个应用。无论你什么时候构建应用，无论你使用的是 Java、Python、C++、Go、Ruby、JavaScript 或任意一种语言，你都在编程。你所编写的程序代码称为源代码。在一开始，特别是只有你一个人工作时，将所有的东西放进本地文件夹里或许都是可以的。但是当项目变得庞大并且邀请其他人协作后，你就需要一种方式来避免共享代码修改时的合并冲突。你也需要一种方式来恢复一个之前的版本——备份、复制并粘贴的方式已经过时了。你（和你的团队）想要更好的解决方式。

这就是 SCM 变得不可或缺的原因。SCM 工具通过在仓库中保存代码来帮助进行版本控制与多人协作。

尽管这里有许多 SCM 工具，但 Git 是最标准恰当的。我极力推荐使用 Git，但如果你喜欢这里仍有其他的开源工具。

| 名称 | 许可证 |
| --- | --- |
| [Git](https://git-scm.com) | GPLv2 & LGPL v2.1 |
| [Subversion](https://subversion.apache.org) | Apache 2.0 |
| [Concurrent Versions System](http://savannah.nongnu.org/projects/cvs) (CVS) | GNU |
| [Vesta](http://www.vestasys.org) | LGPL |
| [Mercurial](https://www.mercurial-scm.org) | GNU GPL v2+ |

拥有 SCM 之后，DevOps 流水线看起来就像这样。

CI/CD 工具能够自动化进行源代码检入检出以及完成成员之间的协作。还不错吧？但是，如何才能把它变成可工作的应用程序，使得数十亿人来使用并欣赏它呢？

### 第三步：自动化构建工具

真棒！现在你可以检出代码并将修改提交到源代码控制，并且可以邀请你的朋友就源代码控制进行协作。但是到目前为止你还没有构建出应用。要想让它成为一个网页应用，必须将其编译并打包成可部署的包或可执行程序（注意，像 JavaScript 或 PHP 这样的解释型编程语言不需要进行编译）。

于是就引出了自动化构建工具。无论你决定使用哪一款构建工具，它们都有一个共同的目标：将源代码构建成某种想要的格式，并且将清理、编译、测试、部署到某个位置这些任务自动化。构建工具会根据你的编程语言而有不同，但这里有一些通常使用的开源工具值得考虑。

| 名称 | 许可证 | 编程语言 |
| --- | --- | --- |
| [Maven](https://maven.apache.org) | Apache 2.0 | Java |
| [Ant](https://ant.apache.org) | Apache 2.0 | Java |
| [Gradle](https://gradle.org/) | Apache 2.0 | Java |
| [Bazel](https://bazel.build) | Apache 2.0 | Java |
| [Make](https://www.gnu.org/software/make) | GNU | N/A |
| [Grunt](https://gruntjs.com) | MIT | JavaScript |
| [Gulp](https://gulpjs.com) | MIT | JavaScript |
| [Buildr](http://buildr.apache.org) | Apache | Ruby |
| [Rake](https://github.com/ruby/rake) | MIT | Ruby |
| [A-A-P](http://www.a-a-p.org) | GNU | Python |
| [SCons](https://www.scons.org) | MIT | Python |
| [BitBake](https://www.yoctoproject.org/software-item/bitbake) | GPLv2 | Python |
| [Cake](https://github.com/cake-build/cake) | MIT | C# |
| [ASDF](https://common-lisp.net/project/asdf) | Expat (MIT) | LISP |
| [Cabal](https://www.haskell.org/cabal) | BSD | Haskell |

太棒了！现在你可以将自动化构建工具的配置文件放进源代码控制管理系统中，并让你的 CI/CD 工具构建它。

一切都如此美好，对吧？但是在哪里部署它呢？

### 第四步：网页应用服务器

到目前为止，你有了一个可执行或可部署的打包文件。对任何真正有用的应用程序来说，它必须提供某种服务或者接口，所以你需要一个容器来发布你的应用。

对于网页应用，网页应用服务器就是容器。应用程序服务器提供了环境，让可部署包中的编程逻辑能够被检测到、呈现界面，并通过打开套接字为外部世界提供网页服务。在其他环境下你也需要一个 HTTP 服务器（比如虚拟机）来安装服务应用。现在，我假设你将会自己学习这些东西（尽管我会在下面讨论容器）。

这里有许多开源的网页应用服务器。

| 名称 | 协议 | 编程语言 |
| --- | --- | --- |
| [Tomcat](https://tomcat.apache.org) | Apache 2.0 | Java |
| [Jetty](https://www.eclipse.org/jetty/) | Apache 2.0 | Java |
| [WildFly](http://wildfly.org) | GNU Lesser Public | Java |
| [GlassFish](https://javaee.github.io/glassfish) | CDDL & GNU Less Public | Java |
| [Django](https://www.djangoproject.com/) | 3-Clause BSD | Python |
| [Tornado](http://www.tornadoweb.org/en/stable) | Apache 2.0 | Python |
| [Gunicorn](https://gunicorn.org) | MIT | Python |
| [Python Paste](https://github.com/cdent/paste) | MIT | Python |
| [Rails](https://rubyonrails.org) | MIT | Ruby |
| [Node.js](https://nodejs.org/en) | MIT | Javascript |

现在 DevOps 流水线差不多能用了，干得好！

尽管你可以在这里停下来并进行进一步的集成，但是代码质量对于应用开发者来说是一件非常重要的事情。

### 第五步：代码覆盖测试

实现代码测试件可能是另一个麻烦的需求，但是开发者需要尽早地捕捉程序中的所有错误并提升代码质量来保证最终用户满意度。幸运的是，这里有许多开源工具来测试你的代码并提出改善质量的建议。甚至更好的，大部分 CI/CD 工具能够集成这些工具并将测试过程自动化进行。

代码测试分为两个部分：“代码测试框架”帮助进行编写与运行测试，“代码质量改进工具”帮助提升代码的质量。

#### 代码测试框架

| 名称 | 许可证 | 编程语言 |
| --- | --- | --- |
| [JUnit](https://junit.org/junit5) | Eclipse Public License | Java |
| [EasyMock](http://easymock.org) | Apache | Java |
| [Mockito](https://site.mockito.org) | MIT | Java |
| [PowerMock](https://github.com/powermock/powermock) | Apache 2.0 | Java |
| [Pytest](https://docs.pytest.org) | MIT | Python |
| [Hypothesis](https://hypothesis.works) | Mozilla | Python |
| [Tox](https://github.com/tox-dev/tox) | MIT | Python |

#### 代码质量改进工具

| 名称 | 许可证 | 编程语言 |
| --- | --- | --- |
| [Cobertura](http://cobertura.github.io/cobertura) | GNU | Java |
| [CodeCover](http://codecover.org/) | Eclipse Public (EPL) | Java |
| [Coverage.py](https://github.com/nedbat/coveragepy) | Apache 2.0 | Python |
| [Emma](http://emma.sourceforge.net) | Common Public License | Java |
| [JaCoCo](https://github.com/jacoco/jacoco) | Eclipse Public License | Java |
| [Hypothesis](https://hypothesis.works) | Mozilla | Python |
| [Tox](https://github.com/tox-dev/tox) | MIT | Python |
| [Jasmine](https://jasmine.github.io) | MIT | JavaScript |
| [Karma](https://github.com/karma-runner/karma) | MIT | JavaScript |
| [Mocha](https://github.com/mochajs/mocha) | MIT | JavaScript |
| [Jest](https://jestjs.io) | MIT | JavaScript |

注意，之前提到的大多数工具和框架都是为 Java、Python、JavaScript 写的，因为 C++ 和 C# 是专有编程语言（尽管 GCC 是开源的）。

现在你已经运用了代码覆盖测试工具，你的 DevOps 流水线应该就像教程开始那幅图中展示的那样了。

### 可选步骤

#### 容器

正如我之前所说，你可以在虚拟机（VM）或服务器上发布你的应用，但是容器是一个更好的解决方法。

[什么是容器](/resources/what-are-linux-containers)？简要的介绍就是 VM 需要占用操作系统大量的资源，它提升了应用程序的大小，而容器仅仅需要一些库和配置来运行应用程序。显然，VM 仍有重要的用途，但容器对于发布应用（包括应用程序服务器）来说是一个更为轻量的解决方式。

尽管对于容器来说也有其他的选择，但是 Docker 和 Kubernetes 更为广泛。

| 名称 | 许可证 |
| --- | --- |
| [Docker](https://www.docker.com) | Apache 2.0 |
| [Kubernetes](https://kubernetes.io) | Apache 2.0 |

了解更多信息，请查看 [Opensource.com](http://Opensource.com) 上关于 Docker 和 Kubernetes 的其它文章：

* [什么是 Docker？](https://opensource.com/resources/what-docker)
* [Docker 简介](https://opensource.com/business/15/1/introduction-docker)
* [什么是 Kubernetes？](https://opensource.com/resources/what-is-kubernetes)
* [从零开始的 Kubernetes 实践](https://opensource.com/article/17/11/kubernetes-lightning-talk)

#### 中间件自动化工具

我们的 DevOps 流水线大部分集中在协作构建与部署应用上，但你也可以用 DevOps 工具完成许多其他的事情。其中之一便是利用它实现<ruby> 基础设施管理 <rt>  Infrastructure as Code </rt></ruby>（IaC）工具，这也是熟知的中间件自动化工具。这些工具帮助完成中间件的自动化安装、管理和其他任务。例如，自动化工具可以用正确的配置下拉应用程序，例如网页服务器、数据库和监控工具，并且部署它们到应用服务器上。

这里有几个开源的中间件自动化工具值得考虑：

| 名称 | 许可证 |
| --- | --- |
| [Ansible](https://www.ansible.com) | GNU Public |
| [SaltStack](https://www.saltstack.com) | Apache 2.0 |
| [Chef](https://www.chef.io) | Apache 2.0 |
| [Puppet](https://puppet.com) | Apache or GPL |

获取更多中间件自动化工具，查看 [Opensource.com](http://Opensource.com) 上的其它文章：

* [Ansible 快速入门指南](https://opensource.com/article/19/2/quickstart-guide-ansible)
* [Ansible 自动化部署策略](https://opensource.com/article/19/1/automating-deployment-strategies-ansible)
* [配置管理工具 Top 5](https://opensource.com/article/18/12/configuration-management-tools)

### 之后的发展

这只是一个完整 DevOps 流水线的冰山一角。从 CI/CD 工具开始并且探索其他可以自动化的东西来使你的团队更加轻松的工作。并且，寻找[开源通讯工具](https://opensource.com/alternatives/slack)可以帮助你的团队一起工作的更好。

发现更多见解，这里有一些非常棒的文章来介绍 DevOps ：

* [什么是 DevOps](https://opensource.com/resources/devops)
* [掌握 5 件事成为 DevOps 工程师](https://opensource.com/article/19/2/master-devops-engineer)
* [所有人的 DevOps](https://opensource.com/article/18/11/how-non-engineer-got-devops)
* [在 DevOps 中开始使用预测分析](https://opensource.com/article/19/1/getting-started-predictive-analytics-devops)

使用开源 agile 工具来集成 DevOps 也是一个很好的主意：

* [什么是 agile ？](https://opensource.com/article/18/10/what-agile)
* [4 步成为一个了不起的 agile 开发者](https://opensource.com/article/19/2/steps-agile-developer)

---

via: <https://opensource.com/article/19/4/devops-pipeline>

作者：[Bryant Son](https://opensource.com/users/brson/users/milindsingh/users/milindsingh/users/dscripter) 选题：[lujun9972](https://github.com/lujun9972) 译者：[LuMing](https://github.com/LuuMing) 校对：[wxy](https://github.com/wxy)

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


如何在 Ubuntu 中检查你的 IP 地址

# 如何在 Ubuntu 中检查你的 IP 地址

不知道你的 IP 地址是什么？以下是在 Ubuntu 和其他 Linux 发行版中检查 IP 地址的几种方法。

### 什么是 IP 地址？

**互联网协议地址**（通常称为 **IP 地址**）是分配给连接到计算机网络的每个设备（使用互联网协议）的数字标签。IP 地址用于识别和定位机器。

**IP 地址**在网络中是*唯一的*，使得所有连接设备能够通信。

你还应该知道有两种**类型的 IP 地址**：**公有**和**私有**。**公有 IP 地址**是用于互联网通信的地址，这与你用于邮件的物理地址相同。但是，在本地网络（例如使用路由器的家庭）的环境中，会为每个设备分配在该子网内唯一的**私有 IP 地址**。这在本地网络中使用，而不直接暴露公有 IP（路由器用它与互联网通信）。

另外还有区分 **IPv4** 和 **IPv6** 协议。**IPv4** 是经典的 IP 格式，它由基本的 4 部分结构组成，四个字节用点分隔（例如 127.0.0.1）。但是，随着设备数量的增加，IPv4 很快就无法提供足够的地址。这就是 **IPv6** 被发明的原因，它使用 **128 位地址**的格式（与 **IPv4** 使用的 **32 位地址**相比）。

### 在 Ubuntu 中检查你的 IP 地址（终端方式）

检查 IP 地址的最快和最简单的方法是使用 `ip` 命令。你可以按以下方式使用此命令：

```
ip addr show
```

它将同时显示 IPv4 和 IPv6 地址：

实际上，你可以进一步缩短这个命令 `ip a`。它会给你完全相同的结果。

```
ip a
```

如果你希望获得最少的细节，也可以使用 `hostname`：

```
hostname -I
```

还有一些[在 Linux 中检查 IP 地址的方法](https://linuxhandbook.com/find-ip-address/)，但是这两个命令足以满足这个目的。

`ifconfig` 如何？

老用户可能会想要使用 `ifconfig`（net-tools 软件包的一部分），但该程序已被弃用。一些较新的 Linux 发行版不再包含此软件包，如果你尝试运行它，你将看到 ifconfig 命令未找到的错误。

### 在 Ubuntu 中检查你的 IP 地址（GUI 方式）

如果你对命令行不熟悉，你还可以使用图形方式检查 IP 地址。

打开 Ubuntu 应用菜单（在屏幕左下角**显示应用**）并搜索**Settings**，然后单击图标：

这应该会打开**设置菜单**。进入**网络**：

按下连接旁边的**齿轮图标**会打开一个窗口，其中包含更多设置和有关你网络链接的信息，其中包括你的 IP 地址：

### 额外提示：检查你的公共 IP 地址（适用于台式计算机）

首先，要检查你的**公有 IP 地址**（用于与服务器通信），你可以[使用 curl 命令](https://linuxhandbook.com/curl-command-examples/)。打开终端并输入以下命令：

```
curl ifconfig.me
```

这应该只会返回你的 IP 地址而没有其他多余信息。我建议在分享这个地址时要小心，因为这相当于公布你的个人地址。

**注意：** 如果 `curl` 没有安装，只需使用 `sudo apt install curl -y` 来解决问题，然后再试一次。

另一种可以查看公共 IP 地址的简单方法是在 Google 中搜索 “ip address”。

### 总结

在本文中，我介绍了在 Uuntu Linux 中找到 IP 地址的不同方法，并向你概述了 IP 地址的用途以及它们对我们如此重要的原因。

我希望你喜欢这篇文章。如果你觉得文章有用，请在评论栏告诉我们！

---

via: <https://itsfoss.com/check-ip-address-ubuntu/>

作者：[Sergiu](https://itsfoss.com/author/sergiu/) 选题：[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 终端颜色主题

>
> 你可以用丰富的选项来定义你的终端主题。
>
>
>

如果你大部分时间都盯着终端，那么你很自然地希望它看起来能赏心悦目。美与不美，全在观者，自 CRT 串口控制台以来，终端已经经历了很多变迁。因此，你的软件终端窗口有丰富的选项，可以用来定义你看到的主题，不管你如何定义美，这总是件好事。

### 设置

包括 GNOME、KDE 和 Xfce 在内的流行的软件终端应用，它们都提供了更改其颜色主题的选项。调整主题就像调整应用首选项一样简单。Fedora、RHEL 和 Ubuntu 默认使用 GNOME，因此本文使用该终端作为示例，但对 Konsole、Xfce 终端和许多其他终端的设置流程类似。

首先，进入到应用的“首选项”或“设置”面板。在 GNOME 终端中，你可以通过屏幕顶部或窗口右上角的“应用”菜单访问它。

在“首选项”中，单击“配置文件” 旁边的加号（“+”）来创建新的主题配置文件。在新配置文件中，单击“颜色”选项卡。

在“颜色”选项卡中，取消选择“使用系统主题中的颜色”选项，以使窗口的其余部分变为可选状态。最开始，你可以选择内置的颜色方案。这些包括浅色主题，它有明亮的背景和深色的前景文字；还有深色主题，它有深色背景和浅色前景文字。

当没有其他设置（例如 `dircolors` 命令的设置）覆盖它们时，“默认颜色”色板将同时定义前景色和背景色。“调色板”设置 `dircolors` 命令定义的颜色。这些颜色由终端以 `LS_COLORS` 环境变量的形式使用，以在 [ls](https://opensource.com/article/19/7/master-ls-command) 命令的输出中添加颜色。如果这些颜色不吸引你，请在此更改它们。

如果对主题感到满意，请关闭“首选项”窗口。

要将终端更改为新的配置文件，请单击“应用”菜单，然后选择“配置文件”。选择新的配置文件，接着享受自定义主题。

### 命令选项

如果你的终端没有合适的设置窗口，它仍然可以在启动命令中提供颜色选项。xterm 和 rxvt 终端（旧的和启用 Unicode 的变体，有时称为 urxvt 或 rxvt-unicode）都提供了这样的选项，因此即使没有桌面环境和大型 GUI 框架，你仍然可以设置终端模拟器的主题。

两个明显的选项是前景色和背景色，分别用 `-fg` 和 `-bg` 定义。每个选项的参数是*颜色名*而不是它的 ANSI 编号。例如：

```
$ urxvt -bg black -fg green
```

这些会设置默认的前景和背景。如果有任何其他规则会控制特定文件或设备类型的颜色，那么就使用这些颜色。有关如何设置它们的信息，请参阅 [dircolors](http://man7.org/linux/man-pages/man1/dircolors.1.html) 命令。

你还可以使用 `-cr` 设置文本光标（而不是鼠标光标）的颜色：

```
$ urxvt -bg black -fg green -cr teal
```

你的终端模拟器可能还有更多选项，如边框颜色（rxvt 中的 `-bd`）、光标闪烁（urxvt 中的 `-bc` 和 `+bc`），甚至背景透明度。请参阅终端的手册页，了解更多的功能。

要使用你选择的颜色启动终端，你可以将选项添加到用于启动终端的命令或菜单中（例如，在你的 Fluxbox 菜单文件、`$HOME/.local/share/applications` 目录中的 `.desktop` 或者类似的）。或者，你可以使用 [xrdb](https://www.x.org/releases/X11R7.7/doc/man/man1/xrdb.1.xhtml) 工具来管理与 X 相关的资源（但这超出了本文的范围）。

### 家是可定制的地方

自定义 Linux 机器并不意味着你需要学习如何编程。你可以而且应该进行小而有意义的更改，来使你的数字家庭感觉更舒适。而且没有比终端更好的起点了！

---

via: <https://opensource.com/article/19/8/add-color-linux-terminal>

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


5 个 Ansible 运维任务

Summary: 让 DevOps 少一点，OpsDev 多一点。

# 5 个 Ansible 运维任务

>
> 让 DevOps 少一点，OpsDev 多一点。
>
>
>

在这个 DevOps 世界中，看起来开发（Dev）这一半成为了关注的焦点，而运维（Ops）则是这个关系中被遗忘的另一半。这几乎就好像是领头的开发告诉尾随的运维做什么，几乎所有的“运维”都是开发说要做的。因此，运维被抛到后面，降级到了替补席上。

我想看到更多的 OpsDev。因此，让我们来看看 Ansible 在日常的运维中可以帮助你什么。

我选择在 [Ansible Tower](https://www.ansible.com/products/tower) 中展示这些方案，因为我认为用户界面 （UI） 可以增色大多数的任务。如果你想模拟测试，你可以在 Tower 的上游开源版本 [AWX](https://github.com/ansible/awx) 中测试它。

### 管理用户

在大规模环境中，你的用户将集中在活动目录或 LDAP 等系统中。但我敢打赌，仍然存在许多包含大量的静态用户的全负荷环境。Ansible 可以帮助你将这些分散的环境集中到一起。*社区*已为我们解决了这个问题。看看 [Ansible Galaxy](https://galaxy.ansible.com) 中的 [users](https://galaxy.ansible.com/singleplatform-eng/users) 角色。

这个角色的聪明之处在于它允许我们通过*数据*管理用户，而无需更改运行逻辑。

通过简单的数据结构，我们可以在系统上添加、删除和修改静态用户。这很有用。

### 管理 sudo

提权有[多种形式](https://docs.ansible.com/ansible/latest/plugins/become.html)，但最流行的是 [sudo](https://www.sudo.ws/intro.html)。通过每个 `user`、`group` 等离散文件来管理 sudo 相对容易。但一些人对给予特权感到紧张，并倾向于有时限地给予提权。因此[下面是一种方案](https://github.com/phips/ansible-demos/tree/master/roles/sudo)，它使用简单的 `at` 命令对授权访问设置时间限制。

### 管理服务

给入门级运维团队提供[菜单](https://docs.ansible.com/ansible-tower/latest/html/userguide/job_templates.html#surveys)以便他们可以重启某些服务不是很好吗？看下面！

### 管理磁盘空间

这有[一个简单的角色](https://github.com/phips/ansible-demos/tree/master/roles/disk)，可在特定目录中查找字节大于某个大小的文件。在 Tower 中这么做时，启用[回调](https://docs.ansible.com/ansible-tower/latest/html/userguide/job_templates.html#provisioning-callbacks)有额外的好处。想象一下，你的监控方案发现文件系统已超过 X％ 并触发 Tower 中的任务以找出是什么文件导致的。

### 调试系统性能问题

[这个角色](https://github.com/phips/ansible-demos/tree/master/roles/gather_debug)相当简单：它运行一些命令并打印输出。细节在最后输出，让你 —— 系统管理员快速浏览一眼。另外可以使用 [正则表达式](https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#regular-expression-filters) 在输出中找到某些条件（比如说 CPU 占用率超过 80％）。

### 总结

我已经录制了这五个任务的简短视频。你也可以在 Github 上找到[所有代码](https://github.com/phips/ansible-demos)！

---

via: <https://opensource.com/article/19/8/ops-tasks-ansible>

作者：[Mark Phillips](https://opensource.com/users/markphttps://opensource.com/users/adminhttps://opensource.com/users/alsweigarthttps://opensource.com/users/belljennifer43) 选题：[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/) 荣誉推出


Bash shell 的诞生

# Bash shell 的诞生

>
> 本周的《代码英雄》播客深入研究了最广泛使用的、已经成为事实标准的脚本语言，它来自于自由软件基金会及其作者的早期灵感。
>
>
>

<img src="content_image/album_201909_07_142321vwrwoq0ou0kqu48q.png" alt="Listen to the Command Line Heroes Podcast" title="Listen to the Command Line Heroes Podcast">

对于任何从事于系统管理员方面的人来说，Shell 脚本编程是一门必不可少的技能，而如今人们编写脚本的主要 shell 是 Bash。Bash 是几乎所有的 Linux 发行版和现代 MacOS 版本的默认配置，也很快就会成为 [Windows 终端](https://devblogs.microsoft.com/commandline/introducing-windows-terminal/)的原生部分。你可以说 Bash 无处不在。

那么它是如何做到这一点的呢？本周的《[代码英雄](https://www.redhat.com/en/command-line-heroes)》播客将通过询问编写那些代码的人来深入研究这个问题。

### 肇始于 Unix

像所有编程方面的东西一样，我们必须追溯到 Unix。shell 的简短历史是这样的：1971 年，Ken Thompson 发布了第一个 Unix shell：Thompson shell。但是，脚本用户所能做的存在严重限制，这意味着严重制约了自动化以及整个 IT 运营领域。

这个[奇妙的研究](https://developer.ibm.com/tutorials/l-linux-shells/)概述了早期尝试脚本的挑战：

>
> 类似于它在 Multics 中的前身，这个 shell（`/bin/sh`）是一个在内核外执行的独立用户程序。诸如通配（参数扩展的模式匹配，例如 `*.txt`）之类的概念是在一个名为 `glob` 的单独的实用程序中实现的，就像用于计算条件表达式的 `if` 命令一样。这种分离使 shell 变得更小，才不到 900 行的 C 源代码。
>
>
> shell 引入了紧凑的重定向（`<`、`>` 和 `>>`）和管道（`|` 或 `^`）语法，它们已经存在于现代 shell 中。你还可以找到对调用顺序命令（`;`）和异步命令（`＆`）的支持。
>
>
> Thompson shell 缺少的是编写脚本的能力。它的唯一目的是作为一个交互式 shell（命令解释器）来调用命令和查看结果。
>
>
>

随着对终端使用的增长，对自动化的兴趣随之增长。

### Bourne shell 前进一步

在 Thompson 发布 shell 六年后，1977 年，Stephen Bourne 发布了 Bourne shell，旨在解决Thompson shell 中的脚本限制。（Chet Ramey 是自 1990 年以来 Bash 语言的主要维护者，在这一集的《代码英雄》中讨论了它）。作为 Unix 系统的一部分，这是这个来自贝尔实验室的技术的自然演变。

Bourne 打算做什么不同的事情？[研究员 M. Jones](https://developer.ibm.com/tutorials/l-linux-shells/) 很好地概述了它：

>
> Bourne shell 有两个主要目标：作为命令解释器以交互方式执行操作系统的命令，和用于脚本编程（编写可通过 shell 调用的可重用脚本）。除了替换 Thompson shell，Bourne shell 还提供了几个优于其前辈的优势。Bourne 将控制流、循环和变量引入脚本，提供了更具功能性的语言来（以交互式和非交互式）与操作系统交互。该 shell 还允许你使用 shell 脚本作为过滤器，为处理信号提供集成支持，但它缺乏定义函数的能力。最后，它结合了我们今天使用的许多功能，包括命令替换（使用后引号）和 HERE 文档（以在脚本中嵌入保留的字符串文字）。
>
>
>

Bourne 在[之前的一篇采访中](https://www.computerworld.com.au/article/279011/-z_programming_languages_bourne_shell_sh)这样描述它：

>
> 最初的 shell （编程语言）不是一种真正的语言；它是一种记录 —— 一种从文件中线性执行命令序列的方法，唯一的控制流的原语是 `GOTO` 到一个标签。Ken Thompson 所编写的这个最初的 shell 的这些限制非常重要。例如，你无法简单地将命令脚本用作过滤器，因为命令文件本身是标准输入。而在过滤器中，标准输入是你从父进程继承的，不是命令文件。
>
>
> 最初的 shell 很简单，但随着人们开始使用 Unix 进行应用程序开发和脚本编写，它就太有限了。它没有变量、它没有控制流，而且它的引用能力非常不足。
>
>
>

对于脚本编写者来说，这个新 shell 是一个巨大的进步，但前提是你可以使用它。

### 以自由软件来重新构思 Bourne Shell

在此之前，这个占主导地位的 shell 是由贝尔实验室拥有和管理的专有软件。幸运的话，你的大学可能有权访问 Unix shell。但这种限制性访问远非自由软件基金会（FSF）想要实现的世界。

Richard Stallman 和一群志同道合的开发人员那时正在编写所有的 Unix 功能，其带有可以在 GNU 许可证下免费获得的许可。其中一个开发人员的任务是制作一个 shell，那位开发人员是 Brian Fox。他对他的任务的讲述十分吸引我。正如他在播客上所说：

>
> 它之所以如此具有挑战性，是因为我们必须忠实地模仿 Bourne shell 的所有行为，同时允许扩展它以使其成为一个供人们使用的更好工具。
>
>
>

而那时也恰逢人们在讨论 shell 标准是什么的时候。在这一历史背景和将来的竞争前景下，流行的 Bourne shell 被重新构想，并再次重生。

### 重新打造 Bourne Shell

自由软件的使命和竞争这两个催化剂使重制的 Bourne shell（Bash）具有了生命。和之前不同的是，Fox 并没有把 shell 放到自己的名字之后命名，他专注于从 Unix 到自由软件的演变。（虽然 Fox Shell 这个名字看起来要比 Fish shell 更适合作为 fsh 命令 #missedopportunity）。这个命名选择似乎符合他的个性。正如 Fox 在剧集中所说，他甚至对个人的荣耀也不感兴趣；他只是试图帮助编程文化发展。然而，他并不是一个优秀的双关语。

而 Bourne 也并没有因为他命名 shell 的文字游戏而感到被轻视。Bourne 讲述了一个故事，有人走到他面前，并在会议上给了他一件 Bash T 恤，而那个人是 Brian Fox。

| Shell | 发布于 | 创造者 |
| --- | --- | --- |
| Thompson Shell | 1971 | Ken Thompson |
| Bourne Shell | 1977 | Stephen Bourne |
| Bourne-Again Shell | 1989 | Brian Fox |

随着时间的推移，Bash 逐渐成长。其他工程师开始使用它并对其设计进行改进。事实上，多年后，Fox 坚定地认为学会放弃控制 Bash 是他一生中最重要的事情之一。随着 Unix 让位于 Linux 和开源软件运动，Bash 成为开源世界的至关重要的脚本语言。这个伟大的项目似乎超出了单一一个人的愿景范围。

### 我们能从 shell 中学到什么？

shell 是一项技术，它是笔记本电脑日常使用中的一个组成部分，你很容易忘记它也需要发明出来。从 Thompson 到 Bourne 再到 Bash，shell 的故事为我们描绘了一些熟悉的结论：

* 有动力的人可以在正确的使命中取得重大进展。
* 我们今天所依赖的大部分内容都建立在我们行业中仍然活着的那些传奇人物打下的基础之上。
* 能够生存下来的软件超越了其原始创作者的愿景。 代码英雄在全部的第三季中讲述了编程语言，并且正在接近它的尾声。[请务必订阅，来了解你想知道的有关编程语言起源的各种内容](https://www.redhat.com/en/command-line-heroes)，我很乐意在下面的评论中听到你的 shell 故事。

---

via: <https://opensource.com/19/9/command-line-heroes-bash>

作者：[Matthew Broberg](https://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberghttps://opensource.com/users/mbbroberg) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


从 Yum 更新中排除特定/某些包的三种方法

# 从 Yum 更新中排除特定/某些包的三种方法

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

作为系统更新的一部分，你也许需要在基于 Red Hat 系统中由于应用依赖排除一些软件包。

如果是，如何排除？可以采取多少种方式？有三种方式可以做到，我们会在本篇中教你这三种方法。

包管理器是一组工具，它允许用户在 Linux 系统中轻松管理包。它能让用户在 Linux 系统中安装、更新/升级、删除、查询、重新安装和搜索软件包。

对于基于 Red Hat 的系统，我们使用 [yum 包管理器](https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/) 和 [rpm 包管理器](https://www.2daygeek.com/rpm-command-examples/) 进行包管理。

### 什么是 yum？

yum 代表 “Yellowdog Updater, Modified”。Yum 是用于 rpm 系统的自动更新程序和包安装/卸载器。

它在安装包时自动解决依赖关系。

### 什么是 rpm？

rpm 代表 “Red Hat Package Manager”，它是一款用于 Red Hat 系统的功能强大的包管理工具。

RPM 指的是 `.rpm` 文件格式，它包含已编译的软件和必要的库。

你可能有兴趣阅读以下与本主题相关的文章。如果是的话，请进入相应的链接。

* [如何检查 Red Hat（RHEL）和 CentOS 系统上的可用安全更新](https://www.2daygeek.com/check-list-view-find-available-security-updates-on-redhat-rhel-centos-system/)
* [在 Red Hat（RHEL）和 CentOS 系统上安装安全更新的四种方法](https://www.2daygeek.com/install-security-updates-on-redhat-rhel-centos-system/)
* [在 Redhat（RHEL）和 CentOS 系统上检查或列出已安装的安全更新的两种方法](https://www.2daygeek.com/check-installed-security-updates-on-redhat-rhel-and-centos-system/)

### 方法 1：手动或临时用 yum 命令排除包

我们可以在 yum 中使用 `--exclude` 或 `-x` 开关来阻止 yum 命令获取特定包的更新。

我可以说，这是一种临时方法或按需方法。如果你只想将特定包排除一次，那么我们可以使用此方法。

以下命令将更新除 kernel 之外的所有软件包。

要排除单个包：

```
# yum update --exclude=kernel
或者
# yum update -x 'kernel'
```

要排除多个包。以下命令将更新除 kernel 和 php 之外的所有软件包。

```
# yum update --exclude=kernel* --exclude=php*
或者
# yum update --exclude httpd,php
```

### 方法 2：在 yum 命令中永久排除软件包

这是永久性方法，如果你经常执行修补程序更新，那么可以使用此方法。

为此，请在 `/etc/yum.conf` 中添加相应的软件包以永久禁用软件包更新。

添加后，每次运行 `yum update` 命令时都不需要指定这些包。此外，这可以防止任何意外更新这些包。

```
# vi /etc/yum.conf

[main]
cachedir=/var/cache/yum/$basearch/$releasever
keepcache=0
debuglevel=2
logfile=/var/log/yum.log
exactarch=1
obsoletes=1
gpgcheck=1
plugins=1
installonly_limit=3
exclude=kernel* php*
```

### 方法 3：使用 Yum versionlock 插件排除包

这也是与上面类似的永久方法。Yum versionlock 插件允许用户通过 `yum` 命令锁定指定包的更新。

为此，请运行以下命令。以下命令将从 `yum update` 中排除 freetype 包。

或者，你可以直接在 `/etc/yum/pluginconf.d/versionlock.list` 中添加条目。

```
# yum versionlock add freetype

Loaded plugins: changelog, package_upload, product-id, search-disabled-repos, subscription-manager, verify, versionlock
Adding versionlock on: 0:freetype-2.8-12.el7
versionlock added: 1
```

运行以下命令来检查被 versionlock 插件锁定的软件包列表。

```
# yum versionlock list

Loaded plugins: changelog, package_upload, product-id, search-disabled-repos, subscription-manager, verify, versionlock
0:freetype-2.8-12.el7.*
versionlock list done
```

运行以下命令清空该列表。

```
# yum versionlock clear
```

---

via: <https://www.2daygeek.com/redhat-centos-yum-update-exclude-specific-packages/>

作者：[Magesh Maruthamuthu](https://www.2daygeek.com/author/magesh/) 选题：[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/) 荣誉推出


5 个开源的速读应用

Summary: 使用这五个应用训练自己更快地阅读文本。

使用这五个应用训练自己更快地阅读文本。

# 5 个开源的速读应用

>
> 使用这五个应用训练自己更快地阅读文本。
>
>
>

英国散文家和政治家 [Joseph Addison](https://en.wikipedia.org/wiki/Joseph_Addison) 曾经说过，“读书益智，运动益体。”如今，我们大多数人（如果不是全部）都是通过计算机显示器、电视屏幕、移动设备、街道标志、报纸、杂志上阅读，以及在工作场所和学校阅读论文来训练我们的大脑。

鉴于我们每天都会收到大量的书面信息，通过做一些挑战我们经典的阅读习惯并教会我们吸收更多内容和数据的特定练习，来训练我们的大脑以便更快地阅读似乎是有利的。学习这些技能的目的不仅仅是浏览文本，因为没有理解的阅读就是浪费精力。目标是提高你的阅读速度，同时仍然达到高水平的理解。

### 阅读和处理输入

在深入探讨速读之前，让我们来看看阅读过程。根据法国眼科医生 Louis Emile Javal 的说法，阅读分为三个步骤：

1. 固定
2. 处理
3. <ruby>   扫视  <rt>  saccade </rt></ruby>

在第一步，我们确定文本中的固定点，称为最佳识别点。在第二步中，我们在眼睛固定的同时引入（处理）新信息。最后，我们改变注视点的位置，这是一种称为扫视的操作，此时未获取任何新信息。

在实践中，阅读更快的读者之间的主要差异是固定时间短于平均值，更长距离扫视，重读更少。

### 阅读练习

阅读不是人类的自然过程，因为它是人类生存跨度中一个相当新的发展。第一个书写系统是在大约 5000 年前创建的，它不足以让人们发展成为阅读机器。因此，我们必须运用我们的阅读技巧，在这项沟通的基本任务中变得更加娴熟和高效。

第一项练习包括减少默读，也被称为无声语音，这是一种在阅读时内部发音的习惯。它是一个减慢阅读速度的自然过程，因为阅读速度限于语速。减少默读的关键是只说出一些阅读的单词。一种方法是用其他任务来占据内部声音，例如用口香糖。

第二个练习包括减少回归，或称为重读。回归是一种懒惰的机制，因为我们的大脑可以随时重读任何材料，从而降低注意力。

### 5 个开源应用来训练你的大脑

有几个有趣的开源应用可用于锻炼你的阅读速度。

一个是 [Gritz](https://github.com/jeffkowalski/gritz)，它是一个开源文件阅读器，它一次一个地弹出单词，以减少回归。它适用于 Linux、Windows 和 MacOS，并在 GPL 许可证下发布，因此你可以随意使用它。

其他选择包括 [Spray Speed-Reader](https://github.com/chaimpeck/spray)，一个用 JavaScript 编写的开源速读应用，以及 [Sprits-it!](https://github.com/the-happy-hippo/sprits-it)，一个开源 Web 应用，可以快速阅读网页。

对于 Android 用户，[Comfort Reader](https://github.com/mschlauch/comfortreader) 是一个开源的速读应用。它可以在 [F-droid](https://f-droid.org/packages/com.mschlauch.comfortreader/) 和 [Google Play](https://play.google.com/store/apps/details?id=com.mschlauch.comfortreader) 应用商店中找到。

我最喜欢的应用是 [Speedread](https://github.com/pasky/speedread)，它是一个简单的终端程序，它可以在最佳阅读点逐字显示文本。要安装它，请在你的设备上克隆 Github 仓库，然后输入相应的命令来选择以喜好的每分钟字数 （WPM）来阅读文档。默认速率为 250 WPM。例如，要以 400 WPM 阅读 `your_text_file.txt`，你应该输入：

```
`cat your_text_file.txt | ./speedread -w 400`
```

下面是该程序的运行界面：

由于你可能不会只阅读[纯文本](https://plaintextproject.online/)，因此可以使用 [Pandoc](https://opensource.com/article/18/9/intro-pandoc) 将文件从标记格式转换为文本格式。你还可以使用 Android 终端模拟器 [Termux](https://termux.com/) 在 Android 设备上运行 Speedread。

### 其他方案

对于开源社区来说，构建一个解决方案是一个有趣的项目，它仅为了通过使用特定练习来提高阅读速度，以此改进如默读和重读。我相信这个项目会非常有益，因为在当今信息丰富的环境中，提高阅读速度非常有价值。

---

via: <https://opensource.com/article/19/8/speed-reading-open-source>

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


使用 Python 学习面向对象的编程

Summary: 使用 Python 类使你的代码变得更加模块化。

使用 Python 类使你的代码变得更加模块化。

# 使用 Python 学习面向对象的编程

>
> 使用 Python 类使你的代码变得更加模块化。
>
>
>

在我上一篇文章中，我解释了如何通过使用函数、创建模块或者两者一起来[使 Python 代码更加模块化](/article-11295-1.html)。函数对于避免重复多次使用的代码非常有用，而模块可以确保你在不同的项目中复用代码。但是模块化还有另一种方法：类。

如果你已经听过<ruby> 面向对象编程 <rt>  object-oriented programming </rt></ruby>（OOP）这个术语，那么你可能会对类的用途有一些概念。程序员倾向于将类视为一个虚拟对象，有时与物理世界中的某些东西直接相关，有时则作为某种编程概念的表现形式。无论哪种表示，当你想要在程序中为你或程序的其他部分创建“对象”时，你都可以创建一个类来交互。

### 没有类的模板

假设你正在编写一个以幻想世界为背景的游戏，并且你需要这个应用程序能够涌现出各种坏蛋来给玩家的生活带来一些刺激。了解了很多关于函数的知识后，你可能会认为这听起来像是函数的一个教科书案例：需要经常重复的代码，但是在调用时可以考虑变量而只编写一次。

下面一个纯粹基于函数的敌人生成器实现的例子：

```
#!/usr/bin/env python3

import random

def enemy(ancestry,gear):
    enemy=ancestry
    weapon=gear
    hp=random.randrange(0,20)
    ac=random.randrange(0,20)
    return [enemy,weapon,hp,ac]

def fight(tgt):
    print("You take a swing at the " + tgt[0] + ".")
    hit=random.randrange(0,20)
    if hit > tgt[3]:
        print("You hit the " + tgt[0] + " for " + str(hit) + " damage!")
        tgt[2] = tgt[2] - hit
    else:
        print("You missed.")

foe=enemy("troll","great axe")
print("You meet a " + foe[0] + " wielding a " + foe[1])
print("Type the a key and then RETURN to attack.")

while True:
    action=input()

    if action.lower() == "a":
        fight(foe)

    if foe[2] < 1:
        print("You killed your foe!")
    else:
        print("The " + foe[0] + " has " + str(foe[2]) + " HP remaining")
```

`enemy` 函数创造了一个具有多个属性的敌人，例如谱系、武器、生命值和防御等级。它返回每个属性的列表，表示敌人全部特征。

从某种意义上说，这段代码创建了一个对象，即使它还没有使用类。程序员将这个 `enemy` 称为*对象*，因为该函数的结果（本例中是一个包含字符串和整数的列表）表示游戏中一个单独但复杂的*东西*。也就是说，列表中字符串和整数不是任意的：它们一起描述了一个虚拟对象。

在编写描述符集合时，你可以使用变量，以便随时使用它们来生成敌人。这有点像模板。

在示例代码中，当需要对象的属性时，会检索相应的列表项。例如，要获取敌人的谱系，代码会查询 `foe[0]`，对于生命值，会查询 `foe[2]`，以此类推。

这种方法没有什么不妥，代码按预期运行。你可以添加更多不同类型的敌人，创建一个敌人类型列表，并在敌人创建期间从列表中随机选择，等等，它工作得很好。实际上，[Lua](https://opensource.com/article/17/4/how-program-games-raspberry-pi) 非常有效地利用这个原理来近似了一个面向对象模型。

然而，有时候对象不仅仅是属性列表。

### 使用对象

在 Python 中，一切都是对象。你在 Python 中创建的任何东西都是某个预定义模板的*实例*。甚至基本的字符串和整数都是 Python `type` 类的衍生物。你可以在这个交互式 Python shell 中见证：

```
>>> foo=3
>>> type(foo)
<class 'int'>
>>> foo="bar"
>>> type(foo)
<class 'str'>
```

当一个对象由一个类定义时，它不仅仅是一个属性的集合，Python 类具有各自的函数。从逻辑上讲，这很方便，因为只涉及某个对象类的操作包含在该对象的类中。

在示例代码中，`fight` 的代码是主应用程序的功能。这对于一个简单的游戏来说是可行的，但对于一个复杂的游戏来说，世界中不仅仅有玩家和敌人，还可能有城镇居民、牲畜、建筑物、森林等等，它们都不需要使用战斗功能。将战斗代码放在敌人的类中意味着你的代码更有条理，在一个复杂的应用程序中，这是一个重要的优势。

此外，每个类都有特权访问自己的本地变量。例如，敌人的生命值，除了某些功能之外，是不会改变的数据。游戏中的随机蝴蝶不应该意外地将敌人的生命值降低到 0。理想情况下，即使没有类，也不会发生这种情况。但是在具有大量活动部件的复杂应用程序中，确保不需要相互交互的部件永远不会发生这种情况，这是一个非常有用的技巧。

Python 类也受垃圾收集的影响。当不再使用类的实例时，它将被移出内存。你可能永远不知道这种情况会什么时候发生，但是你往往知道什么时候它不会发生，因为你的应用程序占用了更多的内存，而且运行速度比较慢。将数据集隔离到类中可以帮助 Python 跟踪哪些数据正在使用，哪些不在需要了。

### 优雅的 Python

下面是一个同样简单的战斗游戏，使用了 `Enemy` 类：

```
#!/usr/bin/env python3

import random

class Enemy():
    def __init__(self,ancestry,gear):
        self.enemy=ancestry
        self.weapon=gear
        self.hp=random.randrange(10,20)
        self.ac=random.randrange(12,20)
        self.alive=True

    def fight(self,tgt):
        print("You take a swing at the " + self.enemy + ".")
        hit=random.randrange(0,20)

        if self.alive and hit > self.ac:
            print("You hit the " + self.enemy + " for " + str(hit) + " damage!")
            self.hp = self.hp - hit
            print("The " + self.enemy + " has " + str(self.hp) + " HP remaining")
        else:
            print("You missed.")

        if self.hp < 1:
            self.alive=False

# 游戏开始
foe=Enemy("troll","great axe")
print("You meet a " + foe.enemy + " wielding a " + foe.weapon)

# 主函数循环
while True:

    print("Type the a key and then RETURN to attack.")

    action=input()

    if action.lower() == "a":
        foe.fight(foe)

    if foe.alive == False:
        print("You have won...this time.")
        exit()
```

这个版本的游戏将敌人作为一个包含相同属性（谱系、武器、生命值和防御）的对象来处理，并添加一个新的属性来衡量敌人时候已被击败，以及一个战斗功能。

类的第一个函数是一个特殊的函数，在 Python 中称为 `init` 或初始化的函数。这类似于其他语言中的[构造器](https://opensource.com/article/19/6/what-java-constructor)，它创建了类的一个实例，你可以通过它的属性和调用类时使用的任何变量来识别它（示例代码中的 `foe`）。

### Self 和类实例

类的函数接受一种你在类之外看不到的新形式的输入：`self`。如果不包含 `self`，那么当你调用类函数时，Python 无法知道要使用的类的*哪个*实例。这就像在一间充满兽人的房间里说：“我要和兽人战斗”，向一个兽人发起。没有人知道你指的是谁，所有兽人就都上来了。

*CC-BY-SA by Buch on opengameart.org*

类中创建的每个属性都以 `self` 符号作为前缀，该符号将变量标识为类的属性。一旦派生出类的实例，就用表示该实例的变量替换掉 `self` 前缀。使用这个技巧，你可以在一间满是兽人的房间里说：“我要和谱系是 orc 的兽人战斗”，这样来挑战一个兽人。当 orc 听到 “gorblar.orc” 时，它就知道你指的是谁（他自己），所以你得到是一场公平的战斗而不是斗殴。在 Python 中：

```
gorblar=Enemy("orc","sword")
print("The " + gorblar.enemy + " has " + str(gorblar.hp) + " remaining.")
```

通过检索类属性（`gorblar.enemy` 或 `gorblar.hp` 或你需要的任何对象的任何值）而不是查询 `foe[0]`（在函数示例中）或 `gorblar[0]` 来寻找敌人。

### 本地变量

如果类中的变量没有以 `self` 关键字作为前缀，那么它就是一个局部变量，就像在函数中一样。例如，无论你做什么，你都无法访问 `Enemy.fight` 类之外的 `hit` 变量：

```
>>> print(foe.hit)
Traceback (most recent call last):
  File "./enclass.py", line 38, in <module>
    print(foe.hit)
AttributeError: 'Enemy' object has no attribute 'hit'

>>> print(foe.fight.hit)
Traceback (most recent call last):
  File "./enclass.py", line 38, in <module>
    print(foe.fight.hit)
AttributeError: 'function' object has no attribute 'hit'
```

`hit` 变量包含在 Enemy 类中，并且只能“存活”到在战斗中发挥作用。

### 更模块化

本例使用与主应用程序相同的文本文档中的类。在一个复杂的游戏中，我们更容易将每个类看作是自己独立的应用程序。当多个开发人员处理同一个应用程序时，你会看到这一点：一个开发人员负责一个类，另一个开发人员负责主程序，只要他们彼此沟通这个类必须具有什么属性，就可以并行地开发这两个代码块。

要使这个示例游戏模块化，可以把它拆分为两个文件：一个用于主应用程序，另一个用于类。如果它是一个更复杂的应用程序，你可能每个类都有一个文件，或每个逻辑类组有一个文件（例如，用于建筑物的文件，用于自然环境的文件，用于敌人或 NPC 的文件等）。

将只包含 `Enemy` 类的一个文件保存为 `enemy.py`，将另一个包含其他内容的文件保存为 `main.py`。

以下是 `enemy.py`：

```
import random

class Enemy():
    def __init__(self,ancestry,gear):
        self.enemy=ancestry
        self.weapon=gear
        self.hp=random.randrange(10,20)
        self.stg=random.randrange(0,20)
        self.ac=random.randrange(0,20)
        self.alive=True

    def fight(self,tgt):
        print("You take a swing at the " + self.enemy + ".")
        hit=random.randrange(0,20)

        if self.alive and hit > self.ac:
            print("You hit the " + self.enemy + " for " + str(hit) + " damage!")
            self.hp = self.hp - hit
            print("The " + self.enemy + " has " + str(self.hp) + " HP remaining")
        else:
            print("You missed.")

        if self.hp < 1:
            self.alive=False
```

以下是 `main.py`：

```
#!/usr/bin/env python3

import enemy as en

# game start
foe=en.Enemy("troll","great axe")
print("You meet a " + foe.enemy + " wielding a " + foe.weapon)

# main loop
while True:

    print("Type the a key and then RETURN to attack.")

    action=input()

    if action.lower() == "a":
        foe.fight(foe)

    if foe.alive == False:
        print("You have won...this time.")
        exit()
```

导入模块 `enemy.py` 使用了一条特别的语句，引用类文件名称而不用带有 `.py` 扩展名，后跟你选择的命名空间指示符（例如，`import enemy as en`）。这个指示符是在你调用类时在代码中使用的。你需要在导入时添加指示符，例如 `en.Enemy`，而不是只使用 `Enemy()`。

所有这些文件名都是任意的，尽管在原则上不要使用罕见的名称。将应用程序的中心命名为 `main.py` 是一个常见约定，和一个充满类的文件通常以小写形式命名，其中的类都以大写字母开头。是否遵循这些约定不会影响应用程序的运行方式，但它确实使经验丰富的 Python 程序员更容易快速理解应用程序的工作方式。

在如何构建代码方面有一些灵活性。例如，使用该示例代码，两个文件必须位于同一目录中。如果你只想将类打包为模块，那么必须创建一个名为 `mybad` 的目录，并将你的类移入其中。在 `main.py` 中，你的 `import` 语句稍有变化：

```
from mybad import enemy as en
```

两种方法都会产生相同的结果，但如果你创建的类足够通用，你认为其他开发人员可以在他们的项目中使用它们，那么后者更好。

无论你选择哪种方式，都可以启动游戏的模块化版本：

```
$ python3 ./main.py
You meet a troll wielding a great axe
Type the a key and then RETURN to attack.
a
You take a swing at the troll.
You missed.
Type the a key and then RETURN to attack.
a
You take a swing at the troll.
You hit the troll for 8 damage!
The troll has 4 HP remaining
Type the a key and then RETURN to attack.
a
You take a swing at the troll.
You hit the troll for 11 damage!
The troll has -7 HP remaining
You have won...this time.
```

游戏启动了，它现在更加模块化了。现在你知道了面向对象的应用程序意味着什么，但最重要的是，当你向兽人发起决斗的时候，你知道是哪一个。

---

via: <https://opensource.com/article/19/7/get-modular-python-classes>

作者：[Seth Kenlon](https://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[MjSeven](https://github.com/MjSeven) 校对：[wxy](https://github.com/wxy)

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


Linux 上 5 个最好 CAD 软件

Summary: 计算机辅助设计 (CAD) 是很多工程流程的必不可少的部分。CAD 用于建筑、汽车零部件设计、航天飞机研究、航空、桥梁施工、室内设计，甚至服装和珠宝设计等专业领域。

# Linux 上 5 个最好 CAD 软件

[计算机辅助设计 (CAD)](https://en.wikipedia.org/wiki/Computer-aided_design) 是很多工程流程的必不可少的部分。CAD 用于建筑、汽车零部件设计、航天飞机研究、航空、桥梁施工、室内设计，甚至服装和珠宝设计等专业领域。

在 Linux 上并不原生支持一些专业级 CAD 软件，如 SolidWorks 和 Autodesk AutoCAD。因此，今天，我们将看看排名靠前的 Linux 上可用的 CAD 软件。预知详情，请看下文。

### Linux 可用的最好的 CAD 软件

在我们查看这份 Linux 的 CAD 软件列表前，你应该记住一件事，在这里不是所有的应用程序都是开源软件。我们也将包含一些非自由和开源软件的 CAD 软件来帮助普通的 Linux 用户。

我们为基于 Ubuntu 的 Linux 发行版提供了安装操作指南。对于其它发行版，你可以检查相应的网站来了解安装程序步骤。

该列表没有任何特殊顺序。在第一顺位的 CAD 应用程序不能认为比在第三顺位的好，以此类推。

#### 1、FreeCAD

对于 3D 建模，FreeCAD 是一个极好的选择，它是自由 (免费和自由) 和开源软件。FreeCAD 坚持以构建机械工程和产品设计为目标。FreeCAD 是多平台的，可用于 Windows、Mac OS X+ 以及 Linux。

尽管 FreeCAD 已经是很多 Linux 用户的选择，应该注意到，FreeCAD 仍然是 0.17 版本，因此，不适用于重要的部署。但是最近开发加速了。

* [FreeCAD](https://www.freecadweb.org/)

FreeCAD 并不专注于 direct-2D 绘图和真实形状的动画，但是它对机械工程相关的设计极好。FreeCAD 的 0.15 版本在 Ubuntu 存储库中可用。你可以通过运行下面的命令安装。

```
sudo apt install freecad
```

为获取新的每日构建（目前 0.17），打开一个终端（`ctrl+alt+t`），并逐个运行下面的命令。

```
sudo add-apt-repository ppa:freecad-maintainers/freecad-daily
sudo apt update
sudo apt install freecad-daily
```

#### 2、LibreCAD

LibreCAD 是一个自由开源的、2D CAD 解决方案。一般来说，CAD 是一个资源密集型任务，如果你有一个相当普通的硬件，那么我建议你使用 LibreCAD ，因为它在资源使用方面真的轻量化。LibreCAD 是几何图形结构方面的一个极好的候选者。

作为一个 2D 工具，LibreCAD 是好的，但是它不能在 3D 模型和渲染上工作。它有时可能不稳定，但是，它有一个可靠的自动保存，它不会让你的工作浪费。

* [LibreCAD](https://librecad.org/)

你可以通过运行下面的命令安装 LibreCAD。

```
sudo apt install librecad
```

#### 3、OpenSCAD

OpenSCAD 是一个自由的 3D CAD 软件。OpenSCAD 非常轻量和灵活。OpenSCAD 不是交互式的。你需要‘编程’模型，OpenSCAD 来解释这些代码来渲染一个可视化模型。在某种意义上说，它是一个编译器。你不能直接绘制模型，而是描述模型。

OpenSCAD 是这个列表上最复杂的工具，但是，一旦你了解它，它将提供一个令人愉快的工作经历。

* [OpenSCAD](http://www.openscad.org/)

你可以使用下面的命令来安装 OpenSCAD。

```
sudo apt-get install openscad
```

#### 4、BRL-CAD

BRL-CAD 是最老的 CAD 工具之一。它也深受 Linux/UNIX 用户喜爱，因为它与模块化和自由的 \*nix 哲学相一致。

BRL-CAD 始于 1979 年，并且，它仍然在积极开发。现在，BRL-CAD 不是 AutoCAD，但是对于像热穿透和弹道穿透等等的运输研究仍然是一个极好的选择。BRL-CAD 构成 CSG 的基础，而不是边界表示。在选择 BRL-CAD 时，你可能需要记住这一点。你可以从它的官方网站下载 BRL-CAD 。

* [BRL-CAD](https://brlcad.org/)

#### 5、DraftSight (非开源)

如果你习惯在 AutoCAD 上作业。那么，DraftSight 将是完美的替代。

DraftSight 是一个在 Linux 上可用的极好的 CAD 工具。它有相当类似于 AutoCAD 的工作流，这使得迁移更容易。它甚至提供一种类似的外观和感觉。DrafSight 也兼容 AutoCAD 的 .dwg 文件格式。 但是，DrafSight 是一个 2D CAD 软件。截至当前，它不支持 3D CAD 。

尽管 DrafSight 是一款起价 149 美元的商业软件。在 [DraftSight 网站](https://www.draftsight2018.com/)上可获得一个免费版本。你可以下载 .deb 软件包，并在基于 Ubuntu 的发行版上安装它。为了开始使用 DraftSight ，你需要使用你的电子邮件 ID 来注册你的免费版本。

* [DraftSight](https://www.draftsight2018.com/)

#### 荣誉提名

* 随着云计算技术的巨大发展，像 [OnShape](https://www.onshape.com/) 的云 CAD 解决方案已经变得日渐流行。
* [SolveSpace](http://solvespace.com/index.pl) 是另一个值得一提的开源软件项目。它支持 3D 模型。
* 西门子 NX 是一个在 Windows、Mac OS 及 Linux 上可用的工业级 CAD 解决方案，但是它贵得离谱，所以，在这个列表中被忽略。
* 接下来，你有 [LeoCAD](https://www.leocad.org/)，它是一个 CAD 软件，在软件中你使用乐高积木来构建东西。你使用这些信息做些什么取决于你。

### 我对 Linux 上的 CAD 的看法

尽管在 Linux 上游戏变得流行，我总是告诉我的铁杆游戏朋友坚持使用 Windows。类似地，如果你是一名在你是课程中使用 CAD 的工科学生，我建议你使用学校规定的软件 (AutoCAD、SolidEdge、Catia)，这些软件通常只在 Windows 上运行。

对于高级专业人士来说，当我们讨论行业标准时，这些工具根本达不到标准。

对于想在 WINE 中运行 AutoCAD 的那些人来说，尽管一些较旧版本的 AutoCAD 可以安装在 WINE 上，它们根本不执行工作，小故障和崩溃严重损害这些体验。

话虽如此，我高度尊重上述列表中软件的开发者的工作。他们丰富了 FOSS 世界。很高兴看到像 FreeCAD 一样的软件在近些年中加速开发速度。

好了，今天到此为止。使用下面的评论区与我们分享你的想法，不用忘记分享这篇文章。谢谢。

---

via: <https://itsfoss.com/cad-software-linux/>

作者：[Aquil Roshan](https://itsfoss.com/author/aquil/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[robsean](https://github.com/robsean) 校对：[wxy](https://github.com/wxy)

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


用 Git 管理你的每日行程

Summary: 像源代码一样对待时间并在 Git 的帮助下维护你的日历。

一个Microsoft To-Do搞定一切

不大喜欢task 上面有个category 或者tag

像源代码一样对待时间并在 Git 的帮助下维护你的日历。

# 用 Git 管理你的每日行程

>
> 像源代码一样对待时间并在 Git 的帮助下维护你的日历。
>
>
>

[Git](https://git-scm.com/) 是一个少有的能将如此多的现代计算封装到一个程序之中的应用程序，它可以用作许多其他应用程序的计算引擎。虽然它以跟踪软件开发中的源代码更改而闻名，但它还有许多其他用途，可以让你的生活更轻松、更有条理。在这个 Git 系列中，我们将分享七种鲜为人知的使用 Git 的方法。

今天，我们将使用 Git 来跟踪你的日历。

### 使用 Git 跟踪你的日程安排

如果时间本身只是可以管理和版本控制的源代码呢？虽然证明或反驳这种理论可能超出了本文的范围，但在 Git 的帮助下，你可以将时间视为源代码并管理你的日程安排。

日历的卫冕冠军是 [CalDAV](https://tools.ietf.org/html/rfc4791) 协议，它支撑了如 [NextCloud](http://nextcloud.com) 这样的流行的开源及闭源的日历应用程序。CalDAV 没什么问题（评论者，请注意），但它并不适合所有人，除此之外，它还有一种不同于单一文化的鼓舞人心的东西。

因为我对大量使用 GUI 的 CalDAV 客户端没有兴趣（如果你正在寻找一个好的终端 CalDAV 查看器，请参阅 [khal](https://github.com/pimutils/khal)），我开始研究基于文本的替代方案。基于文本的日历具有在[明文](https://plaintextproject.online/)中工作的所有常见好处。它很轻巧，非常便携，只要它结构化，就很容易解析和美化（无论*美丽*对你意味着什么）。

最重要的是，它正是 Git 旨在管理的内容。

### Org 模式不是一种可怕的方式

如果你没有对你的明文添加结构，它很快就会陷入一种天马行空般的混乱，变成恶魔才能懂的符号。幸运的是，有一种用于日历的标记语法，它包含在令人尊敬的生产力 Emacs 模式 —— [Org 模式](https://orgmode.org) 中（承认吧，你其实一直想开始使用它）。

许多人没有意识到 Org 模式的惊人之处在于[你不需要知道甚至不需要使用 Emacs](https://opensource.com/article/19/1/productivity-tool-org-mode)来利用 Org 模式建立的约定。如果你使用 Emacs，你会得到许多很棒的功能，但是如果 Emacs 对你来说太难了，那么你可以实现一个基于 Git 的 Org 模式的日历系统，而不需要安装 Emacs。

关于 Org 模式你唯一需要知道的部分是它的语法。Org 模式的语法维护成本低、直观。使用 Org 模式而不是 GUI 日历应用程序进行日历记录的最大区别在于工作流程：你可以创建一个任务列表，然后每天分配一个任务，而不是转到日历并查找要安排任务的日期。

组织模式中的列表使用星号（`*`）作为项目符号。这是我的游戏任务列表：

```
* Gaming
** Build Stardrifter character
** Read Stardrifter rules
** Stardrifter playtest

** Blue Planet @ Mike's

** Run Rappan Athuk
*** Purchase hard copy
*** Skim Rappan Athuk
*** Build Rappan Athuk maps in maptool
*** Sort Rappan Athuk tokens
```

如果你熟悉 [CommonMark](https://commonmark.org/) 或 Markdown，你会注意到，Org 模式不是使用空格来创建子任务，而是更明确地使用了其它项目符号。无论你的使用背景和列表是什么，这都是一种构建列表的直观且简单的方法，它显然与 Emacs 没有内在联系（尽管使用 Emacs 为你提供了快捷方式，因此你可以快速地重新排列列表）。

要将列表转换为日历中的计划任务或事件，请返回并添加关键字 `SCHEDULED` 和（可选）`:CATEGORY:`。

```
* Gaming
:CATEGORY: Game
** Build Stardrifter character
SCHEDULED: <2019-03-22 18:00-19:00>
** Read Stardrifter rules
SCHEDULED: <2019-03-22 19:00-21:00>
** Stardrifter playtest
SCHEDULED: <2019-03-25 0900-1300>
** Blue Planet @ Mike's
SCHEDULED: <2019-03-18 18:00-23:00 +1w>

and so on...
```

`SCHEDULED` 关键字将该条目标记为你希望收到通知的事件，并且可选的 `:CATEGORY:` 关键字是一个可供你自己使用的任意标记系统（在 Emacs 中，你可以根据类别对条目使用颜色代码）。

对于重复事件，你可以使用符号（如`+1w`）创建每周事件或 `+2w` 以进行每两周一次的事件，依此类推。

所有可用于 Org 模式的花哨标记都[记录于文档](https://orgmode.org/manual/)，所以不要犹豫，找到更多技巧来让它满足你的需求。

### 放进 Git

如果没有 Git，你的 Org 模式的日程安排只不过是本地计算机上的文件。这是 21 世纪，所以你至少需要可以在手机上使用你的日历，即便不是在你所有的个人电脑上。你可以使用 Git 为自己和他人发布日历。

首先，为 `.org` 文件创建一个目录。我将我的存储在 `~/cal` 中。

```
$ mkdir ~/cal
```

转到你的目录并使其成为 Git 存储库：

```
$ cd cal
$ git init
```

将 `.org` 文件移动到你本地的 Git 存储库。在实践中，我为每个类别维护一个 `.org` 文件。

```
$ mv ~/*.org ~/cal
$ ls
Game.org Meal.org Seth.org Work.org
```

暂存并提交你的文件：

```
$ git add *.org
$ git commit -m 'cal init'
```

### 创建一个 Git 远程源

要在任何地方提供日历，你必须在互联网上拥有 Git 存储库。你的日历是纯文本，因此任何 Git 存储库都可以。你可以将日历放在 [GitLab](http://gitlab.com) 或任何其他公共 Git 托管服务（甚至是专有服务）上，只要你的主机允许，你甚至可以将该存储库标记为私有库。如果你不想将日历发布到你无法控制的服务器，则可以自行托管 Git 存储库，或者为单个用户使用裸存储库，或者使用 [Gitolite](http://gitolite.com/gitolite/index.html) 或 [Gitea](https://gitea.io/en-us/) 等前端服务。

为了简单起见，我将假设一个自托管的 Git 裸存储库。你可以使用 Git 命令在任何具有 SSH 访问权限的服务器上创建一个远程裸存储库：

```
$ ssh -p 22122 [seth@example.com][14]
[remote]$ mkdir cal.git
[remote]$ cd cal.git
[remote]$ git init --bare
[remote]$ exit
```

这个裸存储库可以作为你日历在互联网上的家。

将其设置为本地 Git 存储库（在你的计算机上，而不是你的服务器上）的远程源：

```
$ git remote add origin seth@example.com:/home/seth/cal.git
```

然后推送你的日历到该服务器：

```
$ git push -u origin HEAD
```

将你的日历放在 Git 存储库中，就可以在任何运行 Git 的设备上使用它。这意味着你可以对计划进行更新和更改，并将更改推送到上游，以便在任何地方进行更新。

我使用这种方法使我的日历在我的工作笔记本电脑和家庭工作站之间保持同步。由于我每天大部分时间都在使用 Emacs，因此能够在 Emacs 中查看和编辑我的日历是一个很大的便利。对于大多数使用移动设备的人来说也是如此，因此下一步是在移动设备上设置 Org 模式的日历系统。

### 移动设备上的 Git

由于你的日历数据是纯文本的，严格来说，你可以在任何可以读取文本文件的设备上“使用”它。这是这个系统之美的一部分；你永远不会缺少原始数据。但是，要按照你希望的现代日历的工作方式将日历集成到移动设备上，你需要两个组件：移动设备上的 Git 客户端和 Org 模式查看器。

#### 移动设备上的 Git 客户端

[MGit](https://f-droid.org/en/packages/com.manichord.mgit) 是 Android 上的优秀 Git 客户端。同样，iOS 也有 Git 客户端。

一旦安装了 MGit（或类似的 Git 客户端），你必须克隆日历存储库，以便在你的手机上有副本。要从移动设备访问服务器，必须设置 SSH 密钥进行身份验证。MGit 可以为你生成和存储密钥，你必须将其添加到服务器的 `~/.ssh/authorized_keys` 文件或托管的 Git 的帐户设置中的 SSH 密钥中。

你必须手动执行此操作。MGit 没有登录你的服务器或托管的 Git 帐户的界面。如果你不这样做，你的移动设备将无法访问你的服务器以访问你的日历数据。

我是通过将我在 MGit 中生成的密钥文件通过 [KDE Connect](https://community.kde.org/KDEConnect) 复制到我的笔记本电脑来实现的（但你可以通过蓝牙、SD 卡读卡器或 USB 电缆进行相同操作，具体取决于你访问手机上的数据的首选方法）。 我用这个命令将密钥（一个名为 `calkey` 的文件）复制到我的服务器：

```
$ cat calkey | ssh seth@example.com "cat >> /home/seth/.ssh/authorized_keys"
```

你可能有不同的方法，但如果你曾经将服务器设置为无密码登录，这是完全相同的过程。如果你使用的是 GitLab 等托管的 Git 服务，则必须将密钥文件的内容复制并粘贴到用户帐户的 SSH 密钥面板中。

完成后，你的移动设备可以向你的服务器授权，但仍需要知道在哪里查找你的日历数据。不同的应用程序可能使用不同的表示法，但 MGit 使用普通的旧式 Git-over-SSH。这意味着如果你使用的是非标准 SSH 端口，则必须指定要使用的 SSH 端口：

```
$ git clone ssh://seth@example.com:22122//home/seth/git/cal.git
```

如果你使用其他应用程序，它可能会使用不同的语法，允许你在特殊字段中提供端口，或删除 `ssh://` 前缀。如果遇到问题，请参阅应用程序文档。

将存储库克隆到手机。

很少有 Git 应用程序设置为自动更新存储库。有一些应用程序可以用来自动拉取，或者你可以设置 Git 钩子来推送服务器的更新 —— 但我不会在这里讨论这些。目前，在对日历进行更新后，请务必在 MGit 中手动提取新更改（或者如果在手机上更改了事件，请将更改推送到服务器）。

#### 移动设备上的日历

有一些应用程序可以为移动设备上的 Org 模式提供前端。[Orgzly](https://f-droid.org/en/packages/com.orgzly/) 是一个很棒的开源 Android 应用程序，它为 Org 模式的从 Agenda 模式到 TODO 列表的大多数功能提供了一个界面。安装并启动它。

从主菜单中，选择“设置同步存储库”，然后选择包含日历文件的目录（即，从服务器克隆的 Git 存储库）。

给 Orgzly 一点时间来导入数据，然后使用 Orgzly 的[汉堡包](https://en.wikipedia.org/wiki/Hamburger_button)菜单选择日程视图。

在 Orgzly 的“设置提醒”菜单中，你可以选择在手机上触发通知的事件类型。你可以获得 `SCHEDULED` 任务，`DEADLINE` 任务或任何分配了事件时间的任何通知。如果你将手机用作任务管理器，那么你将永远不会错过 Org 模式和 Orgzly 的活动。

Orgzly 不仅仅是一个解析器。你可以编辑和更新事件，甚至标记事件为 `DONE`。

### 专为你而设计

关于使用 Org 模式和 Git 的重要一点是，这两个应用程序都非常灵活，并且你可以自定义它们的工作方式和内容，以便它们能够适应你的需求。如果本文中的内容是对你如何组织生活或管理每周时间表的冒犯，但你喜欢此提案提供的其他部分，那么请丢弃你不喜欢的部分。如果需要，你可以在 Emacs 中使用 Org 模式，或者你可以将其用作日历标记。你可以将手机设置为在一天结束时从计算机上拉取 Git 数据，而不是从互联网上的服务器上，或者你可以将计算机配置为在手机插入时同步日历，或者你可以每天管理它，就像你把你工作日所需的所有东西都装到你的手机上一样。这取决于你，而这是关于 Git、Org 模式和开源的最重要的事情。

---

via: <https://opensource.com/article/19/4/calendar-git>

作者：[Seth Kenlon](https://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


在不使用 mv 命令的情况下移动文件

Summary: 有时当你需要移动一个文件时，mv 命令似乎不是最佳选项，那么你会如何做呢？

有时当你需要移动一个文件时，mv 命令似乎不是最佳选项，那么你会如何做呢？

# 在不使用 mv 命令的情况下移动文件

>
> 有时当你需要移动一个文件时，mv 命令似乎不是最佳选项，那么你会如何做呢？
>
>
>

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

不起眼的 `mv` 命令是在你见过的每个 POSIX 系统中都能找到的有用工具之一。它的作用是明确定义的，并且做得很好：将文件从文件系统中的一个位置移动到另一个位置。但是 Linux 非常灵活，还有其他移动文件的办法。使用不同的工具可以完美匹配一些特殊用例，这算一个小优势。

在远离 `mv` 之前，先看看这个命令的默认结果。首先，创建一个目录并生成一些权限为 777 的文件：

```
$ mkdir example
$ touch example/{foo,bar,baz}
$ for i in example/*; do ls /bin > "${i}"; done
$ chmod 777 example/*
```

你可能不会这么认为，但是文件在一个[文件系统](https://opensource.com/article/18/11/partition-format-drive-linux#what-is-a-filesystem)中作为条目存在，称为索引节点（通常称为 inode），你可以使用 [ls 命令](https://opensource.com/article/19/7/master-ls-command)及其 `--inode` 选项查看一个文件占用的 inode：

```
$ ls --inode example/foo
7476868 example/foo
```

作为测试，将文件从示例目录移动到当前目录，然后查看文件的属性：

```
$ mv example/foo .
$ ls -l -G -g --inode
7476868 -rwxrwxrwx. 1 29545 Aug  2 07:28 foo
```

如你所见，原始文件及权限已经被“移动”，但它的 inode 没有变化。

这就是 `mv` 工具用来移动的方式：保持 inode 不变（除非文件被移动到不同的文件系统），并保留其所有权和权限。

其他工具提供了不同的选项。

### 复制和删除

在某些系统上，移动操作是真的在做移动：比特从文件系统中的某个位置删除并重新分配给另一个位置。这种行为在很大程度上已经失宠。现在，移动操作要么是属性重新分配（inode 现在指向文件组织中的不同位置），要么是复制和删除操作的组合。这种设计的哲学意图是确保在移动失败时，文件不会碎片化。

与 `mv` 不同，`cp` 命令会在文件系统中创建一个全新的数据对象，它有一个新的 inode 位置，并取决于 umask。你可以使用 `cp` 和 `rm`（如果有的话，或者 [trash](https://gitlab.com/trashy) —— LCTT 译注：它是一个命令行回收站工具）命令来模仿 `mv` 命令。

```
$ cp example/foo .
$ ls -l -G -g --inode
7476869 -rwxrwxr-x. 29545 Aug  2 11:58 foo
$ trash example/foo
```

示例中的新 `foo` 文件获得了 755 权限，因为此处的 umask 明确排除了写入权限。

```
$ umask
0002
```

有关 umask 的更多信息，阅读 Alex Juarez 这篇关于[文件权限](https://opensource.com/article/19/8/linux-permissions-101#umask)的文章。

### 查看和删除

与复制和删除类似，使用 [cat](https://opensource.com/article/19/2/getting-started-cat-command)（或 `tac`）命令在创建“移动”文件时分配不同的权限。假设当前目录中是一个没有 `foo` 的新测试环境：

```
$ cat example/foo > foo
$ ls -l -G -g --inode
7476869 -rw-rw-r--. 29545 Aug 8 12:21 foo
$ trash example/foo
```

这次，创建了一个没有事先设置权限的新文件，所以文件最终权限完全取决于 umask 设置，它不会阻止用户和组的权限位（无论 umask 是什么，都不会为新文件授予可执行权限），但它会阻止其他人的写入（值为 2）。所以结果是一个权限是 664 的文件。

### Rsync

`rsync` 命令是一个强大的多功能工具，用于在主机和文件系统位置之间发送文件。此命令有许多可用选项，包括使其目标镜像成为源。

你可以使用带有 `--remove-source-files` 选项的 `rsync` 复制，然后删除文件，并可以带上你选择执行同步的任何其他选项（常见的通用选项是 `--archive`）：

```
$ rsync --archive --remove-source-files example/foo .
$ ls example
bar  baz
$ ls -lGgi
7476870 -rwxrwxrwx. 1 seth users 29545 Aug 8 12:23 foo
```

在这里，你可以看到保留了文件权限和所有权，只是更新了时间戳，并删除了源文件。

警告：不要将此选项与 `--delete` 混淆，后者会从*目标*目录中删除（源目录中不存在的）文件。误用 `--delete` 会清除很多数据，建议你不要使用此选项，除非是在测试环境中。

你可以覆盖其中一些默认值，更改权限和修改设置：

```
$ rsync --chmod=666 --times \
    --remove-source-files example/foo .
$ ls example
bar  baz
$ ls -lGgi
7476871 -rw-rw-r--. 1 seth users 29545 Aug 8 12:55 foo
```

这里，目标的 umask 会生效，因此 `--chmod=666` 选项会产生一个权限为 644 的文件。

好处不仅仅是权限，与简单的 `mv` 命令相比，`rsync` 命令有[很多](https://opensource.com/article/19/5/advanced-rsync)有用的[选项](https://opensource.com/article/17/1/rsync-backup-linux)（其中最重要的是 `--exclude` 选项，这样你可以在一个大型移动操作中排除某些项目），这使它成为一个更强大的工具。例如，要在移动文件集合时排除所有备份文件：

```
$ rsync --chmod=666 --times \
    --exclude '*~' \
    --remove-source-files example/foo .
```

### 使用 install 设置权限

`install` 命令是一个专门面向开发人员的复制命令，主要是作为软件编译安装例程的一部分调用。它并不为用户所知（我经常想知道为什么它有这么一个直观的名字，而剩下的包管理器却只能使用缩写和昵称），但是 `install` 实际上是一种将文件放在你想要地方的有用方法。

`install` 命令有很多选项，包括 `--backup` 和 `--compare` 命令（以避免*更新*文件的新副本）。

与 `cp` 和 `cat` 命令不同，但与 `mv` 完全相同，`install` 命令可以在复制文件的同时而保留其时间戳：

```
$ install --preserve-timestamp example/foo .
$ ls -l -G -g --inode
7476869 -rwxr-xr-x. 1 29545 Aug  2 07:28 foo
$ trash example/foo
```

在这里，文件被复制到一个新的 inode，但它的 mtime（修改时间）没有改变。但权限被设置为 `install` 的默认值 `755`。

你可以使用 `install` 来设置文件的权限，所有者和组：

```
$ install --preserve-timestamp \
    --owner=skenlon \
    --group=dialout \
    --mode=666 example/foo .
$ ls -li
7476869 -rw-rw-rw-. 1 skenlon dialout 29545 Aug  2 07:28 foo
$ trash example/foo
```

### 移动、复制和删除

文件包含数据，而真正重要的文件包含*你的*数据。学会聪明地管理它们是很重要的，现在你有了确保以你想要的方式来处理数据的工具包。

你是否有不同的数据管理方式？在评论中告诉我们你的想法。

---

via: <https://opensource.com/article/19/8/moving-files-linux-without-mv>

作者：[Seth Kenlon](https://opensource.com/users/sethhttps://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[MjSeven](https://github.com/MjSeven) 校对：[wxy](https://github.com/wxy)

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


你通常打开多少个浏览器标签页？

Summary: 外加一些提高浏览器效率的技巧。

正常不超过10个,打开多了占资源.

十来个是正常的

家里百分浏览器 一次100个标签页，看完就干别的了。

标准页一直保留着，大概有两年了，好像有800多个吧，我的电脑内存128g

外加一些提高浏览器效率的技巧。

# 你通常打开多少个浏览器标签页？

>
> 外加一些提高浏览器效率的技巧。
>
>
>

<img src="content_image/album_201909_09_070357cnk8qnqqqq2znlqd.png" alt="Browser of things" title="Browser of things">

这里有一个别有用心的问题：你通常一次打开多少个浏览器标签页？你是否有多个窗口，每个窗口都有多个标签页？或者你是一个极简主义者，一次只打开两个标签页。另一种选择是将一个 20 个标签页的浏览器窗口移动到另一个屏幕上去，这样在处理特定任务时它就不会碍事了。你的处理方法在工作、个人和移动浏览器之间有什么不同吗？你的浏览器策略是否与你的[工作习惯](https://enterprisersproject.com/article/2019/1/5-time-wasting-habits-break-new-year)有关？

### 4 个提高浏览器效率的技巧

1. 了解浏览器快捷键以节省单击。无论你使用 Firefox 还是 Chrome，都有很多快捷键可以让你方便地执行包括切换标签页在内的某些功能。例如，Chrome 可以很方便地打开一个空白的谷歌文档。使用快捷键 `Ctrl + t` 打开一个新标签页，然后键入 `doc.new` 即可。电子表格、幻灯片和表单也可以这样做。
2. 用书签文件夹组织最频繁的任务。当开始一项特定的任务时，只需打开文件夹中的所有书签 (`Ctrl + 点击`)，就可以快速地从列表中勾选它。
3. 使用正确的浏览器扩展。成千上万的浏览器扩展都声称可以提高工作效率。在安装之前，确定你不是仅仅在屏幕上添加更多的干扰而已。
4. 使用计时器减少看屏幕的时间。无论你使用的是老式的 egg 定时器还是花哨的浏览器扩展，都没有关系。为了防止眼睛疲劳，执行 20/20/20 规则。每隔 20 分钟，离开屏幕 20 秒，看看 20 英尺以外的东西。

参加我们的投票来分享你一次打开多少个浏览器标签。请务必在评论中告诉我们你最喜欢的浏览器技巧。

生产力有两个组成部分——做正确的事情和高效地做那些事情……

---

via: <https://opensource.com/article/19/6/how-many-browser-tabs>

作者：[Lauren Pritchett](https://opensource.com/users/lauren-pritchett/users/sarahwall/users/ksonney/users/jwhitehurst) 选题：[lujun9972](https://github.com/lujun9972) 译者：[lujun9972](https://github.com/lujun9972) 校对：[wxy](https://github.com/wxy)

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


如何改变你的终端颜色

Summary: 使 Linux 变得丰富多彩（或单色）。

使 Linux 变得丰富多彩（或单色）。

# 如何改变你的终端颜色

>
> 使 Linux 变得丰富多彩（或单色）。
>
>
>

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

你可以使用特殊的 ANSI 编码设置为 Linux 终端添加颜色，可以在终端命令或配置文件中动态添加，也可以在终端仿真器中使用现成的主题。无论哪种方式，你都可以黑色屏幕上找回怀旧的绿色或琥珀色文本。本文演示了如何使 Linux 变得丰富多彩（或单色）的方法。

### 终端的功能特性

现代系统的终端的颜色配置通常默认至少是 xterm-256color，但如果你尝试为终端添加颜色但未成功，则应检查你的 `TERM` 设置。

从历史上看，Unix 终端从字面上讲是：用户可以输入命令的共享计算机系统上实际的物理端点（终点）。它们专指通常用于远程发出命令的电传打字机（这也是我们今天在 Linux 中仍然使用 `/dev/tty` 设备的原因）。终端内置了 CRT 显示器，因此用户可以坐在办公室的终端上直接与大型机进行交互。CRT 显示器价格昂贵 —— 无论是制造还是使用控制；比担心抗锯齿和现代计算机专家理所当然认为的漂亮信息，让计算机吐出原始 ASCII 文本更容易。然而，即使在那时，技术的发展也很快，很快人们就会发现，随着新的视频显示终端的设计，他们需要新的功能特性来提供可选功能。

例如，1978 年发布的花哨的新 VT100 支持 ANSI 颜色，因此如果用户将终端类型识别为 vt100，则计算机可以提供彩色输出，而基本串行设备可能没有这样的选项。同样的原则适用于今天，它是由 `TERM` [环境变量](https://opensource.com/article/19/8/what-are-environment-variables)设定的。你可以使用 `echo` 检查你的 `TERM` 定义：

```
$ echo $TERM
xterm-256color
```

过时的（但在一些系统上仍然为了向后兼容而维护）`/etc/termcap` 文件定义了终端和打印机的功能特性。现代的版本是 `terminfo`，位于 `/etc` 或 `/usr/share` 中，具体取决于你的发行版。 这些文件列出了不同类型终端中可用的功能特性，其中许多都是由历史上的硬件定义的，如 vt100 到 vt220 的定义，以及 xterm 和 Xfce 等现代软件仿真器。大多数软件并不关心你使用的终端类型； 在极少数情况下，登录到检查兼容功能的服务器时，你可能会收到有关错误的终端类型的警告或错误。如果你的终端设置为功能特性很少的配置文件，但你知道你所使用的仿真器能够支持更多功能特性，那么你可以通过定义 `TERM` 环境变量来更改你的设置。你可以通过在 `~/.bashrc` 配置文件中导出 `TERM` 变量来完成此操作：

```
export TERM=xterm-256color
```

保存文件并重新载入设置：

```
$ source ~/.bashrc
```

### ANSI 颜色代码

现代终端继承了用于“元”特征的 ANSI 转义序列。这些是特殊的字符序列，终端将其解释为操作而不是字符。例如，此序列将清除屏幕，直到下一个提示符：

```
$ printf '\033[2J'
```

它不会清除你的历史信息；它只是清除终端仿真器中的屏幕，因此它是一个安全且具有示范性的 ANSI 转义序列。

ANSI 还具有设置终端颜色的序列。例如，键入此代码会将后续文本更改为绿色：

```
$ printf '\033[32m'
```

只要你对相同的计算机使用同一个颜色，就可以使用颜色来帮助你记住你登录的系统。例如，如果你经常通过 SSH 连接到服务器，则可以将服务器的提示符设置为绿色，以帮助你一目了然地将其与本地的提示符区分开来。 要设置绿色提示符，请在提示符前使用 ANSI 代码设置为绿色，并使用代表正常默认颜色的代码结束：

```
export PS1=`printf "\033[32m$ \033[39m"`
```

### 前景色和背景色

你不仅可以设置文本的颜色。使用 ANSI 代码，你还可以控制文本的背景颜色以及做一些基本的样式。

例如，使用 `\033[4m`，你可以为文本加上下划线，或者使用 `\033[5m` 你可以将其设置为闪烁的文本。起初这可能看起来很愚蠢，因为你可能不会将你的终端设置为所有文本带有下划线并整天闪烁， 但它对某些功能很有用。例如，你可以将 shell 脚本生成的紧急错误设置为闪烁（作为对用户的警报），或者你可以为 URL 添加下划线。

作为参考，以下是前景色和背景色的代码。前景色在 30 范围内，而背景色在 40 范围内：

| 颜色 | 前景色 | 背景色 |
| --- | --- | --- |
| 黑色 | \033[30m | \033[40m |
| 红色 | \033[31m | \033[41m |
| 绿色 | \033[32m | \033[42m |
| 橙色 | \033[33m | \033[43m |
| 蓝色 | \033[34m | \033[44m |
| 品红 | \033[35m | \033[45m |
| 青色 | \033[36m | \033[46m |
| 浅灰 | \033[37m | \033[47m |
| 回退到发行版默认值 | \033[39m | \033[49m |

还有一些可用于背景的其他颜色：

| 颜色 | 背景色 |
| --- | --- |
| 深灰 | \033[100m |
| 浅红 | \033[101m |
| 浅绿 | \033[102m |
| 黄色 | \033[103m |
| 浅蓝 | \033[104m |
| 浅紫 | \033[105m |
| 蓝绿 | \033[106m |
| 白色 | \033[107m |

### 持久设置

在终端会话中设置颜色只是暂时的，相对无条件的。有时效果会持续几行；这是因为这种设置颜色的方法依赖于 `printf` 语句来设置一种模式，该模式仅持续到其他设置覆盖它。

终端仿真器通常获取使用哪种颜色的指令的方式来自于 `LS_COLORS` 环境变量的设置，该设置又由 `dircolors` 的设置填充。你可以使用 `echo` 语句查看当前设置：

```
$ echo $LS_COLORS
rs=0:di=38;5;33:ln=38;5;51:mh=00:pi=40;
38;5;11:so=38;5;13:do=38;5;5:bd=48;5;
232;38;5;11:cd=48;5;232;38;5;3:or=48;
5;232;38;5;9:mi=01;05;37;41:su=48;5;
196;38;5;15:sg=48;5;11;38;5;16:ca=48;5;
196;38;5;226:tw=48;5;10;38;5;16:ow=48;5;
[...]
```

或者你可以直接使用 `dircolors`：

```
$ dircolors --print-database
[...]
# image formats
.jpg 01;35
.jpeg 01;35
.mjpg 01;35
.mjpeg 01;35
.gif 01;35
.bmp 01;35
.pbm 01;35
.tif 01;35
.tiff 01;35
[...]
```

这看起来很神秘。文件类型后面的第一个数字是属性代码，它有六种选择：

* 00 无
* 01 粗体
* 04 下划线
* 05 闪烁
* 07 反白
* 08 暗色

下一个数字是简化形式的颜色代码。你可以通过获取 ANSI 代码的最后一个数字来获取颜色代码（绿色前景为 32，绿色背景为 42；红色为 31 或 41，依此类推）。

你的发行版可能全局设置了 `LS_COLORS`，因此系统上的所有用户都会继承相同的颜色。如果你想要一组自定义的颜色，可以使用 `dircolors`。首先，生成颜色设置的本地副本：

```
$ dircolors --print-database > ~/.dircolors
```

根据需要编辑本地列表。如果你对自己的选择感到满意，请保存文件。你的颜色设置只是一个数据库，不能由 [ls](https://opensource.com/article/19/7/master-ls-command) 直接使用，但你可以使用 `dircolors` 获取可用于设置 `LS_COLORS` 的 shellcode：

```
$ dircolors --bourne-shell ~/.dircolors
LS_COLORS='rs=0:di=01;34:ln=01;36:mh=00:
pi=40;33:so=01;35:do=01;35:bd=40;33;01:
cd=40;33;01:or=40;31;01:mi=00:su=37;41:
sg=30;43:ca=30;41:tw=30;42:ow=34;
[...]
export LS_COLORS
```

将输出复制并粘贴到 `~/.bashrc` 文件中并重新加载。或者，你可以将该输出直接转储到 `.bashrc` 文件中并重新加载。

```
$ dircolors --bourne-shell ~/.dircolors >> ~/.bashrc
$ source ~/.bashrc
```

你也可以在启动时使 Bash 解析 `.dircolors` 而不是手动进行转换。实际上，你可能不会经常改变颜色，所以这可能过于激进，但如果你打算改变你的配色方案，这是一个选择。在 `.bashrc` 文件中，添加以下规则：

```
[[ -e $HOME/.dircolors ]] && eval "`dircolors --sh $HOME/.dircolors`"
```

如果你的主目录中有 `.dircolors` 文件，Bash 会在启动时对其进行评估并相应地设置 `LS_COLORS`。

### 颜色

在终端中使用颜色是一种可以为你自己提供特定信息的快速视觉参考的简单方法。但是，你可能不希望过于依赖它们。毕竟，颜色不是通用的，所以如果其他人使用你的系统，他们可能不会像你那样看懂颜色代表的含义。此外，如果你使用各种工具与计算机进行交互，你可能还会发现某些终端或远程连接无法提供你期望的颜色（或根本不提供颜色）。

除了上述警示之外，颜色在某些工作流程中可能很有用且很有趣，因此创建一个 `.dircolor` 数据库并根据你的想法对其进行自定义吧。

---

via: <https://opensource.com/article/19/9/linux-terminal-colors>

作者：[Seth Kenlon](https://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


如何使用 GNOME 的 Internet Radio 播放流音乐

# 如何使用 GNOME 的 Internet Radio 播放流音乐

>
> 如果你正在寻找一个简单、直观的界面，让你可以播放流媒体，可以尝试一下 GNOME 的 Internet Radio 插件。
>
>
>

网络广播是收听世界各地电台节目的好方法。和许多开发人员一样，我喜欢在编写代码时打开电台。你可以使用 [MPlayer](https://opensource.com/article/18/12/linux-toy-mplayer) 或 [mpv](https://mpv.io/) 等终端媒体播放器收听网络广播，我就是这样通过 Linux 命令行收听广播的。但是，如果你喜欢使用图形用户界面 (GUI)，你可以尝试一下 [GNOME Internet Radio](https://extensions.gnome.org/extension/836/internet-radio/)，这是一个用于 GNOME 桌面的漂亮插件。你可以在包管理器中找到它。

使用图形桌面操作系统收听网络广播通常需要启动一个应用程序，比如 [Audacious](https://audacious-media-player.org/) 或 [Rhythmbox](https://help.gnome.org/users/rhythmbox/stable/)。它们有很好的界面，很多选项，以及很酷的音频可视化工具。但如果你只想要一个简单、直观的界面播放你的流媒体，GNOME Internet Radio 就是你的选择。

安装之后，工具栏中会出现一个小图标，你可以在其中进行所有配置和管理。

我做的第一件事是进入设置菜单。我启用了以下两个选项：显示标题通知和显示音量调整。

GNOME Internet Radio 包含一些预置的电台，并且很容易添加其他电台。只需点击(“+”)符号即可。你需要输入一个频道名称，它可以是你喜欢的任何内容（包括电台名称）和电台地址。例如，我喜欢听 Synthetic FM。我输入名称（Synthetic FM），以及流地址（<https://mediaserv38.live-streams.nl:2199/tunein/syntheticfm.pls>）。

然后单击流旁边的星号将其添加到菜单中。

不管你听什么音乐，不管你选择什么类型，很明显，程序员需要他们的音乐！GNOME Internet Radio 插件使你可以轻松地让你排好喜爱的网络电台。

---

via: <https://opensource.com/article/19/6/gnome-internet-radio>

作者：[Alan Formy-Duval](https://opensource.com/users/alanfdoss/users/r3bl) 选题：[lujun9972](https://github.com/lujun9972) 译者：[lujun9972](https://github.com/lujun9972) 校对：[wxy](https://github.com/wxy)

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


如何构建 Fedora 容器镜像

# 如何构建 Fedora 容器镜像

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

随着容器和容器技术的兴起，现在所有主流的 Linux 发行版都提供了容器基础镜像。本文介绍了 Fedora 项目如何构建其基本镜像，同时还展示了如何使用它来创建分层图像。

### 基础和分层镜像

在看如何构建 Fedora 容器<ruby> 基础镜像 <rt>  base image </rt></ruby>之前，让我们定义基础镜像和<ruby> 分层镜像 <rt>  layered image </rt></ruby>。定义基础镜像的简单方法是没有父镜像层的镜像。但这具体意味着什么呢？这意味着基础镜像通常只包含操作系统的根文件系统基础镜像（rootfs）。基础镜像通常提供安装软件以创建分层镜像所需的工具。

分层镜像在基础镜像上添加了一组层，以便安装、配置和运行应用。分层镜像在 Dockerfile 中使用 `FROM` 指令引用基础镜像：

```
FROM fedora:latest
```

### 如何构建基础镜像

Fedora 有一整套用于构建容器镜像的工具。[其中包括 podman](/article-10156-1.html)，它不需要以 root 身份运行。

#### 构建 rootfs

基础镜像主要由一个 [tarball](https://en.wikipedia.org/wiki/Tar_(computing)) 构成。这个 tarball 包含一个 rootfs。有不同的方法来构建此 rootfs。Fedora 项目使用 [kickstart](https://en.wikipedia.org/wiki/Kickstart_(Linux)) 安装方式以及 [imagefactory](http://imgfac.org/) 来创建这些 tarball。

在创建 Fedora 基础镜像期间使用的 kickstart 文件可以在 Fedora 的构建系统 [Koji](https://koji.fedoraproject.org/koji/) 中找到。[Fedora-Container-Base](https://koji.fedoraproject.org/koji/packageinfo?packageID=26387) 包重新组合了所有基础镜像的构建版本。如果选择了一个构建版本，那么可以访问所有相关文件，包括 kickstart 文件。查看 [示例](https://kojipkgs.fedoraproject.org//packages/Fedora-Container-Base/30/20190902.0/images/koji-f30-build-37420478-base.ks)，文件末尾的 `%packages` 部分定义了要安装的所有软件包。这就是让软件放在基础镜像中的方法。

#### 使用 rootfs 构建基础镜像

rootfs 完成后，构建基础镜像就很容易了。它只需要一个包含以下指令的 Dockerfile：

```
FROM scratch
ADD layer.tar /
CMD ["/bin/bash"]
```

这里的重要部分是 `FROM scratch` 指令，它会创建一个空镜像。然后，接下来的指令将 rootfs 添加到镜像，并设置在运行镜像时要执行的默认命令。

让我们使用 Koji 内置的 Fedora rootfs 构建一个基础镜像：

```
$ curl -o fedora-rootfs.tar.xz https://kojipkgs.fedoraproject.org/packages/Fedora-Container-Base/Rawhide/20190902.n.0/images/Fedora-Container-Base-Rawhide-20190902.n.0.x86_64.tar.xz
$ tar -xJvf fedora-rootfs.tar.xz 51c14619f9dfd8bf109ab021b3113ac598aec88870219ff457ba07bc29f5e6a2/layer.tar
$ mv 51c14619f9dfd8bf109ab021b3113ac598aec88870219ff457ba07bc29f5e6a2/layer.tar layer.tar
$ printf "FROM scratch\nADD layer.tar /\nCMD [\"/bin/bash\"]" > Dockerfile
$ podman build -t my-fedora .
$ podman run -it --rm my-fedora cat /etc/os-release
```

需要从下载的存档中提取包含 rootfs 的 `layer.tar` 文件。这在 Fedora 生成的镜像已经可以被容器运行时使用才需要。

因此，使用 Fedora 生成的镜像，获得基础镜像会更容易。让我们看看它是如何工作的：

```
$ curl -O https://kojipkgs.fedoraproject.org/packages/Fedora-Container-Base/Rawhide/20190902.n.0/images/Fedora-Container-Base-Rawhide-20190902.n.0.x86_64.tar.xz
$ podman load --input Fedora-Container-Base-Rawhide-20190902.n.0.x86_64.tar.xz
$ podman run -it --rm localhost/fedora-container-base-rawhide-20190902.n.0.x86_64:latest cat /etc/os-release
```

### 构建分层镜像

要构建使用 Fedora 基础镜像的分层镜像，只需在 `FROM` 行指令中指定 `fedora`：

```
FROM fedora:latest
```

`latest` 标记引用了最新的 Fedora 版本（编写本文时是 Fedora 30）。但是可以使用镜像的标签来使用其他版本。例如，`FROM fedora:31` 将使用 Fedora 31 基础镜像。

Fedora 支持将软件作为容器来构建并发布。这意味着你可以维护 Dockerfile 来使其他人可以使用你的软件。关于在 Fedora 中成为容器镜像维护者的更多信息，请查看 [Fedora 容器指南](https://docs.fedoraproject.org/en-US/containers/guidelines/guidelines/)。

---

via: <https://fedoramagazine.org/how-to-build-fedora-container-images/>

作者：[Clément Verna](https://fedoramagazine.org/author/cverna/) 选题：[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/) 荣誉推出


Hyperledger Fabric 介绍

# Hyperledger Fabric 介绍

>
> Hyperledger （超级账本）是一组开源工具，旨在构建一个强大的、业务驱动的区块链框架。
>
>
>

[Hyperledger](https://www.hyperledger.org/) （超级账本）是区块链行业中最大的项目之一，它由一组开源工具和多个子项目组成。该项目是由 Linux 基金会主办的一个全球协作项目，其中包括一些不同领域的领导者们，这些领导者们的目标是建立一个强大的、业务驱动的区块链框架。

区块链网络主要有三种类型：公共区块链、联盟或联合区块链，以及私有区块链。Hyperledger 是一个区块链框架，旨在帮助公司建立私人或联盟许可的区块链网络，在该网络中，多个组织可以共享控制和操作网络内节点的权限。

因为区块链是一个透明的，基于不可变模式的安全的去中心化系统，所以它被认为是传统的供应链行业改变游戏规则的一种解决方案。它可以通过以下方式支持有效的供应链系统：

* 跟踪整个区块链中的产品
* 校验和验证区块链中的产品
* 在供应链参与者之间共享整个区块链的信息
* 提供可审核性

本文通过食品供应链的例子来解释 Hyperledger 区块链是如何改变传统供应链系统的。

### 食品行业供应链

传统供应链效率低下的主要原因是由于缺乏透明度而导致报告不可靠和竞争上的劣势。

在传统的供应链模式中，有关实体的信息对该区块链中的其他人来说并不完全透明，这就导致了不准确的报告和缺乏互操作性问题。电子邮件和印刷文档提供了一些信息，但它们不可能包含完整详细的可见性数据，因为很难在整个供应链中去追踪产品。这也使消费者几乎不可能知道产品的真正价值和来源。

食品行业的供应链环境复杂，多个参与者需要协作将货物运送到最终目的地 —— 客户手中。下图显示了食品供应链（多级）网络中的主要参与者。

该区块链的每个阶段都会引入潜在的安全问题、整合问题和其他低效问题。目前食品供应链中的主要威胁仍然是假冒食品和食品欺诈。

基于 Hyperledger 区块链的食品跟踪系统可实现对食品信息全面的可视性和和可追溯性。更重要的是，它以一种不变但可行的方式来记录产品细节，确保食品信息的真实性。最终用户通过在不可变框架上共享产品的详细信息，可以自我验证产品的真实性。

### Hyperledger Fabric

Hyperledger Fabric 是 Hyperledger 项目的基石。它是基于许可的区块链，或者更准确地说是一种分布式分类帐技术（DLT），该技术最初由 IBM 公司和 Digital Asset 创建。分布式分类帐技术被设计为具有不同组件的模块化框架（概述如下）。它也是提供可插入的共识模型的一种灵活的解决方案，尽管它目前仅提供基于投票的许可共识（假设今天的 Hyperledger 网络在部分可信赖的环境中运行）。

鉴于此，无需匿名矿工来验证交易，也无需用作激励措施的相关货币。所有的参与者必须经过身份验证才能参与到该区块链进行交易。与以太坊一样，Hyperledger Fabric 支持智能合约，在 Hyperledger 中称为 <ruby> Chaincodes <rt>  链码 </rt></ruby>，这些合约描述并执行系统的应用程序逻辑。

然而，与以太坊不同，Hyperledger Fabric 不需要昂贵的挖矿计算来提交交易，因此它有助于构建可以在更短的延迟内进行扩展的区块链。

Hyperledger Fabric 不同于以太坊或比特币这样的区块链，不仅在于它们类型不同，或者说是它与货币无关，而且它们在内部机制方面也不同。以下是典型的 Hyperledger 网络的关键要素：

* <ruby> 账本 <rt>  Ledgers </rt></ruby>：存储了一系列块，这些块保留了所有状态交易的所有不可变历史记录。
* <ruby> 节点 <rt>  Nodes </rt></ruby>：区块链的逻辑实体。它有三种类型：
	+ <ruby> 客户端 <rt>  Clients </rt></ruby>：是代表用户向网络提交事务的应用程序。
	+ <ruby> 对等体 <rt>  Peers </rt></ruby>：是提交交易并维护分类帐状态的实体。
	+ <ruby> 排序者 <rt>  Orderers </rt></ruby> 在客户端和对等体之间创建共享通信渠道，还将区块链交易打包成块发送给遵从的对等体节点。

除了这些要素，Hyperledger Fabric 还有以下关键设计功能：

* <ruby> 链码 <rt>  Chaincode </rt></ruby>：类似于其它诸如以太坊的网络中的智能合约。它是用一种更高级的语言编写的程序，在针对分类帐当前状态的数据库执行。
* <ruby> 通道 <rt>  Channels </rt></ruby>：用于在多个网络成员之间共享机密信息的专用通信子网。每笔交易都在一个只有经过身份验证和授权的各方可见的通道上执行。
* <ruby> 背书人 <rt>  Endorsers </rt></ruby> 验证交易，调用链码，并将背书的交易结果返回给调用应用程序。
* <ruby> 成员服务提供商 <rt>  Membership Services Providers </rt></ruby>（MSP）通过颁发和验证证书来提供身份验证和身份验证过程。MSP 确定信任哪些证书颁发机构（CA）去定义信任域的成员，并确定成员可能扮演的特定角色（成员、管理员等）。

### 如何验证交易

探究一笔交易是如何通过验证的是理解 Hyperledger Fabric 在底层如何工作的好方法。此图显示了在典型的 Hyperledger 网络中处理交易的端到端系统流程：

首先，客户端通过向基于 Hyperledger Fabric 的应用程序客户端发送请求来启动交易，该客户端将交易提议提交给背书对等体。这些对等体通过执行由交易指定的链码（使用该状态的本地副本）来模拟该交易，并将结果发送回应用程序。此时，应用程序将交易与背书相结合，并将其广播给<ruby> 排序服务 <rt>  Ordering Service </rt></ruby>。排序服务检查背书并为每个通道创建一个交易块，然后将其广播给通道中的其它节点，对的体验证该交易并进行提交。

Hyperledger Fabric 区块链可以通过透明的、不变的和共享的食品来源数据记录、处理数据，及运输细节等信息将食品供应链中的参与者们连接起来。链码由食品供应链中的授权参与者来调用。所有执行的交易记录都永久保存在分类帐中，所有参与者都可以查看此信息。

### Hyperledger Composer

除了 Fabric 或 Iroha 等区块链框架外，Hyperledger 项目还提供了 Composer、Explorer 和 Cello 等工具。 Hyperledger Composer 提供了一个工具集，可帮助你更轻松地构建区块链应用程序。 它包括：

* CTO，一种建模语言
* Playground，一种基于浏览器的开发工具，用于快速测试和部署
* 命令行界面（CLI）工具

Composer 支持 Hyperledger Fabric 的运行时和基础架构，在内部，Composer 的 API 使用底层 Fabric 的 API。Composer 在 Fabric 上运行，这意味着 Composer 生成的业务网络可以部署到 Hyperledger Fabric 执行。

---

via: <https://opensource.com/article/19/9/introduction-hyperledger-fabric>

作者：[Matt Zand](https://opensource.com/users/mattzandhttps://opensource.com/users/ron-mcfarlandhttps://opensource.com/users/wonderchook) 选题：[lujun9972](https://github.com/lujun9972) 译者：[Morisun029](https://github.com/Morisun029) 校对：[wxy](https://github.com/wxy)

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


USB4 规范获得最终批准，像以太网一样快！

USB4来临的那天应该不会太远的

# USB4 规范获得最终批准，像以太网一样快！

>
> USB4 会是一个统一的接口，可以淘汰笨重的电缆和超大的插头，并提供满足从笔记本电脑用户到服务器管理员的每个人的吞吐量。
>
>
>

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

<ruby> USB 开发者论坛 <rt>  USB Implementers Forum </rt></ruby>（USB-IF）是通用串行总线（USB）规范开发背后的行业协会，上周宣布了它已经完成了下一代 USB4 的技术规范。

USB4 最重要的一个方面（它们在此版本中省略了首字母缩略词和版本号之间的空格）是它将 USB 与 Intel 设计的接口 Thunderbolt 3 融合在了一起。Thunderbolt 尽管有潜力，但在除了笔记本之外并未真正流行起来。出于这个原因，Intel 向 USB 联盟提供了 Thunderbolt 规范。

不幸的是，Thunderbolt 3 被列为 USB4 设备的一个可选项，因此有些设备会有，有些则不会。这无疑会让人头疼，希望所有设备制造商都会包括 Thunderbolt 3。

USB4 将使用与 USB type-C 相同的外形尺寸，这个小型插头用在所有现代 Android 手机和 Thunderbolt 3 中。它将向后兼容 USB 3.2、USB 2.0 以及 Thunderbolt。因此，几乎任何现有的USB type-C 设备都可以连接到具有 USB4 总线的机器，但将以连接电缆的额定速度运行。

### USB4：体积更小，速度更快

因为它支持 Thunderbolt 3，所以这种新连接将同时支持数据和显示协议，因此这可能意味着小型 USB-C 端口将取代显示器上庞大的 DVI 端口，而显示器则带有多个 USB4 端口来作为集线器。

新标准的主要内容：它提供双通道 40Gbps 传输速度，是当前 USB 3.2 规格的两倍，是 USB 3 的八倍。这是以太网的速度，应该足够给你的高清显示器以及其他数据传输提供充足带宽。

USB4 也为视频提供了更好的资源分配，因此如果你使用 USB4 端口同时传输视频和数据，端口将相应地分配带宽。这将允许计算机同时使用外部独立的 GPU（因为有 Thunderbolt 3，它已经上市 ）和外部 SSD。

这可能会启发各种新的服务器设计，因为大型、笨重的设备，如 GPU 或其他不易放入 1U 或 2U 机箱的卡板，现在可以外部连接并以与内部设备相当的速度运行。

当然，我们看到配备了 USB4 端口的电脑还需要一段时间，更别说服务器了。将 USB 3 用于 PC 花费了数年时间，而 USB-C 的采用速度非常缓慢。USB 2 的 U 盘仍然是这些设备的主要市场，并且主板仍然带有 USB 2 端口。

尽管如此，USB4 还是有可能成为一个统一的接口，可以摆脱拥有超大插头的笨重电缆，并提供满足从笔记本电脑用户到服务器管理员的每个人的吞吐量。

---

via: <https://www.networkworld.com/article/3435113/usb4-gets-final-approval-offers-ethernet-like-speed.html>

作者：[Andy Patrizio](https://www.networkworld.com/author/Andy-Patrizio/) 选题：[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/) 荣誉推出


如何在 Ubuntu 上安装和使用 R 语言

请问怎么在manjaro上安装 R 语言？

https://forum.manjaro.org/t/using-the-statistical-package-r-in-manjaro-with-rstudio/484 <br />
希望能帮到你

谢谢，特别感谢

# 如何在 Ubuntu 上安装和使用 R 语言

>
> 这个教程指导你如何在 Ubuntu 上安装 R 语言。你也将同时学习到如何在 Ubuntu 上用不同方法运行简单的 R 语言程序。
>
>
>

[R](https://www.r-project.org/)，和 Python 一样，它是在统计计算和图形处理上最常用的编程语言，易于处理数据。随着数据分析、数据可视化、数据科学（机器学习热）的火热化，对于想深入这一领域的人来说，它是一个很好的工具。

R 语言的优点是它的语法非常简练，你可以找到它的很多实际使用的教程或指南。

本文将介绍包含如何在 Ubuntu 下安装 R 语言，也会介绍在 Linux 下如何运行第一个 R 程序。

### 如何在 Ubuntu 上安装 R 语言

R 默认在 Ubuntu 的软件库里。用以下命令很容易安装：

```
sudo apt install r-base
```

请注意这可能会安装一个较老的版本。在我写这篇文字的时候，Ubuntu 提供的是 3.4，但是最新的是3.6。

*我建议除非你必须使用最新版本，否则直接使用 Ubuntu 的配套版本。*

#### 如何在 Ubuntu 上安装最新 3.6 版本的 R 环境

如果想安装最新的版本（或特殊情况指定版本），你必须用 [CRAN](https://cran.r-project.org/)（Comprehensive R Archive Network）。这个是 R 最新版本的镜像列表。

如需获取 3.6 的版本，需要添加镜像到你的源索引里。我已经简化其命令如下：

```
sudo add-apt-repository "deb https://cloud.r-project.org/bin/linux/ubuntu $(lsb_release -cs)-cran35/"
```

下面你需要添加密钥到服务器中：

```
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
```

然后更新服务器信息并安装R环境：

```
sudo apt update
sudo apt install r-base
```

就这样安装完成。

### 如何在 Ubuntu 下使用 R 语言编程

R 的用法多样，我将介绍运行多个运行 R 语言的方式。

#### R 语言的交互模式

安装了 R 语言后，你可以在控制台上直接运行：

```
R
```

这样会打开交互模式：

R 语言的控制台与 Python 和 Haskell 的交互模式很类似。你可以输入 R 命令做一些基本的数学运算，例如：

```
> 20+40
[1] 60

> print ("Hello World!")
[1] "Hello World!"
```

你可以测试绘图：

如果想退出可以用 `q()`或按下 `CTRL+c`键。接着你会被提示是否保存工作空间镜像；工作空间是创建变量的环境。

#### 用 R 脚本运行程序

第二种运行 R 程序的方式是直接在 Linux 命令行下运行。你可以用 `RScript` 执行，它是一个包含 `r-base` 软件包的工具。

首先，你需要用你在 [Linux 下常用的编辑器](https://itsfoss.com/best-modern-open-source-code-editors-for-linux/)保存 R 程序到文件。文件的扩展名必须是 `.r`。

下面是一个打印 “Hello World” 的 R 程序。你可以保存其为 `hello.r`。

```
print("Hello World!")
a <- rnorm(100)
plot(a)
```

用下面命令运行 R 程序：

```
Rscript hello.r
```

你会得到如下输出结果：

```
[1] "Hello World!"
```

结果将会保存到当前工作目录，文件名为 `Rplots.pdf`：

小提示：`Rscript` 默认不会加载 `methods` 包。确保在脚本中[显式加载](https://www.dummies.com/programming/r/how-to-install-load-and-unload-packages-in-r/)它。

#### 在 Ubuntu 下用 RStudio 运行 R 语言

最常见的 R 环境是 [RStudio](https://www.rstudio.com/)，这是一个强大的跨平台的开源 IDE。你可以用 deb 文件在 Ubuntu 上安装它。下载 deb 文件的链接如下。你需要向下滚动找到 Ubuntu 下的 DEB 文件。

* [下载 Ubuntu 的 Rstudio](https://www.rstudio.com/products/rstudio/download/#download)

下载了 DEB 文件后，直接点击安装。

下载后从菜单搜索启动它。程序主界面会弹出如下：

现在可以看到和 R 命令终端一样的工作台。

创建一个文件：点击顶栏 “File” 然后选择 “New File > Rscript”（或 `CTRL+Shift+n`）：

按下 `CTRL+s` 保存文件选择路径和命名：

这样做了后，点击 “Session > Set Working Directory > To Source File Location” 修改工作目录为你的脚本路径：

现在一切准备就绪！编写代码然后点击运行。你可以在控制台和图形窗口看到结果：

### 结束语

这篇文章中展示了如何在 Ubuntu 下使用 R 语言。包含了以下几个方面：R 控制台 —— 可用于测试，Rscript —— 终端达人操作，RStudio —— 你想要的 IDE。

无论你正在从事数据科学或只是热爱数据统计，作为一个数据分析的完美工具，R 都是一个比较好的编程装备。

你想使用 R 吗？你入门了吗？让我们了解你是如何学习 R 的以及为什么要学习 R!

---

via: <https://itsfoss.com/install-r-ubuntu/>

作者：[Sergiu](https://itsfoss.com/author/sergiu/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[guevaraya](https://github.com/guevaraya) 校对：[wxy](https://github.com/wxy)

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


使用 HTTPie 进行 API 测试

# 使用 HTTPie 进行 API 测试

>
> 使用 HTTPie 调试 API，这是一个用 Python 写的易用的命令行工具。
>
>
>

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

[HTTPie](https://httpie.org/) 是一个非常易用、易于升级的 HTTP 客户端。它的发音为 “aitch-tee-tee-pie” 并以 `http` 命令运行，它是一个用 Python 编写的来用于访问 Web 的命令行工具。

由于这是一篇关于 HTTP 客户端的指导文章，因此你需要一个 HTTP 服务器来试用它。在这里，访问 [httpbin.org](https://github.com/postmanlabs/httpbin)，它是一个简单的开源 HTTP 请求和响应服务。httpbin.org 网站是一种测试 Web API 的强大方式，并能仔细管理并显示请求和响应内容，不过现在让我们专注于 HTTPie 的强大功能。

### Wget 和 cURL 的替代品

你可能听说过古老的 [Wget](https://en.wikipedia.org/wiki/Wget) 或稍微新一些的 [cURL](https://en.wikipedia.org/wiki/CURL) 工具，它们允许你从命令行访问 Web。它们是为访问网站而编写的，而 HTTPie 则用于访问 Web API。

网站请求发生在计算机和正在阅读并响应它所看到的内容的最终用户之间，这并不太依赖于结构化的响应。但是，API 请求会在两台计算机之间进行*结构化*调用，人并不是该流程内的一部分，像 HTTPie 这样的命令行工具的参数可以有效地处理这个问题。

### 安装 HTTPie

有几种方法可以安装 HTTPie。你可以通过包管理器安装，无论你使用的是 `brew`、`apt`、`yum` 还是 `dnf`。但是，如果你已配置 [virtualenvwrapper](https://opensource.com/article/19/6/virtual-environments-python-macos)，那么你可以用自己的方式安装：

```
$ mkvirtualenv httpie
...
(httpie) $ pip install httpie
...
(httpie) $ deactivate
$ alias http=~/.virtualenvs/httpie/bin/http
$ http -b GET https://httpbin.org/get
{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Host": "httpbin.org",
        "User-Agent": "HTTPie/1.0.2"
    },
    "origin": "104.220.242.210, 104.220.242.210",
    "url": "https://httpbin.org/get"
}
```

通过将 `http` 别名指向为虚拟环境中的命令，即使虚拟环境在非活动状态，你也可以运行它。你可以将 `alias` 命令放在 `.bash_profile` 或 `.bashrc` 中，这样你就可以使用以下命令升级 HTTPie：

```
$ ~/.virtualenvs/httpie/bin/pip install -U pip
```

### 使用 HTTPie 查询网站

HTTPie 可以简化查询和测试 API。上面使用了一个选项，`-b`（即 `--body`）。没有它，HTTPie 将默认打印整个响应，包括响应头：

```
$ http GET https://httpbin.org/get
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 177
Content-Type: application/json
Date: Fri, 09 Aug 2019 20:19:47 GMT
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Host": "httpbin.org",
        "User-Agent": "HTTPie/1.0.2"
    },
    "origin": "104.220.242.210, 104.220.242.210",
    "url": "https://httpbin.org/get"
}
```

这在调试 API 服务时非常重要，因为大量信息在响应头中发送。例如，查看发送的 cookie 通常很重要。httpbin.org 提供了通过 URL 路径设置 cookie（用于测试目的）的方式。以下设置一个标题为 `opensource`， 值为 `awesome` 的 cookie：

```
$ http GET https://httpbin.org/cookies/set/opensource/awesome
HTTP/1.1 302 FOUND
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 223
Content-Type: text/html; charset=utf-8
Date: Fri, 09 Aug 2019 20:22:39 GMT
Location: /cookies
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
Set-Cookie: opensource=awesome; Path=/
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL:
<a href="/cookies">/cookies</a>.  If not click the link.
```

注意 `Set-Cookie: opensource=awesome; Path=/` 的响应头。这表明你预期设置的 cookie 已正确设置，路径为 `/`。另请注意，即使你得到了 `302` 重定向，`http` 也不会遵循它。如果你想要遵循重定向，则需要明确使用 `--follow` 标志请求：

```
$ http --follow GET https://httpbin.org/cookies/set/opensource/awesome
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 66
Content-Type: application/json
Date: Sat, 10 Aug 2019 01:33:34 GMT
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

{
    "cookies": {
        "opensource": "awesome"
    }
}
```

但此时你无法看到原来的 `Set-Cookie` 头。为了看到中间响应，你需要使用 `--all`：

```
$ http --headers --all --follow GET https://httpbin.org/cookies/set/opensource/awesome
HTTP/1.1 302 FOUND
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Date: Sat, 10 Aug 2019 01:38:40 GMT
Location: /cookies
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
Set-Cookie: opensource=awesome; Path=/
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Length: 223
Connection: keep-alive

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Encoding: gzip
Content-Type: application/json
Date: Sat, 10 Aug 2019 01:38:41 GMT
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Length: 66
Connection: keep-alive
```

打印响应体并不有趣，因为你大多数时候只关心 cookie。如果你想看到中间请求的响应头，而不是最终请求中的响应体，你可以使用：

```
$ http --print hb --history-print h --all --follow GET https://httpbin.org/cookies/set/opensource/awesome
HTTP/1.1 302 FOUND
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Date: Sat, 10 Aug 2019 01:40:56 GMT
Location: /cookies
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
Set-Cookie: opensource=awesome; Path=/
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Length: 223
Connection: keep-alive

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Encoding: gzip
Content-Type: application/json
Date: Sat, 10 Aug 2019 01:40:56 GMT
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Length: 66
Connection: keep-alive

{
  "cookies": {
    "opensource": "awesome"
  }
}
```

你可以使用 `--print` 精确控制打印的内容（`h`：响应头；`b`：响应体），并使用 `--history-print` 覆盖中间请求的打印内容设置。

### 使用 HTTPie 下载二进制文件

有时响应体并不是文本形式，它需要发送到可被不同应用打开的文件：

```
$ http GET https://httpbin.org/image/jpeg
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 35588
Content-Type: image/jpeg
Date: Fri, 09 Aug 2019 20:25:49 GMT
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

+-----------------------------------------+
| NOTE: binary data not shown in terminal |
+-----------------------------------------+
```

要得到正确的图片，你需要保存到文件：

```
$ http --download GET https://httpbin.org/image/jpeg
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 35588
Content-Type: image/jpeg
Date: Fri, 09 Aug 2019 20:28:13 GMT
Referrer-Policy: no-referrer-when-downgrade
Server: nginx
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block

Downloading 34.75 kB to "jpeg.jpe"
Done. 34.75 kB in 0.00068s (50.05 MB/s)
```

试一下！图片很可爱。

### 使用 HTTPie 发送自定义请求

你可以发送指定的请求头。这对于需要非标准头的自定义 Web API 很有用：

```
$ http GET https://httpbin.org/headers X-Open-Source-Com:Awesome
{
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate",
    "Host": "httpbin.org",
    "User-Agent": "HTTPie/1.0.2",
    "X-Open-Source-Com": "Awesome"
  }
}
```

最后，如果要发送 JSON 字段（尽管可以指定确切的内容），对于许多嵌套较少的输入，你可以使用快捷方式：

```
$ http --body PUT https://httpbin.org/anything open-source=awesome author=moshez
{
  "args": {},
  "data": "{\"open-source\": \"awesome\", \"author\": \"moshez\"}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "application/json, */*",
    "Accept-Encoding": "gzip, deflate",
    "Content-Length": "46",
    "Content-Type": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "HTTPie/1.0.2"
  },
  "json": {
    "author": "moshez",
    "open-source": "awesome"
  },
  "method": "PUT",
  "origin": "73.162.254.113, 73.162.254.113",
  "url": "https://httpbin.org/anything"
}
```

下次在调试 Web API 时，无论是你自己的还是别人的，记得放下 cURL，试试 HTTPie 这个命令行工具。

---

via: <https://opensource.com/article/19/8/getting-started-httpie>

作者：[Moshe Zadka](https://opensource.com/users/moshezhttps://opensource.com/users/mkalindepauleduhttps://opensource.com/users/jamesf) 选题：[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/) 荣誉推出


如何在 Ubuntu 19.04 中安装 Shutter 截图工具

Summary: 虽然 Shutter 一直是一款很棒的工具，但它的开发却停滞了。也许你也是像我这样的 Shutter 粉丝，并且想要使用它。

# 如何在 Ubuntu 19.04 中安装 Shutter 截图工具

Shutter 是我在 [Linux 中最喜欢的截图工具](https://itsfoss.com/take-screenshot-linux/)。你可以使用它截图，还可以用它编辑截图或其他图像。它是一个在图像上添加箭头和文本的不错的工具。你也可以使用它在 Ubuntu 或其它你使用的发行版中[调整图像大小](https://itsfoss.com/resize-images-with-right-click/)。FOSS 上大多数截图教程都使用 Shutter 编辑。

虽然 [Shutter](http://shutter-project.org/) 一直是一款很棒的工具，但它的开发却停滞了。这几年来一直没有新版本的 Shutter。甚至像 [Shutter 中编辑模式被禁用](https://itsfoss.com/shutter-edit-button-disabled/)这样的简单 bug 也没有修复。根本没有开发者的消息。

也许这就是为什么新版本的 Ubuntu 放弃它的原因。在 Ubuntu 18.04 LTS 之前，你可以在软件中心，或者[启用 universe 仓库](https://itsfoss.com/ubuntu-repositories/)来[使用 apt-get 命令](https://itsfoss.com/apt-get-linux-guide/)安装它。但是从 Ubuntu 18.10 及更高版本开始，你就不能再这样做了。

抛开这些缺点，Shutter 是一个很好的工具，我想继续使用它。也许你也是像我这样的 Shutter 粉丝，并且想要使用它。好的方面是你仍然可以在 Ubuntu 19.04 中安装 Shutter，这要归功于非官方 PPA。

### 在 Ubuntu 19.04 上安装 Shutter

我希望你了解 PPA 的概念。如果不了解，我强烈建议阅读我的指南，以了解更多关于[什么是 PPA 以及如何使用它](https://itsfoss.com/ppa-guide/)。

现在，打开终端并使用以下命令添加新仓库：

```
sudo add-apt-repository -y ppa:linuxuprising/shutter
```

不需要再使用 `apt update`，因为从 Ubuntu 18.04 开始，仓库会在添加新条目后自动更新。

现在使用 `apt` 命令安装 Shutter：

```
sudo apt install shutter
```

完成。你应该已经安装 Shutter 截图工具。你可从菜单搜索并启动它。

### 删除通过非官方 PPA 安装的 Shutter

最后我以卸载 Shutter 以及删除添加的仓库来结束教程。

首先，从系统中删除 Shutter：

```
sudo apt remove shutter
```

接下来，从你的仓库列表中删除 PPA：

```
sudo add-apt-repository --remove ppa:linuxuprising/shutter
```

你或许还想了解 [Y PPA Manager](https://itsfoss.com/y-ppa-manager/)，这是一款 PPA 图形管理工具。

Shutter 是一个很好的工具，我希望它能被积极开发。我希望它的开发人员没问题，他/她可以找一些时间来处理它。或者是时候让其他人分叉并继续让它变得更棒。

---

via: <https://itsfoss.com/install-shutter-ubuntu/>

作者：[Abhishek Prakash](https://itsfoss.com/author/abhishek/) 选题：[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 系统上的日志文件包含了很多信息——比你有时间查看的还要多。以下是一些建议，告诉你如何正确的使用它们……而不是淹没在其中。

# 如何在 Linux 中管理日志

>
> Linux 系统上的日志文件包含了**很多**信息——比你有时间查看的还要多。以下是一些建议，告诉你如何正确的使用它们……而不是淹没在其中。
>
>
>

<img src="content_image/album_201909_13_161842c83egb236wwfe6g4.jpg" alt="Greg Lobinski \(CC BY 2.0\)" title="Greg Lobinski \(CC BY 2.0\)">

在 Linux 系统上管理日志文件可能非常容易，也可能非常痛苦。这完全取决于你所认为的日志管理是什么。

如果你认为是如何确保日志文件不会耗尽你的 Linux 服务器上的所有磁盘空间，那么这个问题通常很简单。Linux 系统上的日志文件会自动翻转，系统将只维护固定数量的翻转日志。即便如此，一眼看去一组上百个文件可能会让人不知所措。在这篇文章中，我们将看看日志轮换是如何工作的，以及一些最相关的日志文件。

### 自动日志轮换

日志文件是经常轮转的。当前的日志会获得稍微不同的文件名，并建立一个新的日志文件。以系统日志文件为例。对于许多正常的系统 messages 文件来说，这个文件是一个包罗万象的东西。如果你 `cd` 转到 `/var/log` 并查看一下，你可能会看到一系列系统日志文件，如下所示:

```
$ ls -l syslog*
-rw-r----- 1 syslog adm 28996 Jul 30 07:40 syslog
-rw-r----- 1 syslog adm 71212 Jul 30 00:00 syslog.1
-rw-r----- 1 syslog adm  5449 Jul 29 00:00 syslog.2.gz
-rw-r----- 1 syslog adm  6152 Jul 28 00:00 syslog.3.gz
-rw-r----- 1 syslog adm  7031 Jul 27 00:00 syslog.4.gz
-rw-r----- 1 syslog adm  5602 Jul 26 00:00 syslog.5.gz
-rw-r----- 1 syslog adm  5995 Jul 25 00:00 syslog.6.gz
-rw-r----- 1 syslog adm 32924 Jul 24 00:00 syslog.7.gz
```

轮换发生在每天午夜，旧的日志文件会保留一周，然后删除最早的系统日志文件。`syslog.7.gz` 文件将被从系统中删除，`syslog.6.gz` 将被重命名为 `syslog.7.gz`。日志文件的其余部分将依次改名，直到 `syslog` 变成 `syslog.1` 并创建一个新的 `syslog` 文件。有些系统日志文件会比其他文件大，但是一般来说，没有一个文件可能会变得非常大，并且你永远不会看到超过八个的文件。这给了你一个多星期的时间来回顾它们收集的任何数据。

某种特定日志文件维护的文件数量取决于日志文件本身。有些文件可能有 13 个。请注意 `syslog` 和 `dpkg` 的旧文件是如何压缩以节省空间的。这里的考虑是你对最近的日志最感兴趣，而更旧的日志可以根据需要用 `gunzip` 解压。

```
# ls -t dpkg*
dpkg.log       dpkg.log.3.gz  dpkg.log.6.gz  dpkg.log.9.gz   dpkg.log.12.gz
dpkg.log.1     dpkg.log.4.gz  dpkg.log.7.gz  dpkg.log.10.gz
dpkg.log.2.gz  dpkg.log.5.gz  dpkg.log.8.gz  dpkg.log.11.gz
```

日志文件可以根据时间和大小进行轮换。检查日志文件时请记住这一点。

尽管默认值适用于大多数 Linux 系统管理员，但如果你愿意，可以对日志文件轮换进行不同的配置。查看这些文件，如 `/etc/rsyslog.conf` 和 `/etc/logrotate.conf`。

### 使用日志文件

对日志文件的管理也包括时不时的使用它们。使用日志文件的第一步可能包括：习惯每个日志文件可以告诉你有关系统如何工作以及系统可能会遇到哪些问题。从头到尾读取日志文件几乎不是一个好的选择，但是当你想了解你的系统运行的情况或者需要跟踪一个问题时，知道如何从日志文件中获取信息会是有很大的好处。这也表明你对每个文件中存储的信息有一个大致的了解了。例如:

```
$ who wtmp | tail -10           显示最近的登录信息
$ who wtmp | grep shark         显示特定用户的最近登录信息
$ grep "sudo:" auth.log         查看谁在使用 sudo
$ tail dmesg                    查看（最近的）内核日志
$ tail dpkg.log                 查看最近安装和更新的软件包
$ more ufw.log                  查看防火墙活动（假如你使用 ufw）
```

你运行的一些命令也会从日志文件中提取信息。例如，如果你想查看系统重新启动的列表，可以使用如下命令:

```
$ last reboot
reboot   system boot  5.0.0-20-generic Tue Jul 16 13:19   still running
reboot   system boot  5.0.0-15-generic Sat May 18 17:26 - 15:19 (21+21:52)
reboot   system boot  5.0.0-13-generic Mon Apr 29 10:55 - 15:34 (18+04:39)
```

### 使用更高级的日志管理器

虽然你可以编写脚本来更容易地在日志文件中找到感兴趣的信息，但是你也应该知道有一些非常复杂的工具可用于日志文件分析。一些可以把来自多个来源的信息联系起来，以便更全面地了解你的网络上发生了什么。它们也可以提供实时监控。这些工具，如 [Solarwinds Log & Event Manager](https://www.esecurityplanet.com/products/solarwinds-log-event-manager-siem.html) 和 [PRTG 网络监视器](https://www.paessler.com/prtg)（包括日志监视）浮现在脑海中。

还有一些免费工具可以帮助分析日志文件。其中包括:

* Logwatch — 用于扫描系统日志中感兴趣的日志行的程序
* Logcheck — 系统日志分析器和报告器

在接下来的文章中，我将提供一些关于这些工具的见解和帮助。

---

via: <https://www.networkworld.com/article/3428361/how-to-manage-logs-in-linux.html>

作者：[Sandra Henry-Stocker](https://www.networkworld.com/author/Sandra-Henry_Stocker/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[heguangzhi](https://github.com/heguangzhi) 校对：[wxy](https://github.com/wxy)

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


我为什么使用 Java

# 我为什么使用 Java

>
> 根据你的工作需要，可能有比 Java 更好的语言，但是我还没有看到任何能把我拉走的语言。
>
>
>

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

我记得我是从 1997 年开始使用 Java 的，就在 [Java 1.1 刚刚发布](https://en.wikipedia.org/wiki/Java_version_history)不久之后。从那时起，总的来说，我非常喜欢用 Java 编程；虽然我得承认，这些日子我经常像在 Java 中编写“严肃的代码”一样编写 [Groovy](https://en.wikipedia.org/wiki/Apache_Groovy) 脚本。

来自 [FORTRAN](https://en.wikipedia.org/wiki/Fortran)、[PL/1](https://en.wikipedia.org/wiki/PL/I)、[Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) 以及最后的 [C 语言](https://en.wikipedia.org/wiki/C_(programming_language)) 背景，我发现了许多让我喜欢 Java 的东西。Java 是我[面向对象编程](https://en.wikipedia.org/wiki/Object-oriented_programming)的第一次重要实践经验。到那时，我已经编程了大约 20 年，而且可以说我对什么重要、什么不重要有了一些看法。

### 调试是一个关键的语言特性

我真的很讨厌浪费时间追踪由我的代码不小心迭代到数组末尾而导致的模糊错误，特别是在 IBM 大型机上的 FORTRAN 编程时代。另一个不时出现的隐晦问题是调用一个子程序时，该子程序带有一个四字节整数参数，而预期有两个字节；在小端架构上，这通常是一个良性的错误，但在大端机器上，前两个字节的值通常并不总是为零。

在那种批处理环境中进行调试也非常不便，通过核心转储或插入打印语句进行调试，这些语句本身会移动错误的位置甚至使它们消失。

所以我使用 Pascal 的早期体验，先是在 [MTS](https://en.wikipedia.org/wiki/Michigan_Terminal_System) 上，然后是在 [IBM OS/VS1](https://en.wikipedia.org/wiki/OS/VS1) 上使用相同的 MTS 编译器，让我的生活变得更加轻松。Pascal 的[强类型和静态类型](https://stackoverflow.com/questions/11889602/difference-between-strong-vs-static-typing-and-weak-vs-dynamic-typing)是取得这种胜利的重要组成部分，我使用的每个 Pascal 编译器都会在数组的边界和范围上插入运行时检查，因此错误可以在发生时检测到。当我们在 20 世纪 80 年代早期将大部分工作转移到 Unix 系统时，移植 Pascal 代码是一项简单的任务。

### 适量的语法

但是对于我所喜欢的 Pascal 来说，我的代码很冗长，而且语法似乎要比代码还要多；例如，使用：

```
if ... then begin ... end else ... end
```

而不是 C 或类似语言中的：

```
if (...) { ... } else { ... }
```

另外，有些事情在 Pascal 中很难完成，在 C 中更容易。但是，当我开始越来越多地使用 C 时，我发现自己遇到了我曾经在 FORTRAN 中遇到的同样类型的错误，例如，超出数组边界。在原始的错误点未检测到数组结束，而仅在程序执行后期才会检测到它们的不利影响。幸运的是，我不再生活在那种批处理环境中，并且手头有很好的调试工具。不过，C 对于我来说有点太灵活了。

当我遇到 [awk](https://en.wikipedia.org/wiki/AWK) 时，我发现它与 C 相比又是另外一种样子。那时，我的很多工作都涉及转换字段数据并创建报告。我发现用 `awk` 加上其他 Unix 命令行工具，如 `sort`、`sed`、`cut`、`join`、`paste`、`comm` 等等，可以做到事情令人吃惊。从本质上讲，这些工具给了我一个像是基于文本文件的关系数据库管理器，这种文本文件具有列式结构，是我们很多字段数据的保存方式。或者，即便不是这种格式，大部分时候也可以从关系数据库或某种二进制格式导出到列式结构中。

`awk` 支持的字符串处理、[正则表达式](https://en.wikipedia.org/wiki/Regular_expression)和[关联数组](https://en.wikipedia.org/wiki/Associative_array)，以及 `awk` 的基本特性（它实际上是一个数据转换管道），非常符合我的需求。当面对二进制数据文件、复杂的数据结构和关键性能需求时，我仍然会转回到 C；但随着我越来越多地使用 `awk`，我发现 C 的非常基础的字符串支持越来越令人沮丧。随着时间的推移，更多的时候我只会在必须时才使用 C，并且在其余的时候里大量使用 `awk`。

### Java 的抽象层级合适

然后是 Java。它看起来相当不错 —— 相对简洁的语法，让人联想到 C，或者这种相似性至少要比 Pascal 或其他任何早期的语言更为明显。它是强类型的，因此很多编程错误会在编译时被捕获。它似乎并不需要过多的面向对象的知识就能起步，这是一件好事，因为我当时对 [OOP 设计模式](https://opensource.com/article/19/7/understanding-software-design-patterns)毫不熟悉。但即使在刚刚开始，我也喜欢它的简化[继承模型](https://www.w3schools.com/java/java_inheritance.asp)背后的思想。（Java 允许使用提供的接口进行单继承，以在某种程度上丰富范例。）

它似乎带有丰富的功能库（即“自备电池”的概念），在适当的水平上直接满足了我的需求。最后，我发现自己很快就会想到将数据和行为在对象中组合在一起的想法。这似乎是明确控制数据之间交互的好方法 —— 比大量的参数列表或对全局变量的不受控制的访问要好得多。

从那以后，Java 在我的编程工具箱中成为了 Helvetic 军刀。我仍然偶尔会在 `awk` 中编写程序，或者使用 Linux 命令行实用程序（如 `cut`、`sort` 或 `sed`），因为它们显然是解决手头问题的直接方法。我怀疑过去 20 年我可能没写过 50 行的 C 语言代码；Java 完全满足了我的需求。

此外，Java 一直在不断改进。首先，它变得更加高效。并且它添加了一些非常有用的功能，例如[可以用 try 来测试资源](https://www.baeldung.com/java-try-with-resources)，它可以很好地清理在文件 I/O 期间冗长而有点混乱的错误处理代码；或 [lambda](https://www.baeldung.com/java-8-lambda-expressions-tips)，它提供了声明函数并将其作为参数传递的能力，而旧方法需要创建类或接口来“托管”这些函数；或[流](https://www.tutorialspoint.com/java8/java8_streams)，它在函数中封装了迭代行为，可以创建以链式函数调用形式实现的高效数据转换管道。

### Java 越来越好

许多语言设计者研究了从根本上改善 Java 体验的方法。对我来说，其中大部分没有引起我的太多兴趣；再次，这更多地反映了我的典型工作流程，并且（更多地）减少了这些语言带来的功能。但其中一个演化步骤已经成为我的编程工具中不可或缺的一部分：[Groovy](https://groovy-lang.org/)。当我遇到一个小问题，需要一个简单的解决方案时，Groovy 已经成为了我的首选。而且，它与 Java 高度兼容。对我来说，Groovy 填补了 Python 为许多其他人所提供的相同用处 —— 它紧凑、DRY（不要重复自己）和具有表达性（列表和词典有完整的语言支持）。我还使用了 [Grails](https://grails.org/)，它使用 Groovy 为非常高性能和有用的 Java Web 应用程序提供简化的 Web 框架。

### Java 仍然开源吗？

最近，对 [OpenJDK](https://openjdk.java.net/) 越来越多的支持进一步提高了我对 Java 的舒适度。许多公司以各种方式支持 OpenJDK，包括 [AdoptOpenJDK、Amazon 和 Red Hat](https://en.wikipedia.org/wiki/OpenJDK)。在我的一个更大、更长期的项目中，我们使用 AdoptOpenJDK [来在几个桌面平台上生成自定义的运行时环境](https://opensource.com/article/19/4/java-se-11-removing-jnlp)。

有没有比 Java 更好的语言？我确信有，这取决于你的工作需要。但我一直对 Java 非常满意，我还没有遇到任何可能会让我失望的东西。

---

via: <https://opensource.com/article/19/9/why-i-use-java>

作者：[Chris Hermansen](https://opensource.com/users/clhermansen) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


为什么 const 无法让 C 代码跑得更快？

Summary: 我曾说过“有个一个流行的传言，const 有助于编译器优化 C 和 C++ 代码”。我觉得我需要解释一下，尤其是曾经我自己也以为这是显然对的。

“我不明白为什么会有不了解 const 的人让这些变量加上 static。”这句话翻译不对。原文是：“I don’t see why anyone who didn’t care about const would make those variables static.”。

意思应该是如果去掉了const，我看不出这些变量还有是static的必要。

事实上static const和const局部变量在gcc中的处理是完全不同的。gcc会在栈里面为const局部变量分配空间（就和普通局部变量一样），并在使用前初始化，但是static const是一个放在rodata只读数据段中的变量，不占用栈空间也不需要初始化。

# 为什么 const 无法让 C 代码跑得更快？

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

在几个月前的一篇文章里，我曾说过“[有个一个流行的传言，`const` 有助于编译器优化 C 和 C++ 代码](https://theartofmachinery.com/2019/04/05/d_as_c_replacement.html#const-and-immutable)”。我觉得我需要解释一下，尤其是曾经我自己也以为这是显然对的。我将会用一些理论并构造一些例子来论证，然后在一个真实的代码库 `Sqlite` 上做一些实验和基准测试。

### 一个简单的测试

让我们从一个最简单、最明显的例子开始，以前认为这是一个 `const` 让 C 代码跑得更快的例子。首先，假设我们有如下两个函数声明：

```
void func(int *x);
void constFunc(const int *x);
```

然后假设我们如下两份代码：

```
void byArg(int *x)
{
  printf("%d\n", *x);
  func(x);
  printf("%d\n", *x);
}

void constByArg(const int *x)
{
  printf("%d\n", *x);
  constFunc(x);
  printf("%d\n", *x);
}
```

调用 `printf()` 时，CPU 会通过指针从 RAM 中取得 `*x` 的值。很显然，`constByArg()` 会稍微快一点，因为编译器知道 `*x` 是常量，因此不需要在调用 `constFunc()` 之后再次获取它的值。它仅是打印相同的东西。没问题吧？让我们来看下 GCC 在如下编译选项下生成的汇编代码：

```
$ gcc -S -Wall -O3 test.c
$ view test.s
```

以下是函数 `byArg()` 的完整汇编代码：

```
byArg:
.LFB23:
    .cfi_startproc
    pushq   %rbx
    .cfi_def_cfa_offset 16
    .cfi_offset 3, -16
    movl    (%rdi), %edx
    movq    %rdi, %rbx
    leaq    .LC0(%rip), %rsi
    movl    $1, %edi
    xorl    %eax, %eax
    call    __printf_chk@PLT
    movq    %rbx, %rdi
    call    func@PLT  # constFoo 中唯一不同的指令
    movl    (%rbx), %edx
    leaq    .LC0(%rip), %rsi
    xorl    %eax, %eax
    movl    $1, %edi
    popq    %rbx
    .cfi_def_cfa_offset 8
    jmp __printf_chk@PLT
    .cfi_endproc
```

函数 `byArg()` 和函数 `constByArg()` 生成的汇编代码中唯一的不同之处是 `constByArg()` 有一句汇编代码 `call constFunc@PLT`，这正是源代码中的调用。关键字 `const` 本身并没有造成任何字面上的不同。

好了，这是 GCC 的结果。或许我们需要一个更聪明的编译器。Clang 会有更好的表现吗？

```
$ clang -S -Wall -O3 -emit-llvm test.c
$ view test.ll
```

这是 `IR` 代码（LCTT 译注：LLVM 的中间语言）。它比汇编代码更加紧凑，所以我可以把两个函数都导出来，让你可以看清楚我所说的“除了调用外，没有任何字面上的不同”是什么意思：

```
; Function Attrs: nounwind uwtable
define dso_local void @byArg(i32*) local_unnamed_addr #0 {
  %2 = load i32, i32* %0, align 4, !tbaa !2
  %3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %2)
  tail call void @func(i32* %0) #4
  %4 = load i32, i32* %0, align 4, !tbaa !2
  %5 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4)
  ret void
}

; Function Attrs: nounwind uwtable
define dso_local void @constByArg(i32*) local_unnamed_addr #0 {
  %2 = load i32, i32* %0, align 4, !tbaa !2
  %3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %2)
  tail call void @constFunc(i32* %0) #4
  %4 = load i32, i32* %0, align 4, !tbaa !2
  %5 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4)
  ret void
}
```

### 某些有作用的东西

接下来是一组 `const` 能够真正产生作用的代码：

```
void localVar()
{
  int x = 42;
  printf("%d\n", x);
  constFunc(&x);
  printf("%d\n", x);
}

void constLocalVar()
{
  const int x = 42;  // 对本地变量使用 const
  printf("%d\n", x);
  constFunc(&x);
  printf("%d\n", x);
}
```

下面是 `localVar()` 的汇编代码，其中有两条指令在 `constLocalVar()` 中会被优化掉：

```
localVar:
.LFB25:
    .cfi_startproc
    subq    $24, %rsp
    .cfi_def_cfa_offset 32
    movl    $42, %edx
    movl    $1, %edi
    movq    %fs:40, %rax
    movq    %rax, 8(%rsp)
    xorl    %eax, %eax
    leaq    .LC0(%rip), %rsi
    movl    $42, 4(%rsp)
    call    __printf_chk@PLT
    leaq    4(%rsp), %rdi
    call    constFunc@PLT
    movl    4(%rsp), %edx  # 在 constLocalVar() 中没有
    xorl    %eax, %eax
    movl    $1, %edi
    leaq    .LC0(%rip), %rsi  # 在 constLocalVar() 中没有
    call    __printf_chk@PLT
    movq    8(%rsp), %rax
    xorq    %fs:40, %rax
    jne .L9
    addq    $24, %rsp
    .cfi_remember_state
    .cfi_def_cfa_offset 8
    ret
.L9:
    .cfi_restore_state
    call    __stack_chk_fail@PLT
    .cfi_endproc
```

在 LLVM 生成的 `IR` 代码中更明显一点。在 `constLocalVar()` 中，第二次调用 `printf()` 之前的 `load` 会被优化掉：

```
; Function Attrs: nounwind uwtable
define dso_local void @localVar() local_unnamed_addr #0 {
  %1 = alloca i32, align 4
  %2 = bitcast i32* %1 to i8*
  call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %2) #4
  store i32 42, i32* %1, align 4, !tbaa !2
  %3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 42)
  call void @constFunc(i32* nonnull %1) #4
  %4 = load i32, i32* %1, align 4, !tbaa !2
  %5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 %4)
  call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %2) #4
  ret void
}
```

好吧，现在，`constLocalVar()` 成功的省略了对 `*x` 的重新读取，但是可能你已经注意到一些问题：`localVar()` 和 `constLocalVar()` 在函数体中做了同样的 `constFunc()` 调用。如果编译器能够推断出 `constFunc()` 没有修改 `constLocalVar()` 中的 `*x`，那为什么不能推断出完全一样的函数调用也没有修改 `localVar()` 中的 `*x`？

这个解释更贴近于为什么 C 语言的 `const` 不能作为优化手段的核心原因。C 语言的 `const` 有两个有效的含义：它可以表示这个变量是某个可能是常数也可能不是常数的数据的一个只读别名，或者它可以表示该变量是真正的常量。如果你移除了一个指向常量的指针的 `const` 属性并写入数据，那结果将是一个未定义行为。另一方面，如果是一个指向非常量值的 `const` 指针，将就没问题。

这份 `constFunc()` 的可能实现揭示了这意味着什么：

```
// x 是一个指向某个可能是常数也可能不是常数的数据的只读指针
void constFunc(const int *x)
{
  // local_var 是一个真正的常数
  const int local_var = 42;

  // C 语言规定的未定义行为
  doubleIt((int*)&local_var);
  // 谁知道这是不是一个未定义行为呢？
  doubleIt((int*)x);
}

void doubleIt(int *x)
{
  *x *= 2;
}
```

`localVar()` 传递给 `constFunc()` 一个指向非 `const` 变量的 `const` 指针。因为这个变量并非常量，`constFunc()` 可以撒个谎并强行修改它而不触发未定义行为。所以，编译器不能断定变量在调用 `constFunc()` 后仍是同样的值。在 `constLocalVar()` 中的变量是真正的常量，因此，编译器可以断定它不会改变 —— 因为在 `constFunc()` 去除变量的 `const` 属性并写入它*将*会是一个未定义行为。

第一个例子中的函数 `byArg()` 和 `constByArg()` 是没有可能优化的，因为编译器没有任何方法能知道 `*x` 是否真的是 `const` 常量。

>
> 补充（和题外话）：相当多的读者已经正确地指出，使用 `const int *x`，该指针本身不是限定的常量，只是该数据被加个了别名，而 `const int * const extra_const` 是一个“双向”限定为常量的指针。但是因为指针本身的常量与别名数据的常量无关，所以结果是相同的。仅在 `extra_const` 指向使用 `const` 定义的对象时，`*(int*const)extra_const = 0` 才是未定义行为。（实际上，`*(int*)extra_const = 0` 也不会更糟。）因为它们之间的区别可以一句话说明白，一个是完全的 `const` 指针，另外一个可能是也可能不是常量本身的指针，而是一个可能是也可能不是常量的对象的只读别名，我将继续不严谨地引用“常量指针”。（题外话结束）
>
>
>

但是为什么不一致呢？如果编译器能够推断出 `constLocalVar()` 中调用的 `constFunc()` 不会修改它的参数，那么肯定也能继续在其他 `constFunc()` 的调用上实施相同的优化，是吗？并不。编译器不能假设 `constLocalVar()` 根本没有运行。如果不是这样（例如，它只是代码生成器或者宏的一些未使用的额外输出），`constFunc()` 就能偷偷地修改数据而不触发未定义行为。

你可能需要重复阅读几次上述说明和示例，但不要担心，它听起来很荒谬，它确实是正确的。不幸的是，对 `const` 变量进行写入是最糟糕的未定义行为：大多数情况下，编译器无法知道它是否将会是未定义行为。所以，大多数情况下，编译器看见 `const` 时必须假设它未来可能会被移除掉，这意味着编译器不能使用它进行优化。这在实践中是正确的，因为真实的 C 代码会在“深思熟虑”后移除 `const`。

简而言之，很多事情都可以阻止编译器使用 `const` 进行优化，包括使用指针从另一内存空间接受数据，或者在堆空间上分配数据。更糟糕的是，在大部分编译器能够使用 `const` 进行优化的情况，它都不是必须的。例如，任何像样的编译器都能推断出下面代码中的 `x` 是一个常量，甚至都不需要 `const`：

```
int x = 42, y = 0;
printf("%d %d\n", x, y);
y += x;
printf("%d %d\n", x, y);
```

总结，`const` 对优化而言几乎无用，因为：

1. 除了特殊情况，编译器需要忽略它，因为其他代码可能合法地移除它
2. 在 #1 以外的大多数例外中，编译器无论如何都能推断出该变量是常量

### C++

如果你在使用 C++ 那么有另外一个方法让 `const` 能够影响到代码的生成：函数重载。你可以用 `const` 和非 `const` 的参数重载同一个函数，而非 `const` 版本的代码可能可以被优化（由程序员优化而不是编译器），减少某些拷贝或者其他事情。

```
void foo(int *p)
{
  // 需要做更多的数据拷贝
}

void foo(const int *p)
{
  // 不需要保护性的拷贝副本
}

int main()
{
  const int x = 42;
  // const 影响被调用的是哪一个版本的重载函数
  foo(&x);
  return 0;
}
```

一方面，我不认为这会在实际的 C++ 代码中大量使用。另一方面，为了导致差异，程序员需要假设编译器无法做出，因为它们不受语言保护。

### 用 Sqlite3 进行实验

有了足够的理论和例子。那么 `const` 在一个真正的代码库中有多大的影响呢？我将会在代码库 `Sqlite`（版本：3.30.0）上做一个测试，因为：

* 它真正地使用了 `const`
* 它不是一个简单的代码库（超过 20 万行代码）
* 作为一个数据库，它包括了字符串处理、数学计算、日期处理等一系列内容
* 它能够在绑定 CPU 的情况下进行负载测试

此外，作者和贡献者们已经进行了多年的性能优化工作，因此我能确定他们没有错过任何有显著效果的优化。

#### 配置

我做了两份[源码](https://sqlite.org/src/doc/trunk/README.md)拷贝，并且正常编译其中一份。而对于另一份拷贝，我插入了这个特殊的预处理代码段，将 `const` 变成一个空操作：

```
#define const
```

(GNU) `sed` 可以将一些东西添加到每个文件的顶端，比如 `sed -i '1i#define const' *.c *.h`。

在编译期间使用脚本生成 `Sqlite` 代码稍微有点复杂。幸运的是当 `const` 代码和非 `const` 代码混合时，编译器会产生了大量的提醒，因此很容易发现它并调整脚本来包含我的反 `const` 代码段。

直接比较编译结果毫无意义，因为任意微小的改变就会影响整个内存布局，这可能会改变整个代码中的指针和函数调用。因此，我用每个指令的二进制大小和汇编代码作为识别码（`objdump -d libsqlite3.so.0.8.6`）。举个例子，这个函数：

```
000000000005d570 <sqlite3_blob_read>:
   5d570:       4c 8d 05 59 a2 ff ff    lea    -0x5da7(%rip),%r8        # 577d0 <sqlite3BtreePayloadChecked>
   5d577:       e9 04 fe ff ff          jmpq   5d380 <blobReadWrite>
   5d57c:       0f 1f 40 00             nopl   0x0(%rax)
```

将会变成这样：

```
sqlite3_blob_read   7lea 5jmpq 4nopl
```

在编译时，我保留了所有 `Sqlite` 的编译设置。

#### 分析编译结果

`const` 版本的 `libsqlite3.so` 的大小是 4,740,704 字节，大约比 4,736,712 字节的非 `const` 版本大了 0.1% 。在全部 1374 个导出函数（不包括类似 PLT 里的底层辅助函数）中，一共有 13 个函数的识别码不一致。

其中的一些改变是由于插入的预处理代码。举个例子，这里有一个发生了更改的函数（已经删去一些 `Sqlite` 特有的定义）：

```
#define LARGEST_INT64  (0xffffffff|(((int64_t)0x7fffffff)<<32))
#define SMALLEST_INT64 (((int64_t)-1) - LARGEST_INT64)

static int64_t doubleToInt64(double r){
  /*
  ** Many compilers we encounter do not define constants for the
  ** minimum and maximum 64-bit integers, or they define them
  ** inconsistently.  And many do not understand the "LL" notation.
  ** So we define our own static constants here using nothing
  ** larger than a 32-bit integer constant.
  */
  static const int64_t maxInt = LARGEST_INT64;
  static const int64_t minInt = SMALLEST_INT64;

  if( r<=(double)minInt ){
    return minInt;
  }else if( r>=(double)maxInt ){
    return maxInt;
  }else{
    return (int64_t)r;
  }
}
```

删去 `const` 使得这些常量变成了 `static` 变量。我不明白为什么会有不了解 `const` 的人让这些变量加上 `static`。同时删去 `static` 和 `const` 会让 GCC 再次认为它们是常量，而我们将得到同样的编译输出。由于类似这样的局部的 `static const` 变量，使得 13 个函数中有 3 个函数产生假的变化，但我一个都不打算修复它们。

`Sqlite` 使用了很多全局变量，而这正是大多数真正的 `const` 优化产生的地方。通常情况下，它们类似于将一个变量比较代替成一个常量比较，或者一个循环在部分展开的一步。（[Radare toolkit](https://rada.re/r/) 可以很方便的找出这些优化措施。）一些变化则令人失望。`sqlite3ParseUri()` 有 487 个指令，但 `const` 产生的唯一区别是进行了这个比较：

```
test %al, %al
je <sqlite3ParseUri+0x717>
cmp $0x23, %al
je <sqlite3ParseUri+0x717>
```

并交换了它们的顺序：

```
cmp $0x23, %al
je <sqlite3ParseUri+0x717>
test %al, %al
je <sqlite3ParseUri+0x717>
```

#### 基准测试

`Sqlite` 自带了一个性能回归测试，因此我尝试每个版本的代码执行一百次，仍然使用默认的 `Sqlite` 编译设置。以秒为单位的测试结果如下：

|  | const | 非 const |
| --- | --- | --- |
| 最小值 | 10.658s | 10.803s |
| 中间值 | 11.571s | 11.519s |
| 最大值 | 11.832s | 11.658s |
| 平均值 | 11.531s | 11.492s |

就我个人看来，我没有发现足够的证据来说明这个差异值得关注。我是说，我从整个程序中删去 `const`，所以如果它有明显的差别，那么我希望它是显而易见的。但也许你关心任何微小的差异，因为你正在做一些绝对性能非常重要的事。那让我们试一下统计分析。

我喜欢使用类似 Mann-Whitney U 检验这样的东西。它类似于更著名的 T 检验，但对你在机器上计时时产生的复杂随机变量（由于不可预测的上下文切换、页错误等）更加健壮。以下是结果：

|  | const | 非 const |
| --- | --- | --- |
| N | 100 | 100 |
| Mean rank | 121.38 | 79.62 |

|  |  |
| --- | --- |
| Mann-Whitney U | 2912 |
| Z | -5.10 |
| 2-sided p value | <10-6 |
| HL median difference | -0.056s |
| 95% confidence interval | -0.077s – -0.038s |

U 检验已经发现统计意义上具有显著的性能差异。但是，令人惊讶的是，实际上是非 `const` 版本更快——大约 60ms，0.5%。似乎 `const` 启用的少量“优化”不值得额外代码的开销。这不像是 `const` 启用了任何类似于自动矢量化的重要的优化。当然，你的结果可能因为编译器配置、编译器版本或者代码库等等而有所不同，但是我觉得这已经说明了 `const` 是否能够有效地提高 `C` 的性能，我们现在已经看到答案了。

### 那么，const 有什么用呢？

尽管存在缺陷，C/C++ 的 `const` 仍有助于类型安全。特别是，结合 C++ 的移动语义和 `std::unique_pointer`，`const` 可以使指针所有权显式化。在超过十万行代码的 C++ 旧代码库里，指针所有权模糊是一个大难题，我对此深有感触。

但是，我以前常常使用 `const` 来实现有意义的类型安全。我曾听说过基于性能上的原因，最好是尽可能多地使用 `const`。我曾听说过当性能很重要时，重构代码并添加更多的 `const` 非常重要，即使以降低代码可读性的方式。**当时觉得这没问题，但后来我才知道这并不对。**

---

via: <https://theartofmachinery.com/2019/08/12/c_const_isnt_for_performance.html>

作者：[Simon Arneaud](https://theartofmachinery.com) 选题：[lujun9972](https://github.com/lujun9972) 译者：[LazyWolfLin](https://github.com/LazyWolfLin) 校对：[wxy](https://github.com/wxy)

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