Sed 命令完全指南

Summary: 这将是你全面了解 Sed 命令的一个机会，深入挖掘它的运行细节和精妙之处。

好

6的一批

我只想说，原文作者对地址范围的解读没有问题

这将是你全面了解 Sed 命令的一个机会，深入挖掘它的运行细节和精妙之处。

# Sed 命令完全指南

在前面的文章中，我展示了 [Sed 命令的基本用法](https://linuxhandbook.com/sed-command-basics/)， Sed 是一个实用的流编辑器。今天，我们准备去了解关于 Sed 更多的知识，深入了解 Sed 的运行模式。这将是你全面了解 Sed 命令的一个机会，深入挖掘它的运行细节和精妙之处。因此，如果你已经做好了准备，那就打开终端吧，[下载测试文件](https://gist.github.com/s-leroux/5cb36435bac46c10cfced26e4bf5588c) 然后坐在电脑前：开始我们的探索之旅吧！

### 关于 Sed 的一点点理论知识

#### 首先我们看一下 sed 的运行模式

要准确理解 Sed 命令，你必须先了解工具的运行模式。

当处理数据时，Sed 从输入源一次读入一行，并将它保存到所谓的<ruby> 模式空间 <rt>  pattern space </rt></ruby>中。所有 Sed 的变换都发生在模式空间。变换都是由命令行上或外部 Sed 脚本文件提供的单字母命令来描述的。大多数 Sed 命令都可以由一个地址或一个地址范围作为前导来限制它们的作用范围。

默认情况下，Sed 在结束每个处理循环后输出模式空间中的内容，也就是说，输出发生在输入的下一个行覆盖模式空间之前。我们可以将这种运行模式总结如下：

1. 尝试将下一个行读入到模式空间中
2. 如果读取成功：
	1. 按脚本中的顺序将所有命令应用到与那个地址匹配的当前输入行上
	2. 如果 sed 没有以静默模式（`-n`）运行，那么将输出模式空间中的所有内容（可能会是修改过的）。
	3. 重新回到 1。

因此，在每个行被处理完毕之后，模式空间中的内容将被丢弃，它并不适合长时间保存内容。基于这种目的，Sed 有第二个缓冲区：<ruby> 保持空间 <rt>  hold space </rt></ruby>。除非你显式地要求它将数据置入到保持空间、或从保持空间中取得数据，否则 Sed 从不清除保持空间的内容。在我们后面学习到 `exchange`、`get`、`hold` 命令时将深入研究它。

#### Sed 的抽象机制

你将在许多的 Sed 教程中都会看到上面解释的模式。的确，这是充分正确理解大多数基本 Sed 程序所必需的。但是当你深入研究更多的高级命令时，你将会发现，仅这些知识还是不够的。因此，我们现在尝试去了解更深入的一些知识。

的确，Sed 可以被视为是[抽象机制](http://mathworld.wolfram.com/AbstractMachine.html)的实现，它的[状态](https://en.wikipedia.org/wiki/State_(computer_science))由三个[缓冲区](https://en.wikipedia.org/wiki/Data_buffer) 、两个[寄存器](https://en.wikipedia.org/wiki/Processor_register#Categories_of_registers)和两个[标志](https://www.computerhope.com/jargon/f/flag.htm)来定义的：

* **三个缓冲区**用于去保存任意长度的文本。是的，是三个！在前面的基本运行模式中我们谈到了两个：模式空间和保持空间，但是 Sed 还有第三个缓冲区：<ruby> 追加队列 <rt>  append queue </rt></ruby>。从 Sed 脚本的角度来看，它是一个只写缓冲区，Sed 将在它运行时的预定义阶段来自动刷新它（一般是在从输入源读入一个新行之前，或仅在它退出运行之前）。
* Sed 也维护**两个寄存器**：<ruby> 行计数器 <rt>  line counter </rt></ruby>（LC）用于保存从输入源读取的行数，而<ruby> 程序计数器 <rt>  program counter </rt></ruby>（PC）总是用来保存下一个将要运行的命令的索引（就是脚本中的位置），Sed 将它作为它的主循环的一部分来自动增加 PC。但在使用特定的命令时，脚本也可以直接修改 PC 去跳过或重复执行程序的一部分。这就像使用 Sed 实现的一个循环或条件语句。更多内容将在下面的专用分支一节中描述。
* 最后，**两个标志**可以修改某些 Sed 命令的行为：<ruby> 自动输出 <rt>  auto-print </rt></ruby>（AP）标志和<ruby替换 substitution（SF）标志。当自动输出标志 AP 被设置时，Sed 将在模式空间的内容被覆盖前自动输出（尤其是，包括但不限于，在从输入源读入一个新行之前）。当自动输出标志被清除时（即：没有设置），Sed 在脚本中没有显式命令的情况下，将不会输出模式空间中的内容。你可以通过在“静默模式”（使用命令行选项 `-n` 或者在第一行或脚本中使用特殊注释 `#n`）运行 Sed 命令来清除自动输出标志。当它的地址和查找模式与模式空间中的内容都匹配时，替换标志 SF 将被替换命令（`s` 命令）设置。替换标志在每个新的循环开始时、或当从输入源读入一个新行时、或获得条件分支之后将被清除。我们将在分支一节中详细研究这一话题。

另外，Sed 维护一个进入到它的地址范围（关于地址范围的更多知识将在地址范围一节详细描述）的命令列表，以及用于读取和写入数据的两个文件句柄（你将在读取和写入命令的描述中获得更多有关文件句柄的内容）。

#### 一个更精确的 Sed 运行模式

一图胜千言，所以我画了一个流程图去描述 Sed 的运行模式。我将两个东西放在了旁边，像处理多个输入文件或错误处理，但是我认为这足够你去理解任何 Sed 程序的行为了，并且可以避免你在编写你自己的 Sed 脚本时浪费在摸索上的时间。

你可能已经注意到，在上面的流程图上我并没有描述特定的命令动作。对于命令，我们将逐个详细讲解。因此，不用着急，我们马上开始！

### 打印命令

打印命令（`p`）是用于输出在它运行那一刻模式空间中的内容。它并不会以任何方式改变 Sed 抽象机制中的状态。

<img src="content_image/album_201811_14_190636pcocoolwrzffeaco.png" alt="The Sed `print` command" title="The Sed `print` command">

示例：

```
sed -e 'p' inputfile
```

上面的命令将输出输入文件中每一行的内容……两次，因为你一旦显式地要求使用 `p` 命令时，将会在每个处理循环结束时再隐式地输出一次（因为在这里我们不是在“静默模式”中运行 Sed）。

如果我们不想每个行看到两次，我们可以用两种方式去解决它：

```
sed -n -e 'p' inputfile # 在静默模式中显式输出
sed -e '' inputfile # 空的“什么都不做的”程序，隐式输出
```

注意：`-e` 选项是引入一个 Sed 命令。它被用于区分命令和文件名。由于一个 Sed 表达式必须包含至少一个命令，所以对于第一个命令，`-e` 标志不是必需的。但是，由于我个人使用习惯问题，为了与在这里的大多数的一个命令行上给出多个 Sed 表达式的更复杂的案例保持一致性，我添加了它。你自己去判断这是一个好习惯还是坏习惯，并且在本文的后面部分还将延用这一习惯。

### 地址

显而易见，`print` 命令本身并没有太多的用处。但是，如果你在它之前添加一个地址，这样它就只输出输入文件的一些行，这样它就突然变得能够从一个输入文件中过滤一些不希望的行。那么 Sed 的地址又是什么呢？它是如何来辨别输入文件的“行”呢？

#### 行号

Sed 的地址既可以是一个行号（`$` 表示“最后一行”）也可以是一个正则表达式。在使用行号时，你需要记住 Sed 中的行数是从 1 开始的 —— 并且需要注意的是，它**不是**从 0 行开始的。

```
sed -n -e '1p' inputfile # 仅输出文件的第一行
sed -n -e '5p' inputfile # 仅输出第 5 行
sed -n -e '$p' inputfile # 输出文件的最后一行
sed -n -e '0p' inputfile # 结果将是报错，因为 0 不是有效的行号
```

根据 [POSIX 规范](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html)，如果你指定了几个输出文件，那么它的行号是累加的。换句话说，当 Sed 打开一个新输入文件时，它的行计数器是不会被重置的。因此，以下的两个命令所做的事情是一样的。仅输出一行文本：

```
sed -n -e '1p' inputfile1 inputfile2 inputfile3
cat inputfile1 inputfile2 inputfile3 | sed -n -e '1p'
```

实际上，确实在 POSIX 中规定了多个文件是如何处理的：

>
> 如果指定了多个文件，将按指定的文件命名顺序进行读取并被串联编辑。
>
>
>

但是，一些 Sed 的实现提供了命令行选项去改变这种行为，比如， GNU Sed 的 `-s` 标志（在使用 GNU Sed `-i` 标志时，它也被隐式地应用）：

```
sed -sn -e '1p' inputfile1 inputfile2 inputfile3
```

如果你的 Sed 实现支持这种非标准选项，那么关于它的具体细节请查看 `man` 手册页。

#### 正则表达式

我前面说过，Sed 地址既可以是行号也可以是正则表达式。那么正则表达式是什么呢？

正如它的名字，一个[正则表达式](https://www.regular-expressions.info/)是描述一个字符串集合的方法。如果一个指定的字符串符合一个正则表达式所描述的集合，那么我们就认为这个字符串与正则表达式匹配。

正则表达式可以包含必须完全匹配的文本字符。例如，所有的字母和数字，以及大部分可以打印的字符。但是，一些符号有特定意义：

* 它们相当于锚，像 `^` 和 `$` 它们分别表示一个行的开始和结束；
* 能够做为整个字符集的占位符的其它符号（比如圆点 `.` 可以匹配任意单个字符，或者方括号 `[]` 用于定义一个自定义的字符集）；
* 另外的是表示重复出现的数量（像 [克莱尼星号（`*`）](https://chortle.ccsu.edu/FiniteAutomata/Section07/sect07_16.html) 表示前面的模式出现 0、1 或多次）；

这篇文章的目的不是给大家讲正则表达式。因此，我只粘几个示例。但是，你可以在网络上随便找到很多关于正则表达式的教程，正则表达式的功能非常强大，它可用于许多标准的 Unix 命令和编程语言中，并且是每个 Unix 用户应该掌握的技能。

下面是使用 Sed 地址的几个示例：

```
sed -n -e '/systemd/p' inputfile # 仅输出包含字符串“systemd”的行
sed -n -e '/nologin$/p' inputfile # 仅输出以“nologin”结尾的行
sed -n -e '/^bin/p' inputfile # 仅输出以“bin”开头的行
sed -n -e '/^$/p' inputfile # 仅输出空行（即：开始和结束之间什么都没有的行）
sed -n -e '/./p' inputfile # 仅输出包含字符的行（即：非空行）
sed -n -e '/^.$/p' inputfile # 仅输出只包含一个字符的行
sed -n -e '/admin.*false/p' inputfile # 仅输出包含字符串“admin”后面有字符串“false”的行（在它们之间有任意数量的任意字符）
sed -n -e '/1[0,3]/p' inputfile # 仅输出包含一个“1”并且后面是一个“0”或“3”的行
sed -n -e '/1[0-2]/p' inputfile # 仅输出包含一个“1”并且后面是一个“0”、“1”、“2”或“3”的行
sed -n -e '/1.*2/p' inputfile # 仅输出包含字符“1”后面是一个“2”（在它们之间有任意数量的字符）的行
sed -n -e '/1[0-9]*2/p' inputfile # 仅输出包含字符“1”后面跟着“0”、“1”、或更多数字，最后面是一个“2”的行
```

如果你想在正则表达式（包括正则表达式分隔符）中去除字符的特殊意义，你可以在它前面使用一个反斜杠：

```
# 输出所有包含字符串“/usr/sbin/nologin”的行
sed -ne '/\/usr\/sbin\/nologin/p' inputfile
```

并不限制你只能使用斜杠作为地址中正则表达式的分隔符。你可以通过在第一个分隔符前面加上反斜杠（`\`）的方式，来使用任何你认为适合你需要和偏好的其它字符作为正则表达式的分隔符。当你用地址与带文件路径的字符一起来匹配的时，是非常有用的：

```
# 以下两个命令是完全相同的
sed -ne '/\/usr\/sbin\/nologin/p' inputfile
sed -ne '\=/usr/sbin/nologin=p' inputfile
```

#### 扩展的正则表达式

默认情况下，Sed 的正则表达式引擎仅理解 [POSIX 基本正则表达式](https://www.regular-expressions.info/posix.html#bre) 的语法。如果你需要用到 [扩展正则表达式](https://www.regular-expressions.info/posix.html#ere)，你必须在 Sed 命令上添加 `-E` 标志。扩展正则表达式在基本正则表达式基础上增加了一组额外的特性，并且很多都是很重要的，它们所要求的反斜杠要少很多。我们来比较一下：

```
sed -n -e '/\(www\)\|\(mail\)/p' inputfile
sed -En -e '/(www)|(mail)/p' inputfile
```

#### 花括号量词

正则表达式之所以强大的一个原因是[范围量词](https://www.regular-expressions.info/repeat.html#limit) `{,}`。事实上，当你写一个不太精确匹配的正则表达式时，量词 `*` 就是一个非常完美的符号。但是，（用花括号量词）你可以显式在它边上添加一个下限和上限，这样就有了很好的灵活性。当量词范围的下限省略时，下限被假定为 0。当上限被省略时，上限被假定为无限大：

| 括号 | 速记词 | 解释 |
| --- | --- | --- |
| `{,}` | `*` | 前面的规则出现 0、1、或许多遍 |
| `{,1}` | `?` | 前面的规则出现 0 或 1 遍 |
| `{1,}` | `+` | 前面的规则出现 1 或许多遍 |
| `{n,n}` | `{n}` | 前面的规则精确地出现 n 遍 |

花括号在基本正则表达式中也是可以使用的，但是它要求使用反斜杠。根据 POSIX 规范，在基本正则表达式中可以使用的量词仅有星号（`*`）和花括号（使用反斜杠，如 `\{m,n\}`）。许多正则表达式引擎都扩展支持 `\?` 和 `\+`。但是，为什么魔鬼如此有诱惑力呢？因为，如果你需要这些量词，使用扩展正则表达式将不但易于写而且可移植性更好。

为什么我要花点时间去讨论关于正则表达式的花括号量词，这是因为在 Sed 脚本中经常用这个特性去计数字符。

```
sed -En -e '/^.{35}$/p' inputfile # 输出精确包含 35 个字符的行
sed -En -e '/^.{0,35}$/p' inputfile # 输出包含 35 个字符或更少字符的行
sed -En -e '/^.{,35}$/p' inputfile # 输出包含 35 个字符或更少字符的行
sed -En -e '/^.{35,}$/p' inputfile # 输出包含 35 个字符或更多字符的行
sed -En -e '/.{35}/p' inputfile # 你自己指出它的输出内容（这是留给你的测试题）
```

#### 地址范围

到目前为止，我们使用的所有地址都是唯一地址。在我们使用一个唯一地址时，命令是应用在与那个地址匹配的行上。但是，Sed 也支持地址范围。Sed 命令可以应用到那个地址范围中从开始到结束的所有地址中的所有行上：

```
sed -n -e '1,5p' inputfile # 仅输出 1 到 5 行
sed -n -e '5,$p' inputfile # 从第 5 行输出到文件结尾
sed -n -e '/www/,/systemd/p' inputfile # 输出与正则表达式 /www/ 匹配的第一行到与接下来匹配正则表达式 /systemd/ 的行为止
```

（LCTT 译注：下面用的一个生成的列表例子，如下供参考：）

```
printf "%s\n" {a,b,c}{d,e,f} | cat -n
     1  ad
     2  ae
     3  af
     4  bd
     5  be
     6  bf
     7  cd
     8  ce
     9  cf
```

如果在开始和结束地址上使用了同一个行号，那么范围就缩小为那个行。事实上，如果第二个地址的数字小于或等于地址范围中选定的第一个行的数字，那么仅有一个行被选定：

```
printf "%s\n" {a,b,c}{d,e,f} | cat -n | sed -ne '4,4p'
     4 bd
printf "%s\n" {a,b,c}{d,e,f} | cat -n | sed -ne '4,3p'
     4 bd
```

下面有点难了，但是在前面的段落中给出的规则也适用于起始地址是正则表达式的情况。在那种情况下，Sed 将对正则表达式匹配的第一个行的行号和给定的作为结束地址的显式的行号进行比较。再强调一次，如果结束行号小于或等于起始行号，那么这个范围将缩小为一行：

（LCTT 译注：此处作者陈述有误，Sed 会在处理以正则表达式表示的开始行时，并不会同时测试结束表达式：从匹配开始行的正则表达式开始，直到不匹配时，才会测试结束行的表达式——无论是否是正则表达式——并在结束的表达式测试不通过时停止，并循环此测试。）

```
# 这个 /b/,4 地址将匹配三个单行
# 因为每个匹配的行有一个行号 >= 4
#（LCTT 译注：结果正确，但是说明不正确。4、5、6 行都会因为匹配开始正则表达式而通过，第 7 行因为不匹配开始正则表达式，所以开始比较行数： 7 > 4，遂停止。）
printf "%s\n" {a,b,c}{d,e,f} | cat -n | sed -ne '/b/,4p'
     4  bd
     5  be
     6  bf

# 你自己指出匹配的范围是多少
# 第二个例子：
printf "%s\n" {a,b,c}{d,e,f} | cat -n | sed -ne '/d/,4p'
     1  ad
     2  ae
     3  af
     4  bd
     7  cd
```

但是，当结束地址是一个正则表达式时，Sed 的行为将不一样。在那种情况下，地址范围的第一行将不会与结束地址进行检查，因此地址范围将至少包含两行（当然，如果输入数据不足的情况除外）：

（LCTT 译注：如上译注，当满足开始的正则表达式时，并不会测试结束的表达式；仅当不满足开始的表达式时，才会测试结束表达式。）

```
printf "%s\n" {a,b,c}{d,e,f} | cat -n | sed -ne '/b/,/d/p'
 4 bd
 5 be
 6 bf
 7 cd

printf "%s\n" {a,b,c}{d,e,f} | cat -n | sed -ne '4,/d/p'
 4 bd
 5 be
 6 bf
 7 cd
```

（LCTT 译注：对地址范围的总结，当满足开始的条件时，从该行开始，并不测试该行是否满足结束的条件；从下一行开始测试结束条件，并在结束条件不满足时结束；然后对剩余的行，再从开始条件开始匹配，以此循环——也就是说，匹配结果可以是非连续的单/多行。大家可以调整上述命令行的条件以理解。）

#### 补集

在一个地址选择行后面添加一个感叹号（`!`）表示不匹配那个地址。例如：

```
sed -n -e '5!p' inputfile # 输出除了第 5 行外的所有行
sed -n -e '5,10!p' inputfile # 输出除了第 5 到 10 之间的所有行
sed -n -e '/sys/!p' inputfile # 输出除了包含字符串“sys”的所有行
```

#### 交集

（LCTT 译注：原文标题为“合集”，应为“交集”）

Sed 允许在一个块中使用花括号 `{…}` 组合命令。你可以利用这个特性去组合几个地址的交集。例如，我们来比较下面两个命令的输出：

```
sed -n -e '/usb/{
  /daemon/p
}' inputfile

sed -n -e '/usb.*daemon/p' inputfile
```

通过在一个块中嵌套命令，我们将在任意顺序中选择包含字符串 “usb” 和 “daemon” 的行。而正则表达式 “usb.\*daemon” 将仅匹配在字符串 “daemon” 前面包含 “usb” 字符串的行。

离题太长时间后，我们现在重新回去学习各种 Sed 命令。

### 退出命令

退出命令（`q`）是指在当前的迭代循环处理结束之后停止 Sed。

`q` 命令是在到达输入文件的尾部之前停止处理输入的方法。为什么会有人想去那样做呢？

很好的问题，如果你还记得，我们可以使用下面的命令来输出文件中第 1 到第 5 的行：

```
sed -n -e '1,5p' inputfile
```

对于大多数 Sed 的实现方式，工具将循环读取输入文件的所有行，那怕是你只处理结果中的前 5 行。如果你的输入文件包含了几百万行（或者更糟糕的情况是，你从一个无限的数据流，比如像 `/dev/urandom` 中读取）将有重大影响。

使用退出命令，相同的程序可以被修改的更高效：

```
sed -e '5q' inputfile
```

由于我在这里并不使用 `-n` 选项，Sed 将在每个循环结束后隐式输出模式空间的内容。但是在你处理完第 5 行后，它将退出，并且因此不会去读取更多的数据。

我们能够使用一个类似的技巧只输出文件中一个特定的行。这也是从命令行中提供多个 Sed 表达式的几种方法。下面的三个变体都可以从 Sed 中接受几个命令，要么是不同的 `-e` 选项，要么是在相同的表达式中新起一行，或用分号（`;`）隔开：

```
sed -n -e '5p' -e '5q' inputfile

sed -n -e '
  5p
  5q
' inputfile

sed -n -e '5p;5q' inputfile
```

如果你还记得，我们在前面看到过能够使用花括号将命令组合起来，在这里我们使用它来防止相同的地址重复两次：

```
# 组合命令
sed -e '5{
  p
  q
}' inputfile

# 可以简写为：
sed '5{p;q;}' inputfile

# 作为 POSIX 扩展，有些实现方式可以省略闭花括号之前的分号：
sed '5{p;q}' inputfile
```

### 替换命令

你可以将替换命令（`s`）想像为 Sed 的“查找替换”功能，这个功能在大多数的“所见即所得”的编辑器上都能找到。Sed 的替换命令与之类似，但比它们更强大。替换命令是 Sed 中最著名的命令之一，在网上有大量的关于这个命令的文档。

<img src="content_image/album_201811_14_190640qepmzabc2p6e0dxd.png" alt="The Sed `substitution` command" title="The Sed `substitution` command">

[在前一篇文章](https://linuxhandbook.com/?p=128)中我们已经讲过它了，因此，在这里就不再重复了。但是，如果你对它的使用不是很熟悉，那么你需要记住下面的这些关键点：

* 替换命令有两个参数：查找模式和替换字符串：`sed s/:/-----/ inputfile`
* `s` 命令和它的参数是用任意一个字符来分隔的。这主要看你的习惯，在 99% 的时间中我都使用斜杠，但也会用其它的字符：`sed s%:%-----% inputfile`、`sed sX:X-----X inputfile` 或者甚至是 `sed 's : ----- ' inputfile`
* 默认情况下，替换命令仅被应用到模式空间中匹配到的第一个字符串上。你可以通过在命令之后指定一个匹配指数作为标志来改变这种情况：`sed 's/:/-----/1' inputfile`、`sed 's/:/-----/2' inputfile`、`sed 's/:/-----/3' inputfile`、…
* 如果你想执行一个全局替换（即：在模式空间上的每个非重叠匹配上进行），你需要增加 `g` 标志：`sed 's/:/-----/g' inputfile`
* 在字符串替换中，出现的任何一个 `&` 符号都将被与查找模式匹配的子字符串替换：`sed 's/:/-&&&-/g' inputfile`、`sed 's/.../& /g' inputfile`
* 圆括号（在扩展的正则表达式中的 `(...)` ，或者基本的正则表达式中的 `\(...\)`）被当做<ruby> 捕获组 <rt>  capturing group </rt></ruby>。那是匹配字符串的一部分，可以在替换字符串中被引用。`\1` 是第一个捕获组的内容，`\2` 是第二个捕获组的内容，依次类推：`sed -E 's/(.)(.)/\2\1/g' inputfile`、`sed -E 's/(.):x:(.):(.*)/\1:\3/' inputfile`（后者之所能正常工作是因为 [正则表达式中的量词星号表示尽可能多的匹配，直到不匹配为止](https://www.regular-expressions.info/repeat.html#greedy)，并且它可以匹配许多个字符）
* 在查找模式或替换字符串时，你可以通过使用一个反斜杠来去除任何字符的特殊意义：`sed 's/:/--\&--/g' inputfile`，`sed 's/\//\\/g' inputfile`

所有的这些看起来有点抽象，下面是一些示例。首先，我想去显示我的测试输入文件的第一个字段并给它在右侧附加 20 个空格字符，我可以这样写：

```
sed < inputfile -E -e '
 s/:/ /             # 用 20 个空格替换第一个字段的分隔符
 s/(.{20}).*/\1/    # 只保留一行的前 20 个字符
 s/.*/| & |/        # 为了输出好看添加竖条
'
```

第二个示例是，如果我想将用户 sonia 的 UID/GID 修改为 1100，我可以这样写：

```
sed -En -e '
  /sonia/{
    s/[0-9]+/1100/g
    p
 }' inputfile
```

注意在替换命令结束部分的 `g` 选项。这个选项改变了它的行为，因此它将查找全部的模式空间并替换，如果没有那个选项，它只替换查找到的第一个。

顺便说一下，这也是使用前面讲过的输出（`p`）命令的好机会，可以在命令运行时输出修改前后的模式空间的内容。因此，为了获得替换前后的内容，我可以这样写：

```
sed -En -e '
  /sonia/{
     p
     s/[0-9]+/1100/g
     p
 }' inputfile
```

事实上，替换后输出一个行是很常见的用法，因此，替换命令也接受 `p` 选项：

```
sed -En -e '/sonia/s/[0-9]+/1100/gp' inputfile
```

最后，我就不详细讲替换命令的 `w` 选项了，我们将在稍后的学习中详细介绍。

### 删除命令

删除命令（`d`）用于清除模式空间的内容，然后立即开始下一个处理循环。这样它将会跳过隐式输出模式空间内容的行为，即便是你设置了自动输出标志（AP）也不会输出。

<img src="content_image/album_201811_14_190641h144h1mhhxcpcmst.png" alt="The Sed `delete` command" title="The Sed `delete` command">

只输出一个文件前五行的一个很低效率的方法将是：

```
sed -e '6,$d' inputfile
```

你猜猜看，我为什么说它很低效率？如果你猜不到，建议你再次去阅读前面的关于退出命令的章节，答案就在那里！

当你组合使用正则表达式和地址，从输出中删除匹配的行时，删除命令将非常有用：

```
sed -e '/systemd/d' inputfile
```

### 次行命令

如果 Sed 命令没有运行在静默模式中，这个命令（`n`）将输出当前模式空间的内容，然后，在任何情况下它将读取下一个输入行到模式空间中，并使用新的模式空间中的内容来运行当前循环中剩余的命令。

用次行命令去跳过行的一个常见示例：

```
cat -n inputfile | sed -n -e 'n;n;p'
```

在上面的例子中，Sed 将隐式地读取输入文件的第一行。但是次行命令将丢弃对模式空间中的内容的输出（不输出是因为使用了 `-n` 选项），并从输入文件中读取下一行来替换模式空间中的内容。而第二个次行命令做的事情和前一个是一模一样的，这就实现了跳过输入文件 2 行的目的。最后，这个脚本显式地输出包含在模式空间中的输入文件的第三行的内容。然后，Sed 将启动一个新的循环，由于次行命令，它会隐式地读取第 4 行的内容，然后跳过它，同样地也跳过第 5 行，并输出第 6 行。如此循环，直到文件结束。总体来看，这个脚本就是读取输入文件然后每三行输出一行。

使用次行命令，我们也可以找到一些显示输入文件的前五行的几种方法：

```
cat -n inputfile | sed -n -e '1{p;n;p;n;p;n;p;n;p}'
cat -n inputfile | sed -n -e 'p;n;p;n;p;n;p;n;p;q'
cat -n inputfile | sed -e 'n;n;n;n;q'
```

更有趣的是，如果你需要根据一些地址来处理行时，次行命令也非常有用：

```
cat -n inputfile | sed -n '/pulse/p' # 输出包含 “pulse” 的行
cat -n inputfile | sed -n '/pulse/{n;p}' # 输出包含 “pulse” 之后的行
cat -n inputfile | sed -n '/pulse/{n;n;p}'  # 输出包含 “pulse” 的行的下一行的下一行
```

### 使用保持空间

到目前为止，我们所看到的命令都是仅使用了模式空间。但是，我们在文章的开始部分已经提到过，还有第二个缓冲区：保持空间，它完全由用户管理。它就是我们在第二节中描述的目标。

#### 交换命令

正如它的名字所表示的，交换命令（`x`）将交换保持空间和模式空间的内容。记住，你只要没有把任何东西放入到保持空间中，那么保持空间就是空的。

作为第一个示例，我们可使用交换命令去反序输出一个输入文件的前两行：

```
cat -n inputfile | sed -n -e 'x;n;p;x;p;q'
```

当然，在你设置保持空间之后你并没有立即使用它的内容，因为只要你没有显式地去修改它，保持空间中的内容就保持不变。在下面的例子中，我在输入一个文件的前五行后，使用它去删除第一行：

```
cat -n inputfile | sed -n -e '
 1{x;n} # 交换保持和模式空间
        # 保存第 1 行到保持空间中
        # 然后读取第 2 行
 5{
   p    # 输出第 5 行
   x    # 交换保持和模式空间
        # 去取得第 1 行的内容放回到模式空间
 }

 1,5p   # 输出第 2 到第 5 行
        # (并没有输错！尝试找出这个规则
        # 没有在第 1 行上运行的原因 ;)
'
```

#### 保持命令

保持命令（`h`）是用于将模式空间中的内容保存到保持空间中。但是，与交换命令不同的是，模式空间中的内容不会被改变。保持命令有两种用法：

* `h` 将复制模式空间中的内容到保持空间中，覆盖保持空间中任何已经存在的内容。
* `H` 将模式空间中的内容追加到保持空间中，使用一个新行作为分隔符。

上面使用交换命令的例子可以使用保持命令重写如下：

```
cat -n inputfile | sed -n -e '
 1{h;n} # 保存第 1 行的内容到保持缓冲区并继续
 5{     # 在第 5 行
   x    # 交换模式和保持空间
        # (现在模式空间包含了第 1 行)
   H    # 在保持空间的第 5 行后追加第 1 行
   x    # 再次交换第 5 行和第 1 行，第 5 行回到模式空间
 }

 1,5p   # 输出第 2 行到第 5 行
        # (没有输错！尝试去找到为什么这个规则
        # 不在第 1 行上运行 ;)
'
```

#### 获取命令

获取命令（`g`）与保持命令恰好相反：它从保持空间中取得内容并将它置入到模式空间中。同样它也有两种方式：

* `g` 将复制保持空间中的内容并将其放入到模式空间，覆盖模式空间中已存在的任何内容
* `G` 将保持空间中的内容追加到模式空间中，并使用一个新行作为分隔符

将保持命令和获取命令一起使用，可以允许你去存储并调回数据。作为一个小挑战，我让你重写前一节中的示例，将输入文件的第 1 行放置在第 5 行之后，但是这次必须使用获取和保持命令（使用大写或小写命令的版本）而不能使用交换命令。带点小运气，可以更简单！

同时，我可以给你展示另一个示例，它能给你一些灵感。目标是将拥有登录 shell 权限的用户与其它用户分开：

```
cat -n inputfile | sed -En -e '
 \=(/usr/sbin/nologin|/bin/false)$= { H;d; }
            # 追回匹配的行到保持空间
            # 然后继续下一个循环
 p          # 输出其它行
 $ { g;p }  # 在最后一行上
            # 获取并打印保持空间中的内容
'
```

### 复习打印、删除和次行命令

现在你已经更熟悉使用保持空间了，我们回到打印、删除和次行命令。我们已经讨论了小写的 `p`、`d` 和 `n` 命令了。而它们也有大写的版本。因为每个命令都有大小写版本，似乎是 Sed 的习惯，这些命令的大写版本将与多行缓冲区有关：

* `P` 将模式空间中第一个新行之前的内容输出
* `D` 删除模式空间中第一个新行之前的内容（包含新行），然后不读取任何新的输入而是使用剩余的文本去重启一个循环
* `N` 读取输入并追加一个新行到模式空间，用一个新行作为新旧数据的分隔符。继续运行当前的循环。

<img src="content_image/album_201811_14_190647j4o4j42hdpj92jmi.png" alt="The Sed uppercase `Delete` command" title="The Sed uppercase `Delete` command">

<img src="content_image/album_201811_14_190649vsj1vdqhhwqn52kw.png" alt="The Sed uppercase `Next` command" title="The Sed uppercase `Next` command">

这些命令的使用场景主要用于实现队列（[FIFO 列表](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics))）。从一个输入文件中删除最后 5 行就是一个很权威的例子：

```
cat -n inputfile | sed -En -e '
  1 { N;N;N;N } # 确保模式空间中包含 5 行

  N             # 追加第 6 行到队列中
  P             # 输出队列的第 1 行
  D             # 删除队列的第 1 行
'
```

作为第二个示例，我们可以在两个列上显示输入数据：

```
# 输出两列
sed < inputfile -En -e '
 $!N    # 追加一个新行到模式空间
        # 除了输入文件的最后一行
        # 当在输入文件的最后一行使用 N 命令时
        # GNU Sed 和 POSIX Sed 的行为是有差异的
        # 需要使用一个技巧去处理这种情况
        # https://www.gnu.org/software/sed/manual/sed.html#N_005fcommand_005flast_005fline

        # 用空间填充第 1 行的第 1 个字段
        # 并丢弃其余行
 s/:.*\n/                    \n/
 s/:.*//            # 除了第 2 行上的第 1 个字段外，丢弃其余的行
 s/(.{20}).*\n/\1/  # 修剪并连接行
 p                  # 输出结果
'
```

### 分支

我们刚才已经看到，Sed 因为有保持空间所以有了缓存的功能。其实它还有测试和分支的指令。因为有这些特性使得 Sed 是一个[图灵完备](https://chortle.ccsu.edu/StructuredC/Chap01/struct01_5.html)的语言。虽然它可能看起来很傻，但意味着你可以使用 Sed 写任何程序。你可以实现任何你的目的，但并不意味着实现起来会很容易，而且结果也不一定会很高效。

不过不用担心。在本文中，我们将使用能够展示测试和分支功能的最简单的例子。虽然这些功能乍一看似乎很有限，但请记住，有些人用 Sed 写了 <http://www.catonmat.net/ftp/sed/dc.sed> [计算器]、<http://www.catonmat.net/ftp/sed/sedtris.sed> [俄罗斯方块] 或许多其它类型的应用程序！

#### 标签和分支

从某些方面，你可以将 Sed 看到是一个功能有限的汇编语言。因此，你不会找到在高级语言中常见的 “for” 或 “while” 循环，或者 “if … else” 语句，但是你可以使用分支来实现同样的功能。

如果你在本文开始部分看到了用流程图描述的 Sed 运行模型，那么你应该知道 Sed 会自动增加程序计数器（PC）的值，命令是按程序的指令顺序来运行的。但是，使用分支（`b`）指令，你可以通过选择执行程序中的任意命令来改变顺序运行的程序。跳转目的地是使用一个标签（`:`）来显式定义的。

这是一个这样的示例：

```
echo hello | sed -ne '
  :start    # 在程序的该行上放置一个 “start” 标签
  p         # 输出模式空间内容
  b start   # 继续在 :start 标签上运行
' | less
```

那个 Sed 程序的行为非常类似于 `yes` 命令：它获取一个字符串并产生一个包含那个字符串的无限流。

切换到一个标签就像我们绕开了 Sed 的自动化特性一样：它既不读取任何输入，也不输出任何内容，更不更新任何缓冲区。它只是跳转到源程序指令顺序中下一条的另外一个指令。

值得一提的是，如果在分支命令（`b`）上没有指定一个标签作为它的参数，那么分支将直接切换到程序结束的地方。因此，Sed 将启动一个新的循环。这个特性可以用于去跳过一些指令并且因此可以用于作为“块”的替代者：

```
cat -n inputfile | sed -ne '
/usb/!b
/daemon/!b
p
'
```

#### 条件分支

到目前为止，我们已经看到了无条件分支，这个术语可能有点误导嫌疑，因为 Sed 命令总是基于它们的可选地址来作为条件的。

但是，在传统意义上，一个无条件分支也是一个分支，当它运行时，将跳转到特定的目的地，而条件分支既有可能也或许不可能跳转到特定的指令，这取决于系统的当前状态。

Sed 只有一个条件指令，就是测试（`t`）命令。只有在当前循环的开始或因为前一个条件分支运行了替换，它才跳转到不同的指令。更多的情况是，只有替换标志被设置时，测试命令才会切换分支。

<img src="content_image/album_201811_14_190652yw1tne5ehgtrthht.png" alt="The Sed `test` command" title="The Sed `test` command">

使用测试指令，你可以在一个 Sed 程序中很轻松地执行一个循环。作为一个特定的示例，你可以用它将一个行填充到某个长度（这是使用正则表达式无法实现的）：

```
# 居中文本
cut -d: -f1 inputfile | sed -Ee '
  :start
  s/^(.{,19})$/ \1 /    # 用一个空格填充少于 20 个字符的行的开始处
                        # 并在结束处添加另一个空格
  t start               # 如果我们已经添加了一个空格，则返回到 :start 标签
  s/(.{20}).*/| \1 |/   # 只保留一个行的前 20 个字符
                        # 以修复由于奇数行引起的差一错误
'
```

如果你仔细读前面的示例，你可能注意到，在将要把数据“喂”给 Sed 之前，我通过 `cut` 命令做了一点小修正去预处理数据。

不过，我们也可以只使用 Sed 对程序做一些小修改来执行相同的任务：

```
cat inputfile | sed -Ee '
  s/:.*//               # 除第 1 个字段外删除剩余字段
  t start
  :start
  s/^(.{,19})$/ \1 /    # 用一个空格填充少于 20 个字符的行的开始处
                        # 并在结束处添加另一个空格
  t start               # 如果我们已经添加了一个空格，则返回到 :start 标签
  s/(.{20}).*/| \1 |/   # 仅保留一个行的前 20 个字符
                        # 以修复由于奇数行引起的差一错误
'
```

在上面的示例中，你或许对下列的结构感到惊奇：

```
t start
:start
```

乍一看，在这里的分支并没有用，因为它只是跳转到将要运行的指令处。但是，如果你仔细阅读了测试命令的定义，你将会看到，如果在当前循环的开始或者前一个测试命令运行后发生了一个替换，分支才会起作用。换句话说就是，测试指令有清除替换标志的副作用。这也正是上面的代码片段的真实目的。这是一个在包含条件分支的 Sed 程序中经常看到的技巧，用于在使用多个替换命令时避免出现<ruby> 误报 <rt>  false positive </rt></ruby>的情况。

通过它并不能绝对强制地清除替换标志，我同意这一说法。因为在将字符串填充到正确的长度时我使用的特定的替换命令是<ruby> 幂等 <rt>  idempotent </rt></ruby>的。因此，一个多余的迭代并不会改变结果。不过，我们可以现在再次看一下第二个示例：

```
# 基于它们的登录程序来分类用户帐户
cat inputfile | sed -Ene '
  s/^/login=/
  /nologin/s/^/type=SERV /
  /false/s/^/type=SERV /
  t print
  s/^/type=USER /
  :print
  s/:.*//p
'
```

我希望在这里根据用户默认配置的登录程序，为用户帐户打上 “SERV” 或 “USER” 的标签。如果你运行它，预计你将看到 “SERV” 标签。然而，并没有在输出中跟踪到 “USER” 标签。为什么呢？因为 `t print` 指令不论行的内容是什么，它总是切换，替换标志总是由程序的第一个替换命令来设置。一旦替换标志设置完成后，在下一个行被读取或直到下一个测试命令之前，这个标志将保持不变。下面我们给出修复这个程序的解决方案：

```
# 基于用户登录程序来分类用户帐户
cat inputfile | sed -Ene '
  s/^/login=/

  t classify # clear the "substitution flag"
  :classify

  /nologin/s/^/type=SERV /
  /false/s/^/type=SERV /
  t print
  s/^/type=USER /
  :print
  s/:.*//p
'
```

### 精确地处理文本

Sed 是一个非交互式文本编辑器。虽然是非交互式的，但仍然是文本编辑器。而如果没有在输出中插入一些东西的功能，那它就不算一个完整的文本编辑器。我不是很喜欢它的文本编辑的特性，因为我发现它的语法太难用了（即便是以 Sed 的标准而言），但有时你难免会用到它。

采用严格的 POSIX 语法的只有三个命令：改变（`c`）、插入（`i`）或追加（`a`）一些文字文本到输出，都遵循相同的特定语法：命令字母后面跟着一个反斜杠，并且文本从脚本的下一行上开始插入：

```
head -5 inputfile | sed '
1i\
# List of user accounts
$a\
# end
'
```

插入多行文本，你必须每一行结束的位置使用一个反斜杠：

```
head -5 inputfile | sed '
1i\
# List of user accounts\
# (users 1 through 5)
$a\
# end
'

```

一些 Sed 实现，比如 GNU Sed，在初始的反斜杠后面的换行符是可选的，即便是在 `--posix` 模式下仍然如此。我在标准中并没有找到任何关于该替代语法的说明（如果是因为我没有在标准中找到那个特性，请在评论区留言告诉我！）。因此，如果对可移植性要求很高，请注意使用它的风险：

```
# 非 POSIX 语法：
head -5 inputfile | sed -e '
1i\# List of user accounts
$a\# end
'
```

也有一些 Sed 的实现，让初始的反斜杠完全是可选的。因此毫无疑问，它是一个厂商对 POSIX 标准进行扩展的特定版本，它是否支持那个语法，你需要去查看那个 Sed 版本的手册。

在简单概述之后，我们现在来回顾一下这些命令的更多细节，从我还没有介绍的改变命令开始。

#### 改变命令

改变命令（`c\`）就像 `d` 命令一样删除模式空间的内容并开始一个新的循环。唯一的不同在于，当命令运行之后，用户提供的文本是写往输出的。

```
cat -n inputfile | sed -e '
/systemd/c\
# :REMOVED:
s/:.*// # This will NOT be applied to the "changed" text
'
```

如果改变命令与一个地址范围关联，当到达范围的最后一行时，这个文本将仅输出一次。这在某种程度上成为 Sed 命令将被重复应用在地址范围内所有行这一惯例的一个例外情况：

```
cat -n inputfile | sed -e '
19,22c\
# :REMOVED:
s/:.*// # This will NOT be applied to the "changed" text
'
```

因此，如果你希望将改变命令重复应用到地址范围内的所有行上，除了将它封装到一个块中之外，你将没有其它的选择：

```
cat -n inputfile | sed -e '
19,22{c\
# :REMOVED:
}
s/:.*// # This will NOT be applied to the "changed" text
'
```

#### 插入命令

插入命令（`i\`）将立即在输出中给出用户提供的文本。它并不以任何方式修改程序流或缓冲区的内容。

```
# display the first five user names with a title on the first row
sed < inputfile -e '
1i\
USER NAME
s/:.*//
5q
'
```

#### 追加命令

当输入的下一行被读取时，追加命令（`a\`）将一些文本追加到显示队列。文本在当前循环的结束部分（包含程序结束的情况）或当使用 `n` 或 `N` 命令从输入中读取一个新行时被输出。

与上面相同的一个示例，但这次是插入到底部而不是顶部：

```
sed < inputfile -e '
5a\
USER NAME
s/:.*//
5q
'
```

#### 读取命令

这是插入一些文本内容到输出流的第四个命令：读取命令（`r`）。它的工作方式与追加命令完全一样，但不同的，它不从 Sed 脚本中取得硬编码到脚本中的文本，而是把一个文件的内容写入到一个输出上。

读取命令只调度要读取的文件。当清理追加队列时，后者才被高效地读取，而不是在读取命令运行时。如果这时候对这个文件有并发的访问读取，或那个文件不是一个普通的文件（比如，它是一个字符设备或命名管道），或文件在读取期间被修改，这时可能会产生严重的后果。

作为一个例证，如果你使用我们将在下一节详细讲述的写入命令，它与读取命令共同配合从一个临时文件中写入并重新读取，你可能会获得一些创造性的结果（使用法语版的 [Shiritori](https://en.wikipedia.org/wiki/Shiritori) 游戏作为一个例证）：

```
printf "%s\n" "Trois p'tits chats" "Chapeau d' paille" "Paillasson" |
sed -ne '
  r temp
  a\
  ----
  w temp
'
```

现在，在流输出中专门用于插入一些文本的 Sed 命令清单结束了。我的最后一个示例纯属好玩，但是由于我前面提到过有一个写入命令，这个示例将我们完美地带到下一节，在下一节我们将看到在 Sed 中如何将数据写入到一个外部文件。

### 替代的输出

Sed 的设计思想是，所有的文本转换都将写入到进程的标准输出上。但是，Sed 也有一些特性支持将数据发送到替代的目的地。你有两种方式去实现上述的输出目标替换：使用专门的写入命令（`w`），或者在一个替换命令（`s`）上添加一个写入标志。

#### 写入命令

写入命令（`w`）会追加模式空间的内容到给定的目标文件中。POSIX 要求在 Sed 处理任何数据之前，目标文件能够被 Sed 所创建。如果给定的目标文件已经存在，它将被覆写。

因此，即便是你从未真的写入到该文件中，但该文件仍然会被创建。例如，下列的 Sed 程序将创建/覆写这个 `output` 文件，那怕是这个写入命令从未被运行过：

```
echo | sed -ne '
  q # 立刻退出
  w output # 这个命令从未被运行
'
```

你可以将几个写入命令指向到同一个目标文件。指向同一个目标文件的所有写入命令将追加那个文件的内容（工作方式几乎与 shell 的重定向符 `>>` 相同）：

```
sed < inputfile -ne '
  /:\/bin\/false$/w server
  /:\/usr\/sbin\/nologin$/w server
  w output
'
cat server
```

#### 替换命令的写入标志

在前面，我们已经学习了替换命令（`s`），它有一个 `p` 选项用于在替换之后输出模式空间的内容。同样它也提供一个类似功能的 `w` 选项，用于在替换之后将模式空间的内容输出到一个文件中：

```
sed < inputfile -ne '
  s/:.*\/nologin$//w server
  s/:.*\/false$//w server
'
cat server
```

### 注释

我无数次使用过它们，但我从未花时间正式介绍过它们，因此，我决定现在来正式地介绍它们：就像大多数编程语言一样，注释是添加软件不去解析的自由格式文本的一种方法。Sed 的语法很晦涩，我不得不强调在脚本中需要的地方添加足够的注释。否则，除了作者外其他人将几乎无法理解它。

不过，和 Sed 的其它部分一样，注释也有它自己的微妙之处。首先并且是最重要的，注释并不是语法结构，但它是真正意义的 Sed 命令。注释虽然是一个“什么也不做”的命令，但它仍然是一个命令。至少，它是在 POSIX 中定义了的。因此，严格地说，它们只允许使用在其它命令允许使用的地方。

大多数 Sed 实现都通过允许行内命令来放松了那种要求，就像在那个文章中我到处都使用的那样。

结束那个主题之前，需要说一下 `#n` 注释（`#` 后面紧跟一个字母 `n`，中间没有空格）的特殊情况。如果在脚本的第一行找到这个精确注释，Sed 将切换到静默模式（即：清除自动输出标志），就像在命令行上指定了 `-n` 选项一样。

### 很少用得到的命令

现在，我们已经学习的命令能让你写出你所用到的 99.99% 的脚本。但是，如果我没有提到剩余的 Sed 命令，那么本教程就不能称为完全指南。我把它们留到最后是因为我们很少用到它。但或许你有实际使用案例，那么你就会发现它们很有用。如果是那样，请不要犹豫，在下面的评论区中把它分享给我们吧。

#### 行数命令

这个 `=` 命令将向标准输出上显示当前 Sed 正在读取的行数，这个行数就是行计数器（`LC`）的内容。没有任何方式从任何一个 Sed 缓冲区中捕获那个数字，也不能对它进行输出格式化。由于这两个限制使得这个命令的可用性大大降低。

请记住，在严格的 POSIX 兼容模式中，当在命令行上给定几个输入文件时，Sed 并不重置那个计数器，而是连续地增长它，就像所有的输入文件是连接在一起的一样。一些 Sed 实现，像 GNU Sed，它就有一个选项可以在每个输入文件读取结束后去重置计数器。

#### 明确打印命令

这个 `l`（小写的字母 `l`）作用类似于打印命令（`p`），但它是以精确的格式去输出模式空间的内容。以下引用自 [POSIX 标准](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html)：

>
> 在 XBD 转义序列中列出的字符和相关的动作（`\\`、`\a`、`\b`、`\f`、`\r`、`\t`、`\v`）将被写为相应的转义序列；在那个表中的 `\n` 是不适用的。不在那个表中的不可打印字符将被写为一个三位八进制数字（在前面使用一个反斜杠 `\`），表示字符中的每个字节（最重要的字节在前面）。长行应该被换行，通过写一个反斜杠后跟一个换行符来表示换行位置；发生换行时的长度是不确定的，但应该适合输出设备的具体情况。每个行应该以一个 `$` 标记结束。
>
>
>

我怀疑这个命令是在非 [8 位规则化信道](https://en.wikipedia.org/wiki/8-bit_clean) 上交换数据的。就我本人而言，除了调试用途以外，也从未使用过它。

#### 移译命令

<ruby> 移译 <rt>  transliterate </rt></ruby>（`y`）命令允许从一个源集到一个目标集映射模式空间的字符。它非常类似于 `tr` 命令，但是限制更多。

```
# The `y` c0mm4nd 1s for h4x0rz only
sed < inputfile -e '
 s/:.*//
 y/abcegio/48<3610/
'
```

虽然移译命令语法与替换命令的语法有一些相似之处，但它在替换字符串之后不接受任何选项。这个移译总是全局的。

请注意，移译命令要求源集和目标集之间要一一对应地转换。这意味着下面的 Sed 程序可能所做的事情并不是你乍一看所想的那样：

```
# 注意：这可能并不如你想的那样工作！
sed < inputfile -e '
  s/:.*//
  y/[a-z]/[A-Z]/
'
```

### 写在最后的话

```
# 它要做什么？
# 提示：答案就在不远处...
sed -E '
  s/.*\W(.*)/\1/
  h
  ${ x; p; }
  d' < inputfile
```

我们已经学习了所有的 Sed 命令，真不敢相信我们已经做到了！如果你也读到这里了，应该恭喜你，尤其是如果你花费了一些时间，在你的系统上尝试了所有的不同示例！

正如你所见，Sed 是非常复杂的，不仅因为它的语法比较零乱，也因为许多极端案例或命令行为之间的细微差别。毫无疑问，我们可以将这些归结于历史的原因。尽管它有这么多缺点，但是 Sed 仍然是一个非常强大的工具，甚至到现在，它仍然是 Unix 工具箱中为数不多的大量使用的命令之一。是时候总结一下这篇文章了，没有你们的支持我将无法做到：请节选你对喜欢的或最具创意的 Sed 脚本，并共享给我们。如果我收集到的你们共享出的脚本足够多了，我将会把这些 Sed 脚本结集发布！

---

via: <https://linuxhandbook.com/sed-reference-guide/>

作者：[Sylvain Leroux](https://linuxhandbook.com/author/sylvain/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

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


Caffeinated 6.828：使用的工具

# Caffeinated 6.828：使用的工具

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

在这个课程中你将使用两套工具：一个是 x86 模拟器 QEMU，它用来运行你的内核；另一个是编译器工具链，包括汇编器、链接器、C 编译器，以及调试器，它们用来编译和测试你的内核。本文有你需要去下载和安装你自己的副本相关信息。本课程假定你熟悉所有出现的 Unix 命令的用法。

我们强烈推荐你使用一个 Debathena 机器去做你的实验，比如 athena.dialup.mit.edu。如果你使用运行在 Linux 上的 MIT Athena 机器，那么本课程所需要的所有软件工具都在 6.828 的存储中：只需要输入 `add -f 6.828` 就可以访问它们。

如果你不使用 Debathena 机器，我们建议你使用一台 Linux 虚拟机。如果是这样，你可以在你的 Linux 虚拟机上构建和安装工具。我们将在下面介绍如何在 Linux 和 MacOS 计算上来构建和安装工具。

在 [Cygwin](http://www.cygwin.com) 的帮助下，在 Windows 中运行这个开发环境也是可行的。安装 cygwin，并确保安装了 flex 和 bison 包（它们在开发 header 软件包分类下面）。

对于 6.828 中使用的工具中的有用的命令，请参考[实验工具指南](labguide.html)。

### 编译器工具链

“编译器工具链“ 是一套程序，包括一个 C 编译器、汇编器和链接器，使用它们来将代码转换成可运行的二进制文件。你需要一个能够生成在 32 位 Intel 架构（x86 架构）上运行的 ELF 二进制格式程序的编译器工具链。

#### 测试你的编译器工具链

现代的 Linux 和 BSD UNIX 发行版已经为 6.828 提供了一个合适的工具链。去测试你的发行版，可以输入如下的命令：

```
% objdump -i
```

第二行应该是 `elf32-i386`。

```
% gcc -m32 -print-libgcc-file-name
```

这个命令应该会输出如 `/usr/lib/gcc/i486-linux-gnu/version/libgcc.a` 或 `/usr/lib/gcc/x86_64-linux-gnu/version/32/libgcc.a` 这样的东西。

如果这些命令都运行成功，说明你的工具链都已安装，你不需要去编译你自己的工具链。

如果 `gcc` 命令失败，你可能需要去安装一个开发环境。在 Ubuntu Linux 上，输入如下的命令：

```
% sudo apt-get install -y build-essential gdb
```

在 64 位的机器上，你可能需要去安装一个 32 位的支持库。链接失败的表现是有一个类似于 “`__udivdi3` not found” 和 “`__muldi3` not found” 的错误信息。在 Ubuntu Linux 上，输入如下的命令去尝试修复这个问题：

```
% sudo apt-get install gcc-multilib
```

#### 使用一个虚拟机

获得一个兼容的工具链的最容易的另外的方法是，在你的计算机上安装一个现代的 Linux 发行版。使用虚拟化平台，Linux 可以与你正常的计算环境和平共处。安装一个 Linux 虚拟机共有两步。首先，去下载一个虚拟化平台。

* [VirtualBox](http://www.oracle.com/us/technologies/virtualization/oraclevm/)（对 Mac、Linux、Windows 免费）— [下载地址](http://www.oracle.com/us/technologies/virtualization/oraclevm/)
* [VMware Player](http://www.vmware.com/products/player/)（对 Linux 和 Windows 免费，但要求注册）
* [VMware Fusion](http://www.vmware.com/products/fusion/)（可以从 IS&T 免费下载）。

VirtualBox 有点慢并且灵活性欠佳，但它免费！

虚拟化平台安装完成后，下载一个你选择的 Linux 发行版的引导磁盘镜像。

* 我们使用的是 [Ubuntu 桌面版](http://www.ubuntu.com/download/desktop)。

这将下载一个命名类似于 `ubuntu-10.04.1-desktop-i386.iso` 的文件。启动你的虚拟化平台并创建一个新（32 位）的虚拟机。使用下载的 Ubuntu 镜像作为一个引导磁盘；安装过程在不同的虚拟机上有所不同，但都很简单。就像上面一样输入 `objdump -i`，去验证你的工具是否已安装。你将在虚拟机中完成你的工作。

#### 构建你自己的编译器工具链

你需要花一些时间来设置，但是比起一个虚拟机来说，它的性能要稍好一些，并且让你工作在你熟悉的环境中（Unix/MacOS）。对于 MacOS 命令，你可以快进到文章的末尾部分去看。

##### Linux

通过将下列行添加到 `conf/env.mk` 中去使用你自己的工具链：

```
GCCPREFIX=
```

我们假设你将工具链安装到了 `/usr/local` 中。你将需要大量的空间（大约 1 GB）去编译工具。如果你空间不足，在它的 `make install` 步骤之后删除它们的目录。

下载下列包：

* <ftp://ftp.gmplib.org/pub/gmp-5.0.2/gmp-5.0.2.tar.bz2>
* <https://www.mpfr.org/mpfr-3.1.2/mpfr-3.1.2.tar.bz2>
* <http://www.multiprecision.org/downloads/mpc-0.9.tar.gz>
* <http://ftpmirror.gnu.org/binutils/binutils-2.21.1.tar.bz2>
* <http://ftpmirror.gnu.org/gcc/gcc-4.6.4/gcc-core-4.6.4.tar.bz2>
* <http://ftpmirror.gnu.org/gdb/gdb-7.3.1.tar.bz2>

（你可能也在使用这些包的最新版本。）解包并构建。安装到 `/usr/local` 中，它是我们建议的。要安装到不同的目录，如 `$PFX`，注意相应修改。如果有问题，可以看下面。

```
export PATH=$PFX/bin:$PATH
export LD_LIBRARY_PATH=$PFX/lib:$LD_LIBRARY_PATH

tar xjf gmp-5.0.2.tar.bz2
cd gmp-5.0.2
./configure --prefix=$PFX
make
make install # This step may require privilege (sudo make install)
cd ..

tar xjf mpfr-3.1.2.tar.bz2
cd mpfr-3.1.2
./configure --prefix=$PFX --with-gmp=$PFX
make
make install # This step may require privilege (sudo make install)
cd ..

tar xzf mpc-0.9.tar.gz
cd mpc-0.9
./configure --prefix=$PFX --with-gmp=$PFX --with-mpfr=$PFX
make
make install # This step may require privilege (sudo make install)
cd ..

tar xjf binutils-2.21.1.tar.bz2
cd binutils-2.21.1
./configure --prefix=$PFX --target=i386-jos-elf --disable-werror
make
make install # This step may require privilege (sudo make install)
cd ..

i386-jos-elf-objdump -i
# Should produce output like:
# BFD header file version (GNU Binutils) 2.21.1
# elf32-i386
# (header little endian, data little endian)
# i386...

tar xjf gcc-core-4.6.4.tar.bz2
cd gcc-4.6.4
mkdir build # GCC will not compile correctly unless you build in a separate directory
cd build
../configure --prefix=$PFX --with-gmp=$PFX --with-mpfr=$PFX --with-mpc=$PFX \
 --target=i386-jos-elf --disable-werror \
 --disable-libssp --disable-libmudflap --with-newlib \
 --without-headers --enable-languages=c MAKEINFO=missing
make all-gcc
make install-gcc # This step may require privilege (sudo make install-gcc)
make all-target-libgcc
make install-target-libgcc # This step may require privilege (sudo make install-target-libgcc)
cd ../..

i386-jos-elf-gcc -v
# Should produce output like:
# Using built-in specs.
# COLLECT_GCC=i386-jos-elf-gcc
# COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/i386-jos-elf/4.6.4/lto-wrapper
# Target: i386-jos-elf

tar xjf gdb-7.3.1.tar.bz2
cd gdb-7.3.1
./configure --prefix=$PFX --target=i386-jos-elf --program-prefix=i386-jos-elf- \
 --disable-werror
make all
make install # This step may require privilege (sudo make install)
cd ..
```

**Linux 排错：**

Q：我不能运行 `make install`，因为我在这台机器上没有 root 权限。

A：我们的指令假定你是安装到了 `/usr/local` 目录中。但是，在你的环境中可能并不是这样做的。如果你仅能够在你的家目录中安装代码。那么在上面的命令中，使用 `--prefix=$HOME` 去替换 `--prefix=/usr/local`。你也需要修改你的 `PATH` 和 `LD_LIBRARY_PATH` 环境变量，以通知你的 shell 这个工具的位置。例如：

```
export PATH=$HOME/bin:$PATH
export LD_LIBRARY_PATH=$HOME/lib:$LD_LIBRARY_PATH
```

在你的 `~/.bashrc` 文件中输入这些行，以便于你登入后不需要每次都输入它们。

Q：我构建失败了，错误信息是 “library not found”。

A：你需要去设置你的 `LD_LIBRARY_PATH`。环境变量必须包含 `PREFIX/lib` 目录（例如 `/usr/local/lib`）。

##### MacOS

首先从 Mac OSX 上安装开发工具开始：`xcode-select --install` 。

你可以从 homebrew 上安装 qemu 的依赖，但是不能去安装 qemu，因为我们需要安装打了 6.828 补丁的 qemu。

```
brew install $(brew deps qemu)
```

gettext 工具并不能把它已安装的二进制文件添加到路径中，因此你需要去运行：

```
PATH=${PATH}:/usr/local/opt/gettext/bin make install
```

完成后，开始安装 qemu。

### QEMU 模拟器

[QEMU](http://www.nongnu.org/qemu/) 是一个现代化的、并且速度非常快的 PC 模拟器。QEMU 的 2.3.0 版本是设置在 Athena 上的 6.828 中的 x86 机器存储中的（`add -f 6.828`）。

不幸的是，QEMU 的调试功能虽然很强大，但是有点不成熟，因此我们强烈建议你使用我们打过 6.828 补丁的版本，而不是发行版自带的版本。这个安装在 Athena 上的 QEMU 版本已经打过补丁了。构建你自己的、打 6.828 补丁的 QEMU 版本的过程如下：

1. 克隆 IAP 6.828 QEMU 的 git 仓库：`git clone https://github.com/mit-pdos/6.828-qemu.git qemu`。
2. 在 Linux 上，你或许需要安装几个库。我们成功地在 Debian/Ubuntu 16.04 上构建 6.828 版的 QEMU 需要安装下列的库：libsdl1.2-dev、libtool-bin、libglib2.0-dev、libz-dev 和 libpixman-1-dev。
3. 配置源代码（方括号中是可选参数；用你自己的真实路径替换 `PFX`）
	1. Linux：`./configure --disable-kvm --disable-werror [--prefix=PFX] [--target-list="i386-softmmu x86_64-softmmu"]`。
	2. OS X：`./configure --disable-kvm --disable-werror --disable-sdl [--prefix=PFX] [--target-list="i386-softmmu x86_64-softmmu"]`。`prefix` 参数指定安装 QEMU 的位置；如果不指定，将缺省安装 QEMU 到 `/usr/local` 目录中。`target-list` 参数将简单地简化 QEMU 所支持的架构。
4. 运行 `make && make install`。

---

via: <https://pdos.csail.mit.edu/6.828/2018/tools.html>

作者：[csail.mit](https://pdos.csail.mit.edu) 选题：[lujun9972](https://github.com/lujun9972) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

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


使用 Python 的 toolz 库开始函数式编程

Summary: toolz 库允许你操作函数，使其更容易理解，更容易测试代码。

toolz 库允许你操作函数，使其更容易理解，更容易测试代码。

# 使用 Python 的 toolz 库开始函数式编程

>
> toolz 库允许你操作函数，使其更容易理解，更容易测试代码。
>
>
>

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

在这个由两部分组成的系列文章的第二部分中，我们将继续探索如何将函数式编程方法中的好想法引入到 Python中，以实现两全其美。

在上一篇文章中，我们介绍了[不可变数据结构](/article-10222-1.html)。 这些数据结构使得我们可以编写“纯”函数，或者说是没有副作用的函数，仅仅接受一些参数并返回结果，同时保持良好的性能。

在这篇文章中，我们使用 toolz 库来构建。 这个库具有操作此类函数的函数，并且它们在纯函数中表现得特别好。 在函数式编程世界中，它们通常被称为“高阶函数”，因为它们将函数作为参数，将函数作为结果返回。

让我们从这里开始：

```
def add_one_word(words, word):
    return words.set(words.get(word, 0) + 1)
```

这个函数假设它的第一个参数是一个不可变的类似字典的对象，它返回一个新的类似字典的在相关位置递增的对象：这就是一个简单的频率计数器。

但是，只有将它应用于单词流并做*归纳*时才有用。 我们可以使用内置模块 `functools` 中的归纳器。

```
functools.reduce(function, stream, initializer)
```

我们想要一个函数，应用于流，并且能能返回频率计数。

我们首先使用 `toolz.curry` 函数：

```
add_all_words = curry(functools.reduce, add_one_word)
```

使用此版本，我们需要提供初始化程序。但是，我们不能只将 `pyrsistent.m` 函数添加到 `curry` 函数中； 因为这个顺序是错误的。

```
add_all_words_flipped = flip(add_all_words)
```

`flip` 这个高阶函数返回一个调用原始函数的函数，并且翻转参数顺序。

```
get_all_words = add_all_words_flipped(pyrsistent.m())
```

我们利用 `flip` 自动调整其参数的特性给它一个初始值：一个空字典。

现在我们可以执行 `get_all_words(word_stream)` 这个函数来获取频率字典。 但是，我们如何获得一个单词流呢？ Python 文件是按行供流的。

```
def to_words(lines):
    for line in lines:
        yield from line.split()
```

在单独测试每个函数后，我们可以将它们组合在一起：

```
words_from_file = toolz.compose(get_all_words, to_words)
```

在这种情况下，组合只是使两个函数很容易阅读：首先将文件的行流应用于 `to_words`，然后将 `get_all_words` 应用于 `to_words` 的结果。 但是文字上读起来似乎与代码执行相反。

当我们开始认真对待可组合性时，这很重要。有时可以将代码编写为一个单元序列，单独测试每个单元，最后将它们全部组合。如果有几个组合元素时，组合的顺序可能就很难理解。

`toolz` 库借用了 Unix 命令行的做法，并使用 `pipe` 作为执行相同操作的函数，但顺序相反。

```
words_from_file = toolz.pipe(to_words, get_all_words)
```

现在读起来更直观了：将输入传递到 `to_words`，并将结果传递给 `get_all_words`。 在命令行上，等效写法如下所示：

```
$ cat files | to_words | get_all_words
```

`toolz` 库允许我们操作函数，切片、分割和组合，以使我们的代码更容易理解和测试。

---

via: <https://opensource.com/article/18/10/functional-programming-python-toolz>

作者：[Moshe Zadka](https://opensource.com/users/moshez) 选题：[lujun9972](https://github.com/lujun9972) 译者：[Flowsnow](https://github.com/Flowsnow) 校对：[wxy](https://github.com/wxy)

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


17 种查看 Linux 物理内存的方法

Summary: 我们大多数人只知道很少的命令，在本文中我们试图包含所有可能的命令。

我们大多数人只知道很少的命令，在本文中我们试图包含所有可能的命令。

# 17 种查看 Linux 物理内存的方法

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

大多数系统管理员在遇到性能问题时会检查 CPU 和内存利用率。Linux 中有许多实用程序可以用于检查物理内存。这些命令有助于我们检查系统中存在的物理内存，还允许用户检查各种方面的内存利用率。

我们大多数人只知道很少的命令，在本文中我们试图包含所有可能的命令。

你可能会想，为什么我想知道所有这些命令，而不是知道一些特定的和例行的命令呢。

不要觉得没用或对此有负面的看法，因为每个人都有不同的需求和看法，所以，对于那些在寻找其它目的的人，这对于他们非常有帮助。

### 什么是 RAM

计算机内存是能够临时或永久存储信息的物理设备。RAM 代表随机存取存储器，它是一种易失性存储器，用于存储操作系统，软件和硬件使用的信息。

有两种类型的内存可供选择：

* 主存
* 辅助内存

主存是计算机的主存储器。CPU 可以直接读取或写入此内存。它固定在电脑的主板上。

* **RAM**：随机存取存储器是临时存储。关闭计算机后，此信息将消失。
* **ROM**： 只读存储器是永久存储，即使系统关闭也能保存数据。

### 方法-1：使用 free 命令

`free` 显示系统中空闲和已用的物理内存和交换内存的总量，以及内核使用的缓冲区和缓存。它通过解析 `/proc/meminfo` 来收集信息。

**建议阅读：** [free – 在 Linux 系统中检查内存使用情况统计（空闲和已用）的标准命令](https://www.2daygeek.com/free-command-to-check-memory-usage-statistics-in-linux/)

```
$ free -m
              total        used        free      shared  buff/cache   available
Mem:           1993        1681          82          81         228         153
Swap:         12689        1213       11475

$ free -g
              total        used        free      shared  buff/cache   available
Mem:              1           1           0           0           0           0
Swap:            12           1          11
```

### 方法-2：使用 /proc/meminfo 文件

`/proc/meminfo` 是一个虚拟文本文件，它包含有关系统 RAM 使用情况的大量有价值的信息。

它报告系统上的空闲和已用内存（物理和交换）的数量。

```
$ grep MemTotal /proc/meminfo
MemTotal:        2041396 kB

$ grep MemTotal /proc/meminfo | awk '{print $2 / 1024}'
1993.55

$ grep MemTotal /proc/meminfo | awk '{print $2 / 1024 / 1024}'
1.94683
```

### 方法-3：使用 top 命令

`top` 命令是 Linux 中监视实时系统进程的基本命令之一。它显示系统信息和运行的进程信息，如正常运行时间、平均负载、正在运行的任务、登录的用户数、CPU 数量和 CPU 利用率，以及内存和交换信息。运行 `top` 命令，然后按下 `E` 来使内存利用率以 MB 为单位显示。

**建议阅读：** [TOP 命令示例监视服务器性能](https://www.2daygeek.com/top-command-examples-to-monitor-server-performance/)

```
$ top

top - 14:38:36 up  1:59,  1 user,  load average: 1.83, 1.60, 1.52
Tasks: 223 total,   2 running, 221 sleeping,   0 stopped,   0 zombie
%Cpu(s): 48.6 us, 11.2 sy,  0.0 ni, 39.3 id,  0.3 wa,  0.0 hi,  0.5 si,  0.0 st
MiB Mem : 1993.551 total,   94.184 free, 1647.367 used,  252.000 buff/cache
MiB Swap: 12689.58+total, 11196.83+free, 1492.750 used.  306.465 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 9908 daygeek   20   0 2971440 649324  39700 S  55.8 31.8  11:45.74 Web Content
21942 daygeek   20   0 2013760 308700  69272 S  35.0 15.1   4:13.75 Web Content
 4782 daygeek   20   0 3687116 227336  39156 R  14.5 11.1  16:47.45 gnome-shell
```

### 方法-4：使用 vmstat 命令

`vmstat` 是一个漂亮的标准工具，它报告 Linux 系统的虚拟内存统计信息。`vmstat` 报告有关进程、内存、分页、块 IO、陷阱和 CPU 活动的信息。它有助于 Linux 管理员在故障检修时识别系统瓶颈。

**建议阅读：** [vmstat – 一个报告虚拟内存统计信息的标准且漂亮的工具](https://www.2daygeek.com/linux-vmstat-command-examples-tool-report-virtual-memory-statistics/)

```
$ vmstat -s | grep "total memory"
      2041396 K total memory

$ vmstat -s -S M | egrep -ie 'total memory'
         1993 M total memory

$ vmstat -s | awk '{print $1 / 1024 / 1024}' | head -1
1.94683

```

### 方法-5：使用 nmon 命令

`nmon` 是另一个很棒的工具，用于在 Linux 终端上监视各种系统资源，如 CPU、内存、网络、磁盘、文件系统、NFS、top 进程、Power 的微分区和资源（Linux 版本和处理器）。

只需按下 `m` 键，即可查看内存利用率统计数据（缓存、活动、非活动、缓冲、空闲，以 MB 和百分比为单位）。

**建议阅读：** [nmon – Linux 中一个监视系统资源的漂亮的工具](https://www.2daygeek.com/nmon-system-performance-monitor-system-resources-on-linux/)

```
┌nmon─14g──────[H for help]───Hostname=2daygeek──Refresh= 2secs ───07:24.44─────────────────┐
│ Memory Stats ─────────────────────────────────────────────────────────────────────────────│
│                RAM     High      Low     Swap    Page Size=4 KB                           │
│ Total MB     32079.5     -0.0     -0.0  20479.0                                           │
│ Free  MB     11205.0     -0.0     -0.0  20479.0                                           │
│ Free Percent    34.9%   100.0%   100.0%   100.0%                                          │
│             MB                  MB                  MB                                    │
│                      Cached= 19763.4     Active=  9617.7                                  │
│ Buffers=   172.5 Swapcached=     0.0  Inactive = 10339.6                                  │
│ Dirty  =     0.0 Writeback =     0.0  Mapped   =    11.0                                  │
│ Slab   =   636.6 Commit_AS =   118.2 PageTables=     3.5                                  │
│───────────────────────────────────────────────────────────────────────────────────────────│
│                                                                                │
│                                                                                │
│                                                                                │
│                                                                                │
│                                                                                │
│                                                                                │
└───────────────────────────────────────────────────────────────────────────────────────────┘
```

### 方法-6：使用 dmidecode 命令

`dmidecode` 是一个读取计算机 DMI 表内容的工具，它以人类可读的格式显示系统硬件信息。（DMI 意即桌面管理接口，也有人说是读取的是 SMBIOS —— 系统管理 BIOS）

此表包含系统硬件组件的描述，以及其它有用信息，如序列号、制造商信息、发布日期和 BIOS 修改等。

**建议阅读：** [Dmidecode – 获取 Linux 系统硬件信息的简便方法](https://www.2daygeek.com/dmidecode-get-print-display-check-linux-system-hardware-information/)

```
# dmidecode -t memory | grep  Size:
        Size: 8192 MB
        Size: No Module Installed
        Size: No Module Installed
        Size: 8192 MB
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: 8192 MB
        Size: No Module Installed
        Size: No Module Installed
        Size: 8192 MB
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
        Size: No Module Installed
```

只打印已安装的 RAM 模块。

```
# dmidecode -t memory | grep  Size: | grep -v "No Module Installed"
        Size: 8192 MB
        Size: 8192 MB
        Size: 8192 MB
        Size: 8192 MB
```

汇总所有已安装的 RAM 模块。

```
# dmidecode -t memory | grep  Size: | grep -v "No Module Installed" | awk '{sum+=$2}END{print sum}'
32768
```

### 方法-7：使用 hwinfo 命令

`hwinfo` 意即硬件信息，它是另一个很棒的实用工具，用于探测系统中存在的硬件，并以人类可读的格式显示有关各种硬件组件的详细信息。

它报告有关 CPU、RAM、键盘、鼠标、图形卡、声音、存储、网络接口、磁盘、分区、BIOS 和网桥等的信息。

**建议阅读：** [hwinfo（硬件信息）– 一个在 Linux 系统上检测系统硬件信息的好工具](https://www.2daygeek.com/hwinfo-check-display-detect-system-hardware-information-linux/)

```
$ hwinfo --memory
01: None 00.0: 10102 Main Memory
  [Created at memory.74]
  Unique ID: rdCR.CxwsZFjVASF
  Hardware Class: memory
  Model: "Main Memory"
  Memory Range: 0x00000000-0x7a4abfff (rw)
  Memory Size: 1 GB + 896 MB
  Config Status: cfg=new, avail=yes, need=no, active=unknown
```

### 方法-8：使用 lshw 命令

`lshw`（代表 Hardware Lister）是一个小巧的工具，可以生成机器上各种硬件组件的详细报告，如内存配置、固件版本、主板配置、CPU 版本和速度、缓存配置、USB、网卡、显卡、多媒体、打印机、总线速度等。

它通过读取 `/proc` 目录和 DMI 表中的各种文件来生成硬件信息。

**建议阅读：** [LSHW (Hardware Lister) – 一个在 Linux 上获取硬件信息的好工具](https://www.2daygeek.com/lshw-find-check-system-hardware-information-details-linux/)

```
$ sudo lshw -short -class memory
[sudo] password for daygeek:
H/W path      Device       Class       Description
==================================================
/0/0                       memory      128KiB BIOS
/0/1                       memory      1993MiB System memory
```

### 方法-9：使用 inxi 命令

`inxi` 是一个很棒的工具，它可以检查 Linux 上的硬件信息，并提供了大量的选项来获取 Linux 系统上的所有硬件信息，这些特性是我在 Linux 上的其它工具中从未发现的。它是从 locsmif 编写的古老的但至今看来都异常灵活的 infobash 演化而来的。

`inxi` 是一个脚本，它可以快速显示系统硬件、CPU、驱动程序、Xorg、桌面、内核、GCC 版本、进程、RAM 使用情况以及各种其它有用的信息，还可以用于论坛技术支持和调试工具。

**建议阅读：** [inxi – 一个检查 Linux 上硬件信息的好工具](https://www.2daygeek.com/inxi-system-hardware-information-on-linux/)

```
$ inxi -F | grep "Memory"
Info:      Processes: 234 Uptime: 3:10 Memory: 1497.3/1993.6MB Client: Shell (bash) inxi: 2.3.37
```

### 方法-10：使用 screenfetch 命令

`screenfetch` 是一个 bash 脚本。它将自动检测你的发行版，并在右侧显示该发行版标识的 ASCII 艺术版本和一些有价值的信息。

**建议阅读：** [ScreenFetch – 以 ASCII 艺术标志在终端显示 Linux 系统信息](https://www.2daygeek.com/screenfetch-display-linux-systems-information-ascii-distribution-logo-terminal/)

```
$ screenfetch
                          ./+o+-       daygeek@ubuntu
                  yyyyy- -yyyyyy+      OS: Ubuntu 17.10 artful
               ://+//////-yyyyyyo      Kernel: x86_64 Linux 4.13.0-37-generic
           .++ .:/++++++/-.+sss/`      Uptime: 44m
         .:++o:  /++++++++/:--:/-      Packages: 1831
        o:+o+:++.`..`` `.-/oo+++++/     Shell: bash 4.4.12
       .:+o:+o/.          `+sssoo+/    Resolution: 1920x955
  .++/+:+oo+o:`             /sssooo.   DE: GNOME
 /+++//+:`oo+o               /::--:.   WM: GNOME Shell
 \+/+o+++`o++o               ++////.   WM Theme: Adwaita
  .++.o+++oo+:`             /dddhhh.   GTK Theme: Azure [GTK2/3]
       .+.o+oo:.          `oddhhhh+    Icon Theme: Papirus-Dark
        \+.++o+o``-````.:ohdhhhhh+     Font: Ubuntu 11
         `:o+++ `ohhhhhhhhyo++os:      CPU: Intel Core i7-6700HQ @ 2x 2.592GHz
           .o:`.syhhhhhhh/.oo++o`      GPU: llvmpipe (LLVM 5.0, 256 bits)
               /osyyyyyyo++ooo+++/     RAM: 1521MiB / 1993MiB
                   ````` +oo+++o\:
                          `oo++.
```

### 方法-11：使用 neofetch 命令

`neofetch` 是一个跨平台且易于使用的命令行（CLI）脚本，它收集你的 Linux 系统信息，并将其作为一张图片显示在终端上，也可以是你的发行版徽标，或者是你选择的任何 ascii 艺术。

**建议阅读：** [Neofetch – 以 ASCII 分发标志来显示 Linux 系统信息](https://www.2daygeek.com/neofetch-display-linux-systems-information-ascii-distribution-logo-terminal/)

```
$ neofetch
            .-/+oossssoo+/-.               daygeek@ubuntu
        `:+ssssssssssssssssss+:`           --------------
      -+ssssssssssssssssssyyssss+-         OS: Ubuntu 17.10 x86_64
    .ossssssssssssssssssdMMMNysssso.       Host: VirtualBox 1.2
   /ssssssssssshdmmNNmmyNMMMMhssssss/      Kernel: 4.13.0-37-generic
  +ssssssssshmydMMMMMMMNddddyssssssss+     Uptime: 47 mins
 /sssssssshNMMMyhhyyyyhmNMMMNhssssssss/    Packages: 1832
.ssssssssdMMMNhsssssssssshNMMMdssssssss.   Shell: bash 4.4.12
+sssshhhyNMMNyssssssssssssyNMMMysssssss+   Resolution: 1920x955
ossyNMMMNyMMhsssssssssssssshmmmhssssssso   DE: ubuntu:GNOME
ossyNMMMNyMMhsssssssssssssshmmmhssssssso   WM: GNOME Shell
+sssshhhyNMMNyssssssssssssyNMMMysssssss+   WM Theme: Adwaita
.ssssssssdMMMNhsssssssssshNMMMdssssssss.   Theme: Azure [GTK3]
 /sssssssshNMMMyhhyyyyhdNMMMNhssssssss/    Icons: Papirus-Dark [GTK3]
  +sssssssssdmydMMMMMMMMddddyssssssss+     Terminal: gnome-terminal
   /ssssssssssshdmNNNNmyNMMMMhssssss/      CPU: Intel i7-6700HQ (2) @ 2.591GHz
    .ossssssssssssssssssdMMMNysssso.       GPU: VirtualBox Graphics Adapter
      -+sssssssssssssssssyyyssss+-         Memory: 1620MiB / 1993MiB
        `:+ssssssssssssssssss+:`
            .-/+oossssoo+/-.
```

### 方法-12：使用 dmesg 命令

`dmesg`（代表显示消息或驱动消息）是大多数类 Unix 操作系统上的命令，用于打印内核的消息缓冲区。

```
$ dmesg | grep "Memory"
[    0.000000] Memory: 1985916K/2096696K available (12300K kernel code, 2482K rwdata, 4000K rodata, 2372K init, 2368K bss, 110780K reserved, 0K cma-reserved)
[    0.012044] x86/mm: Memory block size: 128MB
```

### 方法-13：使用 atop 命令

`atop` 是一个用于 Linux 的 ASCII 全屏系统性能监视工具，它能报告所有服务器进程的活动（即使进程在间隔期间已经完成）。

它记录系统和进程活动以进行长期分析（默认情况下，日志文件保存 28 天），通过使用颜色等来突出显示过载的系统资源。它结合可选的内核模块 netatop 显示每个进程或线程的网络活动。

**建议阅读：** [Atop – 实时监控系统性能，资源，进程和检查资源利用历史](https://www.2daygeek.com/atop-system-process-performance-monitoring-tool/)

```
$ atop -m

ATOP - ubuntu                                                   2018/03/31  19:34:08                                                   -------------                                                    10s elapsed
PRC | sys    0.47s  | user   2.75s  |               |              |  #proc    219 |  #trun      1 | #tslpi   802  | #tslpu     0  | #zombie    0  | clones     7 |               |               |  #exit      4 |
CPU | sys       7%  | user     22%  | irq       0%  |              |               |  idle    170% | wait      0%  |               | steal     0%  | guest     0% |               |  curf 2.59GHz |  curscal   ?% |
cpu | sys       3%  | user     11%  | irq       0%  |              |               |  idle     85% | cpu001 w  0%  |               | steal     0%  | guest     0% |               |  curf 2.59GHz |  curscal   ?% |
cpu | sys       4%  | user     11%  | irq       0%  |              |               |  idle     85% | cpu000 w  0%  |               | steal     0%  | guest     0% |               |  curf 2.59GHz |  curscal   ?% |
CPL | avg1    1.98  |               | avg5    3.56  | avg15   3.20 |               |               | csw    14894  |               | intr    6610  |              |               |  numcpu     2 |               |
MEM | tot     1.9G  | free  101.7M  | cache 244.2M  | dirty   0.2M |  buff    6.9M |  slab   92.9M | slrec  35.6M  | shmem  97.8M  | shrss  21.0M  | shswp   3.2M |  vmbal   0.0M |  hptot   0.0M |  hpuse   0.0M |
SWP | tot    12.4G  | free   11.6G  |               |              |               |               |               |               |               |              |  vmcom   7.9G |               |  vmlim  13.4G |
PAG | scan       0  | steal      0  |               | stall      0 |               |               |               |               |               |              |  swin       3 |               |  swout      0 |
DSK |          sda  | busy      0%  |               | read     114 |  write     37 |  KiB/r     21 | KiB/w      6  |               | MBr/s    0.2  | MBw/s    0.0 |  avq     6.50 |               |  avio 0.26 ms |
NET | transport     | tcpi      11  | tcpo      17  | udpi       4 |  udpo       8 |  tcpao      3 | tcppo      0  |               | tcprs      3  | tcpie      0 |  tcpor      0 |  udpnp      0 |  udpie      0 |
NET | network       | ipi       20  |               | ipo       33 |  ipfrw      0 |  deliv     20 |               |               |               |              |  icmpi      5 |               |  icmpo      0 |
NET | enp0s3    0%  | pcki      11  | pcko      28  | sp 1000 Mbps |  si    1 Kbps |  so    1 Kbps |               | coll       0  | mlti       0  | erri       0 |  erro       0 |  drpi       0 |  drpo       0 |
NET | lo      ----  | pcki       9  | pcko       9  | sp    0 Mbps |  si    0 Kbps |  so    0 Kbps |               | coll       0  | mlti       0  | erri       0 |  erro       0 |  drpi       0 |  drpo       0 |

   PID        TID     MINFLT      MAJFLT     VSTEXT      VSLIBS      VDATA      VSTACK      VSIZE       RSIZE      PSIZE       VGROW      RGROW      SWAPSZ     RUID          EUID          MEM      CMD        1/1
  2536          -        941           0       188K      127.3M     551.2M        144K       2.3G      281.2M         0K          0K       344K       6556K     daygeek       daygeek       14%      Web Content
  2464          -         75           0       188K      187.7M     680.6M        132K       2.3G      226.6M         0K          0K       212K      42088K     daygeek       daygeek       11%      firefox
  2039          -       4199           6        16K      163.6M     423.0M        132K       3.5G      220.2M         0K          0K      2936K      109.6M     daygeek       daygeek       11%      gnome-shell
 10822          -          1           0         4K      16680K     377.0M        132K       3.4G      193.4M         0K          0K         0K          0K     root          root          10%      java
```

### 方法-14：使用 htop 命令

`htop` 是由 Hisham 用 ncurses 库开发的用于 Linux 的交互式进程查看器。与 `top` 命令相比，`htop` 有许多特性和选项。

**建议阅读：** [使用 Htop 命令监视系统资源](https://www.2daygeek.com/htop-command-examples-to-monitor-system-resources/)

```
$ htop

  1  [|||||||||||||                                                                             13.0%]   Tasks: 152, 587 thr; 1 running
  2  [|||||||||||||||||||||||||                                                                 25.0%]   Load average: 0.91 2.03 2.66
  Mem[||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||1.66G/1.95G]   Uptime: 01:14:53
  Swp[||||||                                                                               782M/12.4G]

  PID USER      PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
 2039 daygeek    20   0 3541M  214M 46728 S 36.6 10.8 22:36.77 /usr/bin/gnome-shell
 2045 daygeek    20   0 3541M  214M 46728 S 10.3 10.8  3:02.92 /usr/bin/gnome-shell
 2046 daygeek    20   0 3541M  214M 46728 S  8.3 10.8  3:04.96 /usr/bin/gnome-shell
 6080 daygeek    20   0  807M 37228 24352 S  2.1  1.8  0:11.99 /usr/lib/gnome-terminal/gnome-terminal-server
 2880 daygeek    20   0 2205M  164M 17048 S  2.1  8.3  7:16.50 /usr/lib/firefox/firefox -contentproc -childID 6 -isForBrowser -intPrefs 6:50|7:-1|19:0|34:1000|42:20|43:5|44:10|51:0|57:128|58:10000|63:0|65:400|66
 6125 daygeek    20   0 1916M  159M 92352 S  2.1  8.0  2:09.14 /usr/lib/firefox/firefox -contentproc -childID 7 -isForBrowser -intPrefs 6:50|7:-1|19:0|34:1000|42:20|43:5|44:10|51:0|57:128|58:10000|63:0|65:400|66
 2536 daygeek    20   0 2335M  243M 26792 S  2.1 12.2  6:25.77 /usr/lib/firefox/firefox -contentproc -childID 1 -isForBrowser -intPrefs 6:50|7:-1|19:0|34:1000|42:20|43:5|44:10|51:0|57:128|58:10000|63:0|65:400|66
 2653 daygeek    20   0 2237M  185M 20788 S  1.4  9.3  3:01.76 /usr/lib/firefox/firefox -contentproc -childID 4 -isForBrowser -intPrefs 6:50|7:-1|19:0|34:1000|42:20|43:5|44:10|51:0|57:128|58:10000|63:0|65:400|66
```

### 方法-15：使用 corefreq 实用程序

CoreFreq 是为 Intel 64 位处理器设计的 CPU 监控软件，支持的架构有 Atom、Core2、Nehalem、SandyBridge 和 superior，AMD 家族 0F。

CoreFreq 提供了一个框架来以高精确度检索 CPU 数据。

**建议阅读：** [CoreFreq – 一个用于 Linux 系统的强大的 CPU 监控工具](https://www.2daygeek.com/corefreq-linux-cpu-monitoring-tool/)

```
$ ./corefreq-cli -k
Linux:
|- Release                                                   [4.13.0-37-generic]
|- Version                          [#42-Ubuntu SMP Wed Mar 7 14:13:23 UTC 2018]
|- Machine                                                              [x86_64]
Memory:
|- Total RAM                                                          2041396 KB
|- Shared RAM                                                           99620 KB
|- Free RAM                                                            108428 KB
|- Buffer RAM                                                            8108 KB
|- Total High                                                               0 KB
|- Free High                                                                0 KB
```

### 方法-16：使用 glances 命令

Glances 是用 Python 编写的跨平台基于 curses（LCTT 译注：curses 是一个 Linux/Unix 下的图形函数库）的系统监控工具。我们可以说它一应俱全，就像在最小的空间含有最大的信息。它使用 psutil 库从系统中获取信息。

Glances 可以监视 CPU、内存、负载、进程列表、网络接口、磁盘 I/O、Raid、传感器、文件系统（和文件夹）、Docker、监视器、警报、系统信息、正常运行时间、快速预览（CPU、内存、负载）等。

**建议阅读：** [Glances (一应俱全)– 一个 Linux 的高级的实时系 统性能监控工具](https://www.2daygeek.com/install-glances-advanced-real-time-linux-system-performance-monitoring-tool-on-centos-fedora-ubuntu-debian-opensuse-arch-linux/)

```
$ glances

ubuntu (Ubuntu 17.10 64bit / Linux 4.13.0-37-generic) - IP 192.168.1.6/24                                                                                Uptime: 1:08:40

CPU  [||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||        90.6%]   CPU -    90.6%  nice:     0.0%  ctx_sw:    4K      MEM \   78.4%  active:     942M      SWAP -    5.9%      LOAD    2-core
MEM  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||                 78.0%]   user:    55.1%  irq:      0.0%  inter:   1797      total:  1.95G  inactive:   562M      total:   12.4G      1 min:    4.35
SWAP [||||                                                                       5.9%]   system:  32.4%  iowait:   1.8%  sw_int:   897      used:   1.53G  buffers:   14.8M      used:     749M      5 min:    4.38
                                                                                idle:     7.6%  steal:    0.0%                     free:    431M  cached:     273M      free:    11.7G      15 min:   3.38

NETWORK     Rx/s   Tx/s   TASKS 211 (735 thr), 4 run, 207 slp, 0 oth sorted automatically by memory_percent, flat view
docker0       0b   232b
enp0s3      12Kb    4Kb   Systemd          7    Services loaded: 197 active: 196 failed: 1
lo          616b   616b
_h478e48e     0b   232b     CPU%  MEM%  VIRT   RES   PID USER        NI S     TIME+   R/s   W/s Command
                            63.8  18.9 2.33G  377M  2536 daygeek      0 R   5:57.78     0     0 /usr/lib/firefox/firefox -contentproc -childID 1 -isForBrowser -intPrefs 6:50|7:-1|19:0|34:1000|42:20|43:5|44:10|51
DefaultGateway     83ms     78.5  10.9 3.46G  217M  2039 daygeek      0 S  21:07.46     0     0 /usr/bin/gnome-shell
                             8.5  10.1 2.32G  201M  2464 daygeek      0 S   8:45.69     0     0 /usr/lib/firefox/firefox -new-window
DISK I/O     R/s    W/s      1.1   8.5 2.19G  170M  2653 daygeek      0 S   2:56.29     0     0 /usr/lib/firefox/firefox -contentproc -childID 4 -isForBrowser -intPrefs 6:50|7:-1|19:0|34:1000|42:20|43:5|44:10|51
dm-0           0      0      1.7   7.2 2.15G  143M  2880 daygeek      0 S   7:10.46     0     0 /usr/lib/firefox/firefox -contentproc -childID 6 -isForBrowser -intPrefs 6:50|7:-1|19:0|34:1000|42:20|43:5|44:10|51
sda1       9.46M    12K      0.0   4.9 1.78G 97.2M  6125 daygeek      0 S   1:36.57     0     0 /usr/lib/firefox/firefox -contentproc -childID 7 -isForBrowser -intPrefs 6:50|7:-1|19:0|34:1000|42:20|43:5|44:10|51
```

### 方法-17 : 使用 Gnome 系统监视器

Gnome 系统监视器是一个管理正在运行的进程和监视系统资源的工具。它向你显示正在运行的程序以及耗费的处理器时间，内存和磁盘空间。

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

---

via: <https://www.2daygeek.com/easy-ways-to-check-size-of-physical-memory-ram-in-linux/>

作者：[Ramya Nuvvula](https://www.2daygeek.com/author/ramya/) 译者：[MjSeven](https://github.com/MjSeven) 校对：[wxy](https://github.com/wxy)

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


我爱开源的 7 个理由

Summary: 成为开源社区的一员绝对是一个明智之举，原因有很多。

成为开源社区的一员绝对是一个明智之举，原因有很多。

# 我爱开源的 7 个理由

>
> 成为开源社区的一员绝对是一个明智之举，原因有很多。
>
>
>

这就是我为什么包括晚上和周末在内花费非常多的时间待在 [GitHub](https://github.com/ColinEberhardt/) 上，成为开源社区的一个活跃成员。

我参加过各种规模的项目，从个人项目到几个人的协作项目，乃至有数百位贡献者的项目，每一个项目都让我有新的受益。

也就是说，这里有七个原因让我为开源做出贡献：

* **它让我的技能与时俱进。** 在咨询公司的管理职位工作，有时我觉得自己与创建软件的实际过程越来越远。参与开源项目使我可以重新回到我最热爱的编程之中。也使我能够体验新技术，学习新技术和语言，并且使我不被酷酷的孩子们落下。
* **它教我如何与人打交道。** 与一群素未谋面的人合作开源项目在与人交往方面能够教会你很多。你很快会发现每个人有他们自己的压力，他们自己的义务，以及不同的时间表。学习如何与一群陌生人合作是一种很好的生活技能。
* **它使我成为一个更好的沟通者。** 开源项目的维护者的时间有限。你很快就知道，要成功地贡献，你必须能够清楚、简明地表达你所做的改变、添加或修复，最重要的是，你为什么要这么做。
* **它使我成为一个更好的开发者。** 没有什么能像成百上千的其他开发者依赖你的代码一样 —— 它敦促你更加专注软件设计、测试和文档。
* **它使我的造物变得更好。** 可能开源背后最强大的观念是它允许你驾驭一个由有创造力、有智慧、有知识的个人组成的全球网络。我知道我自己一个人的能力是有限的，我不可能什么都知道，但与开源社区的合作有助于我改进我的创作。
* **它告诉我小事物的价值。** 如果一个项目的文档不清楚或不完整，我会毫不犹豫地把它做得更好。一个小小的更新或修复可能只节省开发人员几分钟的时间，但是随着用户数量的增加，您一个小小的更改可能产生巨大的价值。
* **它使我更好的营销。** 好的，这是一个奇怪的例子。有这么多伟大的开源项目在那里，感觉像一场争夺关注的拼搏。从事于开源让我学到了很多营销的价值。这不是关于讲述或创建一个华丽的网站。而是关于如何清楚地传达你所创造的，它是如何使用的，以及它带来的好处。

我可以继续讨论开源是如何帮助你发展伙伴、关系和朋友的，不过你应该都知道了。有许多原因让我乐于成为开源社区的一员。

你可能想知道这些如何用于大型金融服务机构的 IT 战略。简单来说：谁不想要一个擅长与人交流和工作，具有尖端的技能，并能够推销他们的成果的开发团队呢？

---

via: <https://opensource.com/article/18/11/reasons-love-open-source>

作者：[Colin Eberhardt](https://opensource.com/users/colineberhardt) 选题：[lujun9972](https://github.com/lujun9972) 译者：[ChiZelin](https://github.com/ChiZelin) 校对：[wxy](https://github.com/wxy)

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


Terminalizer：一个记录您终端活动并且生成 Gif 图像的工具

# Terminalizer：一个记录您终端活动并且生成 Gif 图像的工具

今天我们要讨论一个广为人知的主题，我们也围绕这个主题写过许多的文章，因此我不会针对这个如何记录终端会话流程给出太多具体的资料。

我们可以使用脚本命令来记录 Linux 的终端会话，这也是大家公认的一种办法。不过今天我们将来介绍一个能起到相同作用的工具 — Terminalizer。

这个工具可以帮助我们记录用户的终端活动，以帮助我们从输出的文件中找到有用的信息。

### 什么是 Terminlizer

用户可以用 Terminlizer 记录他们的终端活动并且生成一个 Gif 图像。它是一个允许高度定制的 CLI 工具。用户可以在网络播放器、在线播放器上用链接分享他们记录下的文件。

**推荐阅读：**

* [Script – 一个记录您终端对话的简单工具](https://www.2daygeek.com/script-command-record-save-your-terminal-session-activity-linux/)
* [在 Linux 上自动记录/捕捉所有用户的终端对话](https://www.2daygeek.com/automatically-record-all-users-terminal-sessions-activity-linux-script-command/)
* [Teleconsole – 一个能立即与任何人分享您终端对话的工具](https://www.2daygeek.com/teleconsole-share-terminal-session-instantly-to-anyone-in-seconds/)
* [tmate – 立即与任何人分享您的终端对话](https://www.2daygeek.com/tmate-instantly-share-your-terminal-session-to-anyone-in-seconds/)
* [Peek – 在 Linux 里制造一个 Gif 记录器](https://www.2daygeek.com/peek-create-animated-gif-screen-recorder-capture-arch-linux-mint-fedora-ubuntu/)
* [Kgif – 一个能生成 Gif 图片，以记录窗口活动的简单 Shell 脚本](https://www.2daygeek.com/kgif-create-animated-gif-file-active-window-screen-recorder-capture-arch-linux-mint-fedora-ubuntu-debian-opensuse-centos/)
* [Gifine – 在 Ubuntu/Debian 里快速制造一个 Gif 视频](https://www.2daygeek.com/gifine-create-animated-gif-vedio-recorder-linux-mint-debian-ubuntu/)

目前没有发行版拥有官方软件包来安装此实用程序，不过我们可以用 Node.js 来安装它。

### 如何在 Linux 上安装 Node.js

安装 Node.js 有许多种方法。我们在这里将会教您一个常用的方法。

在 Ubuntu/LinuxMint 上可以使用 [APT-GET 命令](https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/) 或者 [APT 命令](https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/) 来安装 Node.js。

```
$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
$ sudo apt-get install -y nodejs
```

在 Debian 上使用 [APT-GET 命令](https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/) 或者 [APT 命令](https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/) 来安装 Node.js。

```
# curl -sL https://deb.nodesource.com/setup_8.x | bash -
# apt-get install -y nodejs
```

在 RHEL/CentOS 上，使用 [YUM 命令](https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/) 来安装。

```
$ sudo curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -
$ sudo yum install epel-release
$ sudo yum -y install nodejs
```

在 Fedora 上，用 [DNF 命令](https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/) 来安装 tmux。

```
$ sudo dnf install nodejs
```

在 Arch Linux 上，用 [Pacman 命令](https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/) 来安装 tmux。

```
$ sudo pacman -S nodejs npm
```

在 openSUSE 上，用 [Zypper Command](https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/) 来安装 tmux。

```
$ sudo zypper in nodejs6
```

### 如何安装 Terminalizer

您已经安装了 Node.js 这个先决软件包，现在是时候在您的系统上安装 Terminalizer 了。简单执行如下的 `npm` 命令即可安装。

```
$ sudo npm install -g terminalizer
```

### 如何使用 Terminalizer

您只需要执行如下的命令，即可使用 Terminalizer 记录您的终端会话活动。您可以敲击 `CTRL+D` 来结束并且保存记录。

```
# terminalizer record 2g-session

defaultConfigPath
The recording session is started
Press CTRL+D to exit and save the recording
```

这将会将您记录的会话保存成一个 YAML 文件，在这个例子里，我的文件名将会是 2g-session-activity.yml。

```
# logout
Successfully Recorded
The recording data is saved into the file:
/home/daygeek/2g-session.yml
You can edit the file and even change the configurations.
```

### 如何播放记录下来的文件

使用以下命令来播放您记录的 YAML 文件。在以下操作中，请确保您已经用了您的文件名来替换 “2g-session”。

```
# terminalizer play 2g-session
```

将记录的文件渲染成 Gif 图像。

```
# terminalizer render 2g-session
```

注意： 以下的两个命令在此版本尚且不可用，或许在下一版本这两个命令将会付诸使用。

如果您想要将记录的文件分享给其他人，您可以将您的文件上传到在线播放器，并且将链接分享给对方。

```
terminalizer share 2g-session
```

为记录的文件生成一个网络播放器。

```
# terminalizer generate 2g-session
```

---

via: <https://www.2daygeek.com/terminalizer-a-tool-to-record-your-terminal-and-generate-animated-gif-images/>

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

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


使用 Calcurse 在 Linux 命令行中组织任务

Summary: 使用 Calcurse 了解你的日历和待办事项列表。

使用 Calcurse 了解你的日历和待办事项列表。

# 使用 Calcurse 在 Linux 命令行中组织任务

>
> 使用 Calcurse 了解你的日历和待办事项列表。
>
>
>

你是否需要复杂、功能丰富的图形或 Web 程序才能保持井井有条？我不这么认为。合适的命令行工具可以完成工作并且做得很好。

当然，说出命令行这个词可能会让一些 Linux 用户感到害怕。对他们来说，命令行是未知领域。

使用 [Calcurse](http://www.calcurse.org/) 可以轻松地在命令行中进行组织任务。Calcurse 在基于文本的界面里带来了图形化外观。你可以得到简单、结合易用性的命令行和导航。

让我们仔细看看 Calcurse，它是在 BSD 许可证下开源的。

### 获取软件

如果你喜欢编译代码（我通常不喜欢），你可以从 [Calcurse 网站](http://www.calcurse.org/)获取源码。否则，根据你的 Linux 发行版获取[二进制安装程序](http://www.calcurse.org/downloads/#packages)。你甚至可以从 Linux 发行版的软件包管理器中获取 Calcurse。检查一下不会有错的。

编译或安装 Calcurse 后（两者都不用太长时间），你就可以开始使用了。

### 使用 Calcurse

打开终端并输入 `calcurse`。

Calcurse 的界面由三个面板组成：

* <ruby> 预约 <rt>  Appointments </rt></ruby>（屏幕左侧） \* <ruby> 日历 <rt>  Calendar </rt></ruby>（右上角） \* <ruby> 待办事项清单 <rt>  TODO </rt></ruby>（右下角）

按键盘上的 `Tab` 键在面板之间移动。要在面板添加新项目，请按下 `a`。Calcurse 将指导你完成添加项目所需的操作。

一个有趣的地方地是预约和日历面板配合工作。你选中日历面板并添加一个预约。在那里，你选择一个预约的日期。完成后，你回到预约面板，你就看到了。

按下 `a` 设置开始时间、持续时间（以分钟为单位）和预约说明。开始时间和持续时间是可选的。Calcurse 在它们到期的那天显示预约。

一天的预约看起来像这样：

待办事项列表独立运作。选中待办面板并（再次）按下 `a`。输入任务的描述，然后设置优先级（1 表示最高，9 表示最低）。Calcurse 会在待办事项面板中列出未完成的任务。

如果你的任务有很长的描述，那么 Calcurse 会截断它。你可以使用键盘上的向上或向下箭头键浏览任务，然后按下 `v` 查看描述。

Calcurse 将其信息以文本形式保存在你的主目录下名为 `.calcurse` 的隐藏文件夹中，例如 `/home/scott/.calcurse`。如果 Calcurse 停止工作，那也很容易找到你的信息。

### 其他有用的功能

Calcurse 其他的功能包括设置重复预约的功能。要执行此操作，找出要重复的预约，然后在预约面板中按下 `r`。系统会要求你设置频率（例如，每天或每周）以及你希望重复预约的时间。

你还可以导入 [ICAL](https://tools.ietf.org/html/rfc2445) 格式的日历或以 ICAL 或 [PCAL](http://pcal.sourceforge.net/) 格式导出数据。使用 ICAL，你可以与其他日历程序共享数据。使用 PCAL，你可以生成日历的 Postscript 版本。

你还可以将许多命令行参数传递给 Calcurse。你可以[在文档中](http://www.calcurse.org/files/manual.chunked/ar01s04.html#_invocation)了解它们。

虽然很简单，但 Calcurse 可以帮助你保持井井有条。你需要更加关注自己的任务和预约，但是你将能够更好地关注你需要做什么以及你需要做的方向。

---

via: <https://opensource.com/article/18/10/calcurse>

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


几个用于替代 du 命令的更好选择

Summary: 我发现了五个可以替代原有的 du 命令的更好的工具。

我发现了五个可以替代原有的 du 命令的更好的工具。

# 几个用于替代 du 命令的更好选择

大家对 `du` 命令应该都不陌生，它可以在类 Unix 系统中对文件和目录的空间使用情况进行计算和汇总。如果你也经常需要使用 `du` 命令，你会对以下内容感兴趣的。我发现了五个可以替代原有的 `du` 命令的更好的工具。当然，如果后续有更多更好的选择，我会继续列出来。如果你有其它推荐，也欢迎在评论中留言。

### ncdu

`ncdu` 作为普通 `du` 的替代品，这在 Linux 社区中已经很流行了。`ncdu` 正是基于开发者们对 `du` 的性能不满意而被开发出来的。`ncdu` 是一个使用 C 语言和 ncurses 接口开发的简易快速的磁盘用量分析器，可以用来查看目录或文件在本地或远程系统上占用磁盘空间的情况。如果你有兴趣查看关于 `ncdu` 的详细介绍，可以浏览《[如何在 Linux 上使用 ncdu 查看磁盘占用量](https://www.ostechnix.com/check-disk-space-usage-linux-using-ncdu/)》这一篇文章。

### tin-summer

tin-summer 是使用 Rust 语言编写的自由开源工具，它可以用于查找占用磁盘空间的文件，它也是 `du` 命令的另一个替代品。由于使用了多线程，因此 tin-summer 在计算大目录的大小时会比 `du` 命令快得多。tin-summer 与 `du` 命令之间的区别是前者读取文件的大小，而后者则读取磁盘使用情况。

tin-summer 的开发者认为它可以替代 `du`，因为它具有以下优势：

* 在大目录的操作速度上比 `du` 更快；
* 在显示结果上默认采用易读格式；
* 可以使用正则表达式排除文件或目录；
* 可以对输出进行排序和着色处理；
* 可扩展，等等。

**安装 tin-summer**

要安装 tin-summer，只需要在终端中执行以下命令：

```
$ curl -LSfs https://japaric.github.io/trust/install.sh | sh -s -- --git vmchale/tin-summer
```

你也可以使用 `cargo` 软件包管理器安装 tin-summer，但你需要在系统上先安装 Rust。在 Rust 已经安装好的情况下，执行以下命令：

```
$ cargo install tin-summer
```

如果上面提到的这两种方法都不能成功安装 tin-summer，还可以从它的[软件发布页](https://github.com/vmchale/tin-summer/releases)下载最新版本的二进制文件编译，进行手动安装。

**用法**

（LCTT 译注：tin-summer 的命令名为 `sn`）

如果需要查看当前工作目录的文件大小，可以执行以下命令：

```
$ sn f
749 MB   ./.rustup/toolchains
749 MB   ./.rustup
147 MB   ./.cargo/bin
147 MB   ./.cargo
900 MB   .
```

不需要进行额外声明，它也是默认以易读的格式向用户展示数据。在使用 `du` 命令的时候，则必须加上额外的 `-h` 参数才能得到同样的效果。

只需要按以下的形式执行命令，就可以查看某个特定目录的文件大小。

```
$ sn f <path-to-the-directory>
```

还可以对输出结果进行排序，例如下面的命令可以输出指定目录中最大的 5 个文件或目录：

```
$ sn sort /home/sk/ -n5
749 MB /home/sk/.rustup
749 MB /home/sk/.rustup/toolchains
147 MB /home/sk/.cargo
147 MB /home/sk/.cargo/bin
2.6 MB /home/sk/mcelog
900 MB /home/sk/
```

顺便一提，上面结果中的最后一行是指定目录 `/home/sk` 的总大小。所以不要惊讶为什么输入的是 5 而实际输出了 6 行结果。

在当前目录下查找带有构建工程的目录，可以使用以下命令：

```
$ sn ar
```

tin-summer 同样支持查找指定大小的带有构建工程的目录。例如执行以下命令可以查找到大小在 100 MB 以上的带有构建工程的目录：

```
$ sn ar -t100M
```

如上文所说，tin-summer 在操作大目录的时候速度比较快，因此在操作小目录的时候，速度会相对比较慢一些。不过它的开发者已经表示，将会在以后的版本中优化这个缺陷。

要获取相关的帮助，可以执行以下命令：

```
$ sn --help
```

如果想要更详尽的介绍，可以查看[这个项目的 GitHub 页面](https://github.com/vmchale/tin-summer)。

### dust

`dust` （含义是 `du` + `rust` = `dust`）使用 Rust 编写，是一个免费、开源的更直观的 `du` 工具。它可以在不需要 `head` 或`sort` 命令的情况下即时显示目录占用的磁盘空间。与 tin-summer 一样，它会默认情况以易读的格式显示每个目录的大小。

**安装 dust**

由于 `dust` 也是使用 Rust 编写，因此它也可以通过 `cargo` 软件包管理器进行安装：

```
$ cargo install du-dust
```

也可以从它的[软件发布页](https://github.com/bootandy/dust/releases)下载最新版本的二进制文件，并按照以下步骤安装。在写这篇文章的时候，最新的版本是 0.3.1。

```
$ wget https://github.com/bootandy/dust/releases/download/v0.3.1/dust-v0.3.1-x86_64-unknown-linux-gnu.tar.gz
```

抽取文件：

```
$ tar -xvf dust-v0.3.1-x86_64-unknown-linux-gnu.tar.gz
```

最后将可执行文件复制到你的 `$PATH`（例如 `/usr/local/bin`）下：

```
$ sudo mv dust /usr/local/bin/
```

**用法**

需要查看当前目录及所有子目录下的文件大小，可以执行以下命令：

```
$ dust
```

输出示例：

带上 `-p` 参数可以按照从当前目录起始的完整目录显示。

```
$ dust -p
```

如果需要查看多个目录的大小，只需要同时列出这些目录，并用空格分隔开即可：

```
$ dust <dir1> <dir2>
```

下面再多举几个例子，例如：

显示文件的长度:

```
$ dust -s
```

只显示 10 个目录：

```
$ dust -n 10
```

查看当前目录下最多 3 层子目录：

```
$ dust -d 3
```

查看帮助：

```
$ dust -h
```

如果想要更详尽的介绍，可以查看[这个项目的 GitHub 页面](https://github.com/bootandy/dust)。

### diskus

`diskus` 也是使用 Rust 编写的一个小型、快速的开源工具，它可以用于替代 `du -sh` 命令。`diskus` 将会计算当前目录下所有文件的总大小，它的效果相当于 `du -sh` 或 `du -sh --bytes`，但其开发者表示 `diskus` 的运行速度是 `du -sh` 的 9 倍。

**安装 diskus**

`diskus` 已经存放于 <ruby> Arch Linux 社区用户软件仓库 <rt>  Arch Linux User-community Repository </rt></ruby>（[AUR](https://aur.archlinux.org/packages/diskus-bin/)）当中，可以通过任何一种 AUR 帮助工具（例如 [`yay`](https://www.ostechnix.com/yay-found-yet-another-reliable-aur-helper/)）把它安装在基于 Arch 的系统上：

```
$ yay -S diskus
```

对于 Ubuntu 及其衍生发行版，可以在 `diskus` 的[软件发布页](https://github.com/sharkdp/diskus/releases)上下载最新版的软件包并安装：

```
$ wget "https://github.com/sharkdp/diskus/releases/download/v0.3.1/diskus_0.3.1_amd64.deb"

$ sudo dpkg -i diskus_0.3.1_amd64.deb
```

还可以使用 `cargo` 软件包管理器安装 `diskus`，但必须在系统上先安装 Rust 1.29+。

安装好 Rust 之后，就可以使用以下命令安装 `diskus`：

```
$ cargo install diskus
```

**用法**

在通常情况下，如果需要查看某个目录的大小，我会使用形如 `du -sh` 的命令。

```
$ du -sh dir
```

这里的 `-s` 参数表示显示总大小。

如果使用 `diskus`，直接就可以显示当前目录的总大小。

```
$ diskus
```

我使用 `diskus` 查看 Arch Linux 系统上各个目录的总大小，这个工具的速度确实比 `du -sh` 快得多。但是它目前只能显示当前目录的大小。

要获取相关的帮助，可以执行以下命令：

```
$ diskus -h
```

如果想要更详尽的介绍，可以查看[这个项目的 GitHub 页面](https://github.com/sharkdp/diskus)。

### duu

`duu` 是 Directory Usage Utility 的缩写。它是使用 Python 编写的查看指定目录大小的工具。它具有跨平台的特性，因此在 Windows、Mac OS 和 Linux 系统上都能够使用。

**安装 duu**

安装这个工具之前需要先安装 Python 3。不过目前很多 Linux 发行版的默认软件仓库中都带有 Python 3，所以这个依赖并不难解决。

Python 3 安装完成后，从 `duu` 的[软件发布页](https://github.com/jftuga/duu/releases)下载其最新版本。

```
$ wget https://github.com/jftuga/duu/releases/download/2.20/duu.py
```

**用法**

要查看当前目录的大小，只需要执行以下命令：

```
$ python3 duu.py
```

输出示例：

从上图可以看出，`duu` 会显示当前目录下文件的数量情况，按照 Byte、KB、MB 单位显示这些文件的总大小，以及每个文件的大小。

如果需要查看某个目录的大小，只需要声明目录的绝对路径即可：

```
$ python3 duu.py /home/sk/Downloads/
```

如果想要更详尽的介绍，可以查看[这个项目的 GitHub 页面](https://github.com/jftuga/duu)。

以上就是 `du` 命令的五种替代方案，希望这篇文章能够帮助到你。就我自己而言，我并不会在这五种工具之间交替使用，我更喜欢使用 `ncdu`。欢迎在下面的评论区发表你对这些工具的评论。

---

via: <https://www.ostechnix.com/some-good-alternatives-to-du-command/>

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

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


2018 年 5 款最好的 Linux 游戏

# 2018 年 5 款最好的 Linux 游戏

Linux 可能不会很快成为游戏玩家选择的平台 —— Valve Steam Machines 的失败似乎是对这一点的深刻提醒 —— 但这并不意味着该平台没有稳定增长，并且拥有相当多的优秀游戏。

从独立单机到辉煌的 RPG（角色扮演），2018 年已经可以称得上是 Linux 游戏的丰收年，在这里，我们将列出迄今为止最喜欢的五款。

你是否在寻找优秀的 Linux 游戏却又不想挥霍金钱？来看看我们的最佳 [免费 Linux 游戏](https://www.maketecheasier.com/open-source-linux-games/) 名单吧！

### 1、<ruby> 永恒之柱 2：死亡之火 <rt>  Pillars of Eternity II: Deadfire </rt></ruby>

其中一款最能代表近年来 cRPG 的复兴，它让传统的 Bethesda RPG 看起来更像是轻松的动作冒险游戏。在磅礴的《<ruby> 永恒之柱 <rt>  Pillars of Eternity </rt></ruby>》系列的最新作品中，当你和船员在充满冒险和危机的岛屿周围航行时，你会发现自己更像是一个海盗。

在混合了海战元素的基础上，《死亡之火》延续了前作丰富的游戏剧情和出色的文笔，同时在美丽的画面和手绘背景的基础上更进一步。

这是一款毫无疑问的令人印象深刻的硬核 RPG ，可能会让一些人对它产生抵触情绪，不过那些接受它的人会投入几个月的时间沉迷其中。

### 2、<ruby> 杀戮尖塔 <rt>  Slay the Spire </rt></ruby>

《杀戮尖塔》仍处于早期阶段，却已经成为年度最佳游戏之一，它是一款采用 deck-building 玩法的卡牌游戏，由充满活力的视觉风格和 rogue-like 机制加以点缀，即便在一次次令人愤怒的（但可能是应受的）死亡之后，你还会再次投入其中。

每次游戏都有无尽的卡牌组合和不同的布局，《杀戮尖塔》就像是近年来所有令人震撼的独立游戏的最佳具现 —— 卡牌游戏和永久死亡冒险模式合二为一。

再强调一次，它仍处于早期阶段，所以它只会变得越来越好！

### 3、<ruby> 战斗机甲 <rt>  Battletech </rt></ruby>

这是我们榜单上像“大片”一样的游戏，《战斗机甲》是一款星际战争游戏（基于桌面游戏），你将装载一个机甲战队并引导它们进行丰富的回合制战斗。

战斗发生在一系列的地形上，从寒冷的荒地到阳光普照的地带，你将用巨大的热武器装备你的四人小队，与对手小队作战。如果你觉得这听起来有点“机械战士”的味道，那么你想的没错，只不过这次更注重战术安排而不是直接行动。

除了让你在宇宙冲突中指挥的战役外，多人模式也可能会耗费你数不清的时间。

### 4、<ruby> 死亡细胞 <rt>  Dead Cells </rt></ruby>

这款游戏称得上是年度最佳平台动作游戏。Roguelike 游戏《死亡细胞》将你带入一个黑暗（却色彩绚丽）的世界，在那里进行攻击和躲避以通过程序生成的关卡。它有点像 2D 的《<ruby> 黑暗之魂 <rt>  Dark Souls </rt></ruby>》，如果《黑暗之魂》也充满五彩缤纷的颜色的话。

《死亡细胞》是无情的，只有精确而灵敏的控制才会让你避开死亡，而在两次运行期间的升级系统又会确保你总是有一些进步的成就感。

《死亡细胞》的像素风、动画效果和游戏机制都达到了巅峰，它及时地提醒我们，在没有 3D 图形的过度使用下游戏可以制作成什么样子。

### 5、<ruby> 叛逆机械师 <rt>  Iconoclasts </rt></ruby>

这款游戏不像上面提到的几款那样为人所知，它是一款可爱风格的游戏，可以看作是《死亡细胞》不那么惊悚、更可爱的替代品。玩家将扮演成罗宾，一个发现自己处于政治扭曲的外星世界后开始了逃亡的女孩。

尽管你的角色将在非线性的关卡中行动，游戏却有着扣人心弦的游戏剧情，罗宾会获得各种各样充满想象力的提升，其中最重要的是她的扳手，从发射炮弹到解决巧妙的环境问题，你几乎可以用它来做任何事。

《叛逆机械师》是一个充满快乐与活力的平台游戏，融合了《<ruby> 洛克人 <rt>  Megaman </rt></ruby>》的战斗和《<ruby> 银河战士 <rt>  Metroid </rt></ruby>》的探索。如果你借鉴了那两部伟大的作品，可能不会比它做得更好。

### 总结

以上就是我们选出的 2018 年最佳的 Linux 游戏。有哪些我们错过的游戏精华？请在评论区告诉我们！

---

via: <https://www.maketecheasier.com/best-linux-games/>

作者：[Robert Zak](https://www.maketecheasier.com/author/robzak/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[seriouszyx](https://github.com/seriouszyx) 校对：[wxy](https://github.com/wxy)

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


关于安全，开发人员需要知道的

Summary: 开发人员不需要成为安全专家, 但他们确实需要摆脱将安全视为一些不幸障碍的心态。

开发人员不需要成为安全专家, 但他们确实需要摆脱将安全视为一些不幸障碍的心态。

# 关于安全，开发人员需要知道的

>
> 开发人员不需要成为安全专家, 但他们确实需要摆脱将安全视为一些不幸障碍的心态。
>
>
>

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

DevOps 并不意味着每个人都需要成为开发和运维方面的专家。尤其在大型组织中，其中角色往往更加专业化。相反，DevOps 思想在某种程度上更多地是关注问题的分离。在某种程度上，运维团队可以为开发人员（无论是在本地云还是在公共云中）部署平台，并且不受影响，这对两个团队来说都是好消息。开发人员可以获得高效的开发环境和自助服务，运维人员可以专注于保持基础管道运行和维护平台。

这是一种约定。开发者期望从运维人员那里得到一个稳定和实用的平台，运维人员希望开发者能够自己处理与开发应用相关的大部分任务。

也就是说，DevOps 还涉及更好的沟通、合作和透明度。如果它不仅仅是一种介于开发和运维之间的新型壁垒，它的效果会更好。运维人员需要对开发者想要和需要的工具类型以及他们通过监视和日志记录来编写更好应用程序所需的可见性保持敏感。另一方面，开发人员需要了解如何才能更有效地使用底层基础设施，以及什么能够使运维在夜间（字面上）保持运行。

同样的原则也适用于更广泛的 DevSecOps，这个术语明确地提醒我们，安全需要嵌入到整个 DevOps 管道中，从获取内容到编写应用程序、构建应用程序、测试应用程序以及在生产环境中运行它们。开发人员（和运维人员）除了他们已有的角色不需要突然成为安全专家。但是，他们通常可以从对安全最佳实践（这可能不同于他们已经习惯的）的更高认识中获益，并从将安全视为一些不幸障碍的心态中转变出来。

以下是一些观察结果。

<ruby> 开放式 Web 应用程序安全项目 <rt>  Open Web Application Security Project </rt></ruby>（[OWASP](https://www.owasp.org/index.php/Main_Page)）[Top 10 列表]提供了一个窗口，可以了解 Web 应用程序中的主要漏洞。列表中的许多条目对 Web 程序员来说都很熟悉。跨站脚本（XSS）和注入漏洞是最常见的。但令人震惊的是，2007 年列表中的许多漏洞仍在 2017 年的列表中（[PDF](https://www.owasp.org/images/7/72/OWASP_Top_10-2017_%28en%29.pdf.pdf)）。无论是培训还是工具，都有问题，许多同样的编码漏洞一再出现。

新的平台技术加剧了这种情况。例如，虽然容器不一定要求应用程序以不同的方式编写，但是它们与新模式（例如[微服务](https://opensource.com/tags/microservices)）相吻合，并且可以放大某些对于安全实践的影响。例如，我的同事 [Dan Walsh](https://opensource.com/users/rhatdan)（[@rhatdan](https://twitter.com/rhatdan)）写道：“计算机领域最大的误解是需要 root 权限来运行应用程序，问题是并不是所有开发者都认为他们需要 root，而是他们将这种假设构建到他们建设的服务中，即服务无法在非 root 情况下运行，而这降低了安全性。”

默认使用 root 访问是一个好的实践吗？并不是。但它可能（也许）是一个可以防御的应用程序和系统，否则就会被其它方法完全隔离。但是，由于所有东西都连接在一起，没有真正的边界，多用户工作负载，拥有许多不同级别访问权限的用户，更不用说更加危险的环境了，那么快捷方式的回旋余地就更小了。

[自动化](https://opensource.com/tags/automation)应该是 DevOps 不可分割的一部分。自动化需要覆盖整个过程中，包括安全和合规性测试。代码是从哪里来的？是否涉及第三方技术、产品或容器镜像？是否有已知的安全勘误表？是否有已知的常见代码缺陷？机密信息和个人身份信息是否被隔离？如何进行身份认证？谁被授权部署服务和应用程序？

你不是自己在写你的加密代码吧？

尽可能地自动化渗透测试。我提到过自动化没？它是使安全性持续的一个重要部分，而不是偶尔做一次的检查清单。

这听起来很难吗？可能有点。至少它是不同的。但是，一名 [DevOpsDays OpenSpaces](https://www.devopsdays.org/open-space-format/) 伦敦论坛的参与者对我说：“这只是技术测试。它既不神奇也不神秘。”他接着说，将安全作为一种更广泛地了解整个软件生命周期的方法（这是一种不错的技能）来参与进来并不难。他还建议参加事件响应练习或[夺旗练习](https://dev.to/_theycallmetoni/capture-the-flag-its-a-game-for-hacki-mean-security-professionals)。你会发现它们很有趣。

---

via: <https://opensource.com/article/18/4/what-developers-need-know-about-security>

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


pydbgen：一个数据库随机生成器

Summary: 用这个简单的工具生成带有多表的大型数据库，让你更好地用 SQL 研究数据科学。

用这个简单的工具生成带有多表的大型数据库，让你更好地用 SQL 研究数据科学。

# pydbgen：一个数据库随机生成器

>
> 用这个简单的工具生成带有多表的大型数据库，让你更好地用 SQL 研究数据科学。
>
>
>

在研究数据科学的过程中，最麻烦的往往不是算法或者技术，而是如何获取到一批原始数据。尽管网上有很多真实优质的数据集可以用于机器学习，然而在学习 SQL 时却不是如此。

对于数据科学来说，熟悉 SQL 的重要性不亚于了解 Python 或 R 编程。如果想收集诸如姓名、年龄、信用卡信息、地址这些信息用于机器学习任务，在 Kaggle 上查找专门的数据集比使用足够大的真实数据库要容易得多。

如果有一个简单的工具或库来帮助你生成一个大型数据库，表里还存放着大量你需要的数据，岂不美哉？

不仅仅是数据科学的入门者，即使是经验丰富的软件测试人员也会需要这样一个简单的工具，只需编写几行代码，就可以通过随机（但是是假随机）生成任意数量但有意义的数据集。

因此，我要推荐这个名为 [pydbgen](https://github.com/tirthajyoti/pydbgen) 的轻量级 Python 库。在后文中，我会简要说明这个库的相关内容，你也可以[阅读它的文档](http://pydbgen.readthedocs.io/en/latest/)详细了解更多信息。

### pydbgen 是什么

`pydbgen` 是一个轻量的纯 Python 库，它可以用于生成随机但有意义的数据记录（包括姓名、地址、信用卡号、日期、时间、公司名称、职位、车牌号等等），存放在 Pandas Dataframe 对象中，并保存到 SQLite 数据库或 Excel 文件。

### 如何安装 pydbgen

目前 1.0.5 版本的 pydbgen 托管在 PyPI（<ruby> Python 包索引存储库 <rt>  Python Package Index repository </rt></ruby>）上，并且对 [Faker](https://faker.readthedocs.io/en/latest/index.html) 有依赖关系。安装 pydbgen 只需要执行命令：

```
pip install pydbgen
```

已经在 Python 3.6 环境下测试安装成功，但在 Python 2 环境下无法正常安装。

### 如何使用 pydbgen

在使用 `pydbgen` 之前，首先要初始化 `pydb` 对象。

```
import pydbgen
from pydbgen import pydbgen
myDB=pydbgen.pydb()
```

随后就可以调用 `pydb` 对象公开的各种内部函数了。可以按照下面的例子，输出随机的美国城市和车牌号码：

```
myDB.city_real()
>> 'Otterville'
for _ in range(10):
    print(myDB.license_plate())
>> 8NVX937
   6YZH485
   XBY-564
   SCG-2185
   XMR-158
   6OZZ231
   CJN-850
   SBL-4272
   TPY-658
   SZL-0934
```

另外，如果你输入的是 `city()` 而不是 `city_real()`，返回的将会是虚构的城市名。

```
print(myDB.gen_data_series(num=8,data_type='city'))
>>
New Michelle
Robinborough
Leebury
Kaylatown
Hamiltonfort
Lake Christopher
Hannahstad
West Adamborough
```

### 生成随机的 Pandas Dataframe

你可以指定生成数据的数量和种类，但需要注意的是，返回结果均为字符串或文本类型。

```
testdf=myDB.gen_dataframe(5,['name','city','phone','date'])
testdf
```

最终产生的 Dataframe 类似下图所示。

### 生成数据库表

你也可以指定生成数据的数量和种类，而返回结果是数据库中的文本或者变长字符串类型。在生成过程中，你可以指定对应的数据库文件名和表名。

```
myDB.gen_table(db_file='Testdb.DB',table_name='People',

fields=['name','city','street_address','email'])
```

上面的例子种生成了一个能被 MySQL 和 SQLite 支持的 `.db` 文件。下图则显示了这个文件中的数据表在 SQLite 可视化客户端中打开的画面。

### 生成 Excel 文件

和上面的其它示例类似，下面的代码可以生成一个具有随机数据的 Excel 文件。值得一提的是，通过将 `phone_simple` 参数设为 `False` ，可以生成较长较复杂的电话号码。如果你想要提高自己在数据提取方面的能力，不妨尝试一下这个功能。

```
myDB.gen_excel(num=20,fields=['name','phone','time','country'],
phone_simple=False,filename='TestExcel.xlsx')
```

最终的结果类似下图所示：

### 生成随机电子邮箱地址

`pydbgen` 内置了一个 `realistic_email` 方法，它基于种子来生成随机的电子邮箱地址。如果你不想在网络上使用真实的电子邮箱地址时，这个功能可以派上用场。

```
for _ in range(10):
    print(myDB.realistic_email('Tirtha Sarkar'))
>>
Tirtha_Sarkar@gmail.com
Sarkar.Tirtha@outlook.com
Tirtha_S48@verizon.com
Tirtha_Sarkar62@yahoo.com
Tirtha.S46@yandex.com
Tirtha.S@att.com
Sarkar.Tirtha60@gmail.com
TirthaSarkar@zoho.com
Sarkar.Tirtha@protonmail.com
Tirtha.S@comcast.net
```

### 未来的改进和用户贡献

目前的版本中并不完美。如果你发现了 pydbgen 的 bug 导致它在运行期间发生崩溃，请向我反馈。如果你打算对这个项目贡献代码，[也随时欢迎你](https://github.com/tirthajyoti/pydbgen)。当然现在也还有很多改进的方向：

* pydbgen 作为随机数据生成器，可以集成一些机器学习或统计建模的功能吗？
* pydbgen 是否会添加可视化功能？

一切皆有可能！

如果你有任何问题或想法想要分享，都可以通过 [tirthajyoti@gmail.com](mailto:tirthajyoti@gmail.com) 与我联系。如果你像我一样对机器学习和数据科学感兴趣，也可以添加我的 [LinkedIn](https://www.linkedin.com/in/tirthajyoti-sarkar-2127aa7/) 或在 [Twitter](https://twitter.com/tirthajyotiS) 上关注我。另外，还可以在我的 [GitHub](https://github.com/tirthajyoti?tab=repositories) 上找到更多 Python、R 或 MATLAB 的有趣代码和机器学习资源。

本文以 [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) 许可在 [Towards Data Science](https://towardsdatascience.com/introducing-pydbgen-a-random-dataframe-database-table-generator-b5c7bdc84be5) 首发。

---

via: <https://opensource.com/article/18/11/pydbgen-random-database-table-generator>

作者：[Tirthajyoti Sarkar](https://opensource.com/users/tirthajyoti) 选题：[lujun9972](https://github.com/lujun9972) 译者：[HankChow](https://github.com/HankChow) 校对：[wxy](https://github.com/wxy)

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


一个用于家庭项目的单用户、轻量级操作系统

Summary: 业余爱好者应该了解一下 RISC OS 的五个原因。

业余爱好者应该了解一下 RISC OS 的五个原因。

# 一个用于家庭项目的单用户、轻量级操作系统

>
> 业余爱好者应该了解一下 RISC OS 的五个原因。
>
>
>

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

究竟什么是 RISC OS？嗯，它不是一种新的 Linux。它也不是有些人认为的 Windows。事实上，它发布于 1987 年，它比它们任何一个都要古老。但你看到它时不一定会意识到这一点。

其点击式图形用户界面在底部为活动的程序提供一个固定面板和一个图标栏。因此，它看起来像 Windows 95，并且比它早了 8 年。

这个操作系统最初是为 [Acorn Archimedes](https://en.wikipedia.org/wiki/Acorn_Archimedes) 编写的。这台机器中的 Acorn RISC Machines CPU 是全新的硬件，因此需要在其上运行全新的软件。这是最早的 ARM 芯片上的系统，早于任何人想到的 Android 或 [Armbian](https://www.armbian.com/) 之前。

虽然 Acorn 桌面最终消失了，但 ARM 芯片继续征服世界。在这里，RISC OS 一直有一个优点 —— 通常在嵌入式设备中，你从来没有真正地意识到它。RISC OS 过去长期以来一直是一个完全专有的操作系统。但近年来，该抄系统的所有者已经开始将源代码发布到一个名为 [RISC OS Open](https://www.riscosopen.org/content/) 的项目中。

### 1、你可以将它安装在树莓派上

树莓派的官方操作系统 [Raspbian](https://www.raspbian.org/) 实际上非常棒（如果你对摆弄不同技术上新奇的东西不感兴趣，那么你可能最初也不会选择树莓派）。由于 RISC OS 是专门为 ARM 编写的，因此它可以在各种小型计算机上运行，包括树莓派的各个型号。

### 2、它超轻量级

我的树莓派上安装的 RISC 系统占用了几百兆 —— 这是在我加载了数十个程序和游戏之后。它们大多数时候不超过 1 兆。

如果你真的节俭，RISC OS Pico 可用在 16MB SD 卡上。如果你要在嵌入式系统或物联网项目中鼓捣某些东西，这是很完美的。当然，16MB 实际上比压缩到 512KB 的老 Archimedes 的 ROM 要多得多。但我想 30 年间内存技术的发展，我们可以稍微放宽一下了。

### 3、它非常适合复古游戏

当 Archimedes 处于鼎盛时期时，ARM CPU 的速度比 Apple Macintosh 和 Commodore Amiga 中的 Motorola 68000 要快几倍，它也完全吸了新的 386 技术。这使得它成为对游戏开发者有吸引力的一个平台，他们希望用这个星球上最强大的桌面计算机来支撑他们的东西。

那些游戏的许多拥有者都非常慷慨，允许业余爱好者免费下载他们的老作品。虽然 RISC OS 和硬件已经发展了，但只需要进行少量的调整就可以让它们运行起来。

如果你有兴趣探索这个，[这里有一个指南](https://www.riscosopen.org/wiki/documentation/show/Introduction%20to%20RISC%20OS)让这些游戏在你的树莓派上运行。

### 4、它有 BBC BASIC

就像过去一样，按下 `F12` 进入命令行，输入 `*BASIC`，就可以看到一个完整的 BBC BASIC 解释器。

对于那些在 80 年代没有接触过它的人，请让我解释一下：BBC BASIC 是当时我们很多人的第一个编程语言，因为它专门教孩子如何编码。当时有大量的书籍和杂志文章教我们编写自己的简单但高度可玩的游戏。

几十年后，对于一个想要在学校假期做点什么的有技术头脑的孩子而言，在 BBC BASIC 上编写自己的游戏仍然是一个很棒的项目。但很少有孩子在家里有 BBC micro。那么他们应该怎么做呢？

当然，你可以在每台家用电脑上运行解释器，但是当别人需要使用它时就不能用了。那么为什么不使用装有 RISC OS 的树莓派呢？

### 5、它是一个简单的单用户操作系统

RISC OS 不像 Linux 一样有自己的用户和超级用户访问权限。它有一个用户并可以完全访问整个机器。因此，它可能不是跨企业部署的最佳日常驱动，甚至不适合给老人家做银行业务。但是，如果你正在寻找可以用来修改和鼓捣的东西，那绝对是太棒了。你和机器之间没有那么多障碍，所以你可以直接闯进去。

### 扩展阅读

如果你想了解有关此操作系统的更多信息，请查看 [RISC OS Open](https://www.riscosopen.org/content/)，或者将镜像烧到闪存到卡上并开始使用它。

---

via: <https://opensource.com/article/18/7/gentle-intro-risc-os>

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


# 如何使用 chkconfig 和 systemctl 命令启用或禁用 Linux 服务

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

对于 Linux 管理员来说这是一个重要（美妙）的话题，所以每个人都必须知道，并练习怎样才能更高效的使用它们。

在 Linux 中，无论何时当你安装任何带有服务和守护进程的包，系统默认会把这些服务的初始化及 systemd 脚本添加进去，不过此时它们并没有被启用。

我们需要手动的开启或者关闭那些服务。Linux 中有三个著名的且一直在被使用的初始化系统。

### 什么是初始化系统？

在以 Linux/Unix 为基础的操作系统上，`init` (初始化的简称) 是内核引导系统启动过程中第一个启动的进程。

`init` 的进程 id （pid）是 1，除非系统关机否则它将会一直在后台运行。

`init` 首先根据 `/etc/inittab` 文件决定 Linux 运行的级别，然后根据运行级别在后台启动所有其他进程和应用程序。

BIOS、MBR、GRUB 和内核程序在启动 `init` 之前就作为 Linux 的引导程序的一部分开始工作了。

下面是 Linux 中可以使用的运行级别（从 0～6 总共七个运行级别）：

* `0`：关机
* `1`：单用户模式
* `2`：多用户模式（没有NFS）
* `3`：完全的多用户模式
* `4`：系统未使用
* `5`：图形界面模式
* `6`：重启

下面是 Linux 系统中最常用的三个初始化系统：

* System V（Sys V）
* Upstart
* systemd

### 什么是 System V（Sys V）?

System V（Sys V）是类 Unix 系统第一个也是传统的初始化系统。`init` 是内核引导系统启动过程中第一支启动的程序，它是所有程序的父进程。

大部分 Linux 发行版最开始使用的是叫作 System V（Sys V）的传统的初始化系统。在过去的几年中，已经发布了好几个初始化系统以解决标准版本中的设计限制，例如：launchd、Service Management Facility、systemd 和 Upstart。

但是 systemd 已经被几个主要的 Linux 发行版所采用，以取代传统的 SysV 初始化系统。

### 什么是 Upstart?

Upstart 是一个基于事件的 `/sbin/init` 守护进程的替代品，它在系统启动过程中处理任务和服务的启动，在系统运行期间监视它们，在系统关机的时候关闭它们。

它最初是为 Ubuntu 而设计，但是它也能够完美的部署在其他所有 Linux系统中，用来代替古老的 System-V。

Upstart 被用于 Ubuntu 从 9.10 到 Ubuntu 14.10 和基于 RHEL 6 的系统，之后它被 systemd 取代。

### 什么是 systemd?

systemd 是一个新的初始化系统和系统管理器，它被用于所有主要的 Linux 发行版，以取代传统的 SysV 初始化系统。

systemd 兼容 SysV 和 LSB 初始化脚本。它可以直接替代 SysV 初始化系统。systemd 是被内核启动的第一个程序，它的 PID 是 1。

systemd 是所有程序的父进程，Fedora 15 是第一个用 systemd 取代 upstart 的发行版。`systemctl` 用于命令行，它是管理 systemd 的守护进程/服务的主要工具，例如：（开启、重启、关闭、启用、禁用、重载和状态）

systemd 使用 .service 文件而不是 bash 脚本（SysVinit 使用的）。systemd 将所有守护进程添加到 cgroups 中排序，你可以通过浏览 `/cgroup/systemd` 文件查看系统等级。

### 如何使用 chkconfig 命令启用或禁用引导服务?

`chkconfig` 实用程序是一个命令行工具，允许你在指定运行级别下启动所选服务，以及列出所有可用服务及其当前设置。

此外，它还允许我们从启动中启用或禁用服务。前提是你有超级管理员权限（root 或者 `sudo`）运行这个命令。

所有的服务脚本位于 `/etc/rd.d/init.d`文件中

### 如何列出运行级别中所有的服务

`--list` 参数会展示所有的服务及其当前状态（启用或禁用服务的运行级别）：

```
# chkconfig --list
NetworkManager     0:off    1:off    2:on    3:on    4:on    5:on    6:off
abrt-ccpp          0:off    1:off    2:off    3:on    4:off    5:on    6:off
abrtd              0:off    1:off    2:off    3:on    4:off    5:on    6:off
acpid              0:off    1:off    2:on    3:on    4:on    5:on    6:off
atd                0:off    1:off    2:off    3:on    4:on    5:on    6:off
auditd             0:off    1:off    2:on    3:on    4:on    5:on    6:off
.
.
```

### 如何查看指定服务的状态

如果你想查看运行级别下某个服务的状态，你可以使用下面的格式匹配出需要的服务。

比如说我想查看运行级别中 `auditd` 服务的状态

```
# chkconfig --list| grep auditd
auditd             0:off    1:off    2:on    3:on    4:on    5:on    6:off
```

### 如何在指定运行级别中启用服务

使用 `--level` 参数启用指定运行级别下的某个服务，下面展示如何在运行级别 3 和运行级别 5 下启用 `httpd` 服务。

```
# chkconfig --level 35 httpd on
```

### 如何在指定运行级别下禁用服务

同样使用 `--level` 参数禁用指定运行级别下的服务，下面展示的是在运行级别 3 和运行级别 5 中禁用 `httpd` 服务。

```
# chkconfig --level 35 httpd off
```

### 如何将一个新服务添加到启动列表中

`-–add` 参数允许我们添加任何新的服务到启动列表中，默认情况下，新添加的服务会在运行级别 2、3、4、5 下自动开启。

```
# chkconfig --add nagios
```

### 如何从启动列表中删除服务

可以使用 `--del` 参数从启动列表中删除服务，下面展示的是如何从启动列表中删除 Nagios 服务。

```
# chkconfig --del nagios
```

### 如何使用 systemctl 命令启用或禁用开机自启服务？

`systemctl` 用于命令行，它是一个用来管理 systemd 的守护进程/服务的基础工具，例如：（开启、重启、关闭、启用、禁用、重载和状态）。

所有服务创建的 unit 文件位与 `/etc/systemd/system/`。

### 如何列出全部的服务

使用下面的命令列出全部的服务（包括启用的和禁用的）。

```
# systemctl list-unit-files --type=service
UNIT FILE                                     STATE
arp-ethers.service                            disabled
auditd.service                                enabled
autovt@.service                               enabled
blk-availability.service                      disabled
brandbot.service                              static
chrony-dnssrv@.service                        static
chrony-wait.service                           disabled
chronyd.service                               enabled
cloud-config.service                          enabled
cloud-final.service                           enabled
cloud-init-local.service                      enabled
cloud-init.service                            enabled
console-getty.service                         disabled
console-shell.service                         disabled
container-getty@.service                      static
cpupower.service                              disabled
crond.service                                 enabled
.
.
150 unit files listed.
```

使用下面的格式通过正则表达式匹配出你想要查看的服务的当前状态。下面是使用 `systemctl` 命令查看 `httpd` 服务的状态。

```
# systemctl list-unit-files --type=service | grep httpd
httpd.service disabled
```

### 如何让指定的服务开机自启

使用下面格式的 `systemctl` 命令启用一个指定的服务。启用服务将会创建一个符号链接，如下可见：

```
# systemctl enable httpd
Created symlink from /etc/systemd/system/multi-user.target.wants/httpd.service to /usr/lib/systemd/system/httpd.service.
```

运行下列命令再次确认服务是否被启用。

```
# systemctl is-enabled httpd
enabled
```

### 如何禁用指定的服务

运行下面的命令禁用服务将会移除你启用服务时所创建的符号链接。

```
# systemctl disable httpd
Removed symlink /etc/systemd/system/multi-user.target.wants/httpd.service.
```

运行下面的命令再次确认服务是否被禁用。

```
# systemctl is-enabled httpd
disabled
```

### 如何查看系统当前的运行级别

使用 `systemctl` 命令确认你系统当前的运行级别，`runlevel` 命令仍然可在 systemd 下工作，不过，运行级别对于 systemd 来说是一个历史遗留的概念。所以我建议你全部使用 `systemctl` 命令。

我们当前处于运行级别 3, 它等同于下面显示的 `multi-user.target`。

```
# systemctl list-units --type=target
UNIT                  LOAD   ACTIVE SUB    DESCRIPTION
basic.target          loaded active active Basic System
cloud-config.target   loaded active active Cloud-config availability
cryptsetup.target     loaded active active Local Encrypted Volumes
getty.target          loaded active active Login Prompts
local-fs-pre.target   loaded active active Local File Systems (Pre)
local-fs.target       loaded active active Local File Systems
multi-user.target     loaded active active Multi-User System
network-online.target loaded active active Network is Online
network-pre.target    loaded active active Network (Pre)
network.target        loaded active active Network
paths.target          loaded active active Paths
remote-fs.target      loaded active active Remote File Systems
slices.target         loaded active active Slices
sockets.target        loaded active active Sockets
swap.target           loaded active active Swap
sysinit.target        loaded active active System Initialization
timers.target         loaded active active Timers
```

---

via: <https://www.2daygeek.com/how-to-enable-or-disable-services-on-boot-in-linux-using-chkconfig-and-systemctl-command/>

作者：[Prakash Subramanian](https://www.2daygeek.com/author/prakash/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[way-ww](https://github.com/way-ww) 校对：[wxy](https://github.com/wxy)

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


8 个很棒的 pytest 插件

# 8 个很棒的 pytest 插件

>
> Python 测试工具最好的一方面是其强大的生态系统。这里列出了八个最好的插件。
>
>
>

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

我们是 [pytest](https://docs.pytest.org/en/latest/) 的忠实粉丝，并将其作为工作和开源项目的默认 Python 测试工具。在本月的 Python 专栏中，我们分享了为什么我们喜欢 `pytest` 以及一些让 `pytest` 测试工作更有趣的插件。

### 什么是 pytest？

正如该工具的网站所说，“pytest 框架可以轻松地编写小型测试，也能进行扩展以支持应用和库的复杂功能测试。”

`pytest` 允许你在任何名为 `test_*.py` 的文件中定义测试，并将其定义为以 `test_*` 开头的函数。然后，pytest 将在整个项目中查找所有测试，并在控制台中运行 `pytest` 时自动运行这些测试。pytest 接受[标志和参数](https://docs.pytest.org/en/latest/usage.html)，它们可以在测试运行器停止时更改，这些包含如何输出结果，运行哪些测试以及输出中包含哪些信息。它还包括一个 `set_trace()` 函数，它可以进入到你的测试中。它会暂停您的测试， 并允许你与变量进行交互，不然你只能在终端中“四处翻弄”来调试你的项目。

`pytest` 最好的一方面是其强大的插件生态系统。因为 `pytest` 是一个非常流行的测试库，所以多年来创建了许多插件来扩展、定制和增强其功能。这八个插件是我们的最爱。

### 8 个很棒的插件

#### 1、pytest-sugar

[pytest-sugar](https://github.com/Frozenball/pytest-sugar) 改变了 `pytest` 的默认外观，添加了一个进度条，并立即显示失败的测试。它不需要配置，只需 `pip install pytest-sugar`，用 `pytest` 运行测试，来享受更漂亮、更有用的输出。

#### 2、pytest-cov

[pytest-cov](https://github.com/pytest-dev/pytest-cov) 在 `pytest` 中增加了覆盖率支持，来显示哪些代码行已经测试过，哪些还没有。它还将包括项目的测试覆盖率。

#### 3、pytest-picked

[pytest-picked](https://github.com/anapaulagomes/pytest-picked) 对你已经修改但尚未提交 `git` 的代码运行测试。安装库并运行 `pytest --picked` 来仅测试自上次提交后已更改的文件。

#### 4、pytest-instafail

[pytest-instafail](https://github.com/pytest-dev/pytest-instafail) 修改 `pytest` 的默认行为来立即显示失败和错误，而不是等到 `pytest` 完成所有测试。

#### 5、pytest-tldr

一个全新的 `pytest` 插件，可以将输出限制为你需要的东西。`pytest-tldr`（`tldr` 代表 “too long, didn’t read” —— 太长，不想读），就像 pytest-sugar 一样，除基本安装外不需要配置。不像 pytest 的默认输出那么详细，[pytest-tldr](https://github.com/freakboy3742/pytest-tldr) 将默认输出限制为失败测试的回溯信息，并忽略了一些令人讨厌的颜色编码。添加 `-v` 标志会为喜欢它的人返回更详细的输出。

#### 6、pytest-xdist

[pytest-xdist](https://github.com/pytest-dev/pytest-xdist) 允许你通过 `-n` 标志并行运行多个测试：例如，`pytest -n 2` 将在两个 CPU 上运行你的测试。这可以显著加快你的测试速度。它还包括 `--looponfail` 标志，它将自动重新运行你的失败测试。

#### 7、pytest-django

[pytest-django](https://pytest-django.readthedocs.io/en/latest/) 为 Django 应用和项目添加了 `pytest` 支持。具体来说，`pytest-django` 引入了使用 pytest fixture 测试 Django 项目的能力，而省略了导入 `unittest` 和复制/粘贴其他样板测试代码的需要，并且比标准的 Django 测试套件运行得更快。

#### 8、django-test-plus

[django-test-plus](https://django-test-plus.readthedocs.io/en/latest/) 并不是专门为 `pytest` 开发，但它现在支持 `pytest`。它包含自己的 `TestCase` 类，你的测试可以继承该类，并使你能够使用较少的按键来输出频繁的测试案例，例如检查特定的 HTTP 错误代码。

我们上面提到的库绝不是你扩展 `pytest` 的唯一选择。有用的 pytest 插件的前景是广阔的。查看 [pytest 插件兼容性](https://plugincompat.herokuapp.com/)页面来自行探索。你最喜欢哪些插件？

---

via: <https://opensource.com/article/18/6/pytest-plugins>

作者：[Jeff Triplett](https://opensource.com/users/jefftriplett), [Lacery Williams Henschel](https://opensource.com/users/laceynwilliams) 选题：[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/) 荣誉推出


COPR 仓库中 4 个很酷的新软件（2018.10）

Summary: COPR 是软件的个人存储库的集合，它包含那些不在标准的 Fedora 仓库中的软件。

# COPR 仓库中 4 个很酷的新软件（2018.10）

COPR 是软件的个人存储库的[集合](https://copr.fedorainfracloud.org/)，它包含那些不在标准的 Fedora 仓库中的软件。某些软件不符合允许轻松打包的标准。或者它可能不符合其他 Fedora 标准，尽管它是自由开源的。COPR 可以在标准的 Fedora 包之外提供这些项目。COPR 中的软件不受 Fedora 基础设施的支持，或者是由项目自己背书的。但是，它是尝试新的或实验性软件的一种很好的方法。

这是 COPR 中一组新的有趣项目。

[编者按：这些项目里面有一个并不适合通过 COPR 分发，所以从本文中 也删除了。相关的评论也删除了，以免误导读者。对此带来的不便，我们深表歉意。]

（LCTT 译注：本文后来移除了对“GitKraken”项目的介绍。）

### Music On Console

[Music On Console](http://moc.daper.net/) 播放器（简称 mocp）是一个简单的控制台音频播放器。它有一个类似于 “Midnight Commander” 的界面，并且很容易使用。你只需进入包含音乐的目录，然后选择要播放的文件或目录。此外，mocp 提供了一组命令，允许直接从命令行进行控制。

#### 安装说明

该仓库目前为 Fedora 28 和 29 提供 Music On Console 播放器。要安装 mocp，请使用以下命令：

```
sudo dnf copr enable Krzystof/Moc
sudo dnf install moc
```

### cnping

[Cnping](https://github.com/cnlohr/cnping) 是小型的图形化 ping IPv4 工具，可用于可视化显示 RTT 的变化。它提供了一个选项来控制每个数据包之间的间隔以及发送的数据大小。除了显示的图表外，cnping 还提供 RTT 和丢包的基本统计数据。

#### 安装说明

该仓库目前为 Fedora 27、28、29 和 Rawhide 提供 cnping。要安装 cnping，请使用以下命令：

```
sudo dnf copr enable dreua/cnping
sudo dnf install cnping
```

### Pdfsandwich

[Pdfsandwich](http://www.tobias-elze.de/pdfsandwich/) 是将文本添加到图像形式的文本 PDF 文件 (如扫描书籍) 的工具。它使用光学字符识别 (OCR) 创建一个额外的图层, 包含了原始页面已识别的文本。这对于复制和处理文本很有用。

#### 安装说明

该仓库目前为 Fedora 27、28、29、Rawhide 以及 EPEL 7 提供 pdfsandwich。要安装 pdfsandwich，请使用以下命令：

```
sudo dnf copr enable merlinm/pdfsandwich
sudo dnf install pdfsandwich
```

---

via: <https://fedoramagazine.org/4-cool-new-projects-try-copr-october-2018/>

作者：[Dominik Turecek](https://fedoramagazine.org) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


gitbase：用 SQL 查询 Git 仓库

# gitbase：用 SQL 查询 Git 仓库

>
> gitbase 是一个使用 go 开发的的开源项目，它实现了在 Git 仓库上执行 SQL 查询。
>
>
>

Git 已经成为了代码版本控制的事实标准，但尽管 Git 相当普及，对代码仓库的深入分析的工作难度却没有因此而下降；而 SQL 在大型代码库的查询方面则已经是一种久经考验的语言，因此诸如 Spark 和 BigQuery 这样的项目都采用了它。

所以，source{d} 很顺理成章地将这两种技术结合起来，就产生了 gitbase（LCTT 译注：source{d} 是一家开源公司，本文作者是该公司开发者关系副总裁）。gitbase 是一个<ruby> 代码即数据 <rt>  code-as-data </rt></ruby>的解决方案，可以使用 SQL 对 git 仓库进行大规模分析。

[gitbase](https://github.com/src-d/gitbase) 是一个完全开源的项目。它站在了很多巨人的肩上，因此得到了足够的发展竞争力。下面就来介绍一下其中的一些“巨人”。

*[gitbase playground](https://github.com/src-d/gitbase-web) 为 gitbase 提供了一个可视化的操作环境。*

### 用 Vitess 解析 SQL

gitbase 通过 SQL 与用户进行交互，因此需要能够遵循 MySQL 协议来对通过网络传入的 SQL 请求作出解析和理解，万幸由 YouTube 建立的 [Vitess](https://github.com/vitessio/vitess) 项目已经在这一方面给出了解决方案。Vitess 是一个横向扩展的 MySQL 数据库集群系统。

我们只是使用了这个项目中的部分重要代码，并将其转化为一个可以让任何人在数分钟以内编写出一个 MySQL 服务器的[开源程序](https://github.com/src-d/go-mysql-server)，就像我在 [justforfunc](http://justforfunc.com/) 视频系列中展示的 [CSVQL](https://youtu.be/bcRDXAraprk) 一样，它可以使用 SQL 操作 CSV 文件。

### 用 go-git 读取 git 仓库

在成功解析 SQL 请求之后，还需要对数据集中的 git 仓库进行查询才能返回结果。因此，我们还结合使用了 source{d} 最成功的 [go-git](https://github.com/src-d/go-git) 仓库。go-git 是使用纯 go 语言编写的具有高度可扩展性的 git 实现。

借此我们就可以很方便地将存储在磁盘上的代码仓库保存为 [siva](https://github.com/src-d/siva) 文件格式（这同样是 source{d} 的一个开源项目），也可以通过 `git clone` 来对代码仓库进行复制。

### 使用 enry 检测语言、使用 babelfish 解析文件

gitbase 集成了我们开源的语言检测项目 [enry](https://github.com/src-d/enry) 以及代码解析项目 [babelfish](https://github.com/bblfsh/bblfshd)，因此在分析 git 仓库历史代码的能力也相当强大。babelfish 是一个自托管服务，普适于各种源代码解析，并将代码文件转换为<ruby> 通用抽象语法树 <rt>  Universal Abstract Syntax Tree </rt></ruby>（UAST）。

这两个功能在 gitbase 中可以被用户以函数 `LANGUAGE` 和 `UAST` 调用，诸如“查找上个月最常被修改的函数的名称”这样的请求就需要通过这两个功能实现。

### 提高性能

gitbase 可以对非常大的数据集进行分析，例如来自 GitHub 高达 3 TB 源代码的 Public Git Archive（[公告](https://blog.sourced.tech/post/announcing-pga/)）。面临的工作量如此巨大，因此每一点性能都必须运用到极致。于是，我们也使用到了 Rubex 和 Pilosa 这两个项目。

#### 使用 Rubex 和 Oniguruma 优化正则表达式速度

[Rubex](https://github.com/moovweb/rubex) 是 go 的正则表达式标准库包的一个准替代品。之所以说它是准替代品，是因为它没有在 `regexp.Regexp` 类中实现 `LiteralPrefix` 方法，直到现在都还没有。

Rubex 的高性能是由于使用 [cgo](https://golang.org/cmd/cgo/) 调用了 [Oniguruma](https://github.com/kkos/oniguruma)，它是一个高度优化的 C 代码库。

#### 使用 Pilosa 索引优化查询速度

索引几乎是每个关系型数据库都拥有的特性，但 Vitess 由于不需要用到索引，因此并没有进行实现。

于是我们引入了 [Pilosa](https://github.com/pilosa/pilosa) 这个开源项目。Pilosa 是一个使用 go 实现的分布式位图索引，可以显著提升跨多个大型数据集的查询的速度。通过 Pilosa，gitbase 才得以在巨大的数据集中进行查询。

### 总结

我想用这一篇文章来对开源社区表达我衷心的感谢，让我们能够不负众望的在短时间内完成 gitbase 的开发。我们 source{d} 的每一位成员都是开源的拥护者，github.com/src-d 下的每一行代码都是见证。

你想使用 gitbase 吗？最简单快捷的方式是从 sourced.tech/engine 下载 source{d} 引擎，就可以通过单个命令运行 gitbase 了。

想要了解更多，可以听听我在 [Go SF 大会](https://www.meetup.com/golangsf/events/251690574/)上的演讲录音。

本文在 [Medium](https://medium.com/sourcedtech/gitbase-exploring-git-repos-with-sql-95ec0986386c) 首发，并经许可在此发布。

---

via: <https://opensource.com/article/18/11/gitbase>

作者：[Francesc Campoy](https://opensource.com/users/francesc) 选题：[lujun9972](https://github.com/lujun9972) 译者：[HankChow](https://github.com/HankChow) 校对：[wxy](https://github.com/wxy)

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