如何弄清 Linux 系统运行何种系统管理程序

![](/data/attachment/album/201811/13/003654a6ytyyyomtl62ydy.jpg)

虽然我们经常听到<ruby> 系统管理器 <rt>  System Manager </rt></ruby>这词，但很少有人深究其确切意义。现在我们将向你展示其区别。

我会尽自己所能来解释清楚一切。我们大多都知道 System V 和 systemd 两种系统管理器。 System V （简写 SysV） 是老式系统所使用的古老且传统的初始化系统及系统管理器。

Systemd 是全新的初始化系统及系统管理器，并且已被大部分主流 Linux 发行版所采用。

Linux 系统中主要有三种有名而仍在使用的初始化系统。大多数 Linux 发行版都使用其中之一。

### 什么是初始化系统管理器？

在基于 Linux/Unix 的操作系统中，`init` (初始化的简称) 是内核启动系统时开启的第一个进程。

它持有的进程 ID（PID）号为 1，其在后台一直运行着，直到关机。

`init` 会查找 `/etc/inittab` 文件中相应配置信息来确定系统的运行级别，然后根据运行级别在后台启动所有的其它进程和应用。

作为 Linux 启动过程的一部分，BIOS、MBR、GRUB 和内核进程在此进程之前就被激活了。

下面列出的是 Linux 的可用运行级别（存在七个运行级别，从 0 到 6）。

* `0`：停机
* `1`：单用户模式
* `2`：多用户，无 NFS（LCTT 译注：NFS 即 Network File System，网络文件系统）
* `3`：全功能多用户模式
* `4`：未使用
* `5`：X11（GUI – 图形用户界面）
* `6`：重启

下面列出的是 Linux 系统中广泛使用的三种初始化系统。

* System V (Sys V)：是类 Unix 操作系统传统的也是首款初始化系统。
* Upstart：基于事件驱动，是 `/sbin/init` 守护进程的替代品。
* Systemd：是一款全新的初始化系统及系统管理器，它被所有主流的 Linux 发行版实现/采用，以替代传统的 SysV 初始化系统。

### 什么是 System V (Sys V)?

System V（Sys V）是类 Unix 操作系统传统的也是首款初始化系统。`init` 是系统由内核启动期间启动的第一个进程，它是所有进程的父进程。

起初，大多数 Linux 发行版都使用名为 System V（SysV）的传统的初始化系统。多年来，为了解决标准版本中的设计限制，发布了几个替代的初始化系统，例如 launchd、Service Management Facility、systemd 和 Upstart。

但只有 systemd 最终被几个主流 Linux 发行版所采用，以替代传统的 SysV。

### 什么是 Upstart?

Upstart 基于事件驱动，是 `/sbin/init` 守护进程的替代品。用来在启动期间控制任务和服务的启动，在关机期间停止它们，及在系统运行过程中监视它们。

它最初是为 Ubuntu 发行版开发的，但也可以在所有的 Linux 发行版中部署运行，以替代古老的 System V 初始化系统。

它用于 Ubuntu 9.10 到 14.10 版本和基于 RHEL 6 的系统中，之后的被 systemd 取代了。

### 什么是 systemd?

systemd 是一款全新的初始化系统及系统管理器，它被所有主流的 Linux 发行版实现/采用，以替代传统的 SysV 初始化系统。

systemd 与 SysV 和 LSB（LCTT 译注：Linux Standards Base） 初始化脚本兼容。它可以作为 SysV 初始化系统的直接替代品。其是内核启动的第一个进程并占有数字 1 的 PID，它是所有进程的父进程。

Fedora 15 是第一个采用 systemd 而不是 upstart 的发行版。[systemctl](https://www.2daygeek.com/how-to-check-all-running-services-in-linux/) 是一款命令行工具，它是管理 systemd 守护进程/服务（如 `start`、`restart`、`stop`、`enable`、`disable`、`reload` 和 `status`）的主要工具。

systemd 使用 `.service` 文件而不是（SysV 初始化系统使用的） bash 脚本。systemd 把所有守护进程按顺序排列到自己 Cgroups （LCTT 译注：Cgroups 是 control groups 的缩写，是 Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源，如：cpu、memory、IO 等的机制。最初由 Google 的工程师提出，后来被整合进 Linux 内核。Cgroups 也是 LXC 为实现虚拟化所使用的资源管理手段，可以说没有 cgroups 就没有 LXC）中，所以通过查看 `/cgroup/systemd` 文件就可以查看系统层次结构。

### 在 Linux 上如何识别出系统管理器

在系统上运行如下命令来查看运行着什么系统管理器：

（LCTT 译注：原文繁冗啰嗦，翻译时进行了裁剪整理。）

#### 方法 1：使用 ps 命令

`ps` – 显示当前进程快照。`ps` 会显示选定的活动进程的信息。其输出不能确切区分出是 System V(SysV) 还是 upstart，所以我建议使用其它方法。

```
# ps -p1 | grep "init\|upstart\|systemd"
 1 ? 00:00:00 init
```

#### 方法 2：使用 rpm 命令

RPM 即 Red Hat Package Manager （红帽包管理），是一款功能强大的[安装包管理](https://www.2daygeek.com/category/package-management/)命令行工具，在基于 Red Hat 的发行版中使用，如 RHEL、CentOS、Fedora、openSUSE 和 Mageia。此工具可以在系统/服务上对软件进行安装、更新、删除、查询及验证等操作。通常 RPM 文件都带有 `.rpm` 后缀。

RPM 会使用必要的库和依赖库来构建软件，并且不会与系统上安装的其它包冲突。

```
# rpm -qf /sbin/init
SysVinit-2.86-17.el5
```

#### 方法 3：使用 /sbin/init 文件

`/sbin/init` 程序会将根文件系统从内存加载或切换到磁盘。

这是启动过程的主要部分。这个进程开始时的运行级别为 “N”（无）。`/sbin/init` 程序会按照 `/etc/inittab` 配制文件的描述来初始化系统。

```
# /sbin/init --version
init (upstart 0.6.5)
Copyright (C) 2010 Canonical Ltd.

This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```

---

via: <https://www.2daygeek.com/how-to-determine-which-init-system-manager-is-running-on-linux-system/>

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

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


在命令行使用 Pandoc 进行文件转换

Summary: 这篇指南介绍如何使用 Pandoc 将文档转换为多种不同的格式。

这篇指南介绍如何使用 Pandoc 将文档转换为多种不同的格式。

>
> 这篇指南介绍如何使用 Pandoc 将文档转换为多种不同的格式。
>
>
>

![](/data/attachment/album/201811/14/114139ezvobh2yenkhobzy.jpg)

Pandoc 是一个命令行工具，用于将文件从一种标记语言转换为另一种标记语言。标记语言使用标签来标记文档的各个部分。常用的标记语言包括 Markdown、ReStructuredText、HTML、LaTex、ePub 和 Microsoft Word DOCX。

简单来说，[Pandoc](https://pandoc.org/) 允许你将一些文件从一种标记语言转换为另一种标记语言。典型的例子包括将 Markdown 文件转换为演示文稿、LaTeX，PDF 甚至是 ePub。

本文将解释如何使用 Pandoc 从单一标记语言（在本文中为 Markdown）生成多种格式的文档，引导你完成从 Pandoc 安装，到展示如何创建多种类型的文档，再到提供有关如何编写易于移植到其他格式的文档的提示。

文中还将解释使用元信息文件对文档内容和元信息（例如，作者姓名、使用的模板、书目样式等）进行分离的意义。

### Pandoc 安装和要求

Pandoc 默认安装在大多数 Linux 发行版中。本教程使用 pandoc-2.2.3.2 和 pandoc-citeproc-0.14.3。如果不打算生成 PDF，那么这两个包就足够了。但是，我建议也安装 texlive，这样就可以选择生成 PDF 了。

通过以下命令在 Linux 上安装这些程序：

```
sudo apt-get install pandoc pandoc-citeproc texlive
```

您可以在 Pandoc 的网站上找到其他平台的 [安装说明](http://pandoc.org/installing.html)。

我强烈建议安装 [pandoc-crossref](https://hackage.haskell.org/package/pandoc-crossref)，这是一个“用于对图表，方程式，表格和交叉引用进行编号的过滤器”。最简单的安装方式是下载 [预构建的可执行文件](https://github.com/lierdakil/pandoc-crossref/releases/tag/v0.3.2.1)，但也可以通过以下命令从 Haskell 的软件包管理器 cabal 安装它：

```
cabal update
cabal install pandoc-crossref
```

如果需要额外的 Haskell [安装信息](https://github.com/lierdakil/pandoc-crossref#installation)，请参考 pandoc-crossref 的 GitHub 仓库。

### 几个例子

我将通过解释如何生成三种类型的文档来演示 Pandoc 的工作原理：

* 由包含数学公式的 LaTeX 文件创建的网页
* 由 Markdown 文件生成的 Reveal.js 幻灯片
* 混合 Markdown 和 LaTeX 的合同文件

#### 创建一个包含数学公式的网站

Pandoc 的优势之一是以不同的输出文件格式显示数学公式。例如，我们可以从包含一些数学符号（用 LaTeX 编写）的 LaTeX 文档（名为 `math.tex`）生成一个网页。

`math.tex` 文档如下所示：

```
% Pandoc math demos

$a^2 + b^2 = c^2$

$v(t) = v_0 + \frac{1}{2}at^2$

$\gamma = \frac{1}{\sqrt{1 - v^2/c^2}}$

$\exists x \forall y (Rxy \equiv Ryx)$

$p \wedge q \models p$

$\Box\diamond p\equiv\diamond p$

$\int_{0}^{1} x dx = \left[ \frac{1}{2}x^2 \right]_{0}^{1} = \frac{1}{2}$

$e^x = \sum_{n=0}^\infty \frac{x^n}{n!} = \lim_{n\rightarrow\infty} (1+x/n)^n$
```

通过输入以下命令将 LaTeX 文档转换为名为 `mathMathML.html` 的网站：

```
pandoc math.tex -s --mathml  -o mathMathML.html
```

参数 `-s` 告诉 Pandoc 生成一个独立的网页（而不是网页片段，因此它将包括 HTML 中的 head 和 body 标签），`-mathml` 参数强制 Pandoc 将 LaTeX 中的数学公式转换成 MathML，从而可以由现代浏览器进行渲染。

![](/data/attachment/album/201811/13/115210az5oevcc7nnj7rmr.png)

看一下 [网页效果](http://pandoc.org/demo/mathMathML.html) 和 [代码](https://github.com/kikofernandez/pandoc-examples/tree/master/math)，代码仓库中的 Makefile 使得运行更加简单。

#### 制作一个 Reveal.js 幻灯片

使用 Pandoc 从 Markdown 文件生成简单的演示文稿很容易。幻灯片包含顶级幻灯片和下面的嵌套幻灯片。可以通过键盘控制演示文稿，从一个顶级幻灯片跳转到下一个顶级幻灯片，或者显示顶级幻灯片下面的嵌套幻灯片。 这种结构在基于 HTML 的演示文稿框架中很常见。

创建一个名为 `SLIDES` 的幻灯片文档（参见 [代码仓库](https://github.com/kikofernandez/pandoc-examples/tree/master/slides)）。首先，在 `％` 后面添加幻灯片的元信息（例如，标题、作者和日期）：

```
% Case Study
% Kiko Fernandez Reyes
% Sept 27, 2017
```

这些元信息同时也创建了第一张幻灯片。要添加更多幻灯片，使用 Markdown 的一级标题（在下面例子中的第5行，参考 [Markdown 的一级标题](https://daringfireball.net/projects/markdown/syntax#header) ）生成顶级幻灯片。

例如，可以通过以下命令创建一个标题为 “Case Study”、顶级幻灯片名为 “Wine Management System” 的演示文稿：

```
% Case Study
% Kiko Fernandez Reyes
% Sept 27, 2017

# Wine Management System
```

使用 Markdown 的二级标题将内容（比如包含一个新管理系统的说明和实现的幻灯片）放入刚刚创建的顶级幻灯片。下面添加另外两张幻灯片（在下面例子中的第 7 行和 14 行 ，参考 [Markdown 的二级标题](https://daringfireball.net/projects/markdown/syntax#header) ）。

* 第一个二级幻灯片的标题为 “Idea”，并显示瑞士国旗的图像
* 第二个二级幻灯片的标题为 “Implementation”

```
% Case Study
% Kiko Fernandez Reyes
% Sept 27, 2017

# Wine Management System

## <img src="img/SwissFlag.png" style="vertical-align:middle"/> Idea

## Implementation
```

我们现在有一个顶级幻灯片（`＃Wine Management System`），其中包含两张幻灯片（`## Idea` 和 `## Implementation`）。

通过创建一个由符号 `>` 开头的 Markdown 列表，在这两张幻灯片中添加一些内容。在上面代码的基础上，在第一张幻灯片中添加两个项目（第 9-10 行），第二张幻灯片中添加五个项目（第 16-20 行）：

```
% Case Study
% Kiko Fernandez Reyes
% Sept 27, 2017

# Wine Management System

## <img src="img/SwissFlag.png" style="vertical-align:middle"/> Idea

>- Swiss love their **wine** and cheese
>- Create a *simple* wine tracker system

![](img/matterhorn.jpg)

## Implementation

>- Bottles have a RFID tag
>- RFID reader (emits and read signal)
>- **Raspberry Pi**
>- **Server (online shop)**
>- Mobile app
```

上面的代码添加了马特洪峰的图像，也可以使用纯 Markdown 语法或添加 HTML 标签来改进幻灯片。

要生成幻灯片，Pandoc 需要引用 Reveal.js 库，因此它必须与 `SLIDES` 文件位于同一文件夹中。生成幻灯片的命令如下所示：

```
pandoc -t revealjs -s --self-contained SLIDES \
-V theme=white -V slideNumber=true -o index.html
```

![](/data/attachment/album/201811/13/115212vcexdsaxsk0fe4ze.png)

上面的 Pandoc 命令使用了以下参数：

* `-t revealjs` 表示将输出一个 revealjs 演示文稿
* `-s` 告诉 Pandoc 生成一个独立的文档
* `--self-contained` 生成没有外部依赖关系的 HTML 文件
* `-V` 设置以下变量：
	+ `theme=white` 将幻灯片的主题设为白色
	+ `slideNumber=true` 显示幻灯片编号
* `-o index.html` 在名为 `index.html` 的文件中生成幻灯片

为了简化操作并避免键入如此长的命令，创建以下 Makefile：

```
all: generate

generate:
    pandoc -t revealjs -s --self-contained SLIDES \
    -V theme=white -V slideNumber=true -o index.html

clean: index.html
    rm index.html

.PHONY: all clean generate
```

可以在 [这个仓库](https://github.com/kikofernandez/pandoc-examples/tree/master/slides) 中找到所有代码。

#### 制作一份多种格式的合同

假设你正在准备一份文件，并且（这样的情况现在很常见）有些人想用 Microsoft Word 格式，其他人使用自由软件，想要 ODT 格式，而另外一些人则需要 PDF。你不必使用 OpenOffice 或 LibreOffice 来生成 DOCX 或 PDF 格式的文件，可以用 Markdown 创建一份文档（如果需要高级格式，可以使用一些 LaTeX 语法），并生成任何这些文件类型。

和以前一样，首先声明文档的元信息（标题、作者和日期）：

```
% Contract Agreement for Software X
% Kiko Fernandez-Reyes
% August 28th, 2018
```

然后在 Markdown 中编写文档（如果需要高级格式，则添加 LaTeX）。例如，创建一个固定间隔的表格（在 LaTeX 中用 `\hspace{3cm}` 声明）以及客户端和承包商应填写的行（在 LaTeX 中用 `\hrulefill` 声明）。之后，添加一个用 Markdown 编写的表格。

创建的文档如下所示：

![](/data/attachment/album/201811/13/115214jcfyw02rowc0iocw.png)

创建此文档的代码如下：

```
% Contract Agreement for Software X
% Kiko Fernandez-Reyes
% August 28th, 2018

...

### Work Order

\begin{table}[h]
\begin{tabular}{ccc}
The Contractor & \hspace{3cm} & The Customer \\
& & \\
& & \\
\hrulefill & \hspace{3cm} & \hrulefill \\
%
Name & \hspace{3cm} & Name \\
& & \\
& & \\
\hrulefill & \hspace{3cm} & \hrulefill \\
...
\end{tabular}
\end{table}

\vspace{1cm}

+--------------------------------------------|----------|-------------+
| Type of Service                            | Cost     |     Total   |
+:===========================================+=========:+:===========:+
| Game Engine                                | 70.0     | 70.0        |
|                                            |          |             |
+--------------------------------------------|----------|-------------+
|                                            |          |             |
+--------------------------------------------|----------|-------------+
| Extra: Comply with defined API functions   | 10.0     | 10.0        |
|        and expected returned format        |          |             |
+--------------------------------------------|----------|-------------+
|                                            |          |             |
+--------------------------------------------|----------|-------------+
| **Total Cost**                             |          | **80.0**    |
+--------------------------------------------|----------|-------------+
```

要生成此文档所需的三种不同输出格式，编写如下的 Makefile：

```
DOCS=contract-agreement.md

all: $(DOCS)
    pandoc -s $(DOCS) -o $(DOCS:md=pdf)
    pandoc -s $(DOCS) -o $(DOCS:md=docx)
    pandoc -s $(DOCS) -o $(DOCS:md=odt)

clean:
    rm *.pdf *.docx *.odt

.PHONY: all clean
```

4 到 7 行是生成三种不同输出格式的具体命令：

如果有多个 Markdown 文件并想将它们合并到一个文档中，需要按照希望它们出现的顺序编写命令。例如，在撰写本文时，我创建了三个文档：一个介绍文档、三个示例和一些高级用法。以下命令告诉 Pandoc 按指定的顺序将这些文件合并在一起，并生成一个名为 document.pdf 的 PDF 文件。

```
pandoc -s introduction.md examples.md advanced-uses.md -o document.pdf
```

### 模板和元信息

编写复杂的文档并非易事，你需要遵循一系列独立于内容的规则，例如使用特定的模板、编写摘要、嵌入特定字体，甚至可能要声明关键字。所有这些都与内容无关：简单地说，它就是元信息。

Pandoc 使用模板生成不同的输出格式。例如，有一个 LaTeX 的模板，还有一个 ePub 的模板，等等。这些模板的元信息中有未赋值的变量。使用以下命令找出 Pandoc 模板中可用的元信息：

```
pandoc -D FORMAT
```

例如，LaTex 的模版是：

```
pandoc -D latex
```

按照以下格式输出：

```
$if(title)$
\title{$title$$if(thanks)$\thanks{$thanks$}$endif$}
$endif$
$if(subtitle)$
\providecommand{\subtitle}[1]{}
\subtitle{$subtitle$}
$endif$
$if(author)$
\author{$for(author)$$author$$sep$ \and $endfor$}
$endif$
$if(institute)$
\providecommand{\institute}[1]{}
\institute{$for(institute)$$institute$$sep$ \and $endfor$}
$endif$
\date{$date$}
$if(beamer)$
$if(titlegraphic)$
\titlegraphic{\includegraphics{$titlegraphic$}}
$endif$
$if(logo)$
\logo{\includegraphics{$logo$}}
$endif$
$endif$

\begin{document}
```

如你所见，输出的内容中有标题、致谢、作者、副标题和机构模板变量（还有许多其他可用的变量）。可以使用 YAML 元区块轻松设置这些内容。 在下面例子的第 1-5 行中，我们声明了一个 YAML 元区块并设置了一些变量（使用上面合同协议的例子）：

```
---
title: Contract Agreement for Software X
author: Kiko Fernandez-Reyes
date: August 28th, 2018
---

(continue writing document as in the previous example)
```

这样做非常奏效，相当于以前的代码：

```
% Contract Agreement for Software X
% Kiko Fernandez-Reyes
% August 28th, 2018
```

然而，这样做将元信息与内容联系起来，也即 Pandoc 将始终使用此信息以新格式输出文件。如果你将要生成多种文件格式，最好要小心一点。例如，如果你需要以 ePub 和 HTML 的格式生成合同，并且 ePub 和 HTML 需要不同的样式规则，该怎么办？

考虑一下这些情况：

* 如果你只是尝试嵌入 YAML 变量 `css:style-epub.css`，那么将从 HTML 版本中移除该变量。这不起作用。
* 复制文档显然也不是一个好的解决方案，因为一个版本的更改不会与另一个版本同步。
* 你也可以像下面这样将变量添加到 Pandoc 命令中：

```
pandoc -s -V css=style-epub.css document.md document.epub
pandoc -s -V css=style-html.css document.md document.html
```

我的观点是，这样做很容易从命令行忽略这些变量，特别是当你需要设置数十个变量时（这可能出现在编写复杂文档的情况中）。现在，如果将它们放在同一文件中（`meta.yaml` 文件），则只需更新或创建新的元信息文件即可生成所需的输出格式。然后你会编写这样的命令：

```
pandoc -s meta-pub.yaml document.md document.epub
pandoc -s meta-html.yaml document.md document.html
```

这是一个更简洁的版本，你可以从单个文件更新所有元信息，而无需更新文档的内容。

### 结语

通过以上的基本示例，我展示了 Pandoc 在将 Markdown 文档转换为其他格式方面是多么出色。

---

via: <https://opensource.com/article/18/9/intro-pandoc>

作者：[Kiko Fernandez-Reyes](https://opensource.com/users/kikofernandez) 选题：[lujun9972](https://github.com/lujun9972) 译者：[jlztan](https://github.com/jlztan) 校对：[wxy](https://github.com/wxy)

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


使用 SonarQube 追踪代码问题

>
> 通过不断分析代码以了解潜在的质量问题，开源的 SonarQube 项目支持了 DevOps 的“尽早发布和经常发布” 的思维模式。
>
>
>

![](/data/attachment/album/201811/14/092831ypfrsw7f2z5iyy2w.jpg)

越来越多的组织正在实施 [DevOps](https://en.wikipedia.org/wiki/DevOps) 以便在通过中间开发和测试环境以后更快更好的将新代码引入到生产环境。虽然版本控制、持续集成和部署以及自动化测试都属于 DevOps 的范畴，但仍然存在一个关键问题：组织如何量化代码质量，而不仅仅是部署的速度？

[SonarQube](https://www.sonarqube.org/) 是用来填补这个空隙的一种选择。它是一个开源平台，通过代码的自动化静态分析不断的检查代码质量。 SonarQube 支持 20 多种语言的分析，并在各种类型的项目中输出和存储问题。

SonarQube 同时也提供了一个可同时维护和管理不同项目、不同代码的集中的环境。可以为每个项目定制规则。持续的检查和分析代码的健康轨迹。

SonarQube 还可以集成到可持续集成和开发（CI/CD）流程中，协助和自动确定代码是否为生产环境做好了准备的过程。

### 它可以衡量什么

开箱即用，SonarQube 可以测量的关键指标，包括代码错误、<ruby> 代码异味 <rt>  code smells </rt></ruby>、安全漏洞和重复的代码。

* **代码错误** 是代码中的一部分不正确或无法正常运行、可能会导致错误的结果，是指那些在代码发布到生产环境之前应该被修复的明显的错误。
* [代码异味](https://en.wikipedia.org/wiki/Code_smell) 不同于代码错误，被检测到的代码是可能能正确执行并符合预期。然而，它不容易被修复，也不能被单元测试覆盖，却可能会导致一些未知的错误，或是一些其它的问题。从长期的可维护性来讲，立即修复代码异味是明智之举。通常在编写代码的时候，代码异味并不容易被发现，而 SonarQube 的静态分析是一种发现它们的很好的方式。
* **安全漏洞** 正如听起来的一样：指的是现在的代码中可能存在的安全问题的缺陷。这些缺陷应该立即修复来防止黑客利用它们。
* **重复的代码** 也和听起来的一样：指的是源代码中重复的部分。代码重复在软件设计中是一种很不好的做法。总的来说，如果对一部分代码进行更改而另一部分没有，则会导致一些维护性的问题。例如，识别重复的代码可以很容易的将重复的代码打包成一个库来重复的使用。

### 可自定义的选项

因为它是开源的，所以 SonarQube 鼓励用户开发和提供可定制的选项。目前有超过 60 个[插件](https://docs.sonarqube.org/display/PLUG/Plugin+Library) 可用于增强 SonarQube 开箱即用的分析功能。

大多数的插件是为了增加 SonarQube 可以分析的编程语言的数量。另一些插件可以分析一些额外的指标甚至包括一些显示的仪表盘视图。实际上，如果组织需要检查一些自定义指标，或是想要在自己的仪表盘和以特定的方式查看分析数据，或使用 SonarQube 不支持的编程语言，则可能存在一些自定义的选项可以使用。如果你想要的功能并不支持，SonarQube 源码的开放也为你自己开发新的功能提供了可能性。

用户还可以定制适用于每种特定编程语言分析器的规则。通过 SonarQube 用户界面，可以按语言和按项目选择和取消规则。这些为特定的项目指定的规则，可以很好的在一个集中的位置维护所有的数据和配置。

### 为什么它那么重要

SonarQube 为组织提供了一个集中的位置来管理和跟踪多个项目代码中的问题。它还可以把持续的检查与质量门限相结合。一旦项目分析过一次以后，更进一步的分析会参考软件最新的修改来更新原始的统计信息，以反映最新的变化。这些跟踪可以让用户看到问题解决的程度和速度。这与 “尽早发布并经常发布”不谋而合。

另外，SonarQube 可使用 [可持续集成流程](https://jenkins.io/blog/2017/04/18/continuousdelivery-devops-sonarqube/)，比如像 [Hudson](https://en.wikipedia.org/wiki/Hudson_(software)) 和 [Jenkins](https://en.wikipedia.org/wiki/Jenkins_(software)) 这样的工具。这个质量门限可以很好的反映代码的整体运行状况，并且通过 Jenkins 等集成工具，在发布代码到生产环境时担任一个重要的角色。

本着 DevOps 的精神， SonarQube 可以量化代码质量，来达到组织内部的要求。为了加快代码生产和发布的周期，组织必须意识到它们自己的技术债务和软件问题。通过发现这些信息， SonarQube 可以帮助组织更快的生成高质量的软件。

### 想要了解更多吗？

SonarQube 基于 GUN 通用公共许可证发布，它的源码可以在 [GitHub](https://github.com/SonarSource/sonarqube) 上查看。越来越多的用户对 SonarQube 的特性和功能感兴趣。 [Twitter](https://twitter.com/SonarQube) 和 [Google](https://groups.google.com/forum/#!forum/sonarqube) 上有活跃的社区。这些社区以及 [SonarQube 博客](https://blog.sonarsource.com/) 对任何有兴趣开始和使用 SonarQube 的人有很有帮助。

---

via: <https://opensource.com/article/17/10/sonarqube>

作者：[Sophie Polson](https://opensource.com/users/sophiepolson) 译者：[Jamkr](https://github.com/Jamkr) 校对：[wxy](https://github.com/wxy)

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


使用 Redis 和 Python 构建一个共享单车的应用程序

Summary: 学习如何使用 Redis 和 Python 构建一个位置感知的应用程序。

学习如何使用 Redis 和 Python 构建一个位置感知的应用程序。

>
> 学习如何使用 Redis 和 Python 构建一个位置感知的应用程序。
>
>
>

![](/data/attachment/album/201811/14/112130al1dd1ctqtcs0utc.png)

我经常出差。但不是一个汽车狂热分子，所以当我有空闲时，我更喜欢在城市中散步或者骑单车。我参观过的许多城市都有共享单车系统，你可以租个单车用几个小时。大多数系统都有一个应用程序来帮助用户定位和租用他们的单车，但对于像我这样的用户来说，在一个地方可以获得可租赁的城市中所有单车的信息会更有帮助。

为了解决这个问题并且展示开源的强大还有为 Web 应用程序添加位置感知的功能，我组合了可用的公开的共享单车数据、[Python](https://www.python.org/) 编程语言以及开源的 [Redis](https://redis.io/) 内存数据结构服务，用来索引和查询地理空间数据。

由此诞生的共享单车应用程序包含来自很多不同的共享系统的数据，包括纽约市的 [Citi Bike](https://www.citibikenyc.com/) 共享单车系统（LCTT 译注：Citi Bike 是纽约市的一个私营公共单车系统。在 2013 年 5 月 27 日正式营运，是美国最大的公共单车系统。Citi Bike 的名称有两层意思。Citi 是计划赞助商花旗银行（CitiBank）的名字。同时，Citi 和英文中“城市（city）”一词的读音相同）。它利用了花旗单车系统提供的 <ruby> 通用共享单车数据流 <rt>  General Bikeshare Feed </rt></ruby>，并利用其数据演示了一些使用 Redis 地理空间数据索引的功能。 花旗单车数据可按照 [花旗单车数据许可协议](https://www.citibikenyc.com/data-sharing-policy) 提供。

### 通用共享单车数据流规范

<ruby> 通用共享单车数据流规范 <rt>  General Bikeshare Feed Specification </rt></ruby>（GBFS）是由 [北美共享单车协会](http://nabsa.net/) 开发的 [开放数据规范](https://github.com/NABSA/gbfs)，旨在使地图程序和运输程序更容易的将共享单车系统添加到对应平台中。 目前世界上有 60 多个不同的共享系统使用该规范。

Feed 流由几个简单的 [JSON](https://www.json.org/) 数据文件组成，其中包含系统状态的信息。 Feed 流以一个顶级 JSON 文件开头，其引用了子数据流的 URL：

```
{
    "data": {
        "en": {
            "feeds": [
                {
                    "name": "system_information",
                    "url": "https://gbfs.citibikenyc.com/gbfs/en/system_information.json"
                },
                {
                    "name": "station_information",
                    "url": "https://gbfs.citibikenyc.com/gbfs/en/station_information.json"
                },
                . . .
            ]
        }
    },
    "last_updated": 1506370010,
    "ttl": 10
}
```

第一步是使用 `system_information` 和 `station_information` 的数据将共享单车站的信息加载到 Redis 中。

`system_information` 提供系统 ID，系统 ID 是一个简短编码，可用于为 Redis 键名创建命名空间。 GBFS 规范没有指定系统 ID 的格式，但确保它是全局唯一的。许多共享单车数据流使用诸如“coastbikeshare”，“boisegreenbike” 或者 “topekametro\_bikes” 这样的短名称作为系统 ID。其他的使用常见的有地理缩写，例如 NYC 或者 BA，并且使用通用唯一标识符（UUID）。 这个共享单车应用程序使用该标识符作为前缀来为指定系统构造唯一键。

`station_information` 数据流提供组成整个系统的共享单车站的静态信息。车站由具有多个字段的 JSON 对象表示。车站对象中有几个必填字段，用于提供物理单车站的 ID、名称和位置。还有几个可选字段提供有用的信息，例如最近的十字路口、可接受的付款方式。这是共享单车应用程序这一部分的主要信息来源。

### 建立数据库

我编写了一个示例应用程序 [loadstationdata.py](https://gist.github.com/tague/5a82d96bcb09ce2a79943ad4c87f6e15)，它模仿后端进程中从外部源加载数据时会发生什么。

### 查找共享单车站

从 [GitHub 上 GBFS 仓库](https://github.com/NABSA/gbfs)中的 [systems.csv](https://github.com/NABSA/gbfs/blob/master/systems.csv) 文件开始加载共享单车数据。

仓库中的 [systems.csv](https://github.com/NABSA/gbfs/blob/master/systems.csv) 文件提供已注册的共享单车系统及可用的 GBFS 数据流的<ruby> 发现 URL <rt>  discovery URL </rt></ruby>。 这个发现 URL 是处理共享单车信息的起点。

`load_station_data` 程序获取系统文件中找到的每个发现 URL，并使用它来查找两个子数据流的 URL：系统信息和车站信息。 系统信息提供提供了一条关键信息：系统的唯一 ID。 （注意：系统 ID 也在 `systems.csv` 文件中提供，但文件中的某些标识符与数据流中的标识符不匹配，因此我总是从数据流中获取标识符。）系统上的详细信息，比如共享单车 URL、电话号码和电子邮件， 可以在程序的后续版本中添加，因此使用 `${system_id}:system_info` 这个键名将数据存储在 Redis 中。

### 载入车站数据

车站信息提供系统中每个车站的数据，包括该系统的位置。`load_station_data` 程序遍历车站数据流中的每个车站，并使用 `${system_id}:station:${station_id}` 形式的键名将每个车站的数据存储到 Redis 中。 使用 `GEOADD` 命令将每个车站的位置添加到共享单车的地理空间索引中。

### 更新数据

在后续运行中，我不希望代码从 Redis 中删除所有 Feed 数据并将其重新加载到空的 Redis 数据库中，因此我仔细考虑了如何处理数据的原地更新。

代码首先加载所有需要系统在内存中处理的共享单车站的信息数据集。 当加载了一个车站的信息时，该站就会按照 Redis 键名从内存中的车站集合中删除。 加载完所有车站数据后，我们就剩下一个包含该系统所有必须删除的车站数据的集合。

程序迭代处理该数据集，并创建一个事务删除车站的信息，从地理空间索引中删除该车站的键名，并从系统的车站列表中删除该车站。

### 代码重点

在[示例代码](https://gist.github.com/tague/5a82d96bcb09ce2a79943ad4c87f6e15)中有一些值得注意的地方。 首先，使用 `GEOADD` 命令将所有数据项添加到地理空间索引中，而使用 `ZREM` 命令将其删除。 由于地理空间类型的底层实现使用了有序集合，因此需要使用 ZREM 删除数据项。 需要注意的是：为简单起见，示例代码演示了如何在单个 Redis 节点工作； 为了在集群环境中运行，需要重新构建事务块。

如果你使用的是 Redis 4.0（或更高版本），则可以在代码中使用 `DELETE` 和 `HMSET` 命令。 Redis 4.0 提供 `UNLINK` 命令作为 `DELETE` 命令的异步版本的替代。 `UNLINK` 命令将从键空间中删除键，但它会在另外的线程中回收内存。 在 Redis 4.0 中 [HMSET 命令已经被弃用了而且 HSET 命令现在接收可变参数](https://raw.githubusercontent.com/antirez/redis/4.0/00-RELEASENOTES)（即，它接受的参数个数不定）。

### 通知客户端

处理结束时，会向依赖我们数据的客户端发送通知。 使用 Redis 发布/订阅机制，通知将通过 `geobike:station_changed` 通道和系统 ID 一起发出。

### 数据模型

在 Redis 中构建数据时，最重要的考虑因素是如何查询信息。 共享单车程序需要支持的两个主要查询是：

* 找到我们附近的车站
* 显示车站相关的信息

Redis 提供了两种主要数据类型用于存储数据：哈希和有序集。 [哈希类型](https://redis.io/topics/data-types#Hashes)很好地映射到表示车站的 JSON 对象；由于 Redis 哈希不使用固定的数据结构，因此它们可用于存储可变的车站信息。

当然，在地理位置上寻找站点需要地理空间索引来搜索相对于某些坐标的站点。 Redis 提供了[几个](https://redis.io/commands#geo)使用[有序集](https://redis.io/topics/data-types-intro#redis-sorted-sets)数据结构构建地理空间索引的命令。

我们使用 `${system_id}:station:${station_id}` 这种格式的键名存储车站相关的信息，使用 `${system_id}:stations:location` 这种格式的键名查找车站的地理空间索引。

### 获取用户位置

构建应用程序的下一步是确定用户的当前位置。 大多数应用程序通过操作系统提供的内置服务来实现此目的。 操作系统可以基于设备内置的 GPS 硬件为应用程序提供定位，或者从设备的可用 WiFi 网络提供近似的定位。

### 查找车站

找到用户的位置后，下一步是找到附近的共享单车站。 Redis 的地理空间功能可以返回用户当前坐标在给定距离内的所有车站信息。 以下是使用 Redis 命令行界面的示例。

![](/data/attachment/album/201811/14/112130g9bq4qgi1xqll4qm.png)

想象一下，我正在纽约市第五大道的苹果零售店，我想要向市中心方向前往位于西 37 街的 MOOD 布料店，与我的好友 [Swatch](https://twitter.com/swatchthedog) 相遇。 我可以坐出租车或地铁，但我更喜欢骑单车。 附近有没有我可以使用的单车共享站呢？

苹果零售店位于 40.76384，-73.97297。 根据地图显示，在零售店 500 英尺半径范围内（地图上方的蓝色）有两个单车站，分别是陆军广场中央公园南单车站和东 58 街麦迪逊单车站。

我可以使用 Redis 的 `GEORADIUS` 命令查询 500 英尺半径范围内的车站的 `NYC` 系统索引：

```
127.0.0.1:6379> GEORADIUS NYC:stations:location -73.97297 40.76384 500 ft
1) "NYC:station:3457"
2) "NYC:station:281"
```

Redis 使用地理空间索引中的元素作为特定车站的元数据的键名，返回在该半径内找到的两个共享单车站。 下一步是查找两个站的名称：

```
127.0.0.1:6379> hget NYC:station:281 name
"Grand Army Plaza & Central Park S"

127.0.0.1:6379> hget NYC:station:3457 name
"E 58 St & Madison Ave"
```

这些键名对应于上面地图上标识的车站。 如果需要，可以在 `GEORADIUS` 命令中添加更多标志来获取元素列表，每个元素的坐标以及它们与当前点的距离：

```
127.0.0.1:6379> GEORADIUS NYC:stations:location -73.97297 40.76384 500 ft WITHDIST WITHCOORD ASC
1) 1) "NYC:station:281"
   2) "289.1995"
   3) 1) "-73.97371262311935425"
      2) "40.76439830559216659"
2) 1) "NYC:station:3457"
   2) "383.1782"
   3) 1) "-73.97209256887435913"
      2) "40.76302702144496237"
```

查找与这些键名关联的名称会生成一个我可以从中选择的车站的有序列表。 Redis 不提供方向和路线的功能，因此我使用设备操作系统的路线功能绘制从当前位置到所选单车站的路线。

`GEORADIUS` 函数可以很轻松的在你喜欢的开发框架的 API 里实现，这样就可以向应用程序添加位置功能了。

### 其他的查询命令

除了 `GEORADIUS` 命令外，Redis 还提供了另外三个用于查询索引数据的命令：`GEOPOS`、`GEODIST` 和 `GEORADIUSBYMEMBER`。

`GEOPOS` 命令可以为 <ruby> 地理哈希 <rt>  geohash </rt></ruby> 中的给定元素提供坐标（LCTT 译注：geohash 是一种将二维的经纬度编码为一位的字符串的一种算法，常用于基于距离的查找算法和推荐算法）。 例如，如果我知道西 38 街 8 号有一个共享单车站，ID 是 523，那么该站的元素名称是 `NYC:station:523`。 使用 Redis，我可以找到该站的经度和纬度：

```
127.0.0.1:6379> geopos NYC:stations:location NYC:station:523
1) 1) "-73.99138301610946655"
   2) "40.75466497634030105"
```

`GEODIST` 命令提供两个索引元素之间的距离。 如果我想找到陆军广场中央公园南单车站与东 58 街麦迪逊单车站之间的距离，我会使用以下命令：

```
127.0.0.1:6379> GEODIST NYC:stations:location NYC:station:281 NYC:station:3457 ft
"671.4900"
```

最后，`GEORADIUSBYMEMBER` 命令与 `GEORADIUS` 命令类似，但该命令不是采用一组坐标，而是采用索引的另一个成员的名称，并返回以该成员为中心的给定半径内的所有成员。 要查找陆军广场中央公园南单车站 1000 英尺范围内的所有车站，请输入以下内容：

```
127.0.0.1:6379> GEORADIUSBYMEMBER NYC:stations:location NYC:station:281 1000 ft WITHDIST
1) 1) "NYC:station:281"
   2) "0.0000"
2) 1) "NYC:station:3132"
   2) "793.4223"
3) 1) "NYC:station:2006"
   2) "911.9752"
4) 1) "NYC:station:3136"
   2) "940.3399"
5) 1) "NYC:station:3457"
   2) "671.4900"
```

虽然此示例侧重于使用 Python 和 Redis 来解析数据并构建共享单车系统位置的索引，但可以很容易地衍生为定位餐馆、公共交通或者是开发人员希望帮助用户找到的任何其他类型的场所。

本文基于今年我在北卡罗来纳州罗利市的开源 101 会议上的[演讲](http://opensource101.com/raleigh/talks/building-location-aware-apps-open-source-tools/)。

---

via: <https://opensource.com/article/18/2/building-bikesharing-application-open-source-tools>

作者：[Tague Griffith](https://opensource.com/users/tague) 译者：[Flowsnow](https://github.com/Flowsnow) 校对：[wxy](https://github.com/wxy)

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


Sed 命令完全指南

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

好

6的一批

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

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

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

![](/data/attachment/album/201811/14/191948l9y6vzqu18yz18v4.jpg)

### 关于 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 脚本时浪费在摸索上的时间。

![The Sed execution model](/data/attachment/album/201811/14/190635j1ycygaxrcj1vege.png)

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

### 打印命令

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

![The Sed `print` command](/data/attachment/album/201811/14/190636pcocoolwrzffeaco.png)

示例：

```
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。

![The Sed quit command](/data/attachment/album/201811/14/190639nosvhks8ch5vekvk.png)

`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 中最著名的命令之一，在网上有大量的关于这个命令的文档。

![The Sed `substitution` command](/data/attachment/album/201811/14/190640qepmzabc2p6e0dxd.png)

[在前一篇文章](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）也不会输出。

![The Sed `delete` command](/data/attachment/album/201811/14/190641h144h1mhhxcpcmst.png)

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

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

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

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

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

### 次行命令

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

![The Sed next command](/data/attachment/album/201811/14/190642cvfwtvwefmvlgr5i.png)

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

```
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`）将交换保持空间和模式空间的内容。记住，你只要没有把任何东西放入到保持空间中，那么保持空间就是空的。

![The Sed exchange command](/data/attachment/album/201811/14/190643lzxd4ndcw79al96w.png)

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

```
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` 将模式空间中的内容追加到保持空间中，使用一个新行作为分隔符。

![The Sed hold command](/data/attachment/album/201811/14/190645pp8viir8ipvp8v8i.png)

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

```
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` 将保持空间中的内容追加到模式空间中，并使用一个新行作为分隔符

![The Sed get command](/data/attachment/album/201811/14/190646gb3npiuw8dbw1nz2.png)

将保持命令和获取命令一起使用，可以允许你去存储并调回数据。作为一个小挑战，我让你重写前一节中的示例，将输入文件的第 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` 读取输入并追加一个新行到模式空间，用一个新行作为新旧数据的分隔符。继续运行当前的循环。

![The Sed uppercase `Delete` command](/data/attachment/album/201811/14/190647j4o4j42hdpj92jmi.png)

![The Sed uppercase `Next` command](/data/attachment/album/201811/14/190649vsj1vdqhhwqn52kw.png)

这些命令的使用场景主要用于实现队列（[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” 语句，但是你可以使用分支来实现同样的功能。

![The Sed branch command](/data/attachment/album/201811/14/190650tywzx157elvlcmvz.png)

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

![The Sed label command](/data/attachment/album/201811/14/190651yrcciyy7yf7f55pc.png)

这是一个这样的示例：

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

![The Sed `test` command](/data/attachment/album/201811/14/190652yw1tne5ehgtrthht.png)

使用测试指令，你可以在一个 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` 命令一样删除模式空间的内容并开始一个新的循环。唯一的不同在于，当命令运行之后，用户提供的文本是写往输出的。

![The Sed change command](/data/attachment/album/201811/14/190654kn772h72u887ay72.png)

```
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\`）将立即在输出中给出用户提供的文本。它并不以任何方式修改程序流或缓冲区的内容。

![The Sed insert command](/data/attachment/album/201811/14/190655fj22mhwnmj7zw2ee.png)

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

#### 追加命令

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

![The Sed append command](/data/attachment/album/201811/14/190656lhcr2zq0r2rco0rf.png)

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

```
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 所创建。如果给定的目标文件已经存在，它将被覆写。

![The Sed write command](/data/attachment/album/201811/14/190657abitoduyd2hosysy.png)

因此，即便是你从未真的写入到该文件中，但该文件仍然会被创建。例如，下列的 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 的语法很晦涩，我不得不强调在脚本中需要的地方添加足够的注释。否则，除了作者外其他人将几乎无法理解它。

![The Sed comment command](/data/attachment/album/201811/14/190658fy96v9zdwyz3vc9i.png)

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

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

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

### 很少用得到的命令

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

#### 行数命令

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

![The Sed line number command](/data/attachment/album/201811/14/190659vdnd2uj221nj2ejj.png)

请记住，在严格的 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` 是不适用的。不在那个表中的不可打印字符将被写为一个三位八进制数字（在前面使用一个反斜杠 `\`），表示字符中的每个字节（最重要的字节在前面）。长行应该被换行，通过写一个反斜杠后跟一个换行符来表示换行位置；发生换行时的长度是不确定的，但应该适合输出设备的具体情况。每个行应该以一个 `$` 标记结束。
>
>
>

![The Sed unambiguous print command](/data/attachment/album/201811/14/190700z1fjx4ql299iznz6.png)

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

#### 移译命令

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

![The Sed transliterate command](/data/attachment/album/201811/14/190702th123v1b5lre3l8r.png)

```
# 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：使用的工具

![](/data/attachment/album/201811/15/093141rznmgo1nmnszsc1i.jpg)

在这个课程中你将使用两套工具：一个是 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 库允许你操作函数，使其更容易理解，更容易测试代码。

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

![](/data/attachment/album/201811/15/094212wlddczeiledlztjo.png)

在这个由两部分组成的系列文章的第二部分中，我们将继续探索如何将函数式编程方法中的好想法引入到 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: 我们大多数人只知道很少的命令，在本文中我们试图包含所有可能的命令。

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

![](/data/attachment/album/201811/15/190200pf1w100f20a1g2s1.jpg)

大多数系统管理员在遇到性能问题时会检查 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 系统监视器是一个管理正在运行的进程和监视系统资源的工具。它向你显示正在运行的程序以及耗费的处理器时间，内存和磁盘空间。

![](/data/attachment/album/201811/15/190214w9sxw7cxxsuc9k6x.png)

---

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: 成为开源社区的一员绝对是一个明智之举，原因有很多。

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

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

![](/data/attachment/album/201811/15/232200u1kgbq116bizdcdy.png)

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

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

![](/data/attachment/album/201811/15/232202tqfpic1lzhfgq0ri.gif)

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

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

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

你可能想知道这些如何用于大型金融服务机构的 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 图像的工具

![](/data/attachment/album/201811/15/233937pys5heheqq5ve2h5.jpg)

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

我们可以使用脚本命令来记录 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。

![](/data/attachment/album/201811/15/234008jd9zad6sftswtth3.gif)

```
# 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.
```

![](/data/attachment/album/201811/15/234011spwpf9ef100jzre0.gif)

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

使用以下命令来播放您记录的 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 了解你的日历和待办事项列表。
>
>
>

![](/data/attachment/album/201811/16/221558u8pfoqt65tf5iolo.jpg)

你是否需要复杂、功能丰富的图形或 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`。

![](/data/attachment/album/201811/16/221601lui77meiu769u8um.png)

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

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

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

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

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

![](/data/attachment/album/201811/16/221603sdrlglhwfzhc5bfn.png)

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

![](/data/attachment/album/201811/16/221607jtmpm7x3f6xcfe75.png)

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

![](/data/attachment/album/201811/16/221609yieaekmzhez4mrfh.png)

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

![](/data/attachment/album/201811/16/221610r54qakqquqfdo4v5.png)

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 命令的更好的工具。

![](/data/attachment/album/201811/16/230819b84x77o0i49m1rim.jpg)

大家对 `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
```

输出示例：

![](/data/attachment/album/201811/16/230821p2h2w62nk0223tuk.png)

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

```
$ dust -p
```

![dust 2](/data/attachment/album/201811/16/230822uzft5mik4fmt2xti.png)

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

```
$ 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
```

![](/data/attachment/album/201811/16/230824pwdwjb1wrtt0gp8j.png)

我使用 `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
```

输出示例：

![](/data/attachment/album/201811/16/230825ah0yhby3sf0l5kdd.png)

从上图可以看出，`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 游戏

![](/data/attachment/album/201811/17/100626h2qmsdumimdmumu8.jpg)

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>

![best-linux-games-2018-pillars-of-eternity-2-deadfire](/data/attachment/album/201811/17/100626lzrxe32prn7h27ne.jpg "best-linux-games-2018-pillars-of-eternity-2-deadfire")

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

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

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

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

![best-linux-games-2018-slay-the-spire](/data/attachment/album/201811/17/100627j6nr8rcc6veveoe5.jpg "best-linux-games-2018-slay-the-spire")

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

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

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

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

![best-linux-games-2018-battletech](/data/attachment/album/201811/17/100628ywaa7atsaww7w9ov.jpg "best-linux-games-2018-battletech")

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

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

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

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

![best-linux-games-2018-dead-cells](/data/attachment/album/201811/17/100629eh4t584sit8amaz4.jpg "best-linux-games-2018-dead-cells")

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

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

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

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

![best-linux-games-2018-iconoclasts](/data/attachment/album/201811/17/100630dj7ivd778jwlid8e.jpg "best-linux-games-2018-iconoclasts")

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

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

《叛逆机械师》是一个充满快乐与活力的平台游戏，融合了《<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/) 荣誉推出