使用开源同步工具 Syncthing 控制你的数据

Summary: 决定如何存储和共享您的个人信息。

决定如何存储和共享您的个人信息。

>
> 决定如何存储和共享您的个人信息。
>
>
>

![](/data/attachment/album/201810/28/221100kbn1vv61a3lt2b13.png)

如今，我们的一些最重要的财产 —— 从家人和朋友的照片和视频到财务和医疗文件 —— 都是数据。即便是云存储服务的迅猛发展，我们仍有对隐私和个人数据缺乏控制的担忧。从棱镜监控计划到谷歌[让 APP 开发者扫描你的个人邮件](https://gizmodo.com/google-says-it-doesnt-go-through-your-inbox-anymore-bu-1827299695)，这些新闻的报道应该会让我们对我们个人信息的安全性有所顾虑。

[Syncthing](https://syncthing.net/) 可以让你放下心来。它是一款开源的点对点文件同步工具，可以运行在 Linux、Windows、Mac、Android 和其他（抱歉，没有iOS）。Syncthing 使用自定的协议，叫[块交换协议](3)。简而言之，Syncting 能让你无需拥有服务器来跨设备同步数据。

在这篇文章中，我将解释如何在 Linux 电脑和安卓手机之间安装和同步文件。

### Linux

Syncting 在大多数流行的发行版都能下载。Fedora 28 包含其最新版本。

要在 Fedora 上安装 Syncthing，你能在软件中心搜索，或者执行以下命令：

```
sudo dnf install syncthing syncthing-gtk
```

一旦安装好后，打开它。你将会看到一个助手帮你配置 Syncthing。点击 “Next” 直到它要求配置 WebUI。最安全的选项是选择“Listen on localhost”。那将会禁止 Web 界面并且阻止未经授权的用户。

![](/data/attachment/album/201810/28/221458enntztxnwoe9nnlm.png)

*Syncthing 安装时的 WebUI 对话框*

关闭对话框。现在 Syncthing 安装好了。现在可以分享文件夹，连接一台设备开始同步了。但是，让我们用你其它的客户端继续。

### Android

Syncthing 在 Google Play 和 F-Droid 应用商店都能下载。

![](/data/attachment/album/201810/28/221144r6tj4mjvutbyuz49.png)

安装应用程序后，会显示欢迎界面。给 Syncthing 授予你设备存储的权限。你可能会被要求为了此应用程序而禁用电池优化。这样做是安全的，因为我们将优化应用程序，使其仅在插入电源并连接到无线网络时同步。

点击主菜单图标来到“Settings”，然后是“Run Conditions”（运行条件）。点击“Always run in the background, Run only when charging”（总是在后台运行，仅在充电时运行）和“Run only on wifi”（仅在 WIFI 下运行）。现在你的安卓客户端已经准备好与你的设备交换文件。

Syncting 中有两个重要的概念需要记住：文件夹和设备。文件夹是你想要分享的，但是你必须有一台设备来分享。 Syncthing 允许你用不同的设备分享独立的文件夹。设备是通过交换设备的 ID 来添加的。设备 ID 是在 Syncting 首次启动时创建的一个唯一的密码安全标识符。

### 连接设备

现在让我们连接你的 Linux 机器和你的 Android 客户端。

在您的 Linux 计算机中，打开 Syncting，单击“Settings”图标，然后单击“Show ID”，就会显示一个二维码。

在你的安卓手机上，打开 Syncthing。在主界面上，点击“Devices”页后点击 “+” 。在第一个区域内点击二维码符号来启动二维码扫描。

将你手机的摄像头对准电脑上的二维码。设备 ID 字段将由您的桌面客户端设备 ID 填充。起一个适合的名字并保存。因为添加设备有两种方式，现在你需要在电脑客户端上确认你想要添加安卓手机。你的电脑客户端可能会花上好几分钟来请求确认。当提示确认时，点击“Add”。

![](/data/attachment/album/201810/28/221148eakp1acca5uci4ci.png)

在“New Device”窗口，你能确认并配置一些关于你设备的选项，像是“Device Name”和“Addresses”。如果你在地址那一栏选择 “dynamic” （动态），客户端将会自动探测设备的 IP 地址，但是你想要保持住某一个 IP 地址，你能将该地址填进这一栏里。如果你已经创建了文件夹（或者在这之后），你也能与新设备分享这个文件夹。

![](/data/attachment/album/201810/28/221152pnbehpxxyzvh2hmy.png)

你的电脑和安卓设备已经配对，可以交换文件了。(如果你有多台电脑或手机，只需重复这些步骤。)

### 分享文件夹

既然您想要同步的设备之间已经连接，现在是时候共享一个文件夹了。您可以在电脑上共享文件夹，添加了该文件夹中的设备将获得一份副本。

若要共享文件夹，请转至“Settings”并单击“Add Shared Folder”（添加共享文件夹）：

![](/data/attachment/album/201810/28/221155laigeaimeddxucd4.png)

在下一个窗口中，输入要共享的文件夹的信息：

![](/data/attachment/album/201810/28/221202xylii61nsyis6axu.png)

你可以使用任何你想要的标签。“Folder ID”将随机生成，用于识别客户端之间的文件夹。在“Path”里，点击“Browse”就能定位到你想要分享的文件夹。如果你想 Syncthing 监控文件夹的变化（例如删除、新建文件等），点击“Monitor filesystem for changes”（监控文件系统变化）。

记住，当你分享一个文件夹，在其他客户端的任何改动都将会反映到每一台设备上。这意味着如果你在其他电脑和手机设备之间分享了一个包含图片的文件夹，在这些客户端上的改动都会同步到每一台设备。如果这不是你想要的，你能让你的文件夹“Send Only”（只是发送）给其他客户端，但是其他客户端的改动都不会被同步。

完成后，转至“Share with Devices”（与设备共享）页并选择要与之同步文件夹的主机。

您选择的所有设备都需要接受共享请求；您将在设备上收到通知。

正如共享文件夹时一样，您必须配置新的共享文件夹：

![](/data/attachment/album/201810/28/221222mcfzhb94sb4pa2ss.png)

同样，在这里您可以定义任何标签，但是 ID 必须匹配每个客户端。在文件夹选项中，选择文件夹及其文件的位置。请记住，此文件夹中所做的任何更改都将反映到文件夹所允许同步的每个设备上。

这些是连接设备和与 Syncting 共享文件夹的步骤。开始复制可能需要几分钟时间，这取决于您的网络设置或您是否不在同一网络上。

Syncting 提供了更多出色的功能和选项。试试看，并把握你数据的控制权。

---

via: <https://opensource.com/article/18/9/take-control-your-data-syncthing>

作者：[Michael Zamot](https://opensource.com/users/mzamot) 选题：[lujun9972](https://github.com/lujun9972) 译者：[ypingcn](https://github.com/ypingcn) 校对：[wxy](https://github.com/wxy)

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


如何列出在 Linux 上已启用/激活的仓库

![](/data/attachment/album/201810/28/235306c9l9v687b4q62575.jpg)

有很多方法可以列出在 Linux 已启用的仓库。我们将在下面展示给你列出已激活仓库的简便方法。这有助于你知晓你的系统上都启用了哪些仓库。一旦你掌握了这些信息，你就可以添加任何之前还没有准备启用的仓库了。

举个例子，如果你想启用 epel 仓库，你需要先检查它是否已经启用了。这篇教程将会帮助你做这件事情。

### 什么是仓库？

存储特定程序软件包的中枢位置就是一个软件仓库。

所有的 Linux 发行版都在维护自己的仓库，而且允许用户下载并安装这些软件包到他们的机器上。

每个仓库提供者都提供了一套包管理工具，用以管理他们的仓库，比如搜索、安装、更新、升级、移除等等。

大多数 Linux 发行版都作为免费软件，除了 RHEL 和 SUSE，要访问他们的仓库你需要先购买订阅。

**建议阅读：**

* [在 Linux 上，如何通过 DNF/YUM 设置管理命令添加、启用、关闭一个仓库](https://www.2daygeek.com/how-to-add-enable-disable-a-repository-dnf-yum-config-manager-on-linux/)
* [在 Linux 上如何按大小列出已安装的包](https://www.2daygeek.com/how-to-list-installed-packages-by-size-largest-on-linux/)
* [在 Linux 上如何列出升级的包](https://www.2daygeek.com/how-to-view-list-the-available-packages-updates-in-linux/)
* [在 Linux 上如何查看一个特定包安装/升级/更新/移除/清除的日期](https://www.2daygeek.com/how-to-view-a-particular-package-installed-updated-upgraded-removed-erased-date-on-linux/)
* [在 Linux 上如何查看一个包的详细信息](https://www.2daygeek.com/how-to-view-detailed-information-about-a-package-in-linux/)
* [在你的 Linux 发行版上如何查看一个包是否可用](https://www.2daygeek.com/how-to-search-if-a-package-is-available-on-your-linux-distribution-or-not/)
* [在 Linux 如何列出可用的软件包组](https://www.2daygeek.com/how-to-list-an-available-package-groups-in-linux/)
* [Newbies corner —— 一个图形化的 Linux 包管理的前端工具](https://www.2daygeek.com/list-of-graphical-frontend-tool-for-linux-package-manager/)
* [Linux 专家须知，命令行包管理 & 使用列表](https://www.2daygeek.com/list-of-command-line-package-manager-for-linux/)

### 在 RHEL/CentOS 上列出已启用的库

RHEL 和 CentOS 系统使用的是 RPM 包管理，所以我们可以使用 Yum 包管理器查看这些信息。

YUM 意即 “Yellowdog Updater,Modified”，它是一个开源的包管理器的命令行前端，用于基于 RPM 的系统上，例如 RHEL 和 CentOS。

YUM 是获取、安装、删除、查询和管理来自发行版仓库和其他第三方库的 RPM 包的主要工具。

**建议阅读：** [在 RHEL/CentOS 系统上用 YUM 命令管理包](https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/)

基于 RHEL 的系统主要提供以下三个主要的仓库。这些仓库是默认启用的。

* **base**：它包含了所有的核心包和基础包。
* **extras**：它向 CentOS 提供了不破坏上游兼容性或更新基本组件的额外功能。这是一个上游仓库，还有额外的 CentOS 包。
* **updates**：它提供了 bug 修复包、安全包和增强包。

```
# yum repolist
或者
# yum repolist enabled
```

```
Loaded plugins: fastestmirror
Determining fastest mirrors
 * epel: ewr.edge.kernel.org
repo id                                                                      repo name                                                                                status
!base/7/x86_64                                                               CentOS-7 - Base                                                                                9,911
!epel/x86_64                                                                 Extra Packages for Enterprise Linux 7 - x86_64                                                            12,687
!extras/7/x86_64                                                             CentOS-7 - Extras                                                                                403
!updates/7/x86_64                                                            CentOS-7 - Updates                                                                                1,348
repolist: 24,349
```

### 如何列出 Fedora 上已启用的包

DNF 意即 “Dandified yum”。我们可以说 DNF 是下一代的 yum 包管理器，使用了 hawkey/libsolv 作为后端。自从 Fedroa 18 开始，Aleš Kozumplík 就开始开发 DNF，最终在 Fedora 22 上实现/发布。

Fedora 22 及之后的系统上都使用 DNF 安装、升级、搜索和移除包。它可以自动解决依赖问题，并使包的安装过程平顺没有任何麻烦。

因为 Yum 许多长时间未解决的问题，现在 Yum 已经被 DNF 所替代。你问为什么他没有给 Yum 打补丁。Aleš Kozumplík 解释说修补在技术上太困难了，而 YUM 团队无法立即承受这些变更，还有其他的问题，YUM 是 56k 行代码，而 DNF 是 29k 行代码。因此，除了分叉之外，别无选择。

**建议阅读：** [在 Fedora 上使用 DNF 管理软件](https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/)

Fedora 主要提供下面两个主仓库。这些库将被默认启用。

* **fedora**：它包括所有的核心包和基础包。
* **updates**：它提供了来自稳定发行版的 bug 修复包、安全包和增强包。

```
# dnf repolist
或者
# dnf repolist enabled
```

```
Last metadata expiration check: 0:02:56 ago on Wed 10 Oct 2018 06:12:22 PM IST.
repo id                                         repo name                                                        status
docker-ce-stable                                Docker CE Stable - x86_64                                             6
*fedora                                         Fedora 26 - x86_64                                               53,912
home_mhogomchungu                               mhogomchungu's Home Project (Fedora_25)                              19
home_moritzmolch_gencfsm                        Gnome Encfs Manager (Fedora_25)                                       5
mystro256-gnome-redshift                        Copr repo for gnome-redshift owned by mystro256                       6
nodesource                                      Node.js Packages for Fedora Linux 26 - x86_64                        83
rabiny-albert                                   Copr repo for albert owned by rabiny                                  3
*rpmfusion-free                                 RPM Fusion for Fedora 26 - Free                                     536
*rpmfusion-free-updates                         RPM Fusion for Fedora 26 - Free - Updates                           278
*rpmfusion-nonfree                              RPM Fusion for Fedora 26 - Nonfree                                  202
*rpmfusion-nonfree-updates                      RPM Fusion for Fedora 26 - Nonfree - Updates                         95
*updates                                        Fedora 26 - x86_64 - Updates
```

### 如何列出 Debian/Ubuntu 上已启用的仓库

基于 Debian 的系统使用的是 APT/APT-GET 包管理，因此我们可以使用 APT/APT-GET 包管理器去获取该信息。

APT 意即 “Advanced Packaging Tool”，它取代了 `apt-get`，就像 DNF 取代 Yum 一样。 它具有丰富的命令行工具，在一个命令（`apt`）中包含了所有功能，如 `apt-cache`、`apt-search`、`dpkg`、`apt-cdrom`、`apt-config`、`apt-key` 等，还有其他几个独特的功能。 例如，我们可以通过 APT 轻松安装 .dpkg 软件包，而我们无法通过 APT-GET 获得和包含在 APT 命令中类似的功能。 由于 APT-GET 中未能解决的问题，APT 取代了 APT-GET。

apt-get 是一个强大的命令行工具，它用以自动下载和安装新的软件包、升级已存在的软件包、更新包索引列表、还有升级整个基于 Debian 的系统。

```
# apt-cache policy
Package files:
 100 /var/lib/dpkg/status
     release a=now
 500 http://ppa.launchpad.net/peek-developers/stable/ubuntu artful/main amd64 Packages
     release v=17.10,o=LP-PPA-peek-developers-stable,a=artful,n=artful,l=Peek stable releases,c=main,b=amd64
     origin ppa.launchpad.net
 500 http://ppa.launchpad.net/notepadqq-team/notepadqq/ubuntu artful/main amd64 Packages
     release v=17.10,o=LP-PPA-notepadqq-team-notepadqq,a=artful,n=artful,l=Notepadqq,c=main,b=amd64
     origin ppa.launchpad.net
 500 http://dl.google.com/linux/chrome/deb stable/main amd64 Packages
     release v=1.0,o=Google, Inc.,a=stable,n=stable,l=Google,c=main,b=amd64
     origin dl.google.com
 500 https://download.docker.com/linux/ubuntu artful/stable amd64 Packages
     release o=Docker,a=artful,l=Docker CE,c=stable,b=amd64
     origin download.docker.com
 500 http://security.ubuntu.com/ubuntu artful-security/multiverse amd64 Packages
     release v=17.10,o=Ubuntu,a=artful-security,n=artful,l=Ubuntu,c=multiverse,b=amd64
     origin security.ubuntu.com
 500 http://security.ubuntu.com/ubuntu artful-security/universe amd64 Packages
     release v=17.10,o=Ubuntu,a=artful-security,n=artful,l=Ubuntu,c=universe,b=amd64
     origin security.ubuntu.com
 500 http://security.ubuntu.com/ubuntu artful-security/restricted i386 Packages
     release v=17.10,o=Ubuntu,a=artful-security,n=artful,l=Ubuntu,c=restricted,b=i386
     origin security.ubuntu.com
.
.
     origin in.archive.ubuntu.com
 500 http://in.archive.ubuntu.com/ubuntu artful/restricted amd64 Packages
     release v=17.10,o=Ubuntu,a=artful,n=artful,l=Ubuntu,c=restricted,b=amd64
     origin in.archive.ubuntu.com
 500 http://in.archive.ubuntu.com/ubuntu artful/main i386 Packages
     release v=17.10,o=Ubuntu,a=artful,n=artful,l=Ubuntu,c=main,b=i386
     origin in.archive.ubuntu.com
 500 http://in.archive.ubuntu.com/ubuntu artful/main amd64 Packages
     release v=17.10,o=Ubuntu,a=artful,n=artful,l=Ubuntu,c=main,b=amd64
     origin in.archive.ubuntu.com
Pinned packages:

```

### 如何在 openSUSE 上列出已启用的仓库

openSUSE 使用 zypper 包管理，因此我们可以使用 zypper 包管理获得更多信息。

Zypper 是 suse 和 openSUSE 发行版的命令行包管理。它用于安装、更新、搜索、移除包和管理仓库，执行各种查询等。Zypper 以 ZYpp 系统管理库（libzypp）作为后端。

**建议阅读：** [在 openSUSE 和 suse 系统上使用 Zypper 命令管理包](https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/)

```
# zypper repos

# | Alias                 | Name                                                | Enabled | GPG Check | Refresh
--+-----------------------+-----------------------------------------------------+---------+-----------+--------
1 | packman-repository    | packman-repository                                  | Yes     | (r ) Yes  | Yes
2 | google-chrome         | google-chrome                                       | Yes     | (r ) Yes  | Yes
3 | home_lazka0_ql-stable | Stable Quod Libet / Ex Falso Builds (openSUSE_42.1) | Yes     | (r ) Yes  | No
4 | repo-non-oss          | openSUSE-leap/42.1-Non-Oss                          | Yes     | (r ) Yes  | Yes
5 | repo-oss              | openSUSE-leap/42.1-Oss                              | Yes     | (r ) Yes  | Yes
6 | repo-update           | openSUSE-42.1-Update                                | Yes     | (r ) Yes  | Yes
7 | repo-update-non-oss   | openSUSE-42.1-Update-Non-Oss                        | Yes     | (r ) Yes  | Yes
```

列出仓库及 URI。

```
# zypper lr -u

# | Alias                 | Name                                                | Enabled | GPG Check | Refresh | URI
--+-----------------------+-----------------------------------------------------+---------+-----------+---------+---------------------------------------------------------------------------------
1 | packman-repository    | packman-repository                                  | Yes     | (r ) Yes  | Yes     | http://ftp.gwdg.de/pub/linux/packman/suse/openSUSE_Leap_42.1/
2 | google-chrome         | google-chrome                                       | Yes     | (r ) Yes  | Yes     | http://dl.google.com/linux/chrome/rpm/stable/x86_64
3 | home_lazka0_ql-stable | Stable Quod Libet / Ex Falso Builds (openSUSE_42.1) | Yes     | (r ) Yes  | No      | http://download.opensuse.org/repositories/home:/lazka0:/ql-stable/openSUSE_42.1/
4 | repo-non-oss          | openSUSE-leap/42.1-Non-Oss                          | Yes     | (r ) Yes  | Yes     | http://download.opensuse.org/distribution/leap/42.1/repo/non-oss/
5 | repo-oss              | openSUSE-leap/42.1-Oss                              | Yes     | (r ) Yes  | Yes     | http://download.opensuse.org/distribution/leap/42.1/repo/oss/
6 | repo-update           | openSUSE-42.1-Update                                | Yes     | (r ) Yes  | Yes     | http://download.opensuse.org/update/leap/42.1/oss/
7 | repo-update-non-oss   | openSUSE-42.1-Update-Non-Oss                        | Yes     | (r ) Yes  | Yes     | http://download.opensuse.org/update/leap/42.1/non-oss/
```

通过优先级列出仓库。

```
# zypper lr -p

# | Alias                 | Name                                                | Enabled | GPG Check | Refresh | Priority
--+-----------------------+-----------------------------------------------------+---------+-----------+---------+---------
1 | packman-repository    | packman-repository                                  | Yes     | (r ) Yes  | Yes     |   99
2 | google-chrome         | google-chrome                                       | Yes     | (r ) Yes  | Yes     |   99
3 | home_lazka0_ql-stable | Stable Quod Libet / Ex Falso Builds (openSUSE_42.1) | Yes     | (r ) Yes  | No      |   99
4 | repo-non-oss          | openSUSE-leap/42.1-Non-Oss                          | Yes     | (r ) Yes  | Yes     |   99
5 | repo-oss              | openSUSE-leap/42.1-Oss                              | Yes     | (r ) Yes  | Yes     |   99
6 | repo-update           | openSUSE-42.1-Update                                | Yes     | (r ) Yes  | Yes     |   99
7 | repo-update-non-oss   | openSUSE-42.1-Update-Non-Oss                        | Yes     | (r ) Yes  | Yes     |   99
```

### 如何列出 Arch Linux 上已启用的仓库

基于 Arch Linux 的系统使用 pacman 包管理，因此我们可以使用 pacman 包管理获取这些信息。

pacman 意即 “package manager utility”。pacman 是一个命令行实用程序，用以安装、构建、移除和管理 Arch Linux 包。pacman 使用 libalpm （Arch Linux 包管理库）作为后端去进行这些操作。

**建议阅读：** [在基于 Arch Linux的系统上使用 Pacman命令管理包](https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/)

```
# pacman -Syy
:: Synchronizing package databases...
 core                                               132.6 KiB  1524K/s 00:00 [############################################] 100%
 extra                                             1859.0 KiB   750K/s 00:02 [############################################] 100%
 community                                            3.5 MiB   149K/s 00:24 [############################################] 100%
 multilib                                           182.7 KiB  1363K/s 00:00 [############################################] 100%
```

### 如何使用 INXI Utility 列出 Linux 上已启用的仓库

inix 是 Linux 上检查硬件信息非常有用的工具，还提供很多的选项去获取 Linux 上的所有硬件信息，我从未在 Linux 上发现其他有如此效用的程序。它由 locsmif 分叉自古老而古怪的 infobash。

inix 是一个可以快速显示硬件信息、CPU、硬盘、Xorg、桌面、内核、GCC 版本、进程、内存使用和很多其他有用信息的程序，还使用于论坛技术支持和调试工具上。

这个实用程序将会显示所有发行版仓库的数据信息，例如 RHEL、CentOS、Fedora、Debain、Ubuntu、LinuxMint、ArchLinux、openSUSE、Manjaro等。

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

```
# inxi -r
Repos:     Active apt sources in file: /etc/apt/sources.list
           deb http://in.archive.ubuntu.com/ubuntu/ yakkety main restricted
           deb http://in.archive.ubuntu.com/ubuntu/ yakkety-updates main restricted
           deb http://in.archive.ubuntu.com/ubuntu/ yakkety universe
           deb http://in.archive.ubuntu.com/ubuntu/ yakkety-updates universe
           deb http://in.archive.ubuntu.com/ubuntu/ yakkety multiverse
           deb http://in.archive.ubuntu.com/ubuntu/ yakkety-updates multiverse
           deb http://in.archive.ubuntu.com/ubuntu/ yakkety-backports main restricted universe multiverse
           deb http://security.ubuntu.com/ubuntu yakkety-security main restricted
           deb http://security.ubuntu.com/ubuntu yakkety-security universe
           deb http://security.ubuntu.com/ubuntu yakkety-security multiverse
           Active apt sources in file: /etc/apt/sources.list.d/arc-theme.list
           deb http://download.opensuse.org/repositories/home:/Horst3180/xUbuntu_16.04/ /
           Active apt sources in file: /etc/apt/sources.list.d/snwh-ubuntu-pulp-yakkety.list
           deb http://ppa.launchpad.net/snwh/pulp/ubuntu yakkety main
```

---

via: <https://www.2daygeek.com/how-to-list-the-enabled-active-repositories-in-linux/>

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

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


如何在 Linux 上锁定虚拟控制台会话

![](/data/attachment/album/201810/29/184916y0fikww5t8sh4z75.png)

当你在共享的系统上工作时，你可能不希望其他用户偷窥你的控制台中看你在做什么。如果是这样，我知道有个简单的技巧来锁定自己的会话，同时仍然允许其他用户在其他虚拟控制台上使用该系统。要感谢 **Vlock**（**V**irtual Console **lock**），这是一个命令行程序，用于锁定 Linux 控制台上的一个或多个会话。如有必要，你可以锁定整个控制台并完全禁用虚拟控制台切换功能。Vlock 对于有多个用户访问控制台的共享 Linux 系统特别有用。

### 安装 Vlock

在基于 Arch 的系统上，Vlock 软件包被替换为默认预安装的 **kpd** 包，因此你无需为安装烦恼。

在 Debian、Ubuntu、Linux Mint 上，运行以下命令来安装 Vlock：

```
$ sudo apt-get install vlock
```

在 Fedora 上：

```
$ sudo dnf install vlock
```

在 RHEL、CentOS 上：

```
$ sudo yum install vlock
```

### 在 Linux 上锁定虚拟控制台会话

Vlock 的一般语法是：

```
vlock [ -acnshv ] [ -t <timeout> ] [ plugins... ]
```

这里：

* `a` —— 锁定所有虚拟控制台会话，
* `c` —— 锁定当前虚拟控制台会话，
* `n` —— 在锁定所有会话之前切换到新的空控制台，
* `s` —— 禁用 SysRq 键机制，
* `t` —— 指定屏保插件的超时时间，
* `h` —— 显示帮助，
* `v` —— 显示版本。

让我举几个例子。

#### 1、 锁定当前控制台会话

在没有任何参数的情况下运行 Vlock 时，它默认锁定当前控制台会话 （TYY）。要解锁会话，你需要输入当前用户的密码或 root 密码。

```
$ vlock
```

![](/data/attachment/album/201810/29/184917u5jy5ujjbdjddptp.gif)

你还可以使用 `-c` 标志来锁定当前的控制台会话。

```
$ vlock -c
```

请注意，此命令仅锁定当前控制台。你可以按 `ALT+F2` 切换到其他控制台。有关在 TTY 之间切换的更多详细信息，请参阅以下指南。

此外，如果系统有多个用户，则其他用户仍可以访问其各自的 TTY。

#### 2、 锁定所有控制台会话

要同时锁定所有 TTY 并禁用虚拟控制台切换功能，请运行：

```
$ vlock -a
```

同样，要解锁控制台会话，只需按下回车键并输入当前用户的密码或 root 用户密码。

请记住，**root 用户可以随时解锁任何 vlock 会话**，除非在编译时禁用。

#### 3、 在锁定所有控制台之前切换到新的虚拟控制台

在锁定所有控制台之前，还可以使 Vlock 从 X 会话切换到新的空虚拟控制台。为此，请使用 `-n` 标志。

```
$ vlock -n
```

#### 4、 禁用 SysRq 机制

你也许知道，魔术 SysRq 键机制允许用户在系统死机时执行某些操作。因此，用户可以使用 SysRq 解锁控制台。为了防止这种情况，请传递 `-s` 选项以禁用 SysRq 机制。请记住，这个选项只适用于有 `-a` 选项的时候。

```
$ vlock -sa
```

有关更多选项及其用法，请参阅帮助或手册页。

```
$ vlock -h
$ man vlock
```

Vlock 可防止未经授权的用户获得控制台访问权限。如果你在为 Linux 寻找一个简单的控制台锁定机制，那么 Vlock 值得一试！

就是这些了。希望这篇文章有用。还有更多好东西。敬请关注！

干杯!

---

via: <https://www.ostechnix.com/how-to-lock-virtual-console-sessions-on-linux/>

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

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


如何移除或禁用 Ubuntu Dock

![](/data/attachment/album/201810/29/193718cfrnf5uwtczfq38n.png)

>
> 如果你想用其它 dock（例如 Plank dock）或面板来替换 Ubuntu 18.04 中的 Dock，或者你想要移除或禁用 Ubuntu Dock，本文会告诉你如何做。
>
>
>

Ubuntu Dock - 屏幕左侧栏，可用于固定应用程序或访问已安装的应用程序。使用默认的 Ubuntu 会话时，[无法](https://bugs.launchpad.net/ubuntu/+source/gnome-tweak-tool/+bug/1713020)使用 Gnome Tweaks 禁用它（禁用无效）。但是如果你需要，还是有几种方法来摆脱它的。下面我将列出 4 种方法可以移除或禁用 Ubuntu Dock，以及每个方法的缺点（如果有的话），还有如何撤销每个方法的更改。本文还包括在没有 Ubuntu Dock 的情况下访问<ruby> 活动概览 <rt>  Activities Overview </rt></ruby>和已安装应用程序列表的其它方法。

### 如何在没有 Ubuntu Dock 的情况下访问活动概览

如果没有 Ubuntu Dock，你可能无法访问活动的或已安装的应用程序列表（可以通过单击 Dock 底部的“显示应用程序”按钮从 Ubuntu Dock 访问）。例如，如果你想使用 Plank Dock 就是这样。

显然，如果你安装了 Dash to Panel 扩展来替代 Ubuntu Dock，那么还好。因为 Dash to Panel 提供了一个按钮来访问活动概览或已安装的应用程序。

根据你计划用来替代 Ubuntu Dock 的软件，如果无法访问活动概览，那么你可以启用“活动概览热角”选项，只需将鼠标移动到屏幕的左上角即可打开活动概览。访问已安装的应用程序列表的另一种方法是使用快捷键：`Super + A`。

如果要启用“活动概览热角”，使用以下命令：

```
gsettings set org.gnome.shell enable-hot-corners true
```

如果以后要撤销此操作并禁用该热角，那么你需要使用以下命令：

```
gsettings set org.gnome.shell enable-hot-corners false
```

你可以使用 Gnome Tweaks 应用程序（该选项位于 Gnome Tweaks 的 “Top Bar” 部分）启用或禁用“活动概览热角” 选项，可以使用以下命令进行安装它：

```
sudo apt install gnome-tweaks
```

### 如何移除或禁用 Ubuntu Dock

下面你将找到 4 种摆脱 Ubuntu Dock 的方法，环境在 Ubuntu 18.04 下。

#### 方法 1: 移除 Gnome Shell Ubuntu Dock 包

摆脱 Ubuntu Dock 的最简单方法就是删除包。

这将会从你的系统中完全移除 Ubuntu Dock 扩展，但同时也移除了 `ubuntu-desktop` 元数据包。如果你移除 `ubuntu-desktop` 元数据包，不会马上出现问题，因为它本身没有任何作用。`ubuntu-desktop` 元数据包依赖于组成 Ubuntu 桌面的大量包。它的依赖关系不会被删除，也不会被破坏。问题是如果你以后想升级到新的 Ubuntu 版本，那么将不会安装任何新的 `ubuntu-desktop` 依赖项。

为了解决这个问题，你可以在升级到较新的 Ubuntu 版本之前安装 `ubuntu-desktop` 元数据包（例如，如果你想从 Ubuntu 18.04 升级到 18.10）。

如果你对此没有意见，并且想要从系统中删除 Ubuntu Dock 扩展包，使用以下命令：

```
sudo apt remove gnome-shell-extension-ubuntu-dock
```

如果以后要撤消更改，只需使用以下命令安装扩展：

```
sudo apt install gnome-shell-extension-ubuntu-dock
```

或者重新安装 `ubuntu-desktop` 元数据包（这将会安装你可能已删除的任何 `ubuntu-desktop` 依赖项，包括 Ubuntu Dock），你可以使用以下命令：

```
sudo apt install ubuntu-desktop
```

#### 方法 2：安装并使用 vanilla Gnome 会话而不是默认的 Ubuntu 会话

摆脱 Ubuntu Dock 的另一种方法是安装和使用原生 Gnome 会话。安装 原生 Gnome 会话还将安装此会话所依赖的其它软件包，如 Gnome 文档、地图、音乐、联系人、照片、跟踪器等。

通过安装原生 Gnome 会话，你还将获得默认 Gnome GDM 登录和锁定屏幕主题，而不是 Ubuntu 默认的 Adwaita Gtk 主题和图标。你可以使用 Gnome Tweaks 应用程序轻松更改 Gtk 和图标主题。

此外，默认情况下将禁用 AppIndicators 扩展（因此使用 AppIndicators 托盘的应用程序不会显示在顶部面板上），但你可以使用 Gnome Tweaks 启用此功能（在扩展中，启用 Ubuntu appindicators 扩展）。

同样，你也可以从原生 Gnome 会话启用或禁用 Ubuntu Dock，这在 Ubuntu 会话中是不可能的（使用 Ubuntu 会话时无法从 Gnome Tweaks 禁用 Ubuntu Dock）。

如果你不想安装原生 Gnome 会话所需的这些额外软件包，那么这个移除 Ubuntu Dock 的这个方法不适合你，请查看其它方法。

如果你对此没有意见，以下是你需要做的事情。要在 Ubuntu 中安装原生的 Gnome 会话，使用以下命令：

```
sudo apt install vanilla-gnome-desktop
```

安装完成后，重启系统。在登录屏幕上，单击用户名，单击 “Sign in” 按钮旁边的齿轮图标，然后选择 “GNOME” 而不是 “Ubuntu”，之后继续登录。

![](/data/attachment/album/201810/29/193729zuttwnlqkuqd0wxd.png)

如果要撤销此操作并移除原生 Gnome 会话，可以使用以下命令清除原生 Gnome 软件包，然后删除它安装的依赖项（第二条命令）：

```
sudo apt purge vanilla-gnome-desktop
sudo apt autoremove
```

然后重新启动，并以相同的方式从 GDM 登录屏幕中选择 Ubuntu。

#### 方法 3：从桌面上永久隐藏 Ubuntu Dock，而不是将其移除

如果你希望永久隐藏 Ubuntu Dock，不让它显示在桌面上，但不移除它或使用原生 Gnome 会话，你可以使用 Dconf 编辑器轻松完成此操作。这样做的缺点是 Ubuntu Dock 仍然会使用一些系统资源，即使你没有在桌面上使用它，但你也可以轻松恢复它而无需安装或移除任何包。

Ubuntu Dock 只对你的桌面隐藏，当你进入叠加模式（活动）时，你仍然可以看到并从那里使用 Ubuntu Dock。

要永久隐藏 Ubuntu Dock，使用 Dconf 编辑器导航到 `/org/gnome/shell/extensions/dash-to-dock` 并禁用以下选项（将它们设置为 `false`）：`autohide`、`dock-fixed` 和 `intellihide`。

如果你愿意，可以从命令行实现此目的，运行以下命令：

```
gsettings set org.gnome.shell.extensions.dash-to-dock autohide false
gsettings set org.gnome.shell.extensions.dash-to-dock dock-fixed false
gsettings set org.gnome.shell.extensions.dash-to-dock intellihide false
```

如果你改变主意了并想撤销此操作，你可以使用 Dconf 编辑器从 `/org/gnome/shell/extensions/dash-to-dock` 中启动 `autohide`、 `dock-fixed` 和 `intellihide`（将它们设置为 `true`），或者你可以使用以下这些命令：

```
gsettings set org.gnome.shell.extensions.dash-to-dock autohide true
gsettings set org.gnome.shell.extensions.dash-to-dock dock-fixed true
gsettings set org.gnome.shell.extensions.dash-to-dock intellihide true
```

#### 方法 4：使用 Dash to Panel 扩展

[Dash to Panel](https://www.linuxuprising.com/2018/05/gnome-shell-dash-to-panel-v14-brings.html) 是 Gnome Shell 的一个高度可配置面板，是 Ubuntu Dock 或 Dash to Dock 的一个很好的替代品（Ubuntu Dock 是从 Dash to Dock 分叉而来的）。安装和启动 Dash to Panel 扩展会禁用 Ubuntu Dock，因此你无需执行其它任何操作。

你可以从 [extensions.gnome.org](https://extensions.gnome.org/extension/1160/dash-to-panel/) 来安装 Dash to Panel。

如果你改变主意并希望重新使用 Ubuntu Dock，那么你可以使用 Gnome Tweaks 应用程序禁用 Dash to Panel，或者通过单击以下网址旁边的 X 按钮完全移除 Dash to Panel: <https://extensions.gnome.org/local/> 。

---

via: <https://www.linuxuprising.com/2018/08/how-to-remove-or-disable-ubuntu-dock.html>

作者：[Logix](https://plus.google.com/118280394805678839070) 选题：[lujun9972](https://github.com/lujun9972) 译者：[MjSeven](https://github.com/MjSeven) 校对：[wxy](https://github.com/wxy)

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


命令行：增强版

Summary: 下面就是我现在使用的用于增强原有命令行工具的列表。

超级使用，赞！

下面就是我现在使用的用于增强原有命令行工具的列表。

![](/data/attachment/album/201810/29/204617yz2aaxzfrlyc22nl.jpg)

我不确定有多少 Web 开发者能完全避免使用命令行。就我来说，我从 1997 年上大学就开始使用命令行了，那时的 l33t-hacker 让我着迷，同时我也觉得它很难掌握。

过去这些年我的命令行本领在逐步加强，我经常会去搜寻工作中能用的更好的命令行工具。下面就是我现在使用的用于增强原有命令行工具的列表。

### 怎么忽略我所做的命令行增强

通常情况下我会用别名将新的增强的命令行工具覆盖原来的命令（如 `cat` 和 `ping`）。

如果我需要运行原来的命令的话（有时我确实需要这么做），我会像下面这样来运行未加修改的原始命令。（我用的是 Mac，你的用法可能不一样）

```
$ \cat # 忽略叫 "cat" 的别名 - 具体解释: https://stackoverflow.com/a/16506263/22617
$ command cat # 忽略函数和别名
```

### bat > cat

`cat` 用于打印文件的内容，如果你平时用命令行很多的话，例如语法高亮之类的功能会非常有用。我首先发现了 [ccat](https://github.com/jingweno/ccat) 这个有语法高亮功能的工具，然后我发现了 [bat](https://github.com/sharkdp/bat)，它的功能有语法高亮、分页、行号和 git 集成。

`bat` 命令也能让我在（多于一屏的）输出里使用 `/` 搜索（和用 `less` 搜索功能一样）。

![Simple bat output](/data/attachment/album/201810/29/204653xppy14y8y4zu6h7s.gif "Sample bat output")

我将别名 `cat` 指到了 `bat` 命令：

```
alias cat='bat'
```

* [安装指引](https://github.com/sharkdp/bat)

### prettyping > ping

`ping` 非常有用，当我碰到“糟了，是不是 X 挂了？/我的网不通了？”这种情况下我最先想到的工具就是它了。但是 `prettyping`（“prettyping” 可不是指“pre typing”）在 `ping` 的基础上加了友好的输出，这可让我感觉命令行友好了很多呢。

![prettyping](/data/attachment/album/201810/29/204655zcznq3x70xjexzv3.gif "Sample ping output")

我也将 `ping` 用别名链接到了 `prettyping` 命令：

```
alias ping='prettyping --nolegend'
```

* [安装指引](http://denilson.sa.nom.br/prettyping/)

### fzf > ctrl+r

在终端里，使用 `ctrl+r` 将允许你在命令历史里[反向搜索](https://lifehacker.com/278888/ctrl%252Br-to-search-and-other-terminal-history-tricks)使用过的命令，这是个挺好的小技巧，尽管它有点麻烦。

`fzf` 这个工具相比于 `ctrl+r` 有了**巨大的**进步。它能针对命令行历史进行模糊查询，并且提供了对可能的合格结果进行全面交互式预览。

除了搜索命令历史，`fzf` 还能预览和打开文件，我在下面的视频里展示了这些功能。

为了这个预览的效果，我创建了一个叫 `preview` 的别名，它将 `fzf` 和前文提到的 `bat` 组合起来完成预览功能，还给上面绑定了一个定制的热键 `ctrl+o` 来打开 VS Code：

```
alias preview="fzf --preview 'bat --color \"always\" {}'"
# 支持在 VS Code 里用 ctrl+o 来打开选择的文件
export FZF_DEFAULT_OPTS="--bind='ctrl-o:execute(code {})+abort'"
```

* [安装指引](https://github.com/junegunn/fzf)

### htop > top

`top` 是当我想快速诊断为什么机器上的 CPU 跑的那么累或者风扇为什么突然呼呼大做的时候首先会想到的工具。我在生产环境也会使用这个工具。讨厌的是 Mac 上的 `top` 和 Linux 上的 `top` 有着极大的不同（恕我直言，应该是差的多）。

不过，`htop` 是对 Linux 上的 `top` 和 Mac 上蹩脚的 `top` 的极大改进。它增加了包括颜色输出，键盘热键绑定以及不同的视图输出，这对理解进程之间的父子关系有极大帮助。

一些很容易上手的热键：

* `P` —— 按 CPU 使用率排序
* `M` —— 按内存使用排序
* `F4` —— 用字符串过滤进程（例如只看包括 node 的进程）
* `space` —— 锚定一个单独进程，这样我能观察它是否有尖峰状态

![htop output](/data/attachment/album/201810/29/204657hnfne1peg857gtdq.jpg "Sample htop output")

在 Mac Sierra 上 htop 有个奇怪的 bug，不过这个 bug 可以通过以 root 运行来绕过（我实在记不清这个 bug 是什么，但是这个别名能搞定它，有点讨厌的是我得每次都输入 root 密码。）：

```
alias top="sudo htop" # 给 top 加上别名并且绕过 Sierra 上的 bug
```

* [安装指引](http://hisham.hm/htop/)

### diff-so-fancy > diff

我非常确定我是几年前从 Paul Irish 那儿学来的这个技巧，尽管我很少直接使用 `diff`，但我的 git 命令行会一直使用 `diff`。`diff-so-fancy` 给了我代码语法颜色和更改字符高亮的功能。

![diff so fancy](/data/attachment/album/201810/29/204658r8zajeo2b98ase4b.jpg "Sample diff output")

在我的 `~/.gitconfig` 文件里我用了下面的选项来打开 `git diff` 和 `git show` 的 `diff-so-fancy` 功能。

```
[pager]
    diff = diff-so-fancy | less --tabs=1,5 -RFX
    show = diff-so-fancy | less --tabs=1,5 -RFX
```

* [安装指引](https://github.com/so-fancy/diff-so-fancy)

### fd > find

尽管我使用 Mac，但我绝不是 Spotlight 的粉丝，我觉得它的性能很差，关键字也难记，加上更新它自己的数据库时会拖慢 CPU，简直一无是处。我经常使用 [Alfred](https://www.alfredapp.com/)，但是它的搜索功能也不是很好。

我倾向于在命令行中搜索文件，但是 `find` 的难用在于很难去记住那些合适的表达式来描述我想要的文件。（而且 Mac 上的 `find` 命令和非 Mac 的 `find` 命令还有些许不同，这更加深了我的失望。）

`fd` 是一个很好的替代品（它的作者和 `bat` 的作者是同一个人）。它非常快而且对于我经常要搜索的命令非常好记。

几个上手的例子：

```
$ fd cli # 所有包含 "cli" 的文件名
$ fd -e md # 所有以 .md 作为扩展名的文件
$ fd cli -x wc -w # 搜索 "cli" 并且在每个搜索结果上运行 `wc -w`
```

![fd output](/data/attachment/album/201810/29/204700varcm94gce19m91g.png "Sample fd output")

* [安装指引](https://github.com/sharkdp/fd/)

### ncdu > du

对我来说，知道当前磁盘空间被什么占用了非常重要。我用过 Mac 上的 [DaisyDisk](https://daisydiskapp.com/)，但是我觉得那个程序产生结果有点慢。

`du -sh` 命令是我经常会运行的命令（`-sh` 是指结果以“汇总”和“人类可读”的方式显示），我经常会想要深入挖掘那些占用了大量磁盘空间的目录，看看到底是什么在占用空间。

`ncdu` 是一个非常棒的替代品。它提供了一个交互式的界面并且允许快速的扫描那些占用了大量磁盘空间的目录和文件，它又快又准。（尽管不管在哪个工具的情况下，扫描我的 home 目录都要很长时间，它有 550G）

一旦当我找到一个目录我想要“处理”一下（如删除，移动或压缩文件），我会使用 `cmd` + 点击 [iTerm2](https://www.iterm2.com/) 顶部的目录名字的方法在 Finder 中打开它。

![ncdu output](/data/attachment/album/201810/29/204701qk3aaia5w0z9505o.png "Sample ncdu output")

还有另外[一个叫 nnn 的替代选择](https://github.com/jarun/nnn)，它提供了一个更漂亮的界面，它也提供文件尺寸和使用情况，实际上它更像一个全功能的文件管理器。

我的 `du` 是如下的别名：

```
alias du="ncdu --color dark -rr -x --exclude .git --exclude node_modules"
```

选项说明：

* `--color dark` 使用颜色方案
* `-rr` 只读模式（防止误删和运行新的 shell 程序）
* `--exclude` 忽略不想操作的目录

* [安装指引](https://dev.yorhel.nl/ncdu)

### tldr > man

几乎所有的命令行工具都有一个相伴的手册，它可以被 `man <命令名>` 来调出，但是在 `man` 的输出里找到东西可有点让人困惑，而且在一个包含了所有的技术细节的输出里找东西也挺可怕的。

这就是 TL;DR 项目（LCTT 译注：英文里“文档太长，没空去读”的缩写）创建的初衷。这是一个由社区驱动的文档系统，而且可以用在命令行上。就我现在使用的经验，我还没碰到过一个命令没有它相应的文档，你[也可以做贡献](https://github.com/tldr-pages/tldr#contributing)。

![TLDR output for 'fd'](/data/attachment/album/201810/29/204703q98ze88c128p93ip.png "Sample tldr output for fd")

一个小技巧，我将 `tldr` 的别名链接到 `help`（这样输入会快一点……）

```
alias help='tldr'
```

* [安装指引](http://tldr-pages.github.io/)

### ack || ag > grep

`grep` 毫无疑问是一个强力的命令行工具，但是这些年来它已经被一些工具超越了，其中两个叫 `ack` 和 `ag`。

我个人对 `ack` 和 `ag` 都尝试过，而且没有非常明显的个人偏好，（也就是说它们都很棒，并且很相似）。我倾向于默认只使用 `ack`，因为这三个字符就在指尖，很好打。并且 `ack` 有大量的 `ack --bar` 参数可以使用！（你一定会体会到这一点。）

`ack` 和 `ag` 默认都使用正则表达式来搜索，这非常契合我的工作，我能使用类似于 `--js` 或 `--html` 这种标识指定文件类型搜索。（尽管 `ag` 比 `ack` 在文件类型过滤器里包括了更多的文件类型。）

两个工具都支持常见的 `grep` 选项，如 `-B` 和 `-A` 用于在搜索的上下文里指代“之前”和“之后”。

![ack in action](/data/attachment/album/201810/29/204704c433kka72aki0kk7.png "Sample ack output with grep args")

因为 `ack` 不支持 markdown（而我又恰好写了很多 markdown），我在我的 `~/.ackrc` 文件里加了以下定制语句：

```
--type-set=md=.md,.mkd,.markdown
--pager=less -FRX
```

* 安装指引：[ack](https://beyondgrep.com)，[ag](https://github.com/ggreer/the_silver_searcher)
* [关于 ack & ag 的更多信息](http://conqueringthecommandline.com/book/ack_ag)

### jq > grep 及其它

我是 [jq](https://stedolan.github.io/jq) 的忠实粉丝之一。当然一开始我也在它的语法里苦苦挣扎，好在我对查询语言还算有些使用心得，现在我对 `jq` 可以说是每天都要用。（不过从前我要么使用 `grep` 或者使用一个叫 [json](http://trentm.com/json/) 的工具，相比而言后者的功能就非常基础了。）

我甚至开始撰写一个 `jq` 的教程系列（有 2500 字并且还在增加），我还发布了一个[网页工具](https://jqterm.com)和一个 Mac 上的应用（这个还没有发布。）

`jq` 允许我传入一个 JSON 并且能非常简单的将其转变为一个使用 JSON 格式的结果，这正是我想要的。下面这个例子允许我用一个命令更新我的所有 node 依赖。（为了阅读方便，我将其分成为多行。）

```
$ npm i $(echo $(\
    npm outdated --json | \
    jq -r 'to_entries | .[] | "\(.key)@\(.value.latest)"' \
))
```

上面的命令将使用 npm 的 JSON 输出格式来列出所有过期的 node 依赖，然后将下面的源 JSON 转换为：

```
{
    "node-jq": {
        "current": "0.7.0",
        "wanted": "0.7.0",
        "latest": "1.2.0",
        "location": "node_modules/node-jq"
    },
        "uuid": {
        "current": "3.1.0",
        "wanted": "3.2.1",
        "latest": "3.2.1",
        "location": "node_modules/uuid"
    }
}
```

转换结果为：

```
node-jq@1.2.0
uuid@3.2.1
```

上面的结果会被作为 `npm install` 的输入，你瞧，我的升级就这样全部搞定了。（当然，这里有点小题大做了。）

### 很荣幸提及一些其它的工具

我也在开始尝试一些别的工具，但我还没有完全掌握它们。（除了 `ponysay`，当我打开一个新的终端会话时，它就会出现。）

* [ponysay](https://github.com/erkin/ponysay) > `cowsay`
* [csvkit](https://csvkit.readthedocs.io/en/1.0.3/) > `awk 及其它`
* [noti](https://github.com/variadico/noti) > `display notification`
* [entr](http://www.entrproject.org/) > `watch`

### 你有什么好点子吗？

上面是我的命令行清单。你的呢？你有没有试着去增强一些你每天都会用到的命令呢？请告诉我，我非常乐意知道。

---

via: <https://remysharp.com/2018/08/23/cli-improved>

作者：[Remy Sharp](https://remysharp.com) 选题：[lujun9972](https://github.com/lujun9972) 译者：[DavidChenLiang](https://github.com/DavidChenLiang) 校对：[pityonline](https://github.com/pityonline), [wxy](https://github.com/wxy)

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


用这个漂亮的工具将方程式截图迅速转换为 LaTeX

>
> Mathpix 是一个漂亮的小工具，它允许你截取复杂数学方程式的截图，并立即将其转换为 LaTeX 可编辑文本。
>
>
>

![Mathpix converts math equations images into LaTeX](/data/attachment/album/201810/29/222716hpbmelo5xumpl5mp.jpg)

[LaTeX 编辑器](https://itsfoss.com/latex-editors-linux/)在撰写学术和科学文献时非常出色。

当然它还有一个陡峭的学习曲线。如果你不得不要写复杂的数学方程式，这种学习曲线会变得更加陡峭。

[Mathpix](https://mathpix.com/) 是一个在这方面可以帮助你的小工具。

假设你正在阅读带有数学方程式的文档。如果你想在 [LaTeX 文档](https://www.latex-project.org/)中使用这些方程，你需要使用你的 LaTeX 技能，并且得有充足的时间。

但是 Mathpix 为您解决了这个问题。使用 Mathpix，你可以截取数学方程式的截图，它会立即为你提供 LaTeX 代码。然后，你可以在你[最喜欢的 LaTeX 编辑器](https://itsfoss.com/latex-editors-linux/)中使用此代码。

请参阅[该视频](https://itsfoss.com/wp-content/uploads/2018/10/mathpix.mp4)中的 Mathpix 使用方式。

*[视频来源](https://g.redditmedia.com/b-GL1rQwNezQjGvdlov9U_6vDwb1A7kEwGHYcQ1Ogtg.gif?fm=mp4&mp4-fragmented=false&s=39fd1816b43e2b544986d629f75a7a8e)：Reddit 用户 [kaitlinmcunningham](https://www.reddit.com/user/kaitlinmcunningham)*

不是超酷吗？我想编写 LaTeX 文档最困难的部分是那些复杂的方程式。对于像我这样懒人，Mathpix 是天赐之物。

### 获取 Mathpix

Mathpix 适用于 Linux、macOS、Windows 和 iOS。暂时还没有 Android 应用。

注意：Mathpix 是一个免费使用的工具，但它不是开源的。

在 Linux 上，[Mathpix 有一个 Snap 包](https://snapcraft.io/mathpix-snipping-tool)。这意味着[如果你在 Linux 发行版上启用了 Snap 支持](https://itsfoss.com/install-snap-linux/)，你可以用这个简单命令安装 Mathpix：

```
sudo snap install mathpix-snipping-tool
```

使用 Mathpix 很简单。安装后，打开该工具。你会在顶部面板中找到它。你可以使用键盘快捷键 `Ctrl+Alt+M` 开始使用 Mathpix 截图。

它会立即将方程图片转换为 LaTeX 代码。代码将被复制到剪贴板中，然后你可以将其粘贴到 LaTeX 编辑器中。

Mathpix 的光学字符识别技术[正在被](https://mathpix.com/api.html)像 [WolframAlpha](https://www.wolframalpha.com/)、微软、谷歌等许多公司用于在处理数学符号时提升工具的图像识别能力。

总而言之，它对学生和学者来说是一个很棒的工具。它是免费使用的，我非常希望它是一个开源工具。但我们无法在生活中得到一切，不是么？

在 LaTeX 中处理数学符号时，你是否使用 Mathpix 或其他类似工具？你如何看待 Mathpix？在评论区与我们分享你的观点。

---

via: <https://itsfoss.com/mathpix/>

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

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


理解 Linux 链接（一）

Summary: 链接是可以将文件和目录放在你希望它们放在的位置的另一种方式。

链接是可以将文件和目录放在你希望它们放在的位置的另一种方式。

>
> 链接是可以将文件和目录放在你希望它们放在的位置的另一种方式。
>
>
>

![](/data/attachment/album/201810/30/235607uc4dohmhoal96u28.jpg)

除了 `cp` 和 `mv` 这两个我们在[本系列的前一部分](https://www.linux.com/blog/2018/8/linux-beginners-moving-things-around)中详细讨论过的，链接是可以将文件和目录放在你希望它们放在的位置的另一种方式。它的优点是可以让你同时在多个位置显示一个文件或目录。

如前所述，在物理磁盘这个级别上，文件和目录之类的东西并不真正存在。文件系统是为了方便人类使用，将它们虚构出来。但在磁盘级别上，有一个名为<ruby> 分区表 <rt>  partition table </rt></ruby>的东西，它位于每个分区的开头，然后数据分散在磁盘的其余部分。

虽然有不同类型的分区表，但是在分区开头的那个表包含的数据将映射每个目录和文件的开始和结束位置。分区表的就像一个索引：当从磁盘加载文件时，操作系统会查找表中的条目，分区表会告诉文件在磁盘上的起始位置和结束位置。然后磁盘头移动到起点，读取数据，直到它到达终点，您看：这就是你的文件。

### 硬链接

硬链接只是分区表中的一个条目，它指向磁盘上的某个区域，表示该区域**已经被分配给文件**。换句话说，硬链接指向已经被另一个条目索引的数据。让我们看看它是如何工作的。

打开终端，创建一个实验目录并进入：

```
mkdir test_dir
cd test_dir
```

使用 [touch](https://www.linux.com/blog/2018/8/linux-beginners-moving-things-around) 创建一个文件：

```
touch test.txt
```

为了获得更多的体验（？），在文本编辑器中打开 `test.txt` 并添加一些单词。

现在通过执行以下命令来建立硬链接：

```
ln test.txt hardlink_test.txt
```

运行 `ls`，你会看到你的目录现在包含两个文件，或者看起来如此。正如你之前读到的那样，你真正看到的是完全相同的文件的两个名称： `hardlink_test.txt` 包含相同的内容，没有填充磁盘中的任何更多空间（可以尝试使用大文件来测试），并与 `test.txt` 使用相同的 inode：

```
$ ls -li *test*
16515846 -rw-r--r-- 2 paul paul 14 oct 12 09:50 hardlink_test.txt
16515846 -rw-r--r-- 2 paul paul 14 oct 12 09:50 test.txt
```

`ls` 的 `-i` 选项显示一个文件的 “inode 数值”。“inode” 是分区表中的信息块，它包含磁盘上文件或目录的位置、上次修改的时间以及其它数据。如果两个文件使用相同的 inode，那么无论它们在目录树中的位置如何，它们在实际上都是相同的文件。

### 软链接

软链接，也称为<ruby> 符号链接 <rt>  symlink </rt></ruby>，它与硬链接是不同的：软链接实际上是一个独立的文件，它有自己的 inode 和它自己在磁盘上的小块地方。但它只包含一小段数据，将操作系统指向另一个文件或目录。

你可以使用 `ln` 的 `-s` 选项来创建一个软链接：

```
ln -s test.txt softlink_test.txt
```

这将在当前目录中创建软链接 `softlink_test.txt`，它指向 `test.txt`。

再次执行 `ls -li`，你可以看到两种链接的不同之处：

```
$ ls -li
total 8
16515846 -rw-r--r-- 2 paul paul 14 oct 12 09:50 hardlink_test.txt
16515855 lrwxrwxrwx 1 paul paul 8 oct 12 09:50 softlink_test.txt -> test.txt
16515846 -rw-r--r-- 2 paul paul 14 oct 12 09:50 test.txt
```

`hardlink_test.txt` 和 `test.txt` 包含一些文本并且*字面上*占据相同的空间。它们使用相同的 inode 数值。与此同时，`softlink_test.txt` 占用少得多，并且具有不同的 inode 数值，将其标记为完全不同的文件。使用 `ls` 的 `-l` 选项还会显示软链接指向的文件或目录。

### 为什么要用链接？

它们适用于**带有自己环境的应用程序**。你的 Linux 发行版通常不会附带你需要应用程序的最新版本。以优秀的 [Blender 3D](https://www.blender.org/) 设计软件为例，Blender 允许你创建 3D 静态图像以及动画电影，人人都想在自己的机器上拥有它。问题是，当前版本的 Blender 至少比任何发行版中的自带的高一个版本。

幸运的是，[Blender 提供可以开箱即用的下载](https://www.blender.org/download/)。除了程序本身之外，这些软件包还包含了 Blender 需要运行的复杂的库和依赖框架。所有这些数据和块都在它们自己的目录层次中。

每次你想运行 Blender，你都可以 `cd` 到你下载它的文件夹并运行：

```
./blender
```

但这很不方便。如果你可以从文件系统的任何地方，比如桌面命令启动器中运行 `blender` 命令会更好。

这样做的方法是将 `blender` 可执行文件链接到 `bin/` 目录。在许多系统上，你可以通过将其链接到文件系统中的任何位置来使 `blender` 命令可用，就像这样。

```
ln -s /path/to/blender_directory/blender /home/<username>/bin
```

你需要链接的另一个情况是**软件需要过时的库**。如果你用 `ls -l` 列出你的 `/usr/lib` 目录，你会看到许多软链接文件一闪而过。仔细看看，你会看到软链接通常与它们链接到的原始文件具有相似的名称。你可能会看到 `libblah` 链接到 `libblah.so.2`，你甚至可能会注意到 `libblah.so.2` 相应链接到原始文件 `libblah.so.2.1.0`。

这是因为应用程序通常需要安装比已安装版本更老的库。问题是，即使新版本仍然与旧版本（通常是）兼容，如果程序找不到它正在寻找的版本，程序将会出现问题。为了解决这个问题，发行版通常会创建链接，以便挑剔的应用程序**相信**它找到了旧版本，实际上它只找到了一个链接并最终使用了更新的库版本。

有些是和**你自己从源代码编译的程序**相关。你自己编译的程序通常最终安装在 `/usr/local` 下，程序本身最终在 `/usr/local/bin` 中，它在 `/usr/local/bin` 目录中查找它需要的库。但假设你的新程序需要 `libblah`，但 `libblah` 在 `/usr/lib` 中，这就是所有其它程序都会寻找到它的地方。你可以通过执行以下操作将其链接到 `/usr/local/lib`：

```
ln -s /usr/lib/libblah /usr/local/lib
```

或者如果你愿意，可以 `cd` 到 `/usr/local/lib`：

```
cd /usr/local/lib
```

然后使用链接：

```
ln -s ../lib/libblah
```

还有几十个案例证明软链接是有用的，当你使用 Linux 更熟练时，你肯定会发现它们，但这些是最常见的。下一次，我们将看一些你需要注意的链接怪异。

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

---

via: <https://www.linux.com/blog/intro-to-linux/2018/10/linux-links-part-1>

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

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


如何在 Linux 中一次重命名多个文件

Summary: 在本教程中，我们将学习使用 Linux 中的 mmv 命令一次重命名多个文件。 此命令用于在类 Unix 操作系统中使用标准通配符批量移动、复制、追加和重命名文件。

![](/data/attachment/album/201810/31/092758rlc51ctl3tlcr9cl.png)

你可能已经知道，我们使用 `mv` 命令在类 Unix 操作系统中重命名或者移动文件和目录。 但是，`mv` 命令不支持一次重命名多个文件。 不用担心。 在本教程中，我们将学习使用 Linux 中的 `mmv` 命令一次重命名多个文件。 此命令用于在类 Unix 操作系统中使用标准通配符批量移动、复制、追加和重命名文件。

### 在 Linux 中一次重命名多个文件

`mmv` 程序可在基于 Debian 的系统的默认仓库中使用。 要想在 Debian、Ubuntu、Linux Mint 上安装它，请运行以下命令：

```
$ sudo apt-get install mmv
```

我们假设你在当前目录中有以下文件。

```
$ ls
a1.txt a2.txt a3.txt
```

现在，你想要将所有以字母 “a” 开头的文件重命名为以 “b” 开头的。 当然，你可以在几秒钟内手动执行此操作。 但是想想你是否有数百个文件想要重命名？ 这是一个非常耗时的过程。 这时候 `mmv` 命令就很有帮助了。

要将所有以字母 “a” 开头的文件重命名为以字母 “b” 开头的，只需要运行：

```
$ mmv a\* b\#1
```

让我们检查一下文件是否都已经重命名了。

```
$ ls
b1.txt b2.txt b3.txt
```

如你所见，所有以字母 “a” 开头的文件（即 `a1.txt`、`a2.txt`、`a3.txt`）都重命名为 `b1.txt`、`b2.txt`、`b3.txt`。

**解释**

在上面的例子中，第一个参数（`a\*`）是 “from” 模式，第二个参数是 “to” 模式（`b\#1`）。根据上面的例子，`mmv` 将查找任何以字母 “a” 开头的文件名，并根据第二个参数重命名匹配的文件，即 “to” 模式。我们可以使用通配符，例如用 `*`、`?` 和 `[]` 来匹配一个或多个任意字符。请注意，你必须转义使用通配符，否则它们将被 shell 扩展，`mmv` 将无法理解。

“to” 模式中的 `#1` 是通配符索引。它匹配 “from” 模式中的第一个通配符。 “to” 模式中的 `#2` 将匹配第二个通配符（如果有的话），依此类推。在我们的例子中，我们只有一个通配符（星号），所以我们写了一个 `#1`。并且，`#` 符号也应该被转义。此外，你也可以用引号括起模式。

你甚至可以将具有特定扩展名的所有文件重命名为其他扩展名。例如，要将当前目录中的所有 `.txt` 文件重命名为 `.doc` 文件格式，只需运行：

```
$ mmv \*.txt \#1.doc
```

这是另一个例子。 我们假设你有以下文件。

```
$ ls
abcd1.txt abcd2.txt abcd3.txt
```

你希望在当前目录下的所有文件中将第一次出现的 “abc” 替换为 “xyz”。 你会怎么做呢？

很简单。

```
$ mmv '*abc*' '#1xyz#2'
```

请注意，在上面的示例中，模式被单引号括起来了。

让我们检查下 “abc” 是否实际上被替换为 “xyz”。

```
$ ls
xyzd1.txt xyzd2.txt xyzd3.txt
```

看到没？ 文件 `abcd1.txt`、`abcd2.txt` 和 `abcd3.txt` 已经重命名为 `xyzd1.txt`、`xyzd2.txt` 和 `xyzd3.txt`。

`mmv` 命令的另一个值得注意的功能是你可以使用 `-n` 选项打印输出而不是重命名文件，如下所示。

```
$ mmv -n a\* b\#1
a1.txt -> b1.txt
a2.txt -> b2.txt
a3.txt -> b3.txt
```

这样，你可以在重命名文件之前简单地验证 `mmv` 命令实际执行的操作。

有关更多详细信息，请参阅 man 页面。

```
$ man mmv
```

### 更新：Thunar 文件管理器

**Thunar 文件管理器**默认具有内置**批量重命名**选项。 如果你正在使用 Thunar，那么重命名文件要比使用 `mmv` 命令容易得多。

Thunar 在大多数 Linux 发行版的默认仓库库中都可用。

要在基于 Arch 的系统上安装它，请运行：

```
$ sudo pacman -S thunar
```

在 RHEL、CentOS 上:

```
$ sudo yum install thunar
```

在 Fedora 上:

```
$ sudo dnf install thunar
```

在 openSUSE 上:

```
$ sudo zypper install thunar
```

在 Debian、Ubuntu、Linux Mint 上:

```
$ sudo apt-get install thunar
```

安装后，你可以从菜单或应用程序启动器中启动批量重命名程序。 要从终端启动它，请使用以下命令：

```
$ thunar -B
```

批量重命名方式如下。

![](/data/attachment/album/201810/31/092759xd8i8q3ty8i3iye8.png)

单击“+”，然后选择要重命名的文件列表。 批量重命名可以重命名文件的名称、文件的后缀或者同时重命名文件的名称和后缀。 Thunar 目前支持以下批量重命名：

* 插入日期或时间
* 插入或覆盖
* 编号
* 删除字符
* 搜索和替换
* 大写或小写

当你从选项列表中选择其中一个条件时，你将在“新名称”列中看到更改的预览，如下面的屏幕截图所示。

![](/data/attachment/album/201810/31/092800ev22gd5dmgzlgbov.png)

选择条件后，单击“重命名文件”选项来重命名文件。

你还可以通过选择两个或更多文件从 Thunar 中打开批量重命名器。 选择文件后，按 F2 或右键单击并选择“重命名”。

嗯，这就是本次的所有内容了。希望有所帮助。更多干货即将到来。敬请关注！

祝快乐！

---

via: <https://www.ostechnix.com/how-to-rename-multiple-files-at-once-in-linux/>

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


MidnightBSD 发布 1.0！

几天前，Lucas Holt 宣布发布 MidnightBSD 1.0。让我们快速看一下这个新版本中包含的内容。

### 什么是 MidnightBSD？

![MidnightBSD](/data/attachment/album/201810/31/093807l88xtkvfcqob3tlk.jpg)

[MidnightBSD](https://www.midnightbsd.org/) 是 FreeBSD 的一个分支。Lucas 创建了 MightnightBSD，这成为桌面用户和 BSD 新手的一个选择。他想创造一个能让人们快速体验 BSD 桌面的东西。他认为其他发行版过于关注服务器市场。

### MidnightBSD 1.0 中有什么？

根据[发布说明](https://www.midnightbsd.org/notes/)（[视频](https://www.youtube.com/embed/-rlk2wFsjJ4)），1.0 中的大部分工作都是更新基础系统，改进包管理器和更新工具。新版本与 FreeBSD 10-Stable 兼容。

Mports（MidnightBSD 的包管理系统）已经升级支持使用一个命令安装多个包。`mport upgrade` 命令已经修复。Mports 现在会跟踪已弃用和过期的包。它还引入了新的包格式。

其他变化包括：

* 现在支持 [ZFS](https://itsfoss.com/what-is-zfs/) 作为启动文件系统。以前，ZFS 只能用于附加存储。 \* 支持 NVME SSD。 \* AMD Ryzen 和 Radeon 的支持得到了改善。 \* Intel、Broadcom 和其他驱动程序已更新。 \* 已从 FreeBSD 移植 bhyve 支持。 \* 传感器框架已被删除，因为它导致锁定问题。 \* 删除了 Sudo 并用 OpenBSD 中的 [doas](https://man.openbsd.org/doas) 替换。 \* 增加了对 Microsoft hyper-v 的支持。

### 升级之前

如果你当前是 MidnightBSD 的用户或正在考虑尝试新版本，那么还是再等一会。Lucas 目前正在重建软件包以支持新的软件包格式和工具。他还计划在未来几个月内升级软件包和移植桌面环境。他目前正致力于移植 Firefox 52 ESR，因为它是最后一个不需要 Rust 的版本。他还希望将更新版本的 Chromium 移植到 MidnightBSD。我建议关注 MidnightBSD 的 [Twitter](https://twitter.com/midnightbsd)。

### 0.9 怎么回事？

你可能注意到 MidnightBSD 的先前版本是 0.8.6。你现在可能想知道“为什么跳到 1.0”？根据 Lucas 的说法，他在开发 0.9 时遇到了几个问题。事实上，他重试好几次。他最终采用与 0.9 分支不同的方式，并变成了 1.0。有些软件包在 0.\* 系列也有问题。

### 需要帮助

目前，MidnightBSD 项目几乎是 Lucas Holt 一个人的作品。这是其发展缓慢的主要原因。如果你有兴趣帮忙，可以通过 [Twitter](https://twitter.com/midnightbsd) 与他联系。

在[发布公告视频](https://www.youtube.com/watch?v=-rlk2wFsjJ4)中。Lucas 说他遇到了上游项目接受补丁的问题。他们似乎认为 MidnightBSD 太小了。这通常意味着他必须从头开始移植应用。

### 想法

我对劣势者有一个想法。在我接触的所有 BSD 中，这个外号最适合 MidnightBSD。一个人想要创建一个轻松的桌面体验。当前只有一个其他的 BSD 在尝试做相似的事情：Project Trident。我想这是 BSD 成功的真正的阻碍。Linux 成功是因为人们可以快速容易地安装它。希望 MidnightBSD 为 BSD 做到这一点，但是还有很长的路要走。

你有没有用过 MidnightBSD？如果没有，你最喜欢的 BSD 是什么？我们应该涵盖哪些其他 BSD 主题？请在下面的评论中告诉我们。

如果你觉得这篇文章有趣，请花一点时间在社交媒体，Hacker News 或 [Reddit](http://reddit.com/r/linuxusersgroup) 上分享它。

---

via: <https://itsfoss.com/midnightbsd-1-0-release/>

作者：[John Paul](https://itsfoss.com/author/john/) 选题：[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/) 荣誉推出


用 350 行代码从零开始，将 Lisp 编译成 JavaScript

![](/data/attachment/album/201810/31/101755m9sy6j1zdhzh9gh2.jpg)

我们将会在本篇文章中看到从零开始实现的编译器，将简单的类 LISP 计算语言编译成 JavaScript。完整的源代码在 [这里](https://gist.github.com/soupi/d4ff0727ccb739045fad6cdf533ca7dd)。

我们将会：

1. 自定义语言，并用它编写一个简单的程序
2. 实现一个简单的解析器组合器
3. 为该语言实现一个解析器
4. 为该语言实现一个美观的打印器
5. 为我们的用途定义 JavaScript 的一个子集
6. 实现代码转译器，将代码转译成我们定义的 JavaScript 子集
7. 把所有东西整合在一起

开始吧！

### 1、定义语言

Lisp 族语言最迷人的地方在于，它们的语法就是树状表示的，这就是这门语言很容易解析的原因。我们很快就能接触到它。但首先让我们把自己的语言定义好。关于我们语言的语法的范式（BNF）描述如下：

```
program ::= expr
expr ::= <integer> | <name> | ([<expr>])
```

基本上，我们可以在该语言的最顶层定义表达式并对其进行运算。表达式由一个整数（比如 `5`）、一个变量（比如 `x`）或者一个表达式列表（比如 `(add x 1)`）组成。

整数对应它本身的值，变量对应它在当前环境中绑定的值，表达式列表对应一个函数调用，该列表的第一个参数是相应的函数，剩下的表达式是传递给这个函数的参数。

该语言中，我们保留一些内建的特殊形式，这样我们就能做一些更有意思的事情：

* `let` 表达式使我们可以在它的 `body` 环境中引入新的变量。语法如下：

```
let ::= (let ([<letarg>]) <body>)
letargs ::= (<name> <expr>)
body ::= <expr>
```
* `lambda` 表达式：也就是匿名函数定义。语法如下：

```
lambda ::= (lambda ([<name>]) <body>)
```

还有一些内建函数： `add`、`mul`、`sub`、`div` 和 `print`。

让我们看看用我们这门语言编写的入门示例程序：

```
(let
  ((compose
    (lambda (f g)
      (lambda (x) (f (g x)))))
  (square
    (lambda (x) (mul x x)))
  (add1
    (lambda (x) (add x 1))))
  (print ((compose square add1) 5)))
```

这个程序定义了 3 个函数：`compose`、`square` 和 `add1`。然后将计算结果的值 `((compose square add1) 5)` 输出出来。

我相信了解这门语言，这些信息就足够了。开始实现它吧。

在 Haskell 中，我们可以这样定义语言：

```
type Name = String

data Expr
  = ATOM Atom
  | LIST [Expr]
    deriving (Eq, Read, Show)

data Atom
  = Int Int
  | Symbol Name
    deriving (Eq, Read, Show)
```

我们可以解析用该语言用 `Expr` 定义的程序。而且，这里我们添加了新数据类型 `Eq`、`Read` 和 `Show` 等实例用于测试和调试。你能够在 REPL 中使用这些数据类型，验证它们确实有用。

我们不在语法中定义 `lambda`、`let` 或其它的内建函数，原因在于，当前情况下我们没必要用到这些东西。这些函数仅仅是 `LIST` （表达式列表）的更加特殊的用例。所以我决定将它放到后面的部分。

一般来说你想要在抽象语法中定义这些特殊用例 —— 用于改进错误信息、禁用静态分析和优化等等，但在这里我们不会这样做，对我们来说这些已经足够了。

另一件你想做的事情可能是在语法中添加一些注释信息。比如定位：`Expr` 是来自哪个文件的，具体到这个文件的哪一行哪一列。你可以在后面的阶段中使用这一特性，打印出错误定位，即使它们不是处于解析阶段。

* 练习 1：添加一个 `Program` 数据类型，可以按顺序包含多个 `Expr`
* 练习 2：向语法树中添加一个定位注解。

### 2、实现一个简单的解析器组合库

我们要做的第一件事情是定义一个<ruby> 嵌入式领域专用语言 <rt>  Embedded Domain Specific Language </rt></ruby>（EDSL），我们会用它来定义我们的语言解析器。这常常被称为解析器组合库。我们做这件事完全是出于学习的目的，Haskell 里有很好的解析库，在实际构建软件或者进行实验时，你应该使用它们。[megaparsec](https://mrkkrp.github.io/megaparsec/) 就是这样的一个库。

首先我们来谈谈解析库的实现的思路。本质上，我们的解析器就是一个函数，接受一些输入，可能会读取输入的一些或全部内容，然后返回解析出来的值和无法解析的输入部分，或者在解析失败时抛出异常。我们把它写出来。

```
newtype Parser a
  = Parser (ParseString -> Either ParseError (a, ParseString))

data ParseString
  = ParseString Name (Int, Int) String

data ParseError
  = ParseError ParseString Error

type Error = String
```

这里我们定义了三个主要的新类型。

第一个，`Parser a` 是之前讨论的解析函数。

第二个，`ParseString` 是我们的输入或携带的状态。它有三个重要的部分：

* `Name`: 这是源的名字
* `(Int, Int)`: 这是源的当前位置
* `String`: 这是等待解析的字符串

第三个，`ParseError` 包含了解析器的当前状态和一个错误信息。

现在我们想让这个解析器更灵活，我们将会定义一些常用类型的实例。这些实例让我们能够将小巧的解析器和复杂的解析器结合在一起（因此它的名字叫做 “解析器组合器”）。

第一个是 `Functor` 实例。我们需要 `Functor` 实例，因为我们要能够对解析值应用函数从而使用不同的解析器。当我们定义自己语言的解析器时，我们将会看到关于它的示例。

```
instance Functor Parser where
  fmap f (Parser parser) =
    Parser (\str -> first f <$> parser str)
```

第二个是 `Applicative` 实例。该实例的常见用例是在多个解析器中实现一个纯函数。

```
instance Applicative Parser where
  pure x = Parser (\str -> Right (x, str))
  (Parser p1) <*> (Parser p2) =
    Parser $
      \str -> do
        (f, rest)  <- p1 str
        (x, rest') <- p2 rest
        pure (f x, rest')

```

（注意：我们还会实现一个 Monad 实例，这样我们才能使用符号）

第三个是 `Alternative` 实例。万一前面的解析器解析失败了，我们要能够提供一个备用的解析器。

```
instance Alternative Parser where
  empty = Parser (`throwErr` "Failed consuming input")
  (Parser p1) <|> (Parser p2) =
    Parser $
      \pstr -> case p1 pstr of
        Right result -> Right result
        Left  _      -> p2 pstr
```

第四个是 `Monad` 实例。这样我们就能链接解析器。

```
instance Monad Parser where
  (Parser p1) >>= f =
    Parser $
     \str -> case p1 str of
       Left err -> Left err
       Right (rs, rest) ->
         case f rs of
           Parser parser -> parser rest
```

接下来，让我们定义一种的方式，用于运行解析器和防止失败的助手函数：

```
runParser :: String -> String -> Parser a -> Either ParseError (a, ParseString)
runParser name str (Parser parser) = parser $ ParseString name (0,0) str

throwErr :: ParseString -> String -> Either ParseError a
throwErr ps@(ParseString name (row,col) _) errMsg =
  Left $ ParseError ps $ unlines
    [ "*** " ++ name ++ ": " ++ errMsg
    , "* On row " ++ show row ++ ", column " ++ show col ++ "."
    ]

```

现在我们将会开始实现组合器，这是 EDSL 的 API，也是它的核心。

首先，我们会定义 `oneOf`。如果输入列表中的字符后面还有字符的话，`oneOf` 将会成功，否则就会失败。

```
oneOf :: [Char] -> Parser Char
oneOf chars =
  Parser $ \case
    ps@(ParseString name (row, col) str) ->
      case str of
        []     -> throwErr ps "Cannot read character of empty string"
        (c:cs) ->
          if c `elem` chars
          then Right (c, ParseString name (row, col+1) cs)
          else throwErr ps $ unlines ["Unexpected character " ++ [c], "Expecting one of: " ++ show chars]
```

`optional` 将会抛出异常，停止解析器。失败时它仅仅会返回 `Nothing`。

```
optional :: Parser a -> Parser (Maybe a)
optional (Parser parser) =
  Parser $
    \pstr -> case parser pstr of
      Left _ -> Right (Nothing, pstr)
      Right (x, rest) -> Right (Just x, rest)
```

`many` 将会试着重复运行解析器，直到失败。当它完成的时候，会返回成功运行的解析器列表。`many1` 做的事情是一样的，但解析失败时它至少会抛出一次异常。

```
many :: Parser a -> Parser [a]
many parser = go []
  where go cs = (parser >>= \c -> go (c:cs)) <|> pure (reverse cs)

many1 :: Parser a -> Parser [a]
many1 parser =
  (:) <$> parser <*> many parser
```

下面的这些解析器通过我们定义的组合器来实现一些特殊的解析器：

```
char :: Char -> Parser Char
char c = oneOf [c]

string :: String -> Parser String
string = traverse char

space :: Parser Char
space = oneOf " \n"

spaces :: Parser String
spaces = many space

spaces1 :: Parser String
spaces1 = many1 space

withSpaces :: Parser a -> Parser a
withSpaces parser =
  spaces *> parser <* spaces

parens :: Parser a -> Parser a
parens parser =
     (withSpaces $ char '(')
  *> withSpaces parser
  <* (spaces *> char ')')

sepBy :: Parser a -> Parser b -> Parser [b]
sepBy sep parser = do
  frst <- optional parser
  rest <- many (sep *> parser)
  pure $ maybe rest (:rest) frst
```

现在为该门语言定义解析器所需要的所有东西都有了。

* 练习 ：实现一个 EOF（end of file/input，即文件或输入终止符）解析器组合器。

### 3、为我们的语言实现解析器

我们会用自顶而下的方法定义解析器。

```
parseExpr :: Parser Expr
parseExpr = fmap ATOM parseAtom <|> fmap LIST parseList

parseList :: Parser [Expr]
parseList = parens $ sepBy spaces1 parseExpr

parseAtom :: Parser Atom
parseAtom = parseSymbol <|> parseInt

parseSymbol :: Parser Atom
parseSymbol = fmap Symbol parseName
```

注意到这四个函数是在我们这门语言中属于高阶描述。这解释了为什么 Haskell 执行解析工作这么棒。在定义完高级部分后，我们还需要定义低级别的 `parseName` 和 `parseInt`。

我们能在这门语言中用什么字符作为名字呢？用小写的字母、数字和下划线吧，而且名字的第一个字符必须是字母。

```
parseName :: Parser Name
parseName = do
  c  <- oneOf ['a'..'z']
  cs <- many $ oneOf $ ['a'..'z'] ++ "0123456789" ++ "_"
  pure (c:cs)
```

整数是一系列数字，数字前面可能有负号 `-`：

```
parseInt :: Parser Atom
parseInt = do
  sign <- optional $ char '-'
  num  <- many1 $ oneOf "0123456789"
  let result = read $ maybe num (:num) sign of
  pure $ Int result
```

最后，我们会定义用来运行解析器的函数，返回值可能是一个 `Expr` 或者是一条错误信息。

```
runExprParser :: Name -> String -> Either String Expr
runExprParser name str =
  case runParser name str (withSpaces parseExpr) of
    Left (ParseError _ errMsg) -> Left errMsg
    Right (result, _) -> Right result
```

* 练习 1 ：为第一节中定义的 `Program` 类型编写一个解析器
* 练习 2 ：用 Applicative 的形式重写 `parseName`
* 练习 3 ：`parseInt` 可能出现溢出情况，找到处理它的方法，不要用 `read`。

### 4、为这门语言实现一个更好看的输出器

我们还想做一件事，将我们的程序以源代码的形式打印出来。这对完善错误信息很有用。

```
printExpr :: Expr -> String
printExpr = printExpr' False 0

printAtom :: Atom -> String
printAtom = \case
  Symbol s -> s
  Int i -> show i

printExpr' :: Bool -> Int -> Expr -> String
printExpr' doindent level = \case
  ATOM a -> indent (bool 0 level doindent) (printAtom a)
  LIST (e:es) ->
    indent (bool 0 level doindent) $
      concat
        [ "("
        , printExpr' False (level + 1) e
        , bool "\n" "" (null es)
        , intercalate "\n" $ map (printExpr' True (level + 1)) es
        , ")"
        ]

indent :: Int -> String -> String
indent tabs e = concat (replicate tabs "  ") ++ e
```

* 练习 ：为第一节中定义的 `Program` 类型编写一个美观的输出器

好，目前为止我们写了近 200 行代码，这些代码一般叫做编译器的前端。我们还要写大概 150 行代码，用来执行三个额外的任务：我们需要根据需求定义一个 JS 的子集，定义一个将我们的语言转译成这个子集的转译器，最后把所有东西整合在一起。开始吧。

### 5、根据需求定义 JavaScript 的子集

首先，我们要定义将要使用的 JavaScript 的子集：

```
data JSExpr
  = JSInt Int
  | JSSymbol Name
  | JSBinOp JSBinOp JSExpr JSExpr
  | JSLambda [Name] JSExpr
  | JSFunCall JSExpr [JSExpr]
  | JSReturn JSExpr
    deriving (Eq, Show, Read)

type JSBinOp = String
```

这个数据类型表示 JavaScript 表达式。我们有两个原子类型 `JSInt` 和 `JSSymbol`，它们是由我们这个语言中的 `Atom` 转译来的，我们用 `JSBinOp` 来表示二元操作，比如 `+` 或 `*`，用 `JSLambda` 来表示匿名函数，和我们语言中的 `lambda expression（lambda 表达式）` 一样，我们将会用 `JSFunCall` 来调用函数，用 `let` 来引入新名字，用 `JSReturn` 从函数中返回值，在 JavaScript 中是需要返回值的。

`JSExpr` 类型是对 JavaScript 表达式的 **抽象表示**。我们会把自己语言中表达式的抽象表示 `Expr` 转译成 JavaScript 表达式的抽象表示 `JSExpr`。但为了实现这个功能，我们需要实现 `JSExpr` ，并从这个抽象表示中生成 JavaScript 代码。我们将通过递归匹配 `JSExpr` 实现，将 JS 代码当作 `String` 来输出。这和我们在 `printExpr` 中做的基本上是一样的。我们还会追踪元素的作用域，这样我们才可以用合适的方式缩进生成的代码。

```
printJSOp :: JSBinOp -> String
printJSOp op = op

printJSExpr :: Bool -> Int -> JSExpr -> String
printJSExpr doindent tabs = \case
  JSInt    i     -> show i
  JSSymbol name  -> name
  JSLambda vars expr -> (if doindent then indent tabs else id) $ unlines
    ["function(" ++ intercalate ", " vars ++ ") {"
    ,indent (tabs+1) $ printJSExpr False (tabs+1) expr
    ] ++ indent tabs "}"
  JSBinOp  op e1 e2  -> "(" ++ printJSExpr False tabs e1 ++ " " ++ printJSOp op ++ " " ++ printJSExpr False tabs e2 ++ ")"
  JSFunCall f exprs  -> "(" ++ printJSExpr False tabs f ++ ")(" ++ intercalate ", " (fmap (printJSExpr False tabs) exprs) ++ ")"
  JSReturn expr      -> (if doindent then indent tabs else id) $ "return " ++ printJSExpr False tabs expr ++ ";"
```

* 练习 1 ：添加 `JSProgram` 类型，它可以包含多个 `JSExpr` ，然后创建一个叫做 `printJSExprProgram` 的函数来生成代码。
* 练习 2 ：添加 `JSExpr` 的新类型：`JSIf`，并为其生成代码。

### 6、实现到我们定义的 JavaScript 子集的代码转译器

我们快做完了。这一节将会创建函数，将 `Expr` 转译成 `JSExpr`。

基本思想很简单，我们会将 `ATOM` 转译成 `JSSymbol` 或者 `JSInt`，然后会将 `LIST` 转译成一个函数调用或者转译的特例。

```
type TransError = String

translateToJS :: Expr -> Either TransError JSExpr
translateToJS = \case
  ATOM (Symbol s) -> pure $ JSSymbol s
  ATOM (Int i)    -> pure $ JSInt i
  LIST xs -> translateList xs

translateList :: [Expr] -> Either TransError JSExpr
translateList = \case
  []     -> Left "translating empty list"
  ATOM (Symbol s):xs
    | Just f <- lookup s builtins ->
      f xs
  f:xs ->
    JSFunCall <$> translateToJS f <*> traverse translateToJS xs
```

`builtins` 是一系列要转译的特例，就像 `lambada` 和 `let`。每一种情况都可以获得一系列参数，验证它是否合乎语法规范，然后将其转译成等效的 `JSExpr`。

```
type Builtin  = [Expr] -> Either TransError JSExpr
type Builtins = [(Name, Builtin)]

builtins :: Builtins
builtins =
  [("lambda", transLambda)
  ,("let", transLet)
  ,("add", transBinOp "add" "+")
  ,("mul", transBinOp "mul" "*")
  ,("sub", transBinOp "sub" "-")
  ,("div", transBinOp "div" "/")
  ,("print", transPrint)
  ]
```

我们这种情况，会将内建的特殊形式当作特殊的、非第一类的进行对待，因此不可能将它们当作第一类函数。

我们会把 Lambda 表达式转译成一个匿名函数：

```
transLambda :: [Expr] -> Either TransError JSExpr
transLambda = \case
  [LIST vars, body] -> do
    vars' <- traverse fromSymbol vars
    JSLambda vars' <$> (JSReturn <$> translateToJS body)

  vars ->
    Left $ unlines
      ["Syntax error: unexpected arguments for lambda."
      ,"expecting 2 arguments, the first is the list of vars and the second is the body of the lambda."
      ,"In expression: " ++ show (LIST $ ATOM (Symbol "lambda") : vars)
      ]

fromSymbol :: Expr -> Either String Name
fromSymbol (ATOM (Symbol s)) = Right s
fromSymbol e = Left $ "cannot bind value to non symbol type: " ++ show e
```

我们会将 `let` 转译成带有相关名字参数的函数定义，然后带上参数调用函数，因此会在这一作用域中引入变量：

```
transLet :: [Expr] -> Either TransError JSExpr
transLet = \case
  [LIST binds, body] -> do
    (vars, vals) <- letParams binds
    vars' <- traverse fromSymbol vars
    JSFunCall . JSLambda vars' <$> (JSReturn <$> translateToJS body) <*> traverse translateToJS vals
   where
    letParams :: [Expr] -> Either Error ([Expr],[Expr])
    letParams = \case
      [] -> pure ([],[])
      LIST [x,y] : rest -> ((x:) *** (y:)) <$> letParams rest
      x : _ -> Left ("Unexpected argument in let list in expression:\n" ++ printExpr x)

  vars ->
    Left $ unlines
      ["Syntax error: unexpected arguments for let."
      ,"expecting 2 arguments, the first is the list of var/val pairs and the second is the let body."
      ,"In expression:\n" ++ printExpr (LIST $ ATOM (Symbol "let") : vars)
      ]
```

我们会将可以在多个参数之间执行的操作符转译成一系列二元操作符。比如：`(add 1 2 3)` 将会变成 `1 + (2 + 3)`。

```
transBinOp :: Name -> Name -> [Expr] -> Either TransError JSExpr
transBinOp f _ []   = Left $ "Syntax error: '" ++ f ++ "' expected at least 1 argument, got: 0"
transBinOp _ _ [x]  = translateToJS x
transBinOp _ f list = foldl1 (JSBinOp f) <$> traverse translateToJS list
```

然后我们会将 `print` 转换成对 `console.log` 的调用。

```
transPrint :: [Expr] -> Either TransError JSExpr
transPrint [expr] = JSFunCall (JSSymbol "console.log") . (:[]) <$> translateToJS expr
transPrint xs     = Left $ "Syntax error. print expected 1 arguments, got: " ++ show (length xs)
```

注意，如果我们将这些代码当作 `Expr` 的特例进行解析，那我们就可能会跳过语法验证。

* 练习 1 ：将 `Program` 转译成 `JSProgram`
* 练习 2 ：为 `if Expr Expr Expr` 添加一个特例，并将它转译成你在上一次练习中实现的 `JSIf` 条件语句。

### 7、把所有东西整合到一起

最终，我们将会把所有东西整合到一起。我们会：

1. 读取文件
2. 将文件解析成 `Expr`
3. 将文件转译成 `JSExpr`
4. 将 JavaScript 代码发送到标准输出流

我们还会启用一些用于测试的标志位：

* `--e` 将进行解析并打印出表达式的抽象表示（`Expr`）
* `--pp` 将进行解析，美化输出
* `--jse` 将进行解析、转译、并打印出生成的 JS 表达式（`JSExpr`）的抽象表示
* `--ppc` 将进行解析，美化输出并进行编译

```
main :: IO ()
main = getArgs >>= \case
  [file] ->
    printCompile =<< readFile file
  ["--e",file] ->
    either putStrLn print . runExprParser "--e" =<< readFile file
  ["--pp",file] ->
    either putStrLn (putStrLn . printExpr) . runExprParser "--pp" =<< readFile file
  ["--jse",file] ->
    either print (either putStrLn print . translateToJS) . runExprParser "--jse" =<< readFile file
  ["--ppc",file] ->
    either putStrLn (either putStrLn putStrLn) . fmap (compile . printExpr) . runExprParser "--ppc" =<< readFile file
  _ ->
    putStrLn $ unlines
      ["Usage: runghc Main.hs [ --e, --pp, --jse, --ppc ] <filename>"
      ,"--e     print the Expr"
      ,"--pp    pretty print Expr"
      ,"--jse   print the JSExpr"
      ,"--ppc   pretty print Expr and then compile"
      ]

printCompile :: String -> IO ()
printCompile = either putStrLn putStrLn . compile

compile :: String -> Either Error String
compile str = printJSExpr False 0 <$> (translateToJS =<< runExprParser "compile" str)

```

大功告成。将自己的语言编译到 JS 子集的编译器已经完成了。再说一次，你可以在 [这里](https://gist.github.com/soupi/d4ff0727ccb739045fad6cdf533ca7dd) 看到完整的源文件。

用我们的编译器运行第一节的示例，产生的 JavaScript 代码如下：

```
$ runhaskell Lisp.hs example.lsp
(function(compose, square, add1) {
  return (console.log)(((compose)(square, add1))(5));
})(function(f, g) {
  return function(x) {
    return (f)((g)(x));
  };
}, function(x) {
  return (x * x);
}, function(x) {
  return (x + 1);
})
```

如果你在自己电脑上安装了 node.js，你可以用以下命令运行这段代码：

```
$ runhaskell Lisp.hs example.lsp | node -p
36
undefined
```

* 最终练习 : 编译有多个表达式的程序而非仅编译一个表达式。

---

via: <https://gilmi.me/blog/post/2016/10/14/lisp-to-js>

作者：[Gil Mizrahi](https://gilmi.me/home) 选题：[oska874](https://github.com/oska874) 译者：[BriFuture](https://github.com/BriFuture) 校对：[wxy](https://github.com/wxy)

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


重启和关闭 Linux 系统的 6 个终端命令

![](/data/attachment/album/201810/31/205455k6w9oawuwpbifku9.png)

在 Linux 管理员的日程当中，有很多需要执行的任务，其中就有系统的重启和关闭。

对于 Linux 管理员来说，重启和关闭系统是其诸多风险操作中的一例，有时候，由于某些原因，这些操作可能无法挽回，他们需要更多的时间来排查问题。

在 Linux 命令行模式下我们可以执行这些任务。很多时候，由于熟悉命令行，Linux 管理员更倾向于在命令行下完成这些任务。

重启和关闭系统的 Linux 命令并不多，用户需要根据需要，选择合适的命令来完成任务。

以下所有命令都有其自身特点，并允许被 Linux 管理员使用.

**建议阅读：**

* [查看系统/服务器正常运行时间的 11 个方法](https://www.2daygeek.com/11-methods-to-find-check-system-server-uptime-in-linux/)
* [Tuptime 一款为 Linux 系统保存历史记录、统计运行时间工具](https://www.2daygeek.com/tuptime-a-tool-to-report-the-historical-and-statistical-running-time-of-linux-system/)

系统重启和关闭之始，会通知所有已登录的用户和进程。当然，如果使用了时间参数，系统将拒绝新的用户登入。

执行此类操作之前，我建议您坚持复查，因为您只能得到很少的提示来确保这一切顺利。

下面陈列了一些步骤：

* 确保您拥有一个可以处理故障的控制台，以防之后可能会发生的问题。 VMWare 可以访问虚拟机，而 IPMI、iLO 和 iDRAC 可以访问物理服务器。
* 您需要通过公司的流程，申请修改或故障的执行权直到得到许可。
* 为安全着想，备份重要的配置文件，并保存到其他服务器上.
* 验证日志文件（提前检查）
* 和相关团队交流，比如数据库管理团队，应用团队等。
* 通知数据库和应用服务人员关闭服务，并得到确定答复。
* 使用适当的命令复盘操作，验证工作。
* 最后，重启系统。
* 验证日志文件，如果一切顺利，执行下一步操作，如果发现任何问题，对症排查。
* 无论是回退版本还是运行程序，通知相关团队提出申请。
* 对操作做适当守候，并将预期的一切正常的反馈给团队

使用下列命令执行这项任务。

* `shutdown`、`halt`、`poweroff`、`reboot` 命令：用来停机、重启或切断电源
* `init` 命令：是 “initialization” 的简称，是系统启动的第一个进程。
* `systemctl` 命令：systemd 是 Linux 系统和服务器的管理程序。

### 方案 1：如何使用 shutdown 命令关闭和重启 Linux 系统

`shutdown` 命令用于断电或重启本地和远程的 Linux 机器。它为高效完成作业提供多个选项。如果使用了时间参数，系统关闭的 5 分钟之前，会创建 `/run/nologin` 文件，以确保后续的登录会被拒绝。

通用语法如下：

```
# shutdown [OPTION] [TIME] [MESSAGE]
```

运行下面的命令来立即关闭 Linux 机器。它会立刻杀死所有进程，并关闭系统。

```
# shutdown -h now
```

* `-h`：如果不特指 `-halt` 选项，这等价于 `-poweroff` 选项。

另外我们可以使用带有 `-halt` 选项的 `shutdown` 命令来立即关闭设备。

```
# shutdown --halt now
或者
# shutdown -H now
```

* `-H, --halt`：停止设备运行

另外我们可以使用带有 `poweroff` 选项的 `shutdown` 命令来立即关闭设备。

```
# shutdown --poweroff now
或者
# shutdown -P now
```

* `-P, --poweroff`：切断电源（默认）。

如果您没有使用时间选项运行下面的命令，它将会在一分钟后执行给出的命令。

```
# shutdown -h
Shutdown scheduled for Mon 2018-10-08 06:42:31 EDT, use 'shutdown -c' to cancel.

root@2daygeek.com#
Broadcast message from root@vps.2daygeek.com (Mon 2018-10-08 06:41:31 EDT):

The system is going down for power-off at Mon 2018-10-08 06:42:31 EDT!
```

其他的登录用户都能在中断中看到如下的广播消息：

```
[daygeek@2daygeek.com ~]$
Broadcast message from root@2daygeek.com (Mon 2018-10-08 06:41:31 EDT):

The system is going down for power-off at Mon 2018-10-08 06:42:31 EDT!
```

对于使用了 `-halt` 选项：

```
# shutdown -H
Shutdown scheduled for Mon 2018-10-08 06:37:53 EDT, use 'shutdown -c' to cancel.

root@2daygeek.com#
Broadcast message from root@vps.2daygeek.com (Mon 2018-10-08 06:36:53 EDT):

The system is going down for system halt at Mon 2018-10-08 06:37:53 EDT!
```

对于使用了 `-poweroff` 选项：

```
# shutdown -P
Shutdown scheduled for Mon 2018-10-08 06:40:07 EDT, use 'shutdown -c' to cancel.

root@2daygeek.com#
Broadcast message from root@vps.2daygeek.com (Mon 2018-10-08 06:39:07 EDT):

The system is going down for power-off at Mon 2018-10-08 06:40:07 EDT!
```

可以在您的终端上敲击 `shutdown -c` 选项取消操作。

```
# shutdown -c

Broadcast message from root@vps.2daygeek.com (Mon 2018-10-08 06:39:09 EDT):

The system shutdown has been cancelled at Mon 2018-10-08 06:40:09 EDT!
```

其他的登录用户都能在中断中看到如下的广播消息：

```
[daygeek@2daygeek.com ~]$
Broadcast message from root@vps.2daygeek.com (Mon 2018-10-08 06:41:35 EDT):

The system shutdown has been cancelled at Mon 2018-10-08 06:42:35 EDT!
```

添加时间参数，如果你想在 `N` 秒之后执行关闭或重启操作。这里，您可以为所有登录用户添加自定义广播消息。例如，我们将在五分钟后重启设备。

```
# shutdown -r +5 "To activate the latest Kernel"
Shutdown scheduled for Mon 2018-10-08 07:13:16 EDT, use 'shutdown -c' to cancel.

[root@vps138235 ~]#
Broadcast message from root@vps.2daygeek.com (Mon 2018-10-08 07:08:16 EDT):

To activate the latest Kernel
The system is going down for reboot at Mon 2018-10-08 07:13:16 EDT!
```

运行下面的命令立即重启 Linux 机器。它会立即杀死所有进程并且重新启动系统。

```
# shutdown -r now
```

* `-r, --reboot`： 重启设备。

### 方案 2：如何通过 reboot 命令关闭和重启 Linux 系统

`reboot` 命令用于关闭和重启本地或远程设备。`reboot` 命令拥有两个实用的选项。

它能够优雅的关闭和重启设备（就好像在系统菜单中惦记重启选项一样简单）。

执行不带任何参数的 `reboot` 命令来重启 Linux 机器。

```
# reboot
```

执行带 `-p` 参数的 `reboot` 命令来关闭 Linux 机器电源。

```
# reboot -p
```

* `-p, --poweroff`：调用 `halt` 或 `poweroff` 命令，切断设备电源。

执行带 `-f` 参数的 `reboot` 命令来强制重启 Linux 设备（这类似按压机器上的电源键）。

```
# reboot -f
```

* `-f, --force`：立刻强制中断，切断电源或重启。

### 方案 3：如何通过 init 命令关闭和重启 Linux 系统

`init`（“initialization” 的简写）是系统启动的第一个进程。

它将会检查 `/etc/inittab` 文件并决定 linux 运行级别。同时，允许用户在 Linux 设备上执行关机或重启操作. 这里存在从 `0` 到 `6` 的七个运行等级。

**建议阅读：**

* [如何检查 Linux 上所有运行的服务](https://www.2daygeek.com/how-to-check-all-running-services-in-linux/)

执行以下 `init` 命令关闭系统。

```
# init 0
```

* `0`： 停机 – 关闭系统。

运行下面的 `init` 命令重启设备：

```
# init 6
```

* `6`：重启 – 重启设备。

### 方案 4：如何通过 halt 命令关闭和重启 Linux 系统

`halt` 命令用来切断电源或关闭远程 Linux 机器或本地主机。 中断所有进程并关闭 cpu。

```
# halt
```

### 方案 5：如何通过 poweroff 命令关闭和重启 Linux 系统

`poweroff` 命令用来切断电源或关闭远程 Linux 机器或本地主机。 `poweroff` 很像 `halt`，但是它可以关闭设备硬件（灯和其他 PC 上的其它东西）。它会给主板发送 ACPI 指令，然后信号发送到电源，切断电源。

```
# poweroff
```

### 方案 6：如何通过 systemctl 命令关闭和重启 Linux 系统

systemd 是一款适用于所有主流 Linux 发型版的全新 init 系统和系统管理器，而不是传统的 SysV init 系统。

systemd 兼容与 SysV 和 LSB 初始化脚本。它能够替代 SysV init 系统。systemd 是内核启动的第一个进程，并持有序号为 1 的进程 PID。

**建议阅读：**

* [chkservice – 一款终端下系统单元管理工具](https://www.2daygeek.com/chkservice-a-tool-for-managing-systemd-units-from-linux-terminal/)

它是一切进程的父进程，Fedora 15 是第一个适配安装 systemd （替代了 upstart）的发行版。

`systemctl` 是命令行下管理 systemd 守护进程和服务的主要工具（如 `start`、`restart`、`stop`、`enable`、`disable`、`reload` & `status`）。

systemd 使用 .service 文件而不是 SysV init 使用的 bash 脚本。 systemd 将所有守护进程归与自身的 Linux cgroups 用户组下，您可以浏览 `/cgroup/systemd` 文件查看该系统层次等级。

```
# systemctl halt
# systemctl poweroff
# systemctl reboot
# systemctl suspend
# systemctl hibernate
```

---

via: <https://www.2daygeek.com/6-commands-to-shutdown-halt-poweroff-reboot-the-linux-system/>

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

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


Linux 中国通证商城发布

Summary: 这次上线的“Linux 中国通证商城”—— https://shop.linux.cn ，是基于 WooCommerce 所构建的，并专门自行开发了支持 ERC20 通证的插件，以支持我们的 LCCN 通证的链上支付。

![](/data/attachment/album/201811/01/120610ylpd9pklyiqz1gll.jpg)

在我们发布了 Linux 中国的[社区通证 LCCN](/article-9886-1.html) 之后，我们又在通证经济建设方向迈出了一步。通证作为一个经济体系中流转的价值凭证，其不但可以根据社区成员的行为进行精细的通证分发，而且还需要建立通证的流通渠道。

按照我们之前的设计，我们流通 LCCN 的通道有二：

1. 社区提供通证的 B2C 消费渠道
2. 通证持有者个人的通证消费渠道

作为社区，我们可以将社区所拥有的大部分资源，比如媒体资源、客户置换资源、定制礼品等以 LCCN 兑换的方式提供给社区成员。比如这次，我们就将官方网站的广告位、认证微信的文章推送、认证微博的转发等资源开放给社区来兑换。我们也同时将从合作伙伴中置换和赞助来的资源（如兑换券、书籍等）。

此外，我们也小规模组织了二手物品的交换群，可供大家用 LCCN 交换其他人手中的闲置物品。目前已经发生了多笔交易，交易了包括手机、树莓派、书籍等物品，最高交易额高达 Ⓛ28800。

![](/data/attachment/album/201811/01/115719nkaaaavjla9rkci0.jpg)

这次上线的“Linux 中国通证商城”—— <https://shop.linux.cn> ，是基于 WooCommerce 所构建的，并专门自行开发了支持 ERC20 通证的插件，以支持我们的 LCCN 通证的链上支付。当然，作为开源社区，我们的插件同样遵循开源精神，开源到了 [GitHub](https://github.com/inKerk/woocommerce-erc20-payment-gateway) 上。

我们的通证商城的上线，得到了人民邮电出版社、UCloud、刘遄老师等的大力支持。其中刘遄老师捐献了其畅销书《Linux 就该这么学》 多达 30 册。由衷感谢各位伙伴的支持！

![](/data/attachment/album/201811/01/115319n8ctb4rfelswwbhd.jpg)

欢迎大家体验和参与贡献，也欢迎各位合作伙伴参与我们的生态建设。


用 Pandoc 生成一篇调研论文

Summary: 学习如何用 Markdown 管理章节引用、图像、表格以及更多。

学习如何用 Markdown 管理章节引用、图像、表格以及更多。

>
> 学习如何用 Markdown 管理章节引用、图像、表格以及更多。
>
>
>

![](/data/attachment/album/201811/01/130402un9z690xd92o60ik.jpg)

这篇文章对于使用 [Markdown](https://en.wikipedia.org/wiki/Markdown) 语法做一篇调研论文进行了一个深度体验。覆盖了如何创建和引用章节、图像（用 Markdown 和 [LaTeX](https://www.latex-project.org/)）和参考书目。我们也讨论了一些棘手的案例和为什么使用 LaTex 是一个正确的做法。

### 调研

调研论文一般包括对章节、图像、表格和参考书目的引用。[Pandoc](https://pandoc.org/) 本身并不能交叉引用这些，但是它能够利用 [pandoc-crossref](http://lierdakil.github.io/pandoc-crossref/) 过滤器来完成自动编号和章节、图像、表格的交叉引用。

让我们从重写原本以 LaTax 撰写的 [一个教育调研报告的例子](https://dl.acm.org/citation.cfm?id=3270118) 开始，然后用 Markdown（和一些 LaTax）、Pandoc 和 Pandoc-crossref 重写。

#### 添加并引用章节

要想章节被自动编号，必须使用 Markdown H1 标题编写。子章节使用 H2-H4 子标题编写（通常不需要更多级别了）。例如一个章节的标题是 “Implementation”，写作 `# Implementation {#sec: implementation}`，然后 Pandoc 会把它转化为 `3. Implementation`（或者转换为相应的章节编号）。`Implementation` 这个标题使用了 H1 并且声明了一个 `{#sec: implementation}` 的标签，这是作者用于引用该章节的标签。要想引用一个章节，输入 `@` 符号并跟上对应章节标签，使用方括号括起来即可： `[@ sec:implementation]`

[在这篇论文中](https://dl.acm.org/citation.cfm?id=3270118), 我们发现了下面这个例子：

```
we lack experience (consistency between TAs, [@sec:implementation]).
```

Pandoc 转换：

```
we lack experience (consistency between TAs, Section 4).
```

章节被自动编号（这在本文最后的 `Makefile` 当中说明）。要创建无编号的章节，输入章节的标题并在最后添加 `{-}`。例如：`### Designing a game for maintainability {-}` 就以标题 “Designing a game for maintainability”，创建了一个无标号的章节。

#### 添加并引用图像

添加并引用一个图像，跟添加并引用一个章节和添加一个 Markdown 图片很相似：

```
![Scatterplot matrix](data/scatterplots/RScatterplotMatrix2.png){#fig:scatter-matrix}
```

上面这一行是告诉 Pandoc，有一个标有 Scatterplot matrix 的图像以及这张图片路径是 `data/scatterplots/RScatterplotMatrix2.png`。`{#fig:scatter-matrix}` 表明了用于引用该图像的名字。

这里是从一篇论文中进行图像引用的例子：

```
The boxes "Enjoy", "Grade" and "Motivation" ([@fig:scatter-matrix]) ...
```

Pandoc 产生如下输出：

```
The boxes "Enjoy", "Grade" and "Motivation" (Fig. 1) ...
```

#### 添加及引用参考书目

大多数调研报告都把引用放在一个 BibTeX 的数据库文件中。在这个例子中，该文件被命名为 [biblio.bib](https://github.com/kikofernandez/pandoc-examples/blob/master/research-paper/biblio.bib)，它包含了论文中所有的引用。下面是这个文件的样子：

```
@inproceedings{wrigstad2017mastery,
    Author =       {Wrigstad, Tobias and Castegren, Elias},
    Booktitle =    {SPLASH-E},
    Title =        {Mastery Learning-Like Teaching with Achievements},
    Year =         2017
}

@inproceedings{review-gamification-framework,
  Author =       {A. Mora and D. Riera and C. Gonzalez and J. Arnedo-Moreno},
  Publisher =    {IEEE},
  Booktitle =    {2015 7th International Conference on Games and Virtual Worlds
                  for Serious Applications (VS-Games)},
  Doi =          {10.1109/VS-GAMES.2015.7295760},
  Keywords =     {formal specification;serious games (computing);design
                  framework;formal design process;game components;game design
                  elements;gamification design frameworks;gamification-based
                  solutions;Bibliographies;Context;Design
                  methodology;Ethics;Games;Proposals},
  Month =        {Sept},
  Pages =        {1-8},
  Title =        {A Literature Review of Gamification Design Frameworks},
  Year =         2015,
  Bdsk-Url-1 =   {http://dx.doi.org/10.1109/VS-GAMES.2015.7295760}
}

...
```

第一行的 `@inproceedings{wrigstad2017mastery,` 表明了出版物 的类型（`inproceedings`），以及用来指向那篇论文的标签（`wrigstad2017mastery`）。

引用这篇题为 “Mastery Learning-Like Teaching with Achievements” 的论文, 输入：

```
the achievement-driven learning methodology [@wrigstad2017mastery]
```

Pandoc 将会输出：

```
the achievement- driven learning methodology [30]
```

这篇论文将会产生像下面这样被标号的参考书目：

![](/data/attachment/album/201811/01/130413nn55ey4n556z565j.png)

引用文章的集合也很容易：只要引用使用分号 `;` 分隔开被标记的参考文献就可以了。如果一个引用有两个标签 —— 例如： `SEABORN201514` 和 `gamification-leaderboard-benefits`—— 像下面这样把它们放在一起引用：

```
Thus, the most important benefit is its potential to increase students' motivation
and engagement [@SEABORN201514;@gamification-leaderboard-benefits].
```

Pandoc 将会产生：

```
Thus, the most important benefit is its potential to increase students’ motivation
and engagement [26, 28]
```

### 问题案例

一个常见的问题是所需项目与页面不匹配。不匹配的部分会自动移动到它们认为合适的地方，即便这些位置并不是读者期望看到的位置。因此在图像或者表格接近于它们被提及的地方时，我们需要调节一下那些元素放置的位置，使得它们更加易于阅读。为了达到这个效果，我建议使用 `figure` 这个 LaTeX 环境参数，它可以让用户控制图像的位置。

我们看一个上面提到的图像的例子：

```
![Scatterplot matrix](data/scatterplots/RScatterplotMatrix2.png){#fig:scatter-matrix}
```

然后使用 LaTeX 重写：

```
\begin{figure}[t]
\includegraphics{data/scatterplots/RScatterplotMatrix2.png}
\caption{\label{fig:matrix}Scatterplot matrix}
\end{figure}
```

在 LaTeX 中，`figure` 环境参数中的 `[t]` 选项表示这张图用该位于该页的最顶部。有关更多选项，参阅 [LaTex/Floats, Figures, and Captions](https://en.wikibooks.org/wiki/LaTeX/Floats,_Figures_and_Captions#Figures) 这篇 Wikibooks 的文章。

### 产生一篇论文

到目前为止，我们讲了如何添加和引用（子）章节、图像和参考书目，现在让我们重温一下如何生成一篇 PDF 格式的论文。要生成 PDF，我们将使用 Pandoc 生成一篇可以被构建成最终 PDF 的 LaTeX 文件。我们还会讨论如何以 LaTeX，使用一套自定义的模板和元信息文件生成一篇调研论文，以及如何将 LaTeX 文档编译为最终的 PDF 格式。

很多会议都提供了一个 .cls 文件或者一套论文应有样式的模板；例如，它们是否应该使用两列的格式以及其它的设计风格。在我们的例子中，会议提供了一个名为 `acmart.cls` 的文件。

作者通常想要在他们的论文中包含他们所属的机构，然而，这个选项并没有包含在默认的 Pandoc 的 LaTeX 模板（注意，可以通过输入 `pandoc -D latex` 来查看 Pandoc 模板）当中。要包含这个内容，找一个 Pandoc 默认的 LaTeX 模板，并添加一些新的内容。将这个模板像下面这样复制进一个名为 `mytemplate.tex` 的文件中：

```
pandoc -D latex > mytemplate.tex
```

默认的模板包含以下代码：

```
$if(author)$
\author{$for(author)$$author$$sep$ \and $endfor$}
$endif$
$if(institute)$
\providecommand{\institute}[1]{}
\institute{$for(institute)$$institute$$sep$ \and $endfor$}
$endif$
```

因为这个模板应该包含作者的联系方式和电子邮件地址，在其他一些选项之间，我们更新这个模板以添加以下内容（我们还做了一些其他的更改，但是因为文件的长度，就没有包含在此处）：

```
latex
$for(author)$
    $if(author.name)$
        \author{$author.name$}
        $if(author.affiliation)$
            \affiliation{\institution{$author.affiliation$}}
        $endif$
        $if(author.email)$
            \email{$author.email$}
        $endif$
    $else$
        $author$
    $endif$
$endfor$
```

要让这些更改起作用，我们还应该有下面的文件：

* `main.md` 包含调研论文
* `biblio.bib` 包含参考书目数据库
* `acmart.cls` 我们使用的文档的集合
* `mytemplate.tex` 是我们使用的模板文件（代替默认的）

让我们添加论文的元信息到一个 `meta.yaml` 文件：

```
---
template: 'mytemplate.tex'
documentclass: acmart
classoption: sigconf
title: The impact of opt-in gamification on `\\`{=latex} students' grades in a software design course
author:
- name: Kiko Fernandez-Reyes
  affiliation: Uppsala University
  email: kiko.fernandez@it.uu.se
- name: Dave Clarke
  affiliation: Uppsala University
  email: dave.clarke@it.uu.se
- name: Janina Hornbach
  affiliation: Uppsala University
  email: janina.hornbach@fek.uu.se
bibliography: biblio.bib
abstract: |
  An achievement-driven methodology strives to give students more control over their learning with enough flexibility to engage them in deeper learning. (more stuff continues)

include-before: |
      \` ``{=latex}
      \copyrightyear{2018}
      \acmYear{2018}
      \setcopyright{acmlicensed}
      \acmConference[MODELS '18 Companion]{ACM/IEEE 21th International Conference on Model Driven Engineering Languages and Systems}{October 14--19, 2018}{Copenhagen, Denmark}
      \acmBooktitle{ACM/IEEE 21th International Conference on Model Driven Engineering Languages and Systems (MODELS '18 Companion), October 14--19, 2018, Copenhagen, Denmark}
      \acmPrice{XX.XX}
      \acmDOI{10.1145/3270112.3270118}
      \acmISBN{978-1-4503-5965-8/18/10}

      \begin{CCSXML}
      <ccs2012>
      <concept>
      <concept_id>10010405.10010489</concept_id>
      <concept_desc>Applied computing~Education</concept_desc>
      <concept_significance>500</concept_significance>
      </concept>
      </ccs2012>
      \end{CCSXML}

      \ccsdesc[500]{Applied computing~Education}

      \keywords{gamification, education, software design, UML}
      \` ``
figPrefix:
  - "Fig."
  - "Figs."
secPrefix:
  - "Section"
  - "Sections"
...
```

这个元信息文件使用 LaTeX 设置下列参数：

* `template` 指向使用的模板（`mytemplate.tex`）
* `documentclass` 指向使用的 LaTeX 文档集合（`acmart`）
* `classoption` 是在 `sigconf` 的案例中，指向这个类的选项
* `title` 指定论文的标题
* `author` 是一个包含例如 `name`、`affiliation` 和 `email` 的地方
* `bibliography` 指向包含参考书目的文件（`biblio.bib`）
* `abstract` 包含论文的摘要
* `include-before` 是这篇论文的具体内容之前应该被包含的信息；在 LaTeX 中被称为 [前言](https://www.sharelatex.com/learn/latex/Creating_a_document_in_LaTeX#The_preamble_of_a_document)。我在这里包含它去展示如何产生一篇计算机科学的论文，但是你可以选择跳过
* `figPrefix` 指向如何引用文档中的图像，例如：当引用图像的 `[@fig:scatter-matrix]` 时应该显示什么。例如，当前的 `figPrefix` 在这个例子 `The boxes "Enjoy", "Grade" and "Motivation" ([@fig:scatter-matrix])`中，产生了这样的输出：`The boxes "Enjoy", "Grade" and "Motivation" (Fig. 3)`。如果这里有很多图像，目前的设置表明它应该在图像号码旁边显示 `Figs.`
* `secPrefix` 指定如何引用文档中其他地方提到的部分（类似之前的图像和概览）

现在已经设置好了元信息，让我们来创建一个 `Makefile`，它会产生你想要的输出。`Makefile` 使用 Pandoc 产生 LaTeX 文件，`pandoc-crossref` 产生交叉引用，`pdflatex` 构建 LaTeX 为 PDF，`bibtex` 处理引用。

`Makefile` 已经展示如下：

```
all: paper

paper:
        @pandoc -s -F pandoc-crossref --natbib meta.yaml --template=mytemplate.tex -N \
         -f markdown -t latex+raw_tex+tex_math_dollars+citations -o main.tex main.md
        @pdflatex main.tex &> /dev/null
        @bibtex main &> /dev/null
        @pdflatex main.tex &> /dev/null
        @pdflatex main.tex &> /dev/null

clean:
        rm main.aux main.tex main.log main.bbl main.blg main.out

.PHONY: all clean paper
```

Pandoc 使用下面的标记：

* `-s` 创建一个独立的 LaTeX 文档
* `-F pandoc-crossref` 利用 `pandoc-crossref` 进行过滤
* `--natbib` 用 `natbib` （你也可以选择 `--biblatex`）对参考书目进行渲染
* `--template` 设置使用的模板文件
* `-N` 为章节的标题编号
* `-f` 和 `-t` 指定从哪个格式转换到哪个格式。`-t` 通常包含格式和 Pandoc 使用的扩展。在这个例子中，我们标明的 `raw_tex+tex_math_dollars+citations` 允许在 Markdown 中使用 `raw_tex` LaTeX。 `tex_math_dollars` 让我们能够像在 LaTeX 中一样输入数学符号，`citations` 让我们可以使用 [这个扩展](http://pandoc.org/MANUAL.html#citations)。

要从 LaTeX 产生 PDF，按 [来自bibtex](http://www.bibtex.org/Using/) 的指导处理参考书目：

```
@pdflatex main.tex &> /dev/null
@bibtex main &> /dev/null
@pdflatex main.tex &> /dev/null
@pdflatex main.tex &> /dev/null
```

脚本用 `@` 忽略输出，并且重定向标准输出和错误到 `/dev/null` ，因此我们在使用这些命令的可执行文件时不会看到任何的输出。

最终的结果展示如下。这篇文章的库可以在 [GitHub](https://github.com/kikofernandez/pandoc-examples/tree/master/research-paper) 找到：

![](/data/attachment/album/201811/01/130414iw01zqqb10hmzzqi.png)

### 结论

在我看来，研究的重点是协作、思想的传播，以及在任何一个恰好存在的领域中改进现有的技术。许多计算机科学家和工程师使用 LaTeX 文档系统来写论文，它对数学提供了完美的支持。来自社会科学的研究人员似乎更喜欢 DOCX 文档。

当身处不同社区的研究人员一同写一篇论文时，他们首先应该讨论一下他们将要使用哪种格式。然而如果包含太多的数学符号，DOCX 对于工程师来说不会是最简便的选择，LaTeX 对于缺乏编程经验的研究人员来说也有一些问题。就像这篇文章中展示的，Markdown 是一门工程师和社会科学家都很轻易能够使用的语言。

---

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

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

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


Flameshot：一个简洁但功能丰富的截图工具

Summary: 在本教程中，我们将看到如何安装 Flameshot 以及如何使用它来截图。

在本教程中，我们将看到如何安装 Flameshot 以及如何使用它来截图。

![](/data/attachment/album/201811/01/131936ytmy79zy9fvf69vl.png)

截图是我工作的一部分，我先前使用深度截图工具来截图，深度截图是一个简单、轻量级且非常简洁的截图工具。它自带许多功能例如窗口识别、快捷键支持、图片编辑、延时截图、社交分享、智能存储以及图片清晰度调整等功能。今天我碰巧发现了另一个具备多种功能的截图工具，它就是 **Flameshot** ，一个简单但功能丰富的针对类 Unix 系统的截图工具。它简单易用，可定制并且有选项可以支持上传截图到在线图片分享网站 **imgur** 上。同时 Flameshot 有一个 CLI 版本，所以你也可以从命令行来进行截图。Flameshot 是一个完全免费且开源的工具。在本教程中，我们将看到如何安装 Flameshot 以及如何使用它来截图。

### 安装 Flameshot

**在 Arch Linux 上：**

Flameshot 可以从 Arch LInux 的 [community] 仓库中获取。确保你已经启用了 community 仓库，然后就可以像下面展示的那样使用 pacman 来安装 Flameshot ：

```
$ sudo pacman -S flameshot
```

它也可以从 [**AUR**](https://aur.archlinux.org/packages/flameshot-git) 中获取，所以你还可以使用任意一个 AUR 帮助程序（例如 [**Yay**](https://www.ostechnix.com/yay-found-yet-another-reliable-aur-helper/)）来在基于 Arch 的系统中安装它：

```
$ yay -S flameshot-git
```

**在 Fedora 中：**

```
$ sudo dnf install flameshot
```

在 **Debian 10+** 和 **Ubuntu 18.04+** 中，可以使用 APT 包管理器来安装它：

```
$ sudo apt install flameshot
```

**在 openSUSE 上：**

```
$ sudo zypper install flameshot
```

在其他的 Linux 发行版中，可以从源代码编译并安装它。编译过程中需要 **Qt version 5.3** 以及 **GCC 4.9.2** 或者它们的更高版本。

### 使用

可以从菜单或者应用启动器中启动 Flameshot。在 MATE 桌面环境，它通常可以在 “Applications -> Graphics” 下找到。

一旦打开了它，你就可以在系统面板中看到 Flameshot 的托盘图标。

**注意：**

假如你使用 Gnome 桌面环境，为了能够看到系统托盘图标，你需要安装 [TopIcons](https://extensions.gnome.org/extension/1031/topicons/) 扩展。

在 Flameshot 托盘图标上右击，你便会看到几个菜单项，例如打开配置窗口、信息窗口以及退出该应用。

要进行截图，只需要点击托盘图标就可以了。接着你将看到如何使用 Flameshot 的帮助窗口。选择一个截图区域，然后敲回车键便可以截屏了，点击右键便可以看到颜色拾取器，再敲空格键便可以查看屏幕侧边的面板。你可以使用鼠标的滚轮来增加或者减少指针的宽度。

Flameshot 自带一系列非常好的功能，例如：

* 可以进行手写
* 可以划直线
* 可以画长方形或者圆形框
* 可以进行长方形区域选择
* 可以画箭头
* 可以对要点进行标注
* 可以添加文本
* 可以对图片或者文字进行模糊处理
* 可以展示图片的尺寸大小
* 在编辑图片是可以进行撤销和重做操作
* 可以将选择的东西复制到剪贴板
* 可以保存选区
* 可以离开截屏
* 可以选择另一个 app 来打开图片
* 可以上传图片到 imgur 网站
* 可以将图片固定到桌面上

下面是一个示例的视频：

<http://www.ostechnix.com/wp-content/uploads/2018/09/Flameshot-demo.mp4>

### 快捷键

Frameshot 也支持快捷键。在 Flameshot 的托盘图标上右击并点击 “Information” 窗口便可以看到在 GUI 模式下所有可用的快捷键。下面是在 GUI 模式下可用的快捷键清单：

| 快捷键 | 描述 |
| --- | --- |
| `←`、`↓`、`↑`、`→` | 移动选择区域 1px |
| `Shift` + `←`、`↓`、`↑`、`→` | 将选择区域大小更改 1px |
| `Esc` | 退出截图 |
| `Ctrl` + `C` | 复制到粘贴板 |
| `Ctrl` + `S` | 将选择区域保存为文件 |
| `Ctrl` + `Z` | 撤销最近的一次操作 |
| 鼠标右键 | 展示颜色拾取器 |
| 鼠标滚轮 | 改变工具的宽度 |

边按住 `Shift` 键并拖动选择区域的其中一个控制点将会对它相反方向的控制点做类似的拖放操作。

### 命令行选项

Flameshot 也支持一系列的命令行选项来延时截图和保存图片到自定义的路径。

要使用 Flameshot GUI 模式，运行：

```
$ flameshot gui
```

要使用 GUI 模式截屏并将你选取的区域保存到一个自定义的路径，运行：

```
$ flameshot gui -p ~/myStuff/captures
```

要延时 2 秒后打开 GUI 模式可以使用：

```
$ flameshot gui -d 2000
```

要延时 2 秒并将截图保存到一个自定义的路径（无 GUI）可以使用：

```
$ flameshot full -p ~/myStuff/captures -d 2000
```

要截图全屏并保存到自定义的路径和粘贴板中使用：

```
$ flameshot full -c -p ~/myStuff/captures
```

要在截屏中包含鼠标并将图片保存为 PNG 格式可以使用：

```
$ flameshot screen -r
```

要对屏幕 1 进行截屏并将截屏复制到粘贴板中可以运行：

```
$ flameshot screen -n 1 -c
```

你还需要什么功能呢？Flameshot 拥有几乎截屏的所有功能：添加注释、编辑图片、模糊处理或者对要点做高亮等等功能。我想：在我找到它的最佳替代品之前，我将一直使用 Flameshot 来作为我当前的截图工具。请尝试一下它，你不会失望的。

好了，这就是今天的全部内容了。后续将有更多精彩内容，请保持关注！

Cheers!

---

via: <https://www.ostechnix.com/flameshot-a-simple-yet-powerful-feature-rich-screenshot-tool/>

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

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


模拟 Altair 8800 计算机

Summary: Altair 8800 是 1975 年发布的自建家用电脑套件。Altair 基本上是第一台个人电脑（PC），虽然 PC 这个名词好几年前就出现了。对 Dell、HP 或者 Macbook 而言它是亚当（或者夏娃）。

![](/data/attachment/album/201811/01/212159lb3ie9lpbn4ebpjv.jpg)

[Altair 8800](https://en.wikipedia.org/wiki/Altair_8800) 是 1975 年发布的自建家用电脑套件。Altair 基本上是第一台个人电脑（PC），虽然 PC 这个名词好几年前就出现了。对 Dell、HP 或者 Macbook 而言它是亚当（或者夏娃）。

有些人认为为 Z80（与 Altair 的 Intel 8080 密切相关的处理器）编写仿真器真是太棒了，并认为它需要一个模拟 Altair 的控制面板。所以如果你想知道 1975 年使用电脑是什么感觉，你可以在你的 Macbook 上运行 Altair：

![Altair 8800](/data/attachment/album/201811/01/211940undn6oa3l4at3adg.png)

### 安装它

你可以从[这里](http://www.autometer.de/unix4fun/z80pack/ftp/)的 FTP 服务器下载 Z80 包。你要查找最新的 Z80 包版本，例如 `z80pack-1.26.tgz`。

首先解压文件：

```
$ tar -xvf z80pack-1.26.tgz
```

进入解压目录：

```
$ cd z80pack-1.26
```

控制面板模拟基于名为 `frontpanel` 的库。你必须先编译该库。如果你进入 `frontpanel` 目录，你会发现 `README` 文件列出了这个库自己的依赖项。你在这里的体会几乎肯定会与我的不同，但也许我的痛苦可以作为例子。我安装了依赖项，但是是通过 [Homebrew](http://brew.sh/) 安装的。为了让库能够编译，我必须确保在 `Makefile.osx` 中将 `/usr/local/include`添加到 Clang 的 include 路径中。

如果你觉得依赖没有问题，那么你应该就能编译这个库（我们现在位于 `z80pack-1.26/frontpanel`）：

```
$ make -f Makefile.osx ...
$ make -f Makefile.osx clean
```

你应该会得到 `libfrontpanel.so`。我把它拷贝到 `libfrontpanel.so`。

Altair 模拟器位于 `z80pack-1.26/altairsim` 下。你现在需要编译模拟器本身。进入 `z80pack-1.26/altairsim/srcsim` 并再次运行 `make`：

```
$ make -f Makefile.osx ...
$ make -f Makefile.osx clean
```

该过程将在 `z80pack-1.26/altairsim` 中创建一个名为 `altairsim` 的可执行文件。运行该可执行文件，你应该会看到标志性的 Altair 控制面板！

如果你想要探究，请阅读原始的 [Altair 手册](http://www.classiccmp.org/dunfield/altair/d/88opman.pdf)

如果你喜欢这篇文章，我们每两周更新一次！在 Twitter 上关注 [@TwoBitHistory][6](https://twitter.com/TwoBitHistory) 或订阅 [RSS 源](https://twobithistory.org/feed.xml)了解什么时候有新文章。

---

via: <https://twobithistory.org/2017/12/02/simulating-the-altair.html>

作者：[Two-Bit History](https://twobithistory.org) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


在 Linux 上使用 systemd 设置定时器

Summary: 学习使用 systemd 创建启动你的游戏服务器的定时器。

学习使用 systemd 创建启动你的游戏服务器的定时器。

>
> 学习使用 systemd 创建启动你的游戏服务器的定时器。
>
>
>

![](/data/attachment/album/201811/01/221408u4uar48ciu4bcidy.jpg)

之前，我们看到了如何[手动的](/article-9700-1.html)、[在开机与关机时](/article-9703-1.html)、[在启用某个设备时](https://www.linux.com/blog/intro-to-linux/2018/6/systemd-services-reacting-change)、[在文件系统发生改变时](https://www.linux.com/blog/learn/intro-to-linux/2018/6/systemd-services-monitoring-files-and-directories) 启用与禁用 systemd 服务。

定时器增加了另一种启动服务的方式，基于……时间。尽管与定时任务很相似，但 systemd 定时器稍微地灵活一些。让我们看看它是怎么工作的。

### “定时运行”

让我们展开[本系列前两篇文章](/article-9703-1.html)中[你所设置的](/article-9700-1.html) [Minetest](https://www.minetest.net/) 服务器作为如何使用定时器单元的第一个例子。如果你还没有读过那几篇文章，可以现在去看看。

你将通过创建一个定时器来“改进” Minetest 服务器，使得在服务器启动 1 分钟后运行游戏服务器而不是立即运行。这样做的原因可能是，在启动之前可能会用到其他的服务，例如发邮件给其他玩家告诉他们游戏已经准备就绪，你要确保其他的服务（例如网络）在开始前完全启动并运行。

最终，你的 `minetest.timer` 单元看起来就像这样：

```
# minetest.timer
[Unit]
Description=Runs the minetest.service 1 minute after boot up

[Timer]
OnBootSec=1 m
Unit=minetest.service

[Install]
WantedBy=basic.target
```

一点也不难吧。

如以往一般，开头是 `[Unit]` 和一段描述单元作用的信息，这儿没什么新东西。`[Timer]` 这一节是新出现的，但它的作用不言自明：它包含了何时启动服务，启动哪个服务的信息。在这个例子当中，`OnBootSec` 是告诉 systemd 在系统启动后运行服务的指令。

其他的指令有：

* `OnActiveSec=`，告诉 systemd 在定时器启动后多长时间运行服务。
* `OnStartupSec=`，同样的，它告诉 systemd 在 systemd 进程启动后多长时间运行服务。
* `OnUnitActiveSec=`，告诉 systemd 在上次由定时器激活的服务启动后多长时间运行服务。
* `OnUnitInactiveSec=`，告诉 systemd 在上次由定时器激活的服务停用后多长时间运行服务。

继续 `minetest.timer` 单元，`basic.target` 通常用作<ruby> 后期引导服务 <rt>  late boot services </rt></ruby>的<ruby> 同步点 <rt>  synchronization point </rt></ruby>。这就意味着它可以让 `minetest.timer` 单元运行在安装完<ruby> 本地挂载点 <rt>  local mount points </rt></ruby>或交换设备，套接字、定时器、路径单元和其他基本的初始化进程之后。就像在[第二篇文章中 systemd 单元](/article-9703-1.html)里解释的那样，`targets` 就像<ruby> 旧的运行等级 <rt>  old run levels </rt></ruby>一样，可以将你的计算机置于某个状态，或像这样告诉你的服务在达到某个状态后开始运行。

在前两篇文章中你配置的 `minetest.service` 文件[最终](/article-9703-1.html)看起来就像这样：

```
# minetest.service
[Unit]
Description= Minetest server
Documentation= https://wiki.minetest.net/Main_Page

[Service]
Type= simple
User=

ExecStart= /usr/games/minetest --server
ExecStartPost= /home//bin/mtsendmail.sh "Ready to rumble?" "Minetest Starting up"

TimeoutStopSec= 180
ExecStop= /home//bin/mtsendmail.sh "Off to bed. Nightie night!" "Minetest Stopping in 2 minutes"
ExecStop= /bin/sleep 120
ExecStop= /bin/kill -2 $MAINPID

[Install]
WantedBy= multi-user.target
```

这儿没什么需要修改的。但是你需要将 `mtsendmail.sh`（发送你的 email 的脚本）从：

```
#!/bin/bash
# mtsendmail
sleep 20
echo $1 | mutt -F /home/<username>/.muttrc -s "$2" my_minetest@mailing_list.com
sleep 10
```

改成：

```
#!/bin/bash
# mtsendmail.sh
echo $1 | mutt -F /home/paul/.muttrc -s "$2" pbrown@mykolab.com
```

你做的事是去除掉 Bash 脚本中那些蹩脚的停顿。Systemd 现在来做等待。

### 让它运行起来

确保一切运作正常，禁用 `minetest.service`：

```
sudo systemctl disable minetest
```

这使得系统启动时它不会一同启动；然后，相反地，启用 `minetest.timer`：

```
sudo systemctl enable minetest.timer
```

现在你就可以重启服务器了，当运行 `sudo journalctl -u minetest.*` 后，你就会看到 `minetest.timer` 单元执行后大约一分钟，`minetest.service` 单元开始运行。

![minetest timer](/data/attachment/album/201811/01/221418yyzjjv5060jv0a5e.png "minetest timer")

*图 1：minetest.timer 运行大约 1 分钟后 minetest.service 开始运行*

### 时间的问题

`minetest.timer` 在 systemd 的日志里显示的启动时间为 09:08:33 而 `minetest.service` 启动时间是 09:09:18，它们之间少于 1 分钟，关于这件事有几点需要说明一下：首先，请记住我们说过 `OnBootSec=` 指令是从引导完成后开始计算服务启动的时间。当 `minetest.timer` 的时间到来时，引导已经在几秒之前完成了。

另一件事情是 systemd 给自己设置了一个<ruby> 误差幅度 <rt>  margin of error </rt></ruby>（默认是 1 分钟）来运行东西。这有助于在多个<ruby> 资源密集型进程 <rt>  resource-intensive processes </rt></ruby>同时运行时分配负载：通过分配 1 分钟的时间，systemd 可以等待某些进程关闭。这也意味着 `minetest.service` 会在引导完成后的 1~2 分钟之间启动。但精确的时间谁也不知道。

顺便一提，你可以用 `AccuracySec=` 指令[修改误差幅度](https://www.freedesktop.org/software/systemd/man/systemd.timer.html#AccuracySec=)。

你也可以检查系统上所有的定时器何时运行或是上次运行的时间：

```
systemctl list-timers --all
```

![check timer](/data/attachment/album/201811/01/221426vizdi0z11zjr42mu.png "check timer")

*图 2：检查定时器何时运行或上次运行的时间*

最后一件值得思考的事就是你应该用怎样的格式去表示一段时间。Systemd 在这方面非常灵活：`2 h`，`2 hours` 或 `2hr` 都可以用来表示 2 个小时。对于“秒”，你可以用 `seconds`，`second`，`sec` 和 `s`。“分”也是同样的方式：`minutes`，`minute`，`min` 和 `m`。你可以检查 `man systemd.time` 来查看 systemd 能够理解的所有时间单元。

### 下一次

下次你会看到如何使用日历中的日期和时间来定期运行服务，以及如何通过组合定时器与设备单元在插入某些硬件时运行服务。

回头见！

在 Linux 基金会和 edx 上通过免费课程 [“Introduction to Linux”](https://training.linuxfoundation.org/linux-courses/system-administration-training/introduction-to-linux) 学习更多关于 Linux 的知识。

---

via: <https://www.linux.com/blog/learn/intro-to-linux/2018/7/setting-timer-systemd-linux>

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

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


正确选择开源数据库的 5 个技巧

Summary: 对关键应用的选择不容许丝毫错误。

对关键应用的选择不容许丝毫错误。

>
> 对关键应用的选择不容许丝毫错误。
>
>
>

![](/data/attachment/album/201811/01/232107xuvdld0n5rzcdbc0.jpg)

你或许会遇到需要选择合适的开源数据库的情况。但这无论对于开源方面的老手或是新手，都是一项艰巨的任务。

在过去的几年中，采用开源技术的企业越来越多。面对这样的趋势，众多开源应用公司都纷纷承诺自己提供的解决方案能够各种问题、适应各种负载。但这些承诺不能轻信，在开源应用上的选择是重要而艰难的，尤其是数据库这种关键的应用。

凭借我在 [Percona](https://www.percona.com/) 和其它公司担任 IT 专家的经验，我很幸运能够指导其他人在开源技术的选择上做出正确的决策，因为需要考虑的重要因素太多了。希望通过这篇文章能够向大家分享这方面的一些技巧。

### 有一个明确的目标

这一点看似简单，但在和很多人聊过 MySQL、MongoDB、PostgreSQL 之后，我觉得这一点才是最重要的。

面对繁杂的开源数据库，更需要明确自己的目标。无论这个数据库是作为开发用的标准化数据库后端，抑或是用于替换遗留代码中的原有数据库，这都是一个明确的目标。

目标一旦确定，就可以集中精力与开源软件的提供方商讨更多细节了。

### 了解你的工作负载

尽管开源数据库技术的功能越来越丰富，但这些新加入的功能都不太具有普适性。譬如 MongoDB 新增了事务的支持、MySQL 新增了 JSON 存储的功能等等。目前开源数据库的普遍趋势是不断加入新的功能，但很多人的误区却在于没有选择最适合的工具来完成自己的工作 —— 这样的人或许是一个自大的开发者，又或许是一个视野狭窄的主管 —— 最终导致公司业务上的损失。最致命的是，在业务初期，使用了不适合的工具往往也可以顺利地完成任务，但随着业务的增长，很快就会到达瓶颈，尽管这个时候还可以替换更合适的工具，但成本就比较高了。

例如，如果你需要的是数据分析仓库，关系数据库可能不是一个适合的选择；如果你处理事务的应用要求严格的数据完整性和一致性，就不要考虑 NoSQL 了。

### 不要重新发明轮子

在过去的数十年，开源数据库技术迅速发展壮大。开源数据库从新生，到受到质疑，再到受到认可，现在已经成为很多企业生产环境的数据库。企业不再需要担心选择开源数据库技术会产生风险，因为开源数据库通常都有活跃的社区，可以为越来越多的初创公司、中型企业甚至 500 强公司提供开源数据库领域的支持和第三方工具。

Battery Ventures 是一家专注于技术的投资公司，最近推出了一个用于跟踪最受欢迎开源项目的 [BOSS 指数](https://techcrunch.com/2017/04/07/tracking-the-explosive-growth-of-open-source-software/) 。它提供了对一些被广泛采用的开源项目和活跃的开源项目的详细情况。其中，数据库技术毫无悬念地占据了榜单的主导地位，在前十位之中占了一半。这个 BOSS 指数对于刚接触开源数据库领域的人来说，这是一个很好的切入点。当然，开源技术的提供者也会针对很多常见的典型问题给出对应的解决方案。

我认为，你想要做的事情很可能已经有人解决过了。即使这些先行者的解决方案不一定完全契合你的需求，但也可以从他们成功或失败的案例中根据你自己的需求修改得出合适的解决方案。

如果你采用了一个最前沿的技术，这就是你探索的好机会了。如果你的工作负载刚好适合新的开源数据库技术，放胆去尝试吧。第一个吃螃蟹的人总是会得到意外的挑战和收获。

### 先从简单开始

你的数据库实际上需要达到多少个 [9](https://en.wikipedia.org/wiki/Five_nines) 的可用性？对许多公司来说，“实现高可用性”仅仅只是一个模糊的目标。当然，最常见的答案都会是“它是关键应用，我们无论多短的停机时间都是无法忍受的”。

数据库环境越复杂，管理的难度就越大，成本也会越高。理论上你总可以将数据库的可用性提得更高，但代价将会是大大增加的管理难度和性能下降。所以，先从简单开始，直到有需要时再逐步扩展。

例如，Booking.com 是一个有名的旅游预订网站。但少有人知的是，它使用 MySQL 作为数据库后端。 Booking.com 高级系统架构师 Nicolai Plum 曾经发表过一次[演讲](https://www.percona.com/live/mysql-conference-2015/sessions/bookingcom-evolution-mysql-system-design)，讲述了他们公司使用 MySQL 数据库的历程。其中一个重点就是，在初始阶段数据库可以被配置得很简单，然后逐渐变得复杂。对于早期的数据库需求，一个简单的主从架构就足够了，但随着工作负载和数据量的增加，数据库引入了负载均衡、多个读取副本，还使用 Hadoop 进行分析。尽管如此，早期的架构仍然是非常简单的。

![](/data/attachment/album/201811/01/232125g9b00605ik0kwapy.png)

### 有疑问，找专家

如果你仍然不确定数据库选择的是否合适，可以在论坛、网站或者与软件的提供者处商讨。研究各种开源数据库是否满足自己的需求是一件很有意义的事，因为总会发现你从不知道的技术。而开源社区就是分享这些信息的地方。

当你接触到开源软件和软件提供者时，有一件重要的事情需要注意。很多公司都有开放的核心业务模式，鼓励采用他们的数据库软件。你可以只接受他们的部分建议和指导，然后用你自己的能力去研究和探索替代方案。

### 总结

选择正确的开源数据库是一个重要的过程。很多时候，人们都会在真正理解需求之前就做出决定，这是本末倒置的。

---

via: <https://opensource.com/article/18/10/tips-choosing-right-open-source-database>

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


四个开源的 Android 邮件客户端

Summary: Email 现在还没有绝迹，而且现在大部分邮件都来自于移动设备。

Email 现在还没有绝迹，而且现在大部分邮件都来自于移动设备。

>
> Email 现在还没有绝迹，而且现在大部分邮件都来自于移动设备。
>
>
>

![](/data/attachment/album/201811/02/061323uxqsppsfzzz8yk8m.jpg)

现在一些年轻人正将邮件称之为“老年人的交流方式”，然而事实却是邮件绝对还没有消亡。虽然[协作工具](https://opensource.com/alternatives/trello)、社交媒体，和短信很常用，但是它们还没做好取代邮件这种必要的商业（和社交）通信工具的准备。

考虑到邮件还没有消失，并且（很多研究表明）人们都是在移动设备上阅读邮件，拥有一个好的移动邮件客户端就变得很关键。如果你是一个想使用开源的邮件客户端的 Android 用户，事情就变得有点棘手了。

我们提供了四个开源的 Andorid 邮件客户端供选择。其中两个可以通过 Andorid 官方应用商店 [Google Play](https://play.google.com/store) 下载。你也可以在 [Fossdroid](https://fossdroid.com/) 或者 [F-Droid](https://f-droid.org/) 这些开源 Android 应用库中找到他们。（下方有每个应用的具体下载方式。）

### K-9 Mail

[K-9 Mail](https://k9mail.github.io/) 拥有几乎和 Android 一样长的历史——它起源于 Android 1.0 邮件客户端的一个补丁。它支持 IMAP 和 WebDAV、多用户、附件、emoji 和其它经典的邮件客户端功能。它的[用户文档](https://k9mail.github.io/documentation.html)提供了关于安装、启动、安全、阅读和发送邮件等等的帮助。

K-9 基于 [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) 协议开源，[源码](https://github.com/k9mail/k-9)可以从 GitHub 上获得. 应用可以从 [Google Play](https://play.google.com/store/apps/details?id=com.fsck.k9)、[Amazon](https://www.amazon.com/K-9-Dog-Walkers-Mail/dp/B004JK61K0/) 和 [F-Droid](https://f-droid.org/packages/com.fsck.k9/) 上下载。

### p≡p

正如它的全称，”Pretty Easy Privacy”说的那样，[p≡p](https://www.pep.security/android.html.en) 主要关注于隐私和安全通信。它提供自动的、端到端的邮件和附件加密（但要求你的收件人也要能够加密邮件——否则，p≡p 会警告你的邮件将不加密发出）。

你可以从 GitLab 获得[源码](https://pep-security.lu/gitlab/android/pep)（基于 [GPLv3](https://pep-security.lu/gitlab/android/pep/blob/feature/material/LICENSE) 协议），并且可以从应用的官网上找到相应的[文档](https://www.pep.security/docs/)。应用可以在 [Fossdroid](https://fossdroid.com/a/p%E2%89%A1p.html) 上免费下载或者在 [Google Play](https://play.google.com/store/apps/details?id=security.pEp) 上支付一点儿象征性的费用下载。

### InboxPager

[InboxPager](https://github.com/itprojects/InboxPager) 允许你通过 SSL/TLS 协议收发邮件信息，这也表明如果你的邮件提供商（比如 Gmail ）没有默认开启这个功能的话，你可能要做一些设置。（幸运的是， InboxPager 提供了 Gmail 的[设置教程](https://github.com/itprojects/InboxPager/blob/HEAD/README.md#gmail-configuration)。）它同时也支持通过 OpenKeychain 应用进行 OpenPGP 加密。

InboxPager 基于 [GPLv3](https://github.com/itprojects/InboxPager/blob/c5641a6d644d001bd4cec520b5a96d7e588cb6ad/LICENSE) 协议，其源码可从 GitHub 获得，并且应用可以从 [F-Droid](https://f-droid.org/en/packages/net.inbox.pager/) 下载。

### FairEmail

[FairEmail](https://email.faircode.eu/) 是一个极简的邮件客户端，它的功能集中于读写信息，没有任何多余的可能拖慢客户端的功能。它支持多个帐号和用户、消息线索、加密等等。

它基于 [GPLv3](https://github.com/M66B/open-source-email/blob/master/LICENSE) 协议开源，[源码](https://github.com/M66B/open-source-email)可以从 GitHub 上获得。你可以在 [Fossdroid](https://fossdroid.com/a/fairemail.html) 上下载 FairEamil；对 Google Play 版本感兴趣的人可以从 [testing the software](https://play.google.com/apps/testing/eu.faircode.email) 获得应用。

肯定还有更多的开源 Android 客户端（或者上述软件的加强版本）——活跃的开发者们可以关注一下。如果你知道还有哪些优秀的应用，可以在评论里和我们分享。

---

via: <https://opensource.com/article/18/10/open-source-android-email-clients>

作者：[Opensource.com](https://opensource.com) 选题：[lujun9972](https://github.com/lujun9972) 译者：[zianglei](https://github.com/zianglei) 校对：[wxy](https://github.com/wxy)

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