7 个有助于 AI 技术的最佳开源工具

![](/data/attachment/album/201911/18/100020qccpk635kd5co5xh.jpg "Artificial Intelligence_EB June 17")

>
> 人工智能是一种紧跟未来道路的卓越技术。在这个不断发展的时代，它吸引了所有跨国组织的关注。谷歌、IBM、Facebook、亚马逊、微软等业内知名公司不断投资于这种新时代技术。
>
>
>

预测业务需求需要利用人工智能，并在另一个层面上进行研发。这项先进技术正成为提供超智能解决方案的研发组织不可或缺的一部分。它可以帮助你保持准确性并以更好的结果提高生产率。

AI 开源工具和技术以频繁且准确的结果吸引了每个行业的关注。这些工具可帮助你分析绩效，同时为你带来更大的收益。

无需赘言，这里我们列出了一些最佳的开源工具，来帮助你更好地了解人工智能。

### 1、TensorFlow

TensorFlow 是用于人工智能的开源机器学习框架。它主要是为了进行机器学习和深度学习的研究和生产而开发的。TensorFlow 允许开发者创建数据流的图结构，它会在网络或系统节点中移动，图提供了数据的多维数组或张量。

TensorFlow 是一个出色的工具，它有无数的优势。

* 简化数值计算
* TensorFlow 在多种模型上提供了灵活性。
* TensorFlow 提高了业务效率
* 高度可移植
* 自动区分能力

### 2、Apache SystemML

Apache SystemML 是由 IBM 创建的非常流行的开源机器学习平台，它提供了使用大数据的良好平台。它可以在 Apache Spark 上高效运行，并自动扩展数据，同时确定代码是否可以在磁盘或 Apache Spark 集群上运行。不仅如此，它丰富的功能使其在行业产品中脱颖而出；

* 算法自定义
* 多种执行模式
* 自动优化

它还支持深度学习，让开发者更有效率地实现机器学习代码并优化。

### 3、OpenNN

OpenNN 是用于渐进式分析的开源人工智能神经网络库。它可帮助你使用 C++ 和 Python 开发健壮的模型，它还包含用于处理机器学习解决方案（如预测和分类）的算法和程序。它还涵盖了回归和关联，可提供业界的高性能和技术演化。

它有丰富的功能，如：

* 数字化协助
* 预测分析
* 快速的性能
* 虚拟个人协助
* 语音识别
* 高级分析

它可帮助你设计实现数据挖掘的先进方案，而从取得丰硕结果。

### 4、Caffe

Caffe（快速特征嵌入的卷积结构）是一个开源深度学习框架。它优先考虑速度、模块化和表达式。Caffe 最初由加州大学伯克利分校视觉和学习中心开发，它使用 C++ 编写，带有 Python 接口。能在 Linux、macOS 和 Windows 上顺利运行。

Caffe 中的一些有助于 AI 技术的关键特性。

1. 具有表现力的结构
2. 具有扩展性的代码
3. 大型社区
4. 开发活跃
5. 性能快速

它可以帮助你激发创新，同时引入刺激性增长。充分利用此工具来获得所需的结果。

### 5、Torch

Torch 是一个开源机器学习库，通过提供多种方便的功能，帮助你简化序列化、面向对象编程等复杂任务。它在机器学习项目中提供了最大的灵活性和速度。Torch 使用脚本语言 Lua 编写，底层使用 C 实现。它用于多个组织和研究实验室中。

Torch 有无数的优势，如：

* 快速高效的 GPU 支持
* 线性代数子程序
* 支持 iOS 和 Android 平台
* 数值优化子程序
* N 维数组

### 6、Accord .NET

Accord .NET 是著名的自由开源 AI 开发工具之一。它有一组库，可以用来组合使用 C# 编写的音频和图像处理库。从计算机视觉到计算机听觉、信号处理和统计应用，它可以帮助你构建用于商业用途一切需求。它附带了一套全面的示例应用来快速运行各类库。

你可以使用 Accord .NET 引人注意的功能开发一个高级应用，例如：

* 统计分析
* 数据接入
* 自适应
* 深度学习
* 二阶神经网络学习算法
* 数字协助和多语言
* 语音识别

### 7、Scikit-Learn

Scikit-Learn 是流行的辅助 AI 技术的开源工具之一。它是 Python 中用于机器学习的一个很有价值的库。它包括机器学习和统计建模（包括分类、聚类、回归和降维）等高效工具。

让我们了解下 Scikit-Learn 的更多功能：

* 交叉验证
* 聚类和分类
* 流形学习
* 机器学习
* 虚拟流程自动化
* 工作流自动化

从预处理到模型选择，Scikit-learn 可帮助你处理所有问题。它简化了从数据挖掘到数据分析的所有任务。

### 总结

这些是一些流行的开源 AI 工具，它们提供了全面的功能。在开发新时代应用之前，人们必须选择其中一个工具并做相应的工作。这些工具提供先进的人工智能解决方案，并紧跟最新趋势。

人工智能在全球范围内应用，无处不在。借助 Amazon Alexa、Siri 等应用，AI 为客户提供了很好的用户体验。它在吸引用户关注的行业中具有显著优势。在医疗保健、银行、金融、电子商务等所有行业中，人工智能在促进增长和生产力的同时节省了大量的时间和精力。

选择这些开源工具中的任何一个，获得更好的用户体验和令人难以置信的结果。它将帮助你成长，并在质量和安全性方面获得更好的结果。

---

via: <https://opensourceforu.com/2019/11/7-best-open-source-tools-that-will-help-in-ai-technology/>

作者：[Nitin Garg](https://opensourceforu.com/author/nitin-garg/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


如何修复使用 Python ORM 工具 SQLAlchemy 时的常见陷阱

>
> 在使用 SQLAlchemy 时，那些看似很小的选择可能对这种对象关系映射工具包的性能产生重要影响。
>
>
>

![A python with a package.](/data/attachment/album/201911/19/093238t4z4zmrj736mzx2x.jpg "A python with a package.")

<ruby> 对象关系映射 <rt>  Object-relational mapping </rt></ruby>（[ORM](https://en.wikipedia.org/wiki/Object-relational_mapping)）使应用程序开发人员的工作更轻松，在很大程度是因为它允许你使用你可能知道的语言（例如 Python）与数据库交互，而不是使用原始 SQL 语句查询。[SQLAlchemy](https://www.sqlalchemy.org/) 是一个 Python ORM 工具包，它提供使用 Python 访问 SQL 数据库的功能。它是一个成熟的 ORM 工具，增加了模型关系、强大的查询构造范式、简单的序列化等优点。然而，它的易用性使得人们很容易忘记其背后发生了什么。使用 SQLAlchemy 时做出的看似很小的选择可能产生非常大的性能影响。

本文解释了开发人员在使用 SQLAlchemy 时遇到的一些最重要的性能问题，以及如何解决这些问题。

### 只需要计数但检索整个结果集

有时开发人员只需要一个结果计数，但是没有使用数据库计数功能，而是获取了所有结果，然后使用 Python 中的 `len` 完成计数。

```
count = len(User.query.filter_by(acct_active=True).all())
```

相反，使用 SQLAlchemy 的 `count` 方法将在服务器端执行计数，从而减少发送到客户端的数据。在前面的例子中调用 `all()` 也会导致模型对象的实例化，如果有很多数据，那么时间代价可能会非常昂贵。

除非还需要做其他的事情，否则只需使用 `count` 方法：

```
count = User.query.filter_by(acct_active=True).count()
```

### 只需要几列时检索整个模型

在许多情况下，发出查询时只需要几列数据。SQLAlchemy 可以只获取你想要的列，而不是返回整个模型实例。这不仅减少了发送的数据量，还避免了实例化整个对象。使用列数据的元组而不是模型可以快得多。

```
result = User.query.all()
for user in result:
    print(user.name, user.email)
```

反之，使用 `with_entities` 方法只选择所需要的内容：

```
result = User.query.with_entities(User.name, User.email).all()
for (username, email) in result:
    print(username, email)
```

### 每次循环都更新一个对象

避免使用循环来单独更新集合。虽然数据库可以非常快地执行单个更新，但应用程序和数据库服务器之间的往返时间将快速累加。通常，在合理的情况下争取更少的查询。

```
for user in users_to_update:
  user.acct_active = True
  db.session.add(user)
```

改用批量更新方法：

```
query = User.query.filter(user.id.in_([user.id for user in users_to_update]))
query.update({"acct_active": True}, synchronize_session=False)
```

### 触发级联删除

ORM 允许在模型关系上进行简单的配置，但是有一些微妙的行为可能会令人吃惊。大多数数据库通过外键和各种级联选项维护关系完整性。SQLAlchemy 允许你使用外键和级联选项定义模型，但是 ORM 具有自己的级联逻辑，可以取代数据库。

考虑以下模型：

```
class Artist(Base):
    __tablename__ = "artist"

    id = Column(Integer, primary_key=True)
    songs = relationship("Song", cascade="all, delete")

class Song(Base):
    __tablename__ = "song"

    id = Column(Integer, primary_key=True)
    artist_id = Column(Integer, ForeignKey("artist.id", ondelete="CASCADE"))
```

删除歌手将导致 ORM 在 `song` 表上发出 `delete` 查询，从而防止由于外键导致的删除操作。这种行为可能会成为复杂关系和大量记录的瓶颈。

请包含 `passive_deletes` 选项，以确保让数据库来管理关系。但是，请确保你的数据库具有此功能。例如，SQLite 默认情况下不管理外键。

```
songs = relationship("Song", cascade  all, delete", passive_deletes=True)
```

### 当要使用贪婪加载时，应使用延迟加载

延迟加载是 SQLAlchemy 处理关系的默认方法。从上一个例子构建来看，加载一个歌手时不会同时加载他或她的歌曲。这通常是一个好主意，但是如果总是需要加载某些关系，单独的查询可能会造成浪费。

如果允许以延迟方式加载关系，像 [Marshmallow](https://marshmallow.readthedocs.io/en/stable/) 这样流行的序列化框架可以触发级联查询。

有几种方法可以控制此行为。最简单的方法是通过 relationship 函数本身。

```
songs = relationship("Song", lazy="joined", cascade="all, delete")
```

这将导致一个左连接被添加到任何歌手的查询中，因此，`songs` 集合将立即可用。尽管有更多数据返回给客户端，但往返次数可能会少得多。

SQLAlchemy 为无法采用这种综合方法的情况提供了更细粒度的控制，可以使用 `joinedload()` 函数在每个查询的基础上切换连接的加载。

```
from sqlalchemy.orm import joinedload

artists = Artist.query.options(joinedload(Artist.songs))
print(artists.songs) # Does not incur a roundtrip to load
```

### 使用 ORM 进行批量记录导入

导入成千上万条记录时，构建完整模型实例的开销会成为主要瓶颈。想象一下，从一个文件中加载数千首歌曲记录，其中每首歌曲都先被转换为字典。

```
for song in songs:
    db.session.add(Song(`song))
```

相反，绕过 ORM，只使用核心的 SQLAlchemy 参数绑定功能。

```
batch = []
insert_stmt = Song.__table__.insert()
for song in songs:
    if len(batch) > 1000:
       db.session.execute(insert_stmt, batch)
       batch.clear()
    batch.append(song)
if batch:
    db.session.execute(insert_stmt, batch)
```

请记住，此方法会自然而然地跳过你可能依赖的任何客户端 ORM 逻辑，例如基于 Python 的列默认值。尽管此方法比将对象加载为完整的模型实例要快，但是你的数据库可能具有更快的批量加载方法。例如，PostgreSQL 的 `COPY` 命令为加载大量记录提供了最佳性能。

### 过早调用提交或刷新

在很多情况下，你需要将子记录与其父记录相关联，反之亦然。一种显然的方法是刷新会话，以便为有问题的记录分配一个 ID。

```
artist = Artist(name="Bob Dylan")
song = Song(title="Mr. Tambourine Man")

db.session.add(artist)
db.session.flush()

song.artist_id = artist.id
```

对于每个请求，多次提交或刷新通常是不必要的，也是不可取的。数据库刷新涉及强制在数据库服务器上进行磁盘写入，在大多数情况下，客户端将阻塞，直到服务器确认已写入数据为止。

SQLAlchemy 可以在幕后跟踪关系和管理相关键。

```
artist = Artist(name="Bob Dylan")
song = Song(title="Mr. Tambourine Man")

artist.songs.append(song)
```

### 总结

我希望这一系列常见的陷阱可以帮助你避免这些问题，并使你的应用平稳运行。通常，在诊断性能问题时，测量是关键。大多数数据库都提供性能诊断功能，可以帮助你定位问题，例如 PostgreSQL 的 `pg_stat_statements` 模块。

---

via: <https://opensource.com/article/19/9/common-pitfalls-python>

作者：[Zach Todd](https://opensource.com/users/zchtoddhttps://opensource.com/users/lauren-pritchetthttps://opensource.com/users/liranhaimovitchhttps://opensource.com/users/moshez) 选题：[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/) 荣誉推出


丢失的开放剪贴画库和新的公共艺术品图书馆 FreeSVG.org 的诞生

>
> 开放剪贴画库兴衰的故事以及一个新的公共艺术品图书馆 FreeSVG.org 的诞生。
>
>
>

![](/data/attachment/album/201911/19/102040imbybpl32vgdibbm.jpg)

<ruby> 开放剪贴画库 <rt>  Open Clip Art Library </rt></ruby>（OCAL）发布于 2004 年，成为了免费插图的来源，任何人都可以出于任何目的使用它们，而无需注明出处或提供任何回报。针对 1990 年代每个家庭办公室书架上的大量剪贴画 CD 以及由闭源公司和艺术品软件提供的艺术品转储，这个网站是开源世界的回应。

最初，这个剪贴画库主要由一些贡献者提供，但是在 2010 年，它重新打造成了一个全新的交互式网站，可以让任何人使用矢量插图应用程序创建和贡献剪贴画。该网站立即获得了来自全球的、各种形式的自由软件和自由文化项目的贡献。[Inkscape](https://opensource.com/article/18/1/inkscape-absolute-beginners) 中甚至包含了该库的专用导入器。

但是，在 2019 年初，托管开放剪贴画库的网站离线了，没有任何警告或解释。它已经成长为有着成千上万的人的社区，起初以为这是暂时的故障。但是，这个网站一直离线已超过六个月，而没有任何清楚的解释。

谣言开始膨胀。该网站一直在更新中（“要偿还数年的技术债务”，网站开发者 Jon Philips 在一封电子邮件中说）。一个 Twitter 帐户声称，该网站遭受了猖狂的 DDoS 攻击。另一个 Twitter 帐户声称，该网站维护者已经成为身份盗用的牺牲品。今天，在撰写本文时，该网站的一个且唯一的页面声明它处于“维护和保护模式”，其含义不清楚，只是用户无法访问其内容。

### 恢复公地

网站会随着时间的流逝而消失，但是对其社区而言，开放剪贴画库的丢失尤其令人惊讶，因为它被视为一个社区项目。很少有社区成员知道托管该库的网站已经落入单个维护者手中，因此，由于 [CC0 许可证](https://creativecommons.org/share-your-work/public-domain/cc0/)，该库中的艺术品归所有人所有，但对它的访问是由单个维护者功能性拥有的。而且，由于该社区通过网站彼此保持联系，因此该维护者实际上拥有该社区。

当网站发生故障时，社区以及成员彼此之间都无法访问剪贴画。没有该网站，就没有社区。

最初，该网站离线后其上的所有东西都是被封锁的。不过，在几个月之后，用户开始意识到该网站的数据仍然在线，这意味着用户能够通过输入精确的 URL 访问单个剪贴画。换句话说，你不能通过在网站上到处点击来浏览剪贴画文件，但是如果你确切地知道该地址，你就可以在浏览器中访问它。类似的，技术型（或偷懒的）用户意识到能够通过类似 `wget` 的自动 Web 浏览器将网站“抓取”下来。

Linux 的 `wget` 命令从技术上来说是一个 Web 浏览器，虽然它不能让你像用 Firefox 一样交互式地浏览。相反，`wget` 可以连到互联网，获取文件或文件集，并下载到你的本次硬盘。然后，你可以在 Firefox、文本编辑器或最合适的应用程序中打开这些文件，查看内容。

通常，`wget` 需要知道要提取的特定文件。如果你使用的是安装了 `wget` 的 Linux 或 macOS，则可以通过下载 [example.com](http://example.com) 的索引页来尝试此过程：

```
$ wget example.org/index.html
[...]
$ tail index.html

<body><div>
    <h1>Example Domain</h1>
    <p>This domain is for illustrative examples in documents.
    You may use this domain in examples without permission.</p>
        <p><a href="http://www.iana.org/domains/example">More info</a></p>
</div></body></html>
```

为了抓取 OCAL，我使用了 `--mirror` 选项，以便可以只是将 `wget` 指向到包含剪贴画的目录，就可以下载该目录中的所有内容。此操作持续下载了连续四天（96 个小时），最终得到了超过 50,000 个社区成员贡献的 100,000 个 SVG 文件。不幸的是，任何没有适当元数据的文件的作者信息都是无法恢复的，因为此信息被锁定在该数据库中不可访问的文件中，但是 CC0 许可证意味着此问题*在技术上*无关紧要（因为 CC0 文件不需要归属）。

随意分析了一下下载的文件进行还显示，其中近 45,000 个文件是同一个文件（该网站的徽标）的副本。这是由于指向该网站徽标的重定向引起的（原因未知），仔细分析能够提取到原始的文件，又过了 96 个小时，并且恢复了直到最后一天发布在 OCAL 上的所有剪贴画：总共约有 156,000 张图像。

SVG 文件通常很小，但这仍然是大量工作，并且会带来一些非常实际的问题。首先，将需要数 GB 的在线存储空间，这样这些剪贴画才能供其先前的社区使用。其次，必须使用一种搜索剪贴画的方法，因为手动浏览 55,000 个文件是不现实的。

很明显，社区真正需要的是一个平台。

### 构建新的平台

一段时间以来，[公共领域矢量图](http://publicdomainvectors.org) 网站一直在发布公共领域的矢量图。虽然它仍然是一个受欢迎的网站，但是开源用户通常只是将其用作辅助的图片资源，因为其中大多数文件都是 EPS 和 AI 格式的，这两者均与 Adobe 相关。这两种文件格式通常都可以转换为 SVG，但是特性会有所损失。

当公共领域矢量图网站的维护者（Vedran 和 Boris）得知 OCAL 丢失时，他们决定创建一个面向开源社区的网站。诚然，他们选择了开源 [Laravel](https://github.com/viralsolani/laravel-adminpanel) 框架作为后端，该框架为网站提供了管理控制台和用户访问权限。该框架功能强大且开发完善，还使他们能够快速响应错误报告和功能请求，并根据需要升级网站。他们正在建立的网站称为 [FreeSVG.org](https://freesvg.org)，已经是一个强大而繁荣的公共艺术品图书馆。

从那时起，他们就一直从 OCAL 上载所有剪贴画，并且他们甚至在努力地对这些剪贴画进行标记和分类。作为公共领域矢量图网站的创建者，他们还以 SVG 格式贡献了自己的图像。他们的目标是成为互联网上具有 CC0 许可证的 SVG 图像的主要资源。

### 贡献

[FreeSVG.org](https://freesvg.org) 的维护者意识到他们已经继承了重要的管理权。他们正在努力对网站上的所有图像加上标题和描述，以便用户可以轻松找到这些剪贴画，并在准备就绪后将其提供给社区，同时坚信同这些剪贴画一样，与这些剪贴画有关的元数据属于创建和使用它们的人。他们还意识到可能会发生无法预料的情况，因此他们会定期为其网站和内容创建备份，并打算在其网站出现故障时向公众提供最新备份。

如果要为 [FreeSVG.org](http://freesvg.org)的知识共享内容添砖加瓦，请下载 [Inkscape](http://inkscape.org) 并开始绘制。世界上有很多公共领域的艺术品，例如[历史广告](https://freesvg.org/drinking-coffee-vector-drawing)、[塔罗牌](https://freesvg.org/king-of-swords-tarot-card)和[故事书](https://freesvg.org/space-pioneers-135-scene-vector-image)，只是在等待转换为 SVG，因此即使你对自己的绘画技巧没有信心你也可以做出贡献。访问 [FreeSVG 论坛](http://forum.freesvg.org/)与其他贡献者联系并支持他们。

*公地*的概念很重要。无论你是学生、老师、图书馆员、小企业主还是首席执行官，[知识共享都会使所有人受益](https://opensource.com/article/18/1/creative-commons-real-world)。如果你不直接捐款，那么你随时可以帮助推广。

这是自由文化的力量：它不仅可以扩展，而且随着更多人的参与，它会变得更好。

### 艰辛的教训

从 OCAL 的消亡到 FreeSVG.org 的兴起，开放文化社区已经吸取了一些艰辛的经验。对于以后，以下是我认为最重要的那些。

#### 维护你的元数据

如果你是内容创建者，请帮助将来的档案管理员，将元数据添加到文件中。大多数图像、音乐、字体和视频文件格式都可以嵌入 EXIF 数据，其他格式在创建它们的应用程序中具有元数据输入界面。勤于用你的姓名、网站或公共电子邮件以及许可证来标记你的作品。

#### 做个副本

不要以为别人在做备份。如果你关心公用数字内容，请自己备份，否则不要指望永远提供它。无论*任何上传到互联网上的内容是永久的*的说法是不是正确的，但这并不意味着你永远可以使用。如果 OCAL 文件不再暗地可用，那么任何人都不太可能成功地从网络上的某个位置或从全球范围内的人们的硬盘中成功地发现全部的 55,000 张图像。

#### 创建外部渠道

如果一个社区是由单个网站或实际位置来定义的，那么该社区失去访问该空间的能力就如同解散了一样。如果你是由单个组织或网站驱动的社区的成员，则你应该自己与关心的人共享联系信息，并即使在该网站不可用时也可以建立沟通渠道。

例如，[Opensource.com](http://Opensource.com) 本身维护其作者和通讯者的邮件列表和其他异地渠道，以便在有或没有网站干预或甚至没有网站的情况下相互交流。

#### 自由文化值得为此努力

互联网有时被视为懒人社交俱乐部。你可以在需要时登录并在感到疲倦时将其关闭，也可以漫步到所需的任何社交圈。

但实际上，自由文化可能是项艰难的工作。但是这种艰难从某种意义上讲并不是说要成为其中的一分子很困难，而是你必须努力维护。如果你忽略你所在的社区，那么该社区可能会在你意识到之前就枯萎并褪色。

花点时间环顾四周，确定你属于哪个社区，如果不是，那么请告诉某人你对他们带给你生活的意义表示赞赏。同样重要的是，请记住，这样你也为社区的生活做出了贡献。

几周前，知识共享组织在华沙举行了它的全球峰会，令人惊叹的国际盛会…

>
> 老王文末感想：刚刚看到这篇文章的英文标题（How I used the wget Linux command to recover lost images）时，我以为这是一篇技术文章，然而翻译校对下来之后，却是一篇披着技术外衣的对自由文化社区的教训和反思。作为一个同样建设公地社区的负责人，我不禁深深地对 “Linux 中国”社区的将来有了忧虑。如何避免 “Linux 中国”也同样陷入这种困境，这是一个问题……
>
>
> 就目前来看，我们需要定期不定期的构建离线归档，也需要以更开放的方式留下现有文章的数据，也欢迎大家来支持和帮助我们。
>
>
> 又及，再给产生这一切的 Linux 中国翻译组织 LCTT 打个招新广告吧，欢迎大家加入： <https://linux.cn/lctt/>
>
>
>

---

via: <https://opensource.com/article/19/10/how-community-saved-artwork-creative-commons>

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

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


PostgreSQL 入门

Summary: 安装、设置、创建和开始使用 PostgreSQL 数据库。

PostgreSQL 14.1 版本文档     https://www.rockdata.net/zh-cn/docs/14/

PostgreSQL 13.1 版本文档     https://www.rockdata.net/zh-cn/docs/13/

PostgreSQL 12.2 版本文档     https://www.rockdata.net/zh-cn/docs/12/

PostgreSQL 11.2 版本文档     https://www.rockdata.net/zh-cn/docs/11/

PostgreSQL 10.1 版本文档     https://www.rockdata.net/zh-cn/docs/10/

安装、设置、创建和开始使用 PostgreSQL 数据库。

>
> 安装、设置、创建和开始使用 PostgreSQL 数据库。
>
>
>

![](/data/attachment/album/201911/20/085936u10q7eme1euu4ak3.jpg)

每个人或许都有需要在数据库中保存的东西。即使你执着于使用纸质文件或电子文件，它们也会变得很麻烦。纸质文档可能会丢失或混乱，你需要访问的电子信息可能会隐藏在段落和页面的深处。

在我从事医学工作的时候，我使用 [PostgreSQL](https://www.postgresql.org/) 来跟踪我的住院患者名单并提交有关住院患者的信息。我将我的每日患者名单打印在口袋里，以便快速了解患者房间、诊断或其他细节的任何变化并做出快速记录。

我以为一切没问题，直到去年我妻子决定买一辆新车，我“接手”了她以前的那辆车。她保留了汽车维修和保养服务收据的文件夹，但随着时间的流逝，它变得杂乱。与其花时间筛选所有纸条以弄清楚什么时候做了什么，我认为 PostgreSQL 将是更好的跟踪此信息的方法。

### 安装 PostgreSQL

自上次使用 PostgreSQL 以来已经有一段时间了，我已经忘记了如何使用它。实际上，我甚至没有在计算机上安装它。安装它是第一步。我使用 Fedora，因此在控制台中运行：

```
dnf list postgresql*
```

请注意，你无需使用 `sudo` 即可使用 `list` 选项。该命令返回了很长的软件包列表。看了眼后，我决定只需要三个：postgresql、postgresql-server 和 postgresql-docs。

为了了解下一步需要做什么，我决定查看 [PostgreSQL 文档](http://www.postgresql.org/docs)。文档参考内容非常丰富，实际上，丰富到令人生畏。幸运的是，我发现我在升级 Fedora 时曾经做过的一些笔记，希望有效地导出数据库，在新版本上重新启动 PostgreSQL，以及导入旧数据库。

### 设置 PostgreSQL

与大多数其他软件不同，你不能只是安装好 PostgreSQL 就开始使用它。你必须预先执行两个基本步骤：首先，你需要设置 PostgreSQL，第二，你需要启动它。你必须以 `root` 用户身份执行这些操作（`sudo` 在这里不起作用）。

要设置它，请输入：

```
postgresql-setup –initdb
```

这将确定 PostgreSQL 数据库在计算机上的位置。然后（仍为 `root`）输入以下两个命令：

```
systemctl start postgresql.service
systemctl enable postgresql.service
```

第一个命令为当前会话启动 PostgreSQL（如果你关闭机器，那么 PostgreSQL 也将关闭）。第二个命令使 PostgreSQL 在随后的重启中自动启动。

### 创建用户

PostgreSQL 正在运行，但是你仍然不能使用它，因为你还没有用户。为此，你需要切换到特殊用户 `postgres`。当你仍以 `root` 身份运行时，输入：

```
su postgres
```

由于你是以 `root` 的身份执行此操作的，因此无需输入密码。root 用户可以在不知道密码的情况下以任何用户身份操作；这就是使其强大而危险的原因之一。

现在你就是 `postgres` 了，请运行下面两个命令，如下所示创建用户（创建用户 `gregp`）：

```
createuser gregp
createdb gregp
```

你可能会看到错误消息，如：`Could not switch to /home/gregp`。这只是意味着用户 `postgres`不能访问该目录。尽管如此，你的用户和数据库已创建。接下来，输入 `exit` 并按回车两次，这样就回到了原来的用户下（`root`）。

### 设置数据库

要开始使用 PostgreSQL，请在命令行输入 `psql`。你应该在每行左侧看到类似 `gregp=>` 的内容，以显示你使用的是 PostgreSQL，并且只能使用它理解的命令。你自动获得一个数据库（我的名为 `gregp`），它里面完全没有内容。对 PostgreSQL 来说，数据库只是一个工作空间。在空间内，你可以创建*表*。表包含变量列表，而表中的每个变量是构成数据库的数据。

以下是我设置汽车服务数据库的方式：

```
CREATE TABLE autorepairs (
        date            date,
        repairs         varchar(80),
        location        varchar(80),
        cost            numeric(6,2)
);
```

我本可以在一行内输入，但为了更好地说明结构，并表明 PostgreSQL 不会解释制表符和换行的空白，我分成了多行。字段包含在括号中，每个变量名和数据类型与下一个变量用逗号分隔（最后一个除外），命令以分号结尾。所有命令都必须以分号结尾！

第一个变量名是 `date`，它的数据类型也是 `date`，这在 PostgreSQL 中没关系。第二个和第三个变量 `repairs` 和 `location` 都是 `varchar(80)` 类型，这意味着它们可以是最多 80 个任意字符（字母、数字等）。最后一个变量 `cost` 使用 `numeric` 类型。括号中的数字表示最多有六位数字，其中两位是小数。最初，我尝试了 `real` 类型，这将是一个浮点数。`real` 类型的问题是作为数据类型在使用时，在遇到 `WHERE` 子句，类似 `WHERE cost = 0` 或其他任何特定数字。由于 `real` 值有些不精确，因此特定数字将永远不会匹配。

### 输入数据

接下来，你可以使用 `INSERT INTO` 命令添加一些数据（在 PostgreSQL 中称为*行*）：

```
INSERT INTO autorepairs VALUES ('2017-08-11', 'airbag recall', 'dealer', 0);
```

请注意，括号构成了一个值的容器，它必须以正确的顺序，用逗号分隔，并在命令末尾加上分号。`date` 和 `varchar(80)` 类型的值必须包含在单引号中，但数字值（如 `numeric`）不用。作为反馈，你应该会看到：

```
INSERT 0 1
```

与常规终端会话一样，你会有输入命令的历史记录，因此，在输入后续行时，通常可以按向上箭头键来显示最后一个命令并根据需要编辑数据，从而节省大量时间。

如果出了什么问题怎么办？使用 `UPDATE` 更改值：

```
UPDATE autorepairs SET date = '2017-11-08' WHERE repairs = 'airbag recall';
```

或者，也许你不再需要表中的行。使用 `DELETE`：

```
DELETE FROM autorepairs WHERE repairs = 'airbag recall';
```

这将删除整行。

最后一件事：即使我在 PostgreSQL 命令中一直使用大写字母（在大多数文档中也这么做），你也可以用小写字母输入，我也经常如此。

### 输出数据

如果你想展示数据，使用 `SELECT`：

```
SELECT * FROM autorepairs ORDER BY date;
```

没有 `ORDER BY` 的话，行将不管你输入的内容来显示。例如，以下就是我终端中输出的我的汽车服务数据：

```
SELECT date, repairs FROM autorepairs ORDER BY date;

    date   |                             repairs
-----------+-----------------------------------------------------------------
2008-08-08 | oil change, air filter, spark plugs
2011-09-30 | 35000 service, oil change, rotate tires/balance wheels
2012-03-07 | repl battery
2012-11-14 | 45000 maint, oil/filter
2014-04-09 | 55000 maint, oil/filter, spark plugs, air/dust filters
2014-04-21 | replace 4 tires
2014-04-21 | wheel alignment
2016-06-01 | 65000 mile service, oil change
2017-05-16 | oil change, replce oil filt housing
2017-05-26 | rotate tires
2017-06-05 | air filter, cabin filter,spark plugs
2017-06-05 | brake pads and rotors, flush brakes
2017-08-11 | airbag recall
2018-07-06 | oil/filter change, fuel filter, battery svc
2018-07-06 | transmission fl, p steering fl, rear diff fl
2019-07-22 | oil & filter change, brake fluid flush, front differential flush
2019-08-20 | replace 4 tires
2019-10-09 | replace passenger taillight bulb
2019-10-25 | replace passenger taillight assembly
(19 rows)
```

要将此发送到文件，将输出更改为：

```
\o autorepairs.txt
```

然后再次运行 `SELECT` 命令。

### 退出 PostgreSQL

最后，在终端中退出 PostgreSQL，输入：

```
quit
```

或者它的缩写版：

```
\q
```

虽然这只是 PostgreSQL 的简要介绍，但我希望它展示了将数据库用于这样的简单任务既不困难也不费时。

---

via: <https://opensource.com/article/19/11/getting-started-postgresql>

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


关于 sudo 你可能不知道的

Summary: 觉得你已经了解了 sudo 的所有知识了吗？再想想。

虽然基本看不懂，但是感觉好NB的样子

觉得你已经了解了 sudo 的所有知识了吗？再想想。

>
> 觉得你已经了解了 sudo 的所有知识了吗？再想想。
>
>
>

![](/data/attachment/album/201911/20/091740ape5b74jppjj4q36.jpg)

大家都知道 `sudo`，对吗？默认情况下，该工具已安装在大多数 Linux 系统上，并且可用于大多数 BSD 和商业 Unix 变体。不过，在与数百名 `sudo` 用户交谈之后，我得到的最常见的答案是 `sudo` 是一个使生活复杂化的工具。

有 root 用户和 `su` 命令，那么为什么还要使用另一个工具呢？对于许多人来说，`sudo` 只是管理命令的前缀。只有极少数人提到，当你在同一个系统上有多个管理员时，可以使用 `sudo` 日志查看谁做了什么。

那么，`sudo` 是什么？ 根据 [sudo 网站](https://www.sudo.ws)：

>
> “sudo 允许系统管理员通过授予某些用户以 root 用户或其他用户身份运行某些命令的能力，同时提供命令及其参数的审核记录，从而委派权限。”
>
>
>

默认情况下，`sudo` 只有简单的配置，一条规则允许一个用户或一组用户执行几乎所有操作（在本文后面的配置文件中有更多信息）：

```
%wheel ALL=(ALL) ALL
```

在此示例中，参数表示以下含义：

* 第一个参数（`%wheel`）定义组的成员。
* 第二个参数（`ALL`）定义组成员可以在其上运行命令的主机。
* 第三个参数（`(ALL)`）定义了可以执行命令的用户名。
* 最后一个参数（`ALL`）定义可以运行的应用程序。

因此，在此示例中，`wheel` 组的成员可以以所有主机上的所有用户身份运行所有应用程序。但即使是这个一切允许的规则也很有用，因为它会记录谁在计算机上做了什么。

### 别名

当然，它不仅可以让你和你最好的朋友管理一个共享机器，你还可以微调权限。你可以将以上配置中的项目替换为列表：用户列表、命令列表等。多数情况下，你可能会复制并粘贴配置中的一些列表。

在这种情况下，别名可以派上用场。在多个位置维护相同的列表容易出错。你可以定义一次别名，然后可以多次使用。因此，当你对一位管理员不再信任时，将其从别名中删除就行了。使用多个列表而不是别名，很容易忘记从具有较高特权的列表之一中删除用户。

### 为特定组的用户启用功能

`sudo` 命令带有大量默认设置。不过，在某些情况下，你想覆盖其中的一些情况，这时你可以在配置中使用 `Defaults` 语句。通常，对每个用户都强制使用这些默认值，但是你可以根据主机、用户名等将设置缩小到一部分用户。这里有个我那一代的系统管理员都喜欢玩的一个示例：“羞辱”。这些只不过是一些有人输入错误密码时的有趣信息：

```
czanik@linux-mewy:~> sudo ls
[sudo] password for root:
Hold it up to the light --- not a brain in sight!  # 把灯举高点，脑仁太小看不到
[sudo] password for root:
My pet ferret can type better than you!     # 我的宠物貂也比你输入的好
[sudo] password for root:
sudo: 3 incorrect password attempts
czanik@linux-mewy:~>
```

由于并非所有人都喜欢系统管理员的这种幽默，因此默认情况下会禁用这些羞辱信息。以下示例说明了如何仅对经验丰富的系统管理员（即 `wheel` 组的成员）启用此设置：

```
Defaults !insults
Defaults:%wheel insults
```

我想，感谢我将这些消息带回来的人用两只手也数不过来吧。

### 摘要验证

当然，`sudo` 还有更严肃的功能。其中之一是摘要验证。你可以在配置中包括应用程序的摘要：

```
peter ALL = sha244:11925141bb22866afdf257ce7790bd6275feda80b3b241c108b79c88 /usr/bin/passwd
```

在这种情况下，`sudo` 在运行应用程序之前检查应用程序摘要，并将其与配置中存储的摘要进行比较。如果不匹配，`sudo` 拒绝运行该应用程序。尽管很难在配置中维护此信息（没有用于此目的的自动化工具），但是这些摘要可以为你提供额外的保护层。

### 会话记录

会话记录也是 `sudo` 鲜为人知的功能。在演示之后，许多人离开我的演讲后就计划在其基础设施上实施它。为什么？因为使用会话记录，你不仅可以看到命令名称，还可以看到终端中发生的所有事情。你可以看到你的管理员在做什么，要不他们用 shell 访问了机器而日志仅会显示启动了 `bash`。

当前有一个限制。记录存储在本地，因此具有足够的权限的话，用户可以删除他们的痕迹。所以请继续关注即将推出的功能。

### 插件

从 1.8 版开始，`sudo` 更改为基于插件的模块化体系结构。通过将大多数功能实现为插件，你可以编写自己的功能轻松地替换或扩展 `sudo` 的功能。已经有了 `sudo` 上的开源和商业插件。

在我的演讲中，我演示了 `sudo_pair` 插件，该插件可在 [GitHub](https://github.com/square/sudo_pair/) 上获得。这个插件是用 Rust 开发的，这意味着它不是那么容易编译，甚至更难以分发其编译结果。另一方面，该插件提供了有趣的功能，需要第二个管理员通过 `sudo` 批准（或拒绝）运行命令。不仅如此，如果有可疑活动，可以在屏幕上跟踪会话并终止会话。

在最近的 All Things Open 会议上的一次演示中，我做了一个臭名昭著的演示：

```
czanik@linux-mewy:~> sudo  rm -fr /
```

看着屏幕上显示的命令。每个人都屏住呼吸，想看看我的笔记本电脑是否被毁了，然而它逃过一劫。

### 日志

正如我在开始时提到的，日志记录和警报是 `sudo` 的重要组成部分。如果你不会定期检查 `sudo` 日志，那么日志在使用 `sudo` 中并没有太多价值。该工具通过电子邮件提醒配置中指定的事件，并将所有事件记录到 syslog 中。可以打开调试日志用于调试规则或报告错误。

### 警报

电子邮件警报现在有点过时了，但是如果你使用 syslog-ng 来收集日志消息，则会自动解析 `sudo` 日志消息。你可以轻松创建自定义警报并将其发送到各种各样的目的地，包括 Slack、Telegram、Splunk 或 Elasticsearch。你可以从[我在 syslong-ng.com 上的博客](https://www.syslog-ng.com/community/b/blog/posts/alerting-on-sudo-events-using-syslog-ng)中了解有关此功能的更多信息。

### 配置

我们谈论了很多 `sudo` 功能，甚至还看到了几行配置。现在，让我们仔细看看 `sudo` 的配置方式。配置本身可以在 `/etc/sudoers` 中获得，这是一个简单的文本文件。不过，不建议直接编辑此文件。相反，请使用 `visudo`，因为此工具还会执行语法检查。如果你不喜欢 `vi`，则可以通过将 `EDITOR` 环境变量指向你的首选编辑器来更改要使用的编辑器。

在开始编辑 `sudo` 配置之前，请确保你知道 root 密码。（是的，即使在默认情况下 root 用户没有密码的 Ubuntu 上也是如此。）虽然 `visudo` 会检查语法，但创建语法正确而将你锁定在系统之外的配置也很容易。

如果在紧急情况下，而你手头有 root 密码，你也可以直接编辑配置。当涉及到 `sudoers` 文件时，有一件重要的事情要记住：从上到下读取该文件，以最后的设置为准。这个事实对你来说意味着你应该从通用设置开始，并在末尾放置例外情况，否则，通用设置会覆盖例外情况。

你可以在下面看到一个基于 CentOS 的简单 `sudoers` 文件，并添加我们之前讨论的几行：

```
Defaults !visiblepw
Defaults always_set_home
Defaults match_group_by_gid
Defaults always_query_group_plugin
Defaults env_reset
Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS"
Defaults env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE"
Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin
root ALL=(ALL) ALL
%wheel ALL=(ALL) ALL
Defaults:%wheel insults
Defaults !insults
Defaults log_output
```

该文件从更改多个默认值开始。然后是通常的默认规则：`root` 用户和 `wheel` 组的成员对计算机具有完全权限。接下来，我们对 `wheel` 组启用“羞辱”，但对其他所有人禁用它们。最后一行启用会话记录。

上面的配置在语法上是正确的，但是你可以发现逻辑错误吗？是的，有一个：后一个通用设置覆盖了先前的更具体设置，让所有人均禁用了“羞辱”。一旦交换了这两行的位置，设置就会按预期进行：`wheel` 组的成员会收到有趣的消息，但其他用户则不会收到。

### 配置管理

一旦必须在多台机器上维护 `sudoers` 文件，你很可能希望集中管理配置。这里主要有两种可能的开源方法。两者都有其优点和缺点。

你可以使用也用来配置其余基础设施的配置管理应用程序之一：Red Hat Ansible、Puppet 和 Chef 都具有用于配置 `sudo` 的模块。这种方法的问题在于更新配置远非实时。同样，用户仍然可以在本地编辑 `sudoers` 文件并更改设置。

`sudo` 工具也可以将其配置存储在 LDAP 中。在这种情况下，配置更改是实时的，用户不能弄乱`sudoers` 文件。另一方面，该方法也有局限性。例如，当 LDAP 服务器不可用时，你不能使用别名或使用 `sudo`。

### 新功能

新版本的 `sudo` 即将推出。1.9 版将包含许多有趣的新功能。以下是最重要的计划功能：

* 记录服务可集中收集会话记录，与本地存储相比，它具有许多优点：
	+ 在一个地方搜索更方便。
	+ 即使发送记录的机器关闭，也可以进行记录。
	+ 记录不能被想要删除其痕迹的人删除。
* audit 插件没有向 `sudoers` 添加新功能，而是为插件提供了 API，以方便地访问任何类型的 `sudo` 日志。这个插件允许使用插件从 `sudo` 事件创建自定义日志。
* approval 插件无需使用第三方插件即可启用会话批准。
* 以及我个人最喜欢的：插件的 Python 支持，这使你可以轻松地使用 Python 代码扩展 `sudo`，而不是使用 C 语言进行原生编码。 ### 总结

希望本文能向你证明 `sudo` 不仅仅是一个简单的命令前缀。有无数种可能性可以微调系统上的权限。你不仅可以微调权限，还可以通过检查摘要来提高安全性。会话记录使你能够检查系统上正在发生的事情。你也可以使用插件扩展 `sudo` 的功能，或者使用已有的插件或编写自己的插件。最后，从即将发布的功能列表中你可以看到，即使 `sudo` 已有数十年的历史，它也是一个不断发展的有生命力的项目。

如果你想了解有关 `sudo` 的更多信息，请参考以下资源：

* [sudo 网站](https://www.sudo.ws/)
* [sudo 博客](https://blog.sudo.ws/)
* [在 Twitter 上关注我们](https://twitter.com/sudoproject)

---

via: <https://opensource.com/article/19/10/know-about-sudo>

作者：[Peter Czanik](https://opensource.com/users/czanik) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


如何在 CentOS 8 上安装和配置 Postfix 邮件服务器

我一直不理解这个hostname 设置的含义，有些是mail.mydomain.com 你这里是server1.mydomain.com， 能细致的分析一下吗？

那只是为IP取了个名字，做到IP与名字对应，当你访问server1.mydomain.com时，他会使用这个hostname文件解析为对应IP，这是我自己的一点见解

Postfix 是一个自由开源的 MTA（邮件传输代理），用于在 Linux 系统上路由或传递电子邮件。在本指南中，你将学习如何在 CentOS 8 上安装和配置 Postfix。

![Install-configure-Postfx-Server-CentOS8](/data/attachment/album/201911/21/072857e9bvnzrbqayraxva.jpg)

实验室设置：

* 系统：CentOS 8 服务器
* IP 地址：192.168.1.13
* 主机名：server1.crazytechgeek.info（确保域名指向服务器的 IP）

### 步骤 1）更新系统

第一步是确保系统软件包是最新的。为此，请按如下所示更新系统：

```
# dnf update
```

继续之前，还请确保不存在其他 MTA（如 Sendmail），因为这将导致与 Postfix 配置冲突。例如，要删除 Sendmail，请运行以下命令：

```
# dnf remove sendmail
```

### 步骤 2）设置主机名并更新 /etc/hosts

使用下面的 `hostnamectl` 命令在系统上设置主机名：

```
# hostnamectl set-hostname server1.crazytechgeek.info
# exec bash
```

此外，你需要在 `/etc/hosts` 中添加系统的主机名和 IP：

```
# vim /etc/hosts
192.168.1.13   server1.crazytechgeek.info
```

保存并退出文件。

### 步骤 3）安装 Postfix 邮件服务器

验证系统上没有其他 MTA 在运行后，运行以下命令安装 Postfix：

```
# dnf install postfix
```

![Install-Postfix-Centos8](/data/attachment/album/201911/21/072858z06i6mz66qil576w.png)

### 步骤 4）启动并启用 Postfix 服务

成功安装 Postfix 后，运行以下命令启动并启用 Postfix 服务：

```
# systemctl start postfix
# systemctl enable postfix
```

要检查 Postfix 状态，请运行以下 `systemctl` 命令：

```
# systemctl status postfix
```

![Start-Postfix-check-status-centos8](/data/attachment/album/201911/21/072900e6thztbhncn6ulk5.png)

太好了，我们已经验证了 Postfix 已启动并正在运行。接下来，我们将配置 Postfix 从本地发送邮件到我们的服务器。

### 步骤 5）安装 mailx 邮件客户端

在配置 Postfix 服务器之前，我们需要安装 `mailx`，要安装它，请运行以下命令：

```
# dnf install mailx
```

![Install-Mailx-CentOS8](/data/attachment/album/201911/21/072901e9vhlvwiz1cw1ixv.png)

### 步骤 6）配置 Postfix 邮件服务器

Postfix 的配置文件位于 `/etc/postfix/main.cf` 中。我们需要对配置文件进行一些修改，因此请使用你喜欢的文本编辑器将其打开：

```
# vi /etc/postfix/main.cf
```

更改以下几行：

```
myhostname = server1.crazytechgeek.info
mydomain = crazytechgeek.info
myorigin = $mydomain
## 取消注释并将 inet_interfaces 设置为 all##
inet_interfaces = all
## 更改为 all ##
inet_protocols = all
## 注释 ##
#mydestination = $myhostname, localhost.$mydomain, localhost
## 取消注释 ##
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
## 取消注释并添加 IP 范围 ##
mynetworks = 192.168.1.0/24, 127.0.0.0/8
## 取消注释 ##
home_mailbox = Maildir/
```

完成后，保存并退出配置文件。重新启动 postfix 服务以使更改生效：

```
# systemctl restart postfix
```

### 步骤 7）测试 Postfix 邮件服务器

测试我们的配置是否有效，首先，创建一个测试用户。

```
# useradd postfixuser
# passwd postfixuser
```

接下来，运行以下命令，从本地用户 `pkumar` 发送邮件到另一个用户 `postfixuser`。

```
# telnet localhost smtp
或者
# telnet localhost 25
```

如果未安装 telnet 服务，那么可以使用以下命令进行安装：

```
# dnf install telnet -y
```

如前所述运行命令时，应获得如下输出：

```
[root@linuxtechi ~]# telnet localhost 25
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
220 server1.crazytechgeek.info ESMTP Postfix
```

上面的结果确认与 postfix 邮件服务器的连接正常。接下来，输入命令：

```
# ehlo localhost
```

输出看上去像这样：

```
250-server1.crazytechgeek.info
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250 SMTPUTF8
```

接下来，运行橙色高亮的命令，例如 `mail from`、`rcpt to`、`data`，最后输入 `quit`：

```
mail from:<pkumar>
250 2.1.0 Ok
rcpt to:<postfixuser>
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
Hello, Welcome to my mailserver (Postfix)
.
250 2.0.0 Ok: queued as B56BF1189BEC
quit
221 2.0.0 Bye
Connection closed by foreign host
```

完成 `telnet` 命令可从本地用户 `pkumar` 发送邮件到另一个本地用户 `postfixuser`，如下所示：

![Send-email-with-telnet-centos8](/data/attachment/album/201911/21/072901f78s7ht7bzmkk747.png)

如果一切都按计划进行，那么你应该可以在新用户的家目录中查看发送的邮件：

```
# ls /home/postfixuser/Maildir/new
1573580091.Vfd02I20050b8M635437.server1.crazytechgeek.info
#
```

要阅读邮件，只需使用 cat 命令，如下所示：

```
# cat /home/postfixuser/Maildir/new/1573580091.Vfd02I20050b8M635437.server1.crazytechgeek.info
```

![Read-postfix-email-linux](/data/attachment/album/201911/21/072903kpvvrg53bmcbmx5u.png)

### Postfix 邮件服务器日志

Postfix 邮件服务器邮件日志保存在文件 `/var/log/maillog` 中，使用以下命令查看实时日志，

```
# tail -f /var/log/maillog
```

![postfix-maillogs-centos8](/data/attachment/album/201911/21/072905l7zsofxsoocmxxpr.png)

### 保护 Postfix 邮件服务器

建议始终确保客户端和 Postfix 服务器之间的通信安全，这可以使用 SSL 证书来实现，它们可以来自受信任的权威机构或自签名证书。在本教程中，我们将使用 `openssl` 命令生成用于 Postfix 的自签名证书，

我假设 `openssl` 已经安装在你的系统上，如果未安装，请使用以下 `dnf` 命令：

```
# dnf install openssl -y
```

使用下面的 `openssl` 命令生成私钥和 CSR（证书签名请求）：

```
# openssl req -nodes -newkey rsa:2048 -keyout mail.key -out mail.csr
```

![Postfix-Key-CSR-CentOS8](/data/attachment/album/201911/21/072906uugozol5vr5rdsur.png)

现在，使用以下 openssl 命令生成自签名证书：

```
# openssl x509 -req -days 365 -in mail.csr -signkey mail.key -out mail.crt
Signature ok
subject=C = IN, ST = New Delhi, L = New Delhi, O = IT, OU = IT, CN = server1.crazytechgeek.info, emailAddress = root@linuxtechi
Getting Private key
#
```

现在将私钥和证书文件复制到 `/etc/postfix` 目录下：

```
# cp mail.key mail.crt /etc/postfix
```

在 Postfix 配置文件中更新私钥和证书文件的路径：

```
# vi /etc/postfix/main.cf
………
smtpd_use_tls = yes
smtpd_tls_cert_file = /etc/postfix/mail.crt
smtpd_tls_key_file = /etc/postfix/mail.key
smtpd_tls_security_level = may
………
```

重启 Postfix 服务以使上述更改生效：

```
# systemctl restart postfix
```

让我们尝试使用 `mailx` 客户端将邮件发送到内部本地域和外部域。

从 `pkumar` 发送内部本地邮件到 `postfixuser` 中：

```
# echo "test email" | mailx -s "Test email from Postfix MailServer" -r root@linuxtechi root@linuxtechi
```

使用以下命令检查并阅读邮件：

```
# cd /home/postfixuser/Maildir/new/
# ll
total 8
-rw-------. 1 postfixuser postfixuser 476 Nov 12 17:34 1573580091.Vfd02I20050b8M635437.server1.crazytechgeek.info
-rw-------. 1 postfixuser postfixuser 612 Nov 13 02:40 1573612845.Vfd02I20050bbM466643.server1.crazytechgeek.info
# cat 1573612845.Vfd02I20050bbM466643.server1.crazytechgeek.info
```

![Read-Postfixuser-Email-CentOS8](/data/attachment/album/201911/21/072908u11ytm69g69tdtfl.png)

从 `postfixuser` 发送邮件到外部域（`root@linuxtechi.com`）：

```
# echo "External Test email" | mailx -s "Postfix MailServer" -r root@linuxtechi root@linuxtechi
```

注意：如果你的 IP 没有被任何地方列入黑名单，那么你发送到外部域的邮件将被发送，否则它将被退回，并提示你的 IP 被 spamhaus 之类的数据库列入黑名单。

### 检查 Postfix 邮件队列

使用 `mailq` 命令列出队列中的邮件：

```
# mailq
Mail queue is empty
#
```

完成！我们的 Postfix 配置正常工作了！目前就这样了。我们希望你觉得本教程有见地，并且你可以轻松地设置本地 Postfix 服务器。

---

via: <https://www.linuxtechi.com/install-configure-postfix-mailserver-centos-8/>

作者：[James Kiarie](https://www.linuxtechi.com/author/james/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


在 Ubuntu 19.10 上入门 ZFS 文件系统

Summary: Ubuntu 19.10 的主要新特性之一就是 ZFS。现在你可以很容易的无需额外努力就可以在 Ubuntu 系统上安装 ZFS了。

注意一个最重要的点：<br />
zfs无法移除硬盘，只能替换。这点和btrfs不同

![](/data/attachment/album/201911/21/081609la0a7c7kpypppkt8.jpg)

Ubuntu 19.10 的主要新特性之一就是 [ZFS](/article-10034-1.html)。现在你可以很容易的无需额外努力就可以在 Ubuntu 系统上安装 ZFS了。

一般情况下，安装 Linux 都会选择 Ext4 文件系统。但是如果是全新安装 Ubuntu 19.10，在安装的启动阶段可以看到 ZFS 选项。

![你可以在安装 Ubuntu 19.10 的时候选择 ZFS](/data/attachment/album/201911/21/081613mzv9vj4a0v790ss0.jpg)

让我们看看 ZFS 为何重要，以及如何在已经安装了 ZFS 的 Ubuntu 上使用它。

### ZFS 与其他文件系统有哪些区别？

ZFS 的设计初衷是：处理海量存储和避免数据损坏。ZFS 可以处理 256 千万亿的 ZB 数据。（这就是 ZFS 的 Z）且它可以处理最大 16 EB 的文件。

如果你仅有一个单磁盘的笔记本电脑，你可以体验 ZFS 的数据保护特性。写时复制（COW）特性确保正在使用的数据不会被覆盖，相反，新的数据会被写到一个新的块中，同时文件系统的元数据会被更新到新块中。ZFS 可容易的创建文件系统的快照。这个快照可追踪文件系统的更改，并共享数据块确保节省数据空间。

ZFS 为磁盘上的每个文件分配一个校验和。它会不断的校验文件的状态和校验和。如果发现文件被损坏了，它就会尝试修复文件。

我写过一个文章详细介绍 [什么是 ZFS以及它有哪些特性](/article-10034-1.html)。如果你感兴趣可以去阅读下。

注：请谨记 ZFS 的数据保护特性会导致性能下降。

### Ubuntu 下使用 ZFS [适用于中高级用户]

一旦你在你的主磁盘上全新安装了带有 ZFS 的 Ubuntu，你就可以开始体验它的特性。

请注意所有的 ZFS 设置过程都需要命令行。我不知道它有任何 GUI 工具。

#### 创建一个 ZFS 池

**这段仅针对拥有多个磁盘的系统。如果你只有一个磁盘，Ubuntu 会在安装的时候自动创建池。**

在创建池之前，你需要为池找到磁盘的 id。你可以用命令 `lsblk` 查询出这个信息。

为三个磁盘创建一个基础池，用以下命令：

```
sudo zpool create pool-test /dev/sdb /dev/sdc /dev/sdd
```

请记得替换 `pool-test` 为你选择的的命名。

这个命令将会设置“无冗余 RAID-0 池”。这意味着如果一个磁盘被破坏或有故障，你将会丢失数据。如果你执行以上命令，还是建议做一个常规备份。

你可以用下面命令将另一个磁盘增加到池中：

```
sudo zpool add pool-name /dev/sdx
```

#### 查看 ZFS 池的状态

你可以用这个命令查询新建池的状态：

```
sudo zpool status pool-test
```

![Zpool 状态](/data/attachment/album/201911/21/081614bkaa04jjkjgsjxyj.png)

#### 镜像一个 ZFS 池

为确保数据的安全性，你可以创建镜像。镜像意味着每个磁盘包含同样的数据。使用镜像设置，你可能会丢失三个磁盘中的两个，并且仍然拥有所有信息。

要创建镜像你可以用下面命令：

```
sudo zpool create pool-test mirror /dev/sdb /dev/sdc /dev/sdd
```

#### 创建 ZFS 用于备份恢复的快照

快照允许你创建一个后备，以防某个文件被删除或被覆盖。比如，我们创建一个快照，当在用户主目录下删除一些目录后，然后把它恢复。

首先，你需要找到你想要的快照数据集。你可以这样做：

```
zfs list
```

![Zfs List](/data/attachment/album/201911/21/081616d4qfpf5l50fwal0a.png)

你可以看到我的家目录位于 `rpool/USERDATA/johnblood_uwcjk7`。

我们用下面命令创建一个名叫 `1910` 的快照：

```
sudo zfs snapshot rpool/USERDATA/johnblood_uwcjk7@1019
```

快照很快创建完成。现在你可以删除 `Downloads` 和 `Documents` 目录。

现在你用以下命令恢复快照：

```
sudo zfs rollback rpool/USERDATA/johnblood_uwcjk7@1019
```

回滚的时间长短取决于有多少信息改变。现在你可以查看家目录，被删除的目录（和它的内容）将会被恢复过来。

### 要不要试试 ZFS ？

这篇文章仅简单介绍的 Ubuntu下 ZFS 的用法。更多的信息请参考 [Ubuntu 的 ZFS Wiki页面](https://wiki.ubuntu.com/Kernel/Reference/ZFS)。我也推荐阅读 [ArsTechnica 的精彩文章](https://arstechnica.com/information-technology/2019/10/a-detailed-look-at-ubuntus-new-experimental-zfs-installer/)。

这个是试验性的功能。如果你还不了解 ZFS，你想用一个简单稳定的系统，请安装标准文件系统 EXT4。如果你想用闲置的机器体验，可以参照上面了解 ZFS。如果你是一个“专家”，并且知道自己在做什么，则可以随时随地随意尝试ZFS。

你之前用过 ZFS 吗？请在下面留言。

---

via: <https://itsfoss.com/zfs-ubuntu/>

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

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

know in the comments below. If you found this article interesting, please take a minute to share it on social media, Hacker News or [Reddit](https://reddit.com/r/linuxusersgroup).

---

via: <https://itsfoss.com/zfs-ubuntu/>

作者：[John Paul](https://itsfoss.com/author/john/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[译者ID](https://github.com/%E8%AF%91%E8%80%85ID) 校对：[校对者ID](https://github.com/%E6%A0%A1%E5%AF%B9%E8%80%85ID)

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


如何使用 Protobuf 做数据交换

>
> 在以不同语言编写并在不同平台上运行的应用程序之间交换数据时，Protobuf 编码可提高效率。
>
>
>

![](/data/attachment/album/201911/22/075757pn2fxfth30ntwefg.jpg)

<ruby> 协议缓冲区 <rt>  Protocol Buffers </rt></ruby>（[Protobufs](https://developers.google.com/protocol-buffers/)）像 XML 和 JSON 一样，可以让用不同语言编写并在不同平台上运行的应用程序交换数据。例如，用 Go 编写的发送程序可以在 Protobuf 中对以 Go 表示的销售订单数据进行编码，然后用 Java 编写的接收方可以对它进行解码，以获取所接收订单数据的 Java 表示方式。这是在网络连接上的结构示意图：

>
> Go 销售订单 —> Pbuf 编码 —> 网络 —> Pbuf 界面 —> Java 销售订单
>
>
>

与 XML 和 JSON 相比，Protobuf 编码是二进制而不是文本，这会使调试复杂化。但是，正如本文中的代码示例所确认的那样，Protobuf 编码在大小上比 XML 或 JSON 编码要有效得多。

Protobuf 以另一种方式提供了这种有效性。在实现级别，Protobuf 和其他编码系统对结构化数据进行<ruby> 序列化 <rt>  serialize </rt></ruby>和<ruby> 反序列化 <rt>  deserialize </rt></ruby>。序列化将特定语言的数据结构转换为字节流，反序列化是将字节流转换回特定语言的数据结构的逆运算。序列化和反序列化可能成为数据交换的瓶颈，因为这些操作会占用大量 CPU。高效的序列化和反序列化是 Protobuf 的另一个设计目标。

最近的编码技术，例如 Protobuf 和 FlatBuffers，源自 1990 年代初期的 [DCE/RPC](https://en.wikipedia.org/wiki/DCE/RPC)（<ruby> 分布式计算环境/远程过程调用 <rt>  Distributed Computing Environment/Remote Procedure Call </rt></ruby>）计划。与 DCE/RPC 一样，Protobuf 在数据交换中为 [IDL](https://en.wikipedia.org/wiki/Interface_description_language)（接口定义语言）和编码层做出了贡献。

本文将着眼于这两层，然后提供 Go 和 Java 中的代码示例以充实 Protobuf 的细节，并表明 Protobuf 是易于使用的。

### Protobuf 作为一个 IDL 和编码层

像 Protobuf 一样，DCE/RPC 被设计为与语言和平台无关。适当的库和实用程序允许任何语言和平台用于 DCE/RPC 领域。此外，DCE/RPC 体系结构非常优雅。IDL 文档是一侧的远程过程与另一侧的调用者之间的协定。Protobuf 也是以 IDL 文档为中心的。

IDL 文档是文本，在 DCE/RPC 中，使用基本 C 语法以及元数据的语法扩展（方括号）和一些新的关键字，例如 `interface`。这是一个例子：

```
[uuid (2d6ead46-05e3-11ca-7dd1-426909beabcd), version(1.0)]
interface echo {
   const long int ECHO_SIZE = 512;
   void echo(
      [in]          handle_t h,
      [in, string]  idl_char from_client[ ],
      [out, string] idl_char from_service[ECHO_SIZE]
   );
}
```

该 IDL 文档声明了一个名为 `echo` 的过程，该过程带有三个参数：类型为 `handle_t`（实现指针）和 `idl_char`（ASCII 字符数组）的 `[in]` 参数被传递给远程过程，而 `[out]` 参数（也是一个字符串）从该过程中传回。在此示例中，`echo` 过程不会显式返回值（`echo` 左侧的 `void`），但也可以返回值。返回值，以及一个或多个 `[out]` 参数，允许远程过程任意返回许多值。下一节将介绍 Protobuf IDL，它的语法不同，但同样用作数据交换中的协定。

DCE/RPC 和 Protobuf 中的 IDL 文档是创建用于交换数据的基础结构代码的实用程序的输入：

>
> IDL 文档 —> DCE/PRC 或 Protobuf 实用程序 —> 数据交换的支持代码
>
>
>

作为相对简单的文本，IDL 是同样便于人类阅读的关于数据交换细节的文档（特别是交换的数据项的数量和每个项的数据类型）。

Protobuf 可用于现代 RPC 系统，例如 [gRPC](https://grpc.io/)；但是 Protobuf 本身仅提供 IDL 层和编码层，用于从发送者传递到接收者的消息。与原本的 DCE/RPC 一样，Protobuf 编码是二进制的，但效率更高。

目前，XML 和 JSON 编码仍在通过 Web 服务等技术进行的数据交换中占主导地位，这些技术利用 Web 服务器、传输协议（例如 TCP、HTTP）以及标准库和实用程序等原有的基础设施来处理 XML 和 JSON 文档。 此外，各种类型的数据库系统可以存储 XML 和 JSON 文档，甚至旧式关系型系统也可以轻松生成查询结果的 XML 编码。现在，每种通用编程语言都具有支持 XML 和 JSON 的库。那么，是什么让我们回到 Protobuf 之类的**二进制**编码系统呢？

让我们看一下负十进制值 `-128`。以 2 的补码二进制表示形式（在系统和语言中占主导地位）中，此值可以存储在单个 8 位字节中：`10000000`。此整数值在 XML 或 JSON 中的文本编码需要多个字节。例如，UTF-8 编码需要四个字节的字符串，即 `-128`，即每个字符一个字节（十六进制，值为 `0x2d`、`0x31`、`0x32` 和 `0x38`）。XML 和 JSON 还添加了标记字符，例如尖括号和大括号。有关 Protobuf 编码的详细信息下面就会介绍，但现在的关注点是一个通用点：文本编码的压缩性明显低于二进制编码。

### 在 Go 中使用 Protobuf 的示例

我的代码示例着重于 Protobuf 而不是 RPC。以下是第一个示例的概述：

* 名为 `dataitem.proto` 的 IDL 文件定义了一个 Protobuf 消息，它具有六个不同类型的字段：具有不同范围的整数值、固定大小的浮点值以及两个不同长度的字符串。
* Protobuf 编译器使用 IDL 文件生成 Go 版本（以及后面的 Java 版本）的 Protobuf 消息及支持函数。
* Go 应用程序使用随机生成的值填充原生的 Go 数据结构，然后将结果序列化为本地文件。为了进行比较， XML 和 JSON 编码也被序列化为本地文件。
* 作为测试，Go 应用程序通过反序列化 Protobuf 文件的内容来重建其原生数据结构的实例。
* 作为语言中立性测试，Java 应用程序还会对 Protobuf 文件的内容进行反序列化以获取原生数据结构的实例。

[我的网站](http://condor.depaul.edu/mkalin)上提供了该 IDL 文件以及两个 Go 和一个 Java 源文件，打包为 ZIP 文件。

最重要的 Protobuf IDL 文档如下所示。该文档存储在文件 `dataitem.proto` 中，并具有常规的`.proto` 扩展名。

#### 示例 1、Protobuf IDL 文档

```
syntax = "proto3";

package main;

message DataItem {
  int64  oddA  = 1;
  int64  evenA = 2;
  int32  oddB  = 3;
  int32  evenB = 4;
  float  small = 5;
  float  big   = 6;
  string short = 7;
  string long  = 8;
}
```

该 IDL 使用当前的 proto3 而不是较早的 proto2 语法。软件包名称（在本例中为 `main`）是可选的，但是惯例使用它以避免名称冲突。这个结构化的消息包含八个字段，每个字段都有一个 Protobuf 数据类型（例如，`int64`、`string`）、名称（例如，`oddA`、`short`）和一个等号 `=` 之后的数字标签（即键）。标签（在此示例中为 1 到 8）是唯一的整数标识符，用于确定字段序列化的顺序。

Protobuf 消息可以嵌套到任意级别，而一个消息可以是另外一个消息的字段类型。这是一个使用 `DataItem` 消息作为字段类型的示例：

```
message DataItems {
  repeated DataItem item = 1;
}
```

单个 `DataItems` 消息由重复的（零个或多个）`DataItem` 消息组成。

为了清晰起见，Protobuf 还支持枚举类型：

```
enum PartnershipStatus {
  reserved "FREE", "CONSTRAINED", "OTHER";
}
```

`reserved` 限定符确保用于实现这三个符号名的数值不能重复使用。

为了生成一个或多个声明 Protobuf 消息结构的特定于语言的版本，包含这些结构的 IDL 文件被传递到`protoc` 编译器（可在 [Protobuf GitHub 存储库](https://github.com/protocolbuffers/protobuf)中找到）。对于 Go 代码，可以以通常的方式安装支持的 Protobuf 库（这里以 `％` 作为命令行提示符）：

```
% go get github.com/golang/protobuf/proto
```

将 Protobuf IDL 文件 `dataitem.proto` 编译为 Go 源代码的命令是：

```
% protoc --go_out=. dataitem.proto
```

标志 `--go_out` 指示编译器生成 Go 源代码。其他语言也有类似的标志。在这种情况下，结果是一个名为 `dataitem.pb.go` 的文件，该文件足够小，可以将其基本内容复制到 Go 应用程序中。以下是生成的代码的主要部分：

```
var _ = proto.Marshal

type DataItem struct {
   OddA  int64   `protobuf:"varint,1,opt,name=oddA" json:"oddA,omitempty"`
   EvenA int64   `protobuf:"varint,2,opt,name=evenA" json:"evenA,omitempty"`
   OddB  int32   `protobuf:"varint,3,opt,name=oddB" json:"oddB,omitempty"`
   EvenB int32   `protobuf:"varint,4,opt,name=evenB" json:"evenB,omitempty"`
   Small float32 `protobuf:"fixed32,5,opt,name=small" json:"small,omitempty"`
   Big   float32 `protobuf:"fixed32,6,opt,name=big" json:"big,omitempty"`
   Short string  `protobuf:"bytes,7,opt,name=short" json:"short,omitempty"`
   Long  string  `protobuf:"bytes,8,opt,name=long" json:"long,omitempty"`
}

func (m *DataItem) Reset()         { *m = DataItem{} }
func (m *DataItem) String() string { return proto.CompactTextString(m) }
func (*DataItem) ProtoMessage()    {}
func init() {}
```

编译器生成的代码具有 Go 结构 `DataItem`，该结构导出 Go 字段（名称现已大写开头），该字段与 Protobuf IDL 中声明的名称匹配。该结构字段具有标准的 Go 数据类型：`int32`、`int64`、`float32` 和 `string`。在每个字段行的末尾，是描述 Protobuf 类型的字符串，提供 Protobuf IDL 文档中的数字标签及有关 JSON 信息的元数据，这将在后面讨论。

此外也有函数；最重要的是 `Proto.Marshal`，用于将 `DataItem` 结构的实例序列化为 Protobuf 格式。辅助函数包括：清除 `DataItem` 结构的 `Reset`，生成 `DataItem` 的单行字符串表示的 `String`。

描述 Protobuf 编码的元数据应在更详细地分析 Go 程序之前进行仔细研究。

### Protobuf 编码

Protobuf 消息的结构为键/值对的集合，其中数字标签为键，相应的字段为值。字段名称（例如，`oddA` 和 `small`）是供人类阅读的，但是 `protoc` 编译器的确使用了字段名称来生成特定于语言的对应名称。例如，Protobuf IDL 中的 `oddA` 和 `small` 名称在 Go 结构中分别成为字段 `OddA` 和 `Small`。

键和它们的值都被编码，但是有一个重要的区别：一些数字值具有固定大小的 32 或 64 位的编码，而其他数字（包括消息标签）则是 `varint` 编码的，位数取决于整数的绝对值。例如，整数值 1 到 15 需要 8 位 `varint` 编码，而值 16 到 2047 需要 16 位。`varint` 编码在本质上与 UTF-8 编码类似（但细节不同），它偏爱较小的整数值而不是较大的整数值。（有关详细分析，请参见 Protobuf [编码指南](https://developers.google.com/protocol-buffers/docs/encoding)）结果是，Protobuf 消息应该在字段中具有较小的整数值（如果可能），并且键数应尽可能少，但每个字段至少得有一个键。

下表 1 列出了 Protobuf 编码的要点：

| 编码 | 示例类型 | 长度 |
| --- | --- | --- |
| `varint` | `int32`、`uint32`、`int64` | 可变长度 |
| `fixed` | `fixed32`、`float`、`double` | 固定的 32 位或 64 位长度 |
| 字节序列 | `string`、`bytes` | 序列长度 |

*表 1. Protobuf 数据类型*

未明确固定长度的整数类型是 `varint` 编码的；因此，在 `varint` 类型中，例如 `uint32`（`u` 代表无符号），数字 32 描述了整数的范围（在这种情况下为 0 到 2<sup> 32</sup> - 1），而不是其位的大小，该位大小取决于值。相比之下，对于固定长度类型（例如 `fixed32` 或 `double`），Protobuf 编码分别需要 32 位和 64 位。Protobuf 中的字符串是字节序列；因此，字段编码的大小就是字节序列的长度。

另一个高效的方法值得一提。回想一下前面的示例，其中的 `DataItems` 消息由重复的 `DataItem` 实例组成：

```
message DataItems {
  repeated DataItem item = 1;
}
```

`repeated` 表示 `DataItem` 实例是*打包的*：集合具有单个标签，在这里是 1。因此，具有重复的 `DataItem` 实例的 `DataItems` 消息比具有多个但单独的 `DataItem` 字段、每个字段都需要自己的标签的消息的效率更高。

了解了这一背景，让我们回到 Go 程序。

### dataItem 程序的细节

`dataItem` 程序创建一个 `DataItem` 实例，并使用适当类型的随机生成的值填充字段。Go 有一个 `rand` 包，带有用于生成伪随机整数和浮点值的函数，而我的 `randString` 函数可以从字符集中生成指定长度的伪随机字符串。设计目标是要有一个具有不同类型和位大小的字段值的 `DataItem` 实例。例如，`OddA` 和 `EvenA` 值分别是 64 位非负整数值的奇数和偶数；但是 `OddB` 和 `EvenB` 变体的大小为 32 位，并存放 0 到 2047 之间的小整数值。随机浮点值的大小为 32 位，字符串为 16（`Short`）和 32（`Long`）字符的长度。这是用随机值填充 `DataItem` 结构的代码段：

```
// 可变长度整数
n1 := rand.Int63()        // 大整数
if (n1 & 1) == 0 { n1++ } // 确保其是奇数
...
n3 := rand.Int31() % UpperBound // 小整数
if (n3 & 1) == 0 { n3++ }       // 确保其是奇数

// 固定长度浮点数
...
t1 := rand.Float32()
t2 := rand.Float32()
...
// 字符串
str1 := randString(StrShort)
str2 := randString(StrLong)

// 消息
dataItem := &DataItem {
   OddA:  n1,
   EvenA: n2,
   OddB:  n3,
   EvenB: n4,
   Big:   f1,
   Small: f2,
   Short: str1,
   Long:  str2,
}
```

创建并填充值后，`DataItem` 实例将以 XML、JSON 和 Protobuf 进行编码，每种编码均写入本地文件：

```
func encodeAndserialize(dataItem *DataItem) {
   bytes, _ := xml.MarshalIndent(dataItem, "", " ")  // Xml to dataitem.xml
   ioutil.WriteFile(XmlFile, bytes, 0644)            // 0644 is file access permissions

   bytes, _ = json.MarshalIndent(dataItem, "", " ")  // Json to dataitem.json
   ioutil.WriteFile(JsonFile, bytes, 0644)

   bytes, _ = proto.Marshal(dataItem)                // Protobuf to dataitem.pbuf
   ioutil.WriteFile(PbufFile, bytes, 0644)
}
```

这三个序列化函数使用术语 `marshal`，它与 `serialize` 意思大致相同。如代码所示，三个 `Marshal` 函数均返回一个字节数组，然后将其写入文件。（为简单起见，忽略可能的错误处理。）在示例运行中，文件大小为：

```
dataitem.xml:  262 bytes
dataitem.json: 212 bytes
dataitem.pbuf:  88 bytes
```

Protobuf 编码明显小于其他两个编码方案。通过消除缩进字符（在这种情况下为空白和换行符），可以稍微减小 XML 和 JSON 序列化的大小。

以下是 `dataitem.json` 文件，该文件最终是由 `json.MarshalIndent` 调用产生的，并添加了以 `##` 开头的注释：

```
{
 "oddA":  4744002665212642479,                ## 64-bit >= 0
 "evenA": 2395006495604861128,                ## ditto
 "oddB":  57,                                 ## 32-bit >= 0 but < 2048
 "evenB": 468,                                ## ditto
 "small": 0.7562016,                          ## 32-bit floating-point
 "big":   0.85202795,                         ## ditto
 "short": "ClH1oDaTtoX$HBN5",                 ## 16 random chars
 "long":  "xId0rD3Cri%3Wt%^QjcFLJgyXBu9^DZI"  ## 32 random chars
}
```

尽管这些序列化的数据写入到本地文件中，但是也可以使用相同的方法将数据写入网络连接的输出流。

### 测试序列化和反序列化

Go 程序接下来通过将先前写入 `dataitem.pbuf` 文件的字节反序列化为 `DataItem` 实例来运行基本测试。这是代码段，其中去除了错误检查部分：

```
filebytes, err := ioutil.ReadFile(PbufFile) // get the bytes from the file
...
testItem.Reset()                            // clear the DataItem structure
err = proto.Unmarshal(filebytes, testItem)  // deserialize into a DataItem instance
```

用于 Protbuf 反序列化的 `proto.Unmarshal` 函数与 `proto.Marshal` 函数相反。原始的 `DataItem` 和反序列化的副本将被打印出来以确认完全匹配：

```
Original:
2041519981506242154 3041486079683013705 1192 1879
0.572123 0.326855
boPb#T0O8Xd&Ps5EnSZqDg4Qztvo7IIs 9vH66AiGSQgCDxk&

Deserialized:
2041519981506242154 3041486079683013705 1192 1879
0.572123 0.326855
boPb#T0O8Xd&Ps5EnSZqDg4Qztvo7IIs 9vH66AiGSQgCDxk&
```

### 一个 Java Protobuf 客户端

用 Java 写的示例是为了确认 Protobuf 的语言中立性。原始 IDL 文件可用于生成 Java 支持代码，其中涉及嵌套类。但是，为了抑制警告信息，可以进行一些补充。这是修订版，它指定了一个 `DataMsg` 作为外部类的名称，内部类在该 Protobuf 消息后面自动命名为 `DataItem`：

```
syntax = "proto3";

package main;

option java_outer_classname = "DataMsg";

message DataItem {
...
```

进行此更改后，`protoc` 编译与以前相同，只是所期望的输出现在是 Java 而不是 Go：

```
% protoc --java_out=. dataitem.proto
```

生成的源文件（在名为 `main` 的子目录中）为 `DataMsg.java`，长度约为 1,120 行：Java 并不简洁。编译然后运行 Java 代码需要具有 Protobuf 库支持的 JAR 文件。该文件位于 [Maven 存储库](https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java)中。

放置好这些片段后，我的测试代码相对较短（并且在 ZIP 文件中以 `Main.java` 形式提供）：

```
package main;
import java.io.FileInputStream;

public class Main {
   public static void main(String[] args) {
      String path = "dataitem.pbuf";  // from the Go program's serialization
      try {
         DataMsg.DataItem deserial =
           DataMsg.DataItem.newBuilder().mergeFrom(new FileInputStream(path)).build();

         System.out.println(deserial.getOddA()); // 64-bit odd
         System.out.println(deserial.getLong()); // 32-character string
      }
      catch(Exception e) { System.err.println(e); }
    }
}
```

当然，生产级的测试将更加彻底，但是即使是该初步测试也可以证明 Protobuf 的语言中立性：`dataitem.pbuf` 文件是 Go 程序对 Go 语言版的 `DataItem` 进行序列化的结果，并且该文件中的字节被反序列化以产生一个 Java 语言的 `DataItem` 实例。Java 测试的输出与 Go 测试的输出相同。

### 用 numPairs 程序来结束

让我们以一个示例作为结尾，来突出 Protobuf 效率，但又强调在任何编码技术中都会涉及到的成本。考虑以下 Protobuf IDL 文件：

```
syntax = "proto3";
package main;

message NumPairs {
  repeated NumPair pair = 1;
}

message NumPair {
  int32 odd = 1;
  int32 even = 2;
}
```

`NumPair` 消息由两个 `int32` 值以及每个字段的整数标签组成。`NumPairs` 消息是嵌入的 `NumPair` 消息的序列。

Go 语言的 `numPairs` 程序（如下）创建了 200 万个 `NumPair` 实例，每个实例都附加到 `NumPairs` 消息中。该消息可以按常规方式进行序列化和反序列化。

#### 示例 2、numPairs 程序

```
package main

import (
   "math/rand"
   "time"
   "encoding/xml"
   "encoding/json"
   "io/ioutil"
   "github.com/golang/protobuf/proto"
)

// protoc-generated code: start
var _ = proto.Marshal
type NumPairs struct {
   Pair []*NumPair `protobuf:"bytes,1,rep,name=pair" json:"pair,omitempty"`
}

func (m *NumPairs) Reset()         { *m = NumPairs{} }
func (m *NumPairs) String() string { return proto.CompactTextString(m) }
func (*NumPairs) ProtoMessage()    {}
func (m *NumPairs) GetPair() []*NumPair {
   if m != nil { return m.Pair }
   return nil
}

type NumPair struct {
   Odd  int32 `protobuf:"varint,1,opt,name=odd" json:"odd,omitempty"`
   Even int32 `protobuf:"varint,2,opt,name=even" json:"even,omitempty"`
}

func (m *NumPair) Reset()         { *m = NumPair{} }
func (m *NumPair) String() string { return proto.CompactTextString(m) }
func (*NumPair) ProtoMessage()    {}
func init() {}
// protoc-generated code: finish

var numPairsStruct NumPairs
var numPairs = &numPairsStruct

func encodeAndserialize() {
   // XML encoding
   filename := "./pairs.xml"
   bytes, _ := xml.MarshalIndent(numPairs, "", " ")
   ioutil.WriteFile(filename, bytes, 0644)

   // JSON encoding
   filename = "./pairs.json"
   bytes, _ = json.MarshalIndent(numPairs, "", " ")
   ioutil.WriteFile(filename, bytes, 0644)

   // ProtoBuf encoding
   filename = "./pairs.pbuf"
   bytes, _ = proto.Marshal(numPairs)
   ioutil.WriteFile(filename, bytes, 0644)
}

const HowMany = 200 * 100  * 100 // two million

func main() {
   rand.Seed(time.Now().UnixNano())

   // uncomment the modulus operations to get the more efficient version
   for i := 0; i < HowMany; i++ {
      n1 := rand.Int31() // % 2047
      if (n1 & 1) == 0 { n1++ } // ensure it's odd
      n2 := rand.Int31() // % 2047
      if (n2 & 1) == 1 { n2++ } // ensure it's even

      next := &NumPair {
                 Odd:  n1,
                 Even: n2,
              }
      numPairs.Pair = append(numPairs.Pair, next)
   }
   encodeAndserialize()
}
```

每个 `NumPair` 中随机生成的奇数和偶数值的范围在 0 到 20 亿之间变化。就原始数据（而非编码数据）而言，Go 程序中生成的整数总共为 16MB：每个 `NumPair` 为两个整数，总计为 400 万个整数，每个值的大小为四个字节。

为了进行比较，下表列出了 XML、JSON 和 Protobuf 编码的示例 `NumsPairs` 消息的 200 万个 `NumPair` 实例。原始数据也包括在内。由于 `numPairs` 程序生成随机值，因此样本运行的输出有所不同，但接近表中显示的大小。

| 编码 | 文件 | 字节大小 | Pbuf/其它 比例 |
| --- | --- | --- | --- |
| 无 | pairs.raw | 16MB | 169% |
| Protobuf | pairs.pbuf | 27MB | — |
| JSON | pairs.json | 100MB | 27% |
| XML | pairs.xml | 126MB | 21% |

*表 2. 16MB 整数的编码开销*

不出所料，Protobuf 和之后的 XML 和 JSON 差别明显。Protobuf 编码大约是 JSON 的四分之一，是 XML 的五分之一。但是原始数据清楚地表明 Protobuf 也会产生编码开销：序列化的 Protobuf 消息比原始数据大 11MB。包括 Protobuf 在内的任何编码都涉及结构化数据，这不可避免地会增加字节。

序列化的 200 万个 `NumPair` 实例中的每个实例都包含**四**个整数值：Go 结构中的 `Even` 和 `Odd` 字段分别一个，而 Protobuf 编码中的每个字段、每个标签一个。对于原始数据（而不是编码数据），每个实例将达到 16 个字节，样本 `NumPairs` 消息中有 200 万个实例。但是 Protobuf 标记（如 `NumPair` 字段中的 `int32` 值）使用 `varint` 编码，因此字节长度有所不同。特别是，小的整数值（在这种情况下，包括标签在内）需要不到四个字节进行编码。

如果对 `numPairs` 程序进行了修改，以使两个 `NumPair` 字段的值小于 2048，且其编码为一或两个字节，则 Protobuf 编码将从 27MB 下降到 16MB，这正是原始数据的大小。下表总结了样本运行中的新编码大小。

| 编码 | 文件 | 字节大小 | Pbuf/其它 比例 |
| --- | --- | --- | --- |
| None | pairs.raw | 16MB | 100% |
| Protobuf | pairs.pbuf | 16MB | — |
| JSON | pairs.json | 77MB | 21% |
| XML | pairs.xml | 103MB | 15% |

*表 3. 编码 16MB 的小于 2048 的整数*

总之，修改后的 `numPairs` 程序的字段值小于 2048，可减少原始数据中每个四字节整数值的大小。但是 Protobuf 编码仍然需要标签，这些标签会在 Protobuf 消息中添加字节。Protobuf 编码确实会增加消息大小，但是如果要编码相对较小的整数值（无论是字段还是键），则可以通过 `varint` 因子来减少此开销。

对于包含混合类型的结构化数据（且整数值相对较小）的中等大小的消息，Protobuf 明显优于 XML 和 JSON 等选项。在其他情况下，数据可能不适合 Protobuf 编码。例如，如果两个应用程序需要共享大量文本记录或大整数值，则可以采用压缩而不是编码技术。

---

via: <https://opensource.com/article/19/10/protobuf-data-interchange>

作者：[Marty Kalin](https://opensource.com/users/mkalindepauledu) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


使用 apt-get 清理

>
> 大多数使用基于 Debian 的系统的人通常会使用 apt-get 来安装软件包和升级，但是我们多久才清理一次？让我们看下该工具本身的一些清理选项。
>
>
>

![](/data/attachment/album/201911/22/082025p39oeuufdote517e.jpg)

在基于 Debian 的系统上运行 `apt-get` 命令是很常规的。软件包的更新相当频繁，诸如 `apt-get update` 和 `apt-get upgrade` 之类的命令使此过程非常容易。另一方面，你多久使用一次 `apt-get clean`、`apt-get autoclean` 或 `apt-get autoremove`？

这些命令会在 `apt-get` 的安装操作后清理并删除仍在系统上但不再需要的文件，这通常是因为需要它们的程序已经卸载。

### apt-get clean

`apt-get clean` 命令清除遗留在 `/var/cache` 中的已取回的包文件的本地仓库。它清除的目录是 `/var/cache/apt/archives/` 和 `/var/cache/apt/archives/partial/`。它留在 `/var/cache/apt/archives` 中的唯一文件是 `lock` 文件和 `partial` 子目录。

在运行清理操作之前，目录中可能包含许多文件：

```
/var/cache/apt/archives/db5.3-util_5.3.28+dfsg1-0.6ubuntu1_amd64.deb
/var/cache/apt/archives/db-util_1%3a5.3.21~exp1ubuntu2_all.deb
/var/cache/apt/archives/lock
/var/cache/apt/archives/postfix_3.4.5-1ubuntu1_amd64.deb
/var/cache/apt/archives/sasl2-bin_2.1.27+dfsg-1build3_amd64.deb
```

之后，只会存在这些：

```
$ sudo ls -lR /var/cache/apt/archives
/var/cache/apt/archives:
total 4
-rw-r----- 1 root root    0 Jan  5  2018 lock
drwx------ 2 _apt root 4096 Nov 12 07:24 partial

/var/cache/apt/archives/partial:
total 0                 <== 空
```

`apt-get clean` 命令通常用于根据需要清除磁盘空间，一般作为定期计划维护的一部分。

### apt-get autoclean

`apt-get autoclean` 类似于 `apt-get clean`，它会清除已检索包文件的本地仓库，但它只会删除不会再下载且几乎无用的文件。它有助于防止缓存过大。

### apt-get autoremove

`apt-get autoremove` 将删除自动安装的软件包，因为某些其他软件包需要它们，但是在删除了其他软件包之后，而不再需要它们。有时会在升级时建议运行此命令。

```
The following packages were automatically installed and are no longer required:
  g++-8 gir1.2-mutter-4 libapache2-mod-php7.2 libcrystalhd3
  libdouble-conversion1 libgnome-desktop-3-17 libigdgmm5 libisl19 libllvm8
  liblouisutdml8 libmutter-4-0 libmysqlclient20 libpoppler85 libstdc++-8-dev
  libtagc0 libvpx5 libx265-165 php7.2 php7.2-cli php7.2-common php7.2-json
  php7.2-opcache php7.2-readline
Use 'sudo apt autoremove' to remove them.                       <==
```

要删除的软件包通常称为“未使用的依赖项”。实际上，一个好的做法是在卸载软件包后使用 `autoremove`，以确保不会留下不需要的文件。

---

via: <https://www.networkworld.com/article/3453032/cleaning-up-with-apt-get.html>

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


Zorin OS 15 Lite 发布：好看的轻量级 Linux

>
> Zorin OS 15 Lite 版刚刚发布。我们将向你展示此新版本的桌面体验，并向你重点展示其主要功能。
>
>
>

[Zorin OS](https://zorinos.com/) 是一款日益流行的 Linux 发行版。它基于 Ubuntu，因此，毫不奇怪，它也正是[适合初学者的最佳 Linux 发行版](https://itsfoss.com/best-linux-beginners/)之一。 类似 Windows 的界面是许多从 Windows 到 Linux 的迁移者偏爱它的主要原因之一。

Zorin OS 有两个主要变体：

* Zorin Core：它使用 GNOME 桌面，用于较新的机器。
* Zorin Lite：它使用轻量级的 [Xfce 桌面](https://www.xfce.org/)，可以用作[老旧的笔记本电脑和计算机的 Linux](https://itsfoss.com/lightweight-linux-beginners/)。

### Zorin OS 15 Lite：新特性

Zorin OS 15 Core 发布之后过了很久，Zorin OS 15 Lite 版终于出现了。你现在就可以使用免费的 Lite 版或付费的 Lite Ultimate 版。

我尝试了 Zorin OS 15 Lite Ultimate 版。在本文中，我将介绍此版本的详细信息以及在为你的计算机下载 Zorin OS 15 Lite 之前应了解的知识。

Zorin OS 15 Lite 与全面的 Zorin OS 15 版本基本差不多。你可以在我们的原来的报道中查看 [Zorin OS 15 的功能](/article-11058-1.html)。

此发行版重点关注资源，因此过去十年中任何类型的旧硬件配置都可以轻松地在其上运行。

![](/data/attachment/album/201911/23/093708axez5ntegftjgtkk.jpg)

在此版本中，它们依靠基于 Xfce 4.14 的轻量级桌面环境在低规格计算机上提供了最佳体验。除了 Xfce 桌面环境外，与使用 GNOME 的完整版本相比，它还做了一些底层更改。

#### Zorin OS 15 Lite 是针对 Windows 7 用户的

![](/data/attachment/album/201911/23/093709j1tr1tyh3rc2y7yn.jpg)

Zorin OS 15 Lite 主要针对 Windows 7 用户，因为对 Windows 7 的官方支持结束于今年 1 月。因此，如果你对 Windows 7 感到满意，可以尝试一下，切换到此版本应该是一种流畅的体验。

Zorin OS 15 Lite 允许你在 “Zorin 外观”设置中将布局切换为 macOS 风格/ Windows 风格的外观。

#### 32 位和 64 位支持

很高兴看到 Zorin OS 考虑到对 32 位/ 64 位 ISO 的支持，因为 Lite 版本是针对具有低规格硬件的用户的。

#### 默认启用 Flatpak 支持

![](/data/attachment/album/201911/23/093712ohdhyqaxc0hy3gco.png)

你可以使用软件中心利用 Flathub 来立即安装 Flatpak 软件包。如果你不确定该怎么做，请务必查看有关[使用 Flatpak](https://itsfoss.com/flatpak-guide/)的指南。

除此之外，你已经拥有 Snap 软件包支持。因此，通过软件中心安装任何内容应该更容易。

#### 用户界面的印象

![](/data/attachment/album/201911/23/093713p6b25dig02dd02ob.jpg)

老实说，默认的 Xfce 界面看起来很陈旧。有一些方法可以 [定制 Xfce](https://itsfoss.com/customize-xfce/)，但是 Zorin 也可以开箱即用。定制外观给人以良好印象。它看起来非常整洁，可以按预期工作。

#### 性能

![](/data/attachment/album/201911/23/093714dtewhhq4pe2ittjh.jpg)

即使我没有在超级老旧的系统上尝试过，但我也在老式硬盘上安装了它，它难以启动 Ubuntu 或类似发行版。

根据我的体验，我可以肯定该性能非常出色。感觉就好像我将其安装在 SSD 上。这显然是一件好事。如果你碰巧在超级老旧的系统上尝试使用它，可以在本文底部的评论部分中告诉我您的经验。

### Ultimate Lite 版和免费的 Lite 版有何区别？

![](/data/attachment/album/201911/23/093716g0ilrfiybqpjyry7.jpg)

没错，你可以免费下载 Zorin OS 15。

但是，有一个单独的“终极版”（Ultimate），其基本目的是用来支持该项目及其开发者。除此之外，它还捆绑了许多预安装的软件，作为计算机的“最终”软件包。

因此，如果你购买 Ultimate 版，则可以访问 Lite 版和完整版。

如果你不想为此付费，仍然可以根据需要选择免费版本（Core、Lite、Education）。你可以在它们的[下载页面](https://zorinos.com/download/)上了解更多信息。

### 如何下载 Zorin OS 15 Lite？

你可以转到其[官方下载网页](https://zorinos.com/download/)，然后向下滚动找到 Zorin OS 15 Lite 版。

你可以找到 32 位/ 64 位的 ISO，可以下载所需的 ISO。

* [Zorin OS 15 Lite](https://zorinos.com/download/)

安装 Zorin OS 与安装 Ubuntu 类似。

### 总结

虽然 Zorin OS 15 作为向 Windows / macOS 老手提供的 Linux 发行版已经是一个不错的产品，但新的 Lite 版本肯定会吸引更多的眼球。

您是否尝试过 Lite 版？在下面的评论中让我知道你的想法。

---

via: <https://itsfoss.com/zorin-os-lite/>

作者：[Ankush Das](https://itsfoss.com/author/ankush/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


使用 Flutter 开发简单的 Web 应用

Summary: 本文指导读者如何使用 Flutter 运行和部署第一个 Web 应用。

本文指导读者如何使用 Flutter 运行和部署第一个 Web 应用。

![](/data/attachment/album/201911/23/100430bf3mtnbrv7btftz2.png)

>
> 本文指导读者如何使用 Flutter 运行和部署第一个 Web 应用。
>
>
>

Flutter 在 Android 和 iOS 开发方面走了很长一段路之后，已经迈入了一个新的阶段，即 Web 开发。Google 发布了 Flutter 1.5，同时支持 Web 应用开发。

### 为 Web 开发配置 Flutter

为了使用 Web 包，输入命令 `flutter upgrade` 更新到 Flutter 1.5.4。

* 打开终端
* 输入 `flutter upgrade`
* 输入 `flutter –version` 检查版本

![图 1: 升级 Flutter 到最新版](/data/attachment/album/201911/23/100433cssnpgpbyl36yl2k.jpg)

也可以将 Android Studio 3.0 或更高版本用于 Flutter Web 开发，但在本教程中，我们使用 Visual Studio Code。

### 使用 Flutter Web 创建新项目

打开 Visual Studio Code，然后按 `Shift+Ctrl+P` 开始一个新项目。输入 `flutter` 并选择 “New Web Project”。

![图 2：在 VSC 中开始一个新的 Flatter 项目](/data/attachment/album/201911/23/100434qb60zzjwbp86tp1b.jpg)

现在，为项目命名。我将其命名为 `open_source_for_you`。

![图 3: 给项目命名](/data/attachment/album/201911/23/100436yfc1jpoc2fvdw4pc.jpg)

在 VSC 中打开终端窗口，然后输入以下命令：

```
flutter packages pub global activate webdev
flutter packages upgrade
```

现在，使用以下命令在 localhost 上运行网站，IP 地址是 127.0.0.1。

```
flutter packages pub global run webdev serve
```

打开任何浏览器，然后输入 `http://127.0.0.1:8080/`。

![图 4：运行于 8080 端口的 Flutter 演示应用](/data/attachment/album/201911/23/100436umcjtypo5w77owzt.jpg)

在项目目录中有个 Web 文件夹，其中包含了 `index.html`。`dart` 文件被编译成 JavaScript 文件，并使用以下代码包含在 HTML 文件中：

```
<script defer src="main.dart.js" type="application/javascript"></script>
```

### 编码和修改演示页面

让我们创建一个简单的应用，它会在网页上打印 “Welcome to OSFY”。

现在打开 Dart 文件，它位于 `lib` 文件夹 `main.dart`（默认名）中（参见图 5）。

![图 5：main.dart 文件的位置](/data/attachment/album/201911/23/100438mippviev39v3vvic.jpg)

现在，我们可以在 `MaterialApp` 的属性中删除调试标记，如下所示：

```
debugShowCheckedModeBanner: false
```

现在，向 Dart 中添加更多内容与用 Dart 编写 Flutter 很类似。为此，我们可以声明一个名为 `MyClass` 的类，它继承了 `StatelessWidget`。

我们使用 `Center` 部件将元素定位到中心。我们还可以添加 `Padding` 部件来添加填充。使用以下代码获得图 5 所示的输出。使用刷新按钮查看更改。

```
class MyClass extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.all(20.0),
              child: Text(
                'Welcome to OSFY',
                style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
```

![图 6：MyClass 的输出](/data/attachment/album/201911/23/100439c9pkhk2hp2pg8zpw.jpg)

让我们从互联网中添加一张图片，我已经从一个杂志网站选择了一张 “Open Source for You” 徽标。我们使用 `Image.network`。

```
Image.network(
  'https://opensourceforu.com/wp-content/uploads/2014/03/OSFY-Logo.jpg',
  height: 100,
  width: 150
),
```

最终输出如图 7 所示。

![图 7：最终输出](/data/attachment/album/201911/23/100439nh9b9ldydxl970u2.jpg)

---

via: <https://opensourceforu.com/2019/11/developing-a-simple-web-application-using/>

作者：[Jis Joe Mathew](https://opensourceforu.com/author/jis-joe/) 选题：[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/) 荣誉推出


容器如何工作：OverlayFS

Summary: 你可以运行一条 mount 咒语，而实际上并没有做任何与容器相关的其他事情，看看叠加层是如何工作的！

删除文件后的验证部分似乎有错误，另外为何挂载 work 目录没有解释

今天早上，我为未来潜在容器[杂志](https://wizardzines.com)画了一幅 OverlayFS 的漫画，我对这个主题感到兴奋，想写一篇关于它的博客来提供更多详细信息。

![](/data/attachment/album/201911/23/102537d50a06jizddcc0c8.jpg)

### 容器镜像很大

容器镜像可能会很大（尽管有些很小，例如 [alpine linux 才 2.5MB](https://hub.docker.com/_/alpine?tab=tags)）。Ubuntu 16.04 约为 27 MB，[Anaconda Python 发行版为 800MB 至 1.5GB](https://hub.docker.com/r/continuumio/anaconda3/tags)。

你以镜像启动的每个容器都是原始空白状态，仿佛它只是为使用容器而复制的一份镜像拷贝一样。但是对于大的容器镜像，像 800MB 的 Anaconda 镜像，复制一份拷贝既浪费磁盘空间也很慢。因此 Docker 不会复制，而是采用**叠加**。

### 叠加如何工作

OverlayFS，也被称为 **联合文件系统**或 **联合挂载**，它可让你使用 2 个目录挂载文件系统：“下层”目录和“上层”目录。

基本上：

* 文件系统的**下层**目录是只读的
* 文件系统的**上层**目录可以读写

当进程“读取”文件时，OverlayFS 文件系统驱动将在上层目录中查找并从该目录中读取文件（如果存在）。否则，它将在下层目录中查找。

当进程“写入”文件时，OverlayFS 会将其写入上层目录。

### 让我们使用 mount 制造一个叠加层！

这有点抽象，所以让我们制作一个 OverlayFS 并尝试一下！这将只包含一些文件：我将创建上、下层目录，以及用来挂载合并的文件系统的 `merged` 目录：

```
$ mkdir upper lower merged work
$ echo "I'm from lower!" > lower/in_lower.txt
$ echo "I'm from upper!" > upper/in_upper.txt
$ # `in_both` is in both directories
$ echo "I'm from lower!" > lower/in_both.txt
$ echo "I'm from upper!" > upper/in_both.txt
```

合并上层目录和下层目录非常容易：我们可以通过 `mount` 来完成！

```
$ sudo mount -t overlay overlay
    -o lowerdir=/home/bork/test/lower,upperdir=/home/bork/test/upper,workdir=/home/bork/test/work
    /home/bork/test/merged
```

在执行此操作时，我不断收到一条非常烦人的错误消息，内容为：`mount: /home/bork/test/merged: special device overlay does not exist.`。这条消息是错误的，实际上只是意味着我指定的一个目录缺失（我写成了 `~/test/merged`，但它没有被展开）。

让我们尝试从 OverlayFS 中读取其中一个文件！文件 `in_both.txt` 同时存在于 `lower/` 和 `upper/` 中，因此应从 `upper/` 目录中读取该文件。

```
$ cat merged/in_both.txt
"I'm from upper!
```

可以成功！

目录的内容就是我们所期望的：

```
find lower/ upper/ merged/
lower/
lower/in_lower.txt
lower/in_both.txt
upper/
upper/in_upper.txt
upper/in_both.txt
merged/
merged/in_lower.txt
merged/in_both.txt
merged/in_upper.txt
```

### 创建新文件时会发生什么？

```
$ echo 'new file' > merged/new_file
$ ls -l */new_file
-rw-r--r-- 1 bork bork 9 Nov 18 14:24 merged/new_file
-rw-r--r-- 1 bork bork 9 Nov 18 14:24 upper/new_file
```

这是有作用的，新文件会在 `upper` 目录创建。

### 删除文件时会发生什么？

读写似乎很简单。但是删除会发生什么？开始试试！

```
$ rm merged/in_both.txt
```

发生了什么？让我们用 `ls` 看下：

```
ls -l upper/in_both.txt  lower/lower1.txt  merged/lower1.txt
ls: cannot access 'merged/in_both.txt': No such file or directory
-rw-r--r-- 1 bork bork    6 Nov 18 14:09 lower/in_both.txt
c--------- 1 root root 0, 0 Nov 18 14:19 upper/in_both.txt
```

所以：

* `in_both.txt` 仍在 `lower` 目录中，并且保持不变
* 它不在 `merged` 目录中。到目前为止，这就是我们所期望的。
* 但是在 `upper` 中发生的事情有点奇怪：有一个名为 `upper/in_both.txt` 的文件，但是它是字符设备？我想这就是 overlayfs 驱动表示删除的文件的方式。

如果我们尝试复制这个奇怪的字符设备文件，会发生什么？

```
$ sudo cp upper/in_both.txt upper/in_lower.txt
cp: cannot open 'upper/in_both.txt' for reading: No such device or address
```

好吧，这似乎很合理，复制这个奇怪的删除信号文件并没有任何意义。

### 你可以挂载多个“下层”目录

Docker 镜像通常由 25 个“层”组成。OverlayFS 支持具有多个下层目录，因此你可以运行：

```
mount -t overlay overlay
      -o lowerdir:/dir1:/dir2:/dir3:...:/dir25,upperdir=...
```

因此，我假设这是有多个 Docker 层的容器的工作方式，它只是将每个层解压缩到一个单独的目录中，然后要求 OverlayFS 将它们全部合并在一起，并使用一个空的上层目录，容器将对其进行更改。

### Docker 也可以使用 btrfs 快照

现在，我使用的是 ext4，而 Docker 使用 OverlayFS 快照来运行容器。但是我曾经用过 btrfs，接着 Docker 将改为使用 btrfs 的写时复制快照。（这是 Docker 何时使用哪种[存储驱动](https://docs.docker.com/storage/storagedriver/select-storage-driver/)的列表）

以这种方式使用 btrfs 快照会产生一些有趣的结果：去年某个时候，我在笔记本上运行了数百个临时的 Docker 容器，这导致我用尽了 btrfs 元数据空间（像[这个人](https://www.reddit.com/r/archlinux/comments/5jrmfe/btrfs_metadata_and_docker/)一样）。这真的很令人困惑，因为我以前从未听说过 btrfs 元数据，而且弄清楚如何清理文件系统以便再次运行 Docker 容器非常棘手。（[这个 docker github 上的提案](https://github.com/moby/moby/issues/27653)描述了 Docker 和 btrfs 的类似问题）

### 以简单的方式尝试容器功能很有趣！

我认为容器通常看起来像是在做“复杂的”事情，我认为将它们分解成这样很有趣。你可以运行一条 `mount` 咒语，而实际上并没有做任何与容器相关的其他事情，看看叠加层是如何工作的！

---

via: [https://jvns.ca/blog/2019/11/18/how-containers-work–overlayfs/](https://jvns.ca/blog/2019/11/18/how-containers-work--overlayfs/)

作者：[Julia Evans](https://jvns.ca/) 选题：[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 更快吗？

![](/data/attachment/album/201911/23/235729l71755he6e4mpvkq.jpg)

>
> Oracle Linux 内核开发人员 Steve Sistare 参与了这场有关内核调度程序改进的讨论。
>
>
>

### 通过可扩展的任务窃取进行负载平衡

Linux 任务调度程序通过将唤醒的任务推送到空闲的 CPU，以及在 CPU 空闲时从繁忙的 CPU 中拉取任务来平衡整个系统的负载。在大型系统上的推送侧和拉取侧，有效的伸缩都是挑战。对于拉取，调度程序搜索连续的更大范围中的所有 CPU，直到找到过载的 CPU，然后从最繁忙的组中拉取任务。这代价非常昂贵，在大型系统上要花费 10 到 100 微秒，因此搜索时间受到平均空闲时间的限制，并且某些范围不会被搜索。并非总能达到平衡，而且闲置的 CPU 依旧闲置。

我实现了一种备用机制，该机制在 `idle_balance()` 中的现有搜索中自身受限并且没有找到之后被调用。我维护了一个过载的 CPU 的位图，当可运行的 CFS 任务计数超过 1 时，CPU 会设置该位。这个位图是稀疏的，每个高速缓存线的有效位数量有限。当许多线程同时设置、清除和访问元素时，这可以减少缓存争用。每个末级缓存都有一个位图。当 CPU 空闲时，它将搜索该位图以查找第一个具有可迁移任务的过载 CPU，然后将其窃取。这种简单的窃取会比单独的 `idle_balance()` 产生更高的 CPU 利用率，因为该搜索的成本很便宜，花费 1 到 2 微秒，因此每次 CPU 即将空闲时都可以调用它。窃取不会减轻全局最繁忙的队列的负担，但是它比根本不执行任何操作要好得多。

### 结果

偷窃仅在调度程序代码中占用少量 CPU 开销即可提高利用率。在以下实验中，以不同数量的组（每个组 40 个任务）运行 hackbench，并对每次运行结果显示 `/proc/schedstat` 中的增量（按 CPU 平均），并增加了这些非标准的统计信息：

* `％find`：在旧函数和新函数中花费的时间百分比，这些函数用于搜索空闲的 CPU 和任务以窃取并设置过载的 CPU 位图。
* `steal`：任务从另一个 CPU 窃取的次数。经过的时间增加了 8％ 到 36％，最多增加了 0.4％ 的发现时间。

![load balancing](/data/attachment/album/201911/23/235819qjcu3i3c9av7147u.png "load balancing")

如下图的绿色曲线所示，新内核的 CPU 繁忙利用率接近 100％，作为比较的基线内核是橙色曲线：  ![](/data/attachment/album/201911/23/235822p3d67ddozped52db.png)

根据负载的不同，窃取可将 Oracle 数据库 OLTP 性能提高多达 9％，并且我们已经看到 MySQL、Pgsql、gcc、Java 和网络方面有了一些不错的改进。通常，窃取对上下文切换率高的工作负载最有帮助。

### 代码

截至撰写本文时，这项工作尚未完成，但最新的修补程序系列位于 <https://lkml.org/lkml/2018/12/6/1253>。如果你的内核是使用 `CONFIG_SCHED_DEBUG=y` 构建的，则可以使用以下命令验证其是否包含窃取优化：

```
# grep -q STEAL /sys/kernel/debug/sched_features && echo Yes
Yes
```

如果要尝试使用，请注意，对于具有 2 个以上 NUMA 节点的系统，禁用了窃取功能，因为 hackbench 在此类系统上发生了回归，正如我在 <https://lkml.org/lkml/2018/12/6/1250> 中解释的那样。但是，我怀疑这种影响是特定于 hackbench 的，并且窃取将有助于多节点系统上的其他工作负载。要尝试使用它，请用内核参数 `sched_steal_node_limit=8`（或更大）重新启动。

### 进一步工作

在将基本盗用算法推向上游之后，我正在考虑以下增强功能：

* 如果在末级缓存中进行窃取找不到候选者，在 LLC 和 NUMA 节点之间进行窃取。
* 维护稀疏位图以标识 RT 调度类中的偷窃候选者。当前 `pull_rt_task()` 搜索所有运行队列。
* 从 `idle_balance()` 中删除核心和套接字级别，因为窃取会处理这些级别。当支持跨 LLC 窃取时，完全删除 `idle_balance()`。
* 维护位图以标识空闲核心和空闲 CPU，以实现推平衡。

这篇文章最初发布于 [Oracle Developers Blog](https://blogs.oracle.com/linux/can-better-task-stealing-make-linux-faster)。

---

via: <https://www.linux.com/blog/can-better-task-stealing-make-linux-faster>

作者：[Oracle](https://www.linux.com/author/oracle/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


你需要知道什么才能成为系统管理员？

Summary: 通过获得这些起码的能力，开始你的系统管理员职业。

通过获得这些起码的能力，开始你的系统管理员职业。

>
> 通过获得这些起码的能力，开始你的系统管理员职业。
>
>
>

![](/data/attachment/album/201911/24/103900w5m09jyyyeyrnovu.jpg)

昔日的系统管理员整天都在调教用户和摆弄服务器，一天的时间都奔波在几百米长的电缆之间。随着云计算、容器和虚拟机的复杂性的增加，而今依然如此。

以外行人来看，很难准确确定系统管理员的确切职能，因为他们在许多地方都扮演着一个不起眼的角色。没人能在培训中知道自己工作中所需要的一切知识，但是每个人其实都需要一个坚实的基础。如果你想走上系统管理的道路，那么这是你个人自学或在正式培训中应重点关注的内容。

### Bash

当你学习 Bash Shell 时，你学习的不仅是 Bash Shell，你学习的也是 Linux、BSD、MacOS 甚至Windows（在适当条件下）的通用界面。你将了解语法的重要性，因此可以快速适应思科路由器的命令行或微软 PowerShell 等系统，最终你甚至可以学习更强大的语言，如 Python 或 Go。而且，你还会开始进行程序性思考，以便可以分析复杂的问题并将其分解为单个组件，这很关键，因为这就是系统（例如互联网、组织的内部网、Web 服务器、备份解决方案）是如何设计的。

不止于此，还有更多。

由于最近 DevOps 和[容器](https://opensource.com/article/19/6/kubernetes-dump-truck)的发展趋势，了解 Bash shell 变得尤为重要。你的系统管理员职业可能会将你带入一个视基础设施为代码的世界，这通常意味着你必须了解脚本编写的基础知识、[基于 YAML](https://www.redhat.com/sysadmin/yaml-tips)配置的结构，以及如何与[容器](https://opensource.com/resources/what-are-linux-containers)（运行在[沙盒文件](https://opensource.com/article/18/11/behind-scenes-linux-containers)内部的微型 Linux 系统）[交互](https://opensource.com/article/19/6/how-ssh-running-container)。你会知道 Bash 是高效管理激动人心的开源技术的门户，因此请进入 [Bash](https://opensource.com/article/18/7/admin-guide-bash) 世界吧。

#### 资源

有很多方法可以在 Bash shell 中进行练习。

尝试一下[便携式 Linux 发行版](https://opensource.com/article/19/6/linux-distros-to-try)。你无需安装 Linux 即可使用 Linux，因此，请拿一块闲置的 U 盘，花个晚上或周末的空闲时光，来适应基于文本的界面。

这里有[几篇很棒的](https://www.redhat.com/sysadmin/managing-files-linux-terminal) [Bash 文章](https://opensource.com/tags/bash)。

要注意的是 Bash 练习的关键在于要练习，你必须有要做的练习才行。而且，在你知道如何使用 Bash 之前，你可能不知道该练习什么。如果是这样，请去 Over The Wire 玩一下 [Bandit](http://overthewire.org/wargames/bandit) 游戏。这是一款针对绝对初学者的游戏，具有 34 个级别的交互式基本技巧，可让你熟悉 Linux shell。

### Web 服务器设置

一旦你习惯了 Bash，你应该尝试设置一个 Web 服务器。并不是所有的系统管理员都会四处设置 Web 服务器甚至维护 Web 服务器，但是你在安装和启动 HTTP 守护程序、配置 Apache 或 Nginx，设置[正确权限](https://opensource.com/article/19/6/understanding-linux-permissions)和[配置防火墙](https://www.redhat.com/sysadmin/secure-linux-network-firewall-cmd)时所掌握的技能是你每天都需要使用的技能。经过一些努力，你可能会开始注意到自己的某些工作模式。在尝试管理可用于生产环境的软件和硬件之前，你可能认为某些概念是理所当然的，而你在成为新手的管理员角色时，将不再受到它们的影响。起初这可能会令人沮丧，因为每个人都喜欢在自己做好所做的事情，但这实际上是一件好事。让自己接触新技能，那就是你学习的方式。

此外，你在第一步中付出的努力越多，最终当你在默认的 index.html 上看到胜利的“it works!”就越甜蜜！

#### 资源

David Both 撰写了有关 [Apache Web 服务器](https://opensource.com/article/18/2/how-configure-apache-web-server)配置的出色文章。值得一提的是，请逐步阅读他的后续文章，其中介绍了如何在一台计算机上[托管多个站点](https://opensource.com/article/18/3/configuring-multiple-web-sites-apache)。

### DHCP

动态主机配置协议（DHCP）是为网络上的设备分配 IP 地址的系统。在家里，ISP（互联网服务提供商）支持的调制解调器或路由器可能内置了 DHCP 服务器，因此可能不在你的权限范围内。如果你曾经登录家用路由器来调整 IP 地址范围或为某些网络设备设置了静态地址，那么你至少对该概念有所了解。你可能会将其理解为对网络上的设备分配了一种 IP 地址形式的电话号码，并且你可能会意识到计算机之间通过广播发送到特定 IP 地址的消息彼此进行通信。消息标头由路径上的路由器读取，每个消息标头都将消息定向到路径上的第二个逻辑路由器，以达到其最终目标。

即使你了解了这些概念，要从对 DHCP 的基本了解再进一步是架设 DHCP 服务器。安装和配置自己的 DHCP 服务器可能会导致家庭网络中的 DHCP 冲突（如果可以的话，请尽量避免这样做，因为它肯定会干掉你的网络，直到解决为止），要控制地址的分配、创建子网，并监控连接和租赁时间。

更重要的是，设置 DHCP 并尝试不同的配置有助于你了解网络之间的关系。你会了解网络如何在数据传输中表示“分区”，以及必须采取哪些步骤才能将信息从一个网络传递到另一个。这对于系统管理员来说至关重要，因为网络肯定是工作中最重要的方面之一。

#### 资源

在运行自己的 DHCP 服务器之前，请确保家庭路由器（如果有）中的 DHCP 服务器处于非活动状态。一旦启动并运行了 DHCP 服务器，请阅读 Archit Modi 的[网络命令指南](https://opensource.com/article/18/7/sysadmin-guide-networking-commands)，以获取有关如何探索网络的提示。

### 网络电缆

这听起来很普通，但是熟悉网络电缆的工作方式不仅使你的周末变得非常有趣，而且还使你对数据是如何通过缆线的得到了全新的了解。最好的学习方法是去当地的业余爱好商店并购买五类线剥线钳和压线钳以及一些五类线水晶头。然后回家，拿一根备用的以太网电缆，切断水晶头，花一些时间重新制作网线接头，将电缆重新投入使用。

解决了这个难题后，请再做一次，这次创建一条有效的[交叉电缆](https://en.wikipedia.org/wiki/Ethernet_crossover_cable)。

你现在应该还在沉迷于有关电缆管理。如果你有些强迫症，喜欢沿着地板线或桌子的边缘整齐地排列电缆，或者将电缆绑在一起以保持它们的整齐有序，那么就可以使自己免受永久混乱的电缆困扰。你一开始可能不会理解这样做的必要性，但是当你第一次走进服务器机房时，你会马上知道原因。

### Ansible

[Ansible](https://opensource.com/sitewide-search?search_api_views_fulltext=ansible) 是配置管理软件，它在系统管理员和 DevOps 之间架起了一座桥梁。系统管理员使用 Ansible 来配置全新安装的操作系统并在计算机上维护特定的状态。DevOps 使用 Ansible 减少了在工具上花费的时间和精力，从而在开发上可以花费更多的时间和精力。作为系统管理员培训的一部分，你应该学习 Ansible，并着眼于 DevOps 实践，因为 DevOps 现在开创的大多数功能将最终成为将来系统管理中工作流的一部分。

Ansible 的好处是你可以立即开始使用它。它是跨平台的，并且可以向上和向下缩放。对于单用户计算机， Ansible 可能是小题大做，但是话又说回来，Ansible 可能会改变你启动虚拟机的方式，或者可以帮助你同步家庭或[家庭实验室](https://opensource.com/article/19/6/create-centos-homelab-hour)中所有计算机的状态。

#### 资源

阅读 Jay LaCroix 的[如何使用 Ansible 管理工作站配置](https://opensource.com/article/18/3/manage-workstation-ansible)中的典型介绍，以轻松地在日常之中开始使用 Ansible。

### 破坏

由于用户的错误、软件的错误、管理员（就是你！）的错误以及许多其他因素，计算机上会出现问题。无法预测将要失败的原因，因此你的个人系统管理员培训制度的一部分应该是破坏你设置的系统，直到它们失败为止。你自己的实验室基础设施越是脆弱，发现弱点的可能性就越大。而且，你越是经常修复这些弱点，你对解决问题的能力就越有信心。

除了严格设置所有常见的软件和硬件之外，作为系统管理员的主要工作是查找解决方案。有时候，你可能会遇到职位描述之外的问题，甚至可能无法解决，但这完全取决于你的解决方法。

现在，你越多地折腾并努力加以解决，则以系统管理员的身份进行的准备就越充分。

你是系统管理员吗？你是否希望自己为更好的任务做好准备？在下面的评论中写下它们！

---

via: <https://opensource.com/article/19/7/be-a-sysadmin>

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

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


Linux 平台上的写作者必备工具

![](/data/attachment/album/201911/25/000129eee2zydelz22vj9h.jpg)

我从事作家已有 20 多年了。我撰写了数千篇有关各种技术主题的文章和指南，并撰写了 40 多本小说。因此，书面文字不仅对我很重要，还很熟悉，成为了我的第二种自然交流的方式。在过去的二十年中（而且还在继续），我几乎都是在 Linux 平台上完成的所有工作。我必须承认，在早期，这并不总是那么容易。格式并不总是与编辑器所需要的相吻合，在某些情况下，开源平台根本没有完成工作所需的必要工具。

那时已经过去，现在已经不同了。

Linux 演进和基于 Web 的工具的相得益彰使得它可以让任何写作者都能在 Linux 上完成工作（并且做得很好）。但是你需要什么工具？你可能会惊讶地发现，在某些情况下，使用 100％ 开源的工具无法有效完成这项工作。不过即使如此，工作总是可以完成的。让我们来看看我作为技术作家和小说作者一直使用的工具。我将通过小说和非小说类的写作过程来概述这一点（因为过程不同，需要特定的工具）。

对认真的 Linux 硬核用户预先做个预警。很久以前，我就放弃了使用 LaTeX 和 DocBook 之类的工具进行写作。为什么？因为对我而言，重点必须放在内容上，而不是过程上。当你面临最后期限时，必须以效率为先。

### 非小说类

我们将从非虚构写作入手，因为这是两者中较简单的过程。为了编写技术指南，我与不同的编辑人员合作，并且在某些情况下，必须将内容复制/粘贴到 CMS 中。但是就像我的小说一样，整个过程总是从 Google 云端硬盘开始。在这一点上，许多开源纯粹主义者会转身走开。不用担心，你始终可以选择将所有文件保存在本地，也可以使用更开放友好的云服务（例如 [Zoho](https://www.zoho.com/) 或 [nextCloud](https://nextcloud.com/)）。

为什么要从云端开始？多年来，我发现我需要能够随时随地访问那些内容。最简单的解决方案是迁移到云上。我对丢失工作成果这件事也很偏执。为此，我使用了 [Insync](https://www.insynchq.com) 之类的工具来使我的 Google 云端硬盘与桌面保持同步。有了桌面同步功能，我知道我的工作成果总是有备份，以防万一 Google 云端硬盘出了问题。

对于那些我必须与之一起将内容输入到内容管理系统（CMS）的客户，该过程到此结束。我可以直接从 Google 文档复制/粘贴到 CMS 中，并完成此操作。当然，对于技术内容，总是涉及到屏幕截图。为此，我使用 [Gimp](https://www.gimp.org/)，它使得截取屏幕截图变得简单：

![screenshot with Gimp](/data/attachment/album/201911/25/000230wf0ccrr4frr4ynkw.jpg "screenshot with Gimp")

*图 1：使用 Gimp 截屏。*

1. 打开 Gimp。
2. 单击“文件>创建>屏幕快照”。
3. 选择单个窗口、整个屏幕或要抓取的区域（图 1）。
4. 单击“抓取”。

我的大多数客户倾向于使用 Google 文档，因为我可以共享文件夹，以便他们可以可靠地访问该内容。我有一些无法使用 Google 文档的客户，因此我必须将文件下载为可以使用的格式。为此，我要做的是下载 .odt 格式，以 [LibreOffice](https://www.libreoffice.org/) 打开文档（图 2），根据需要设置格式，保存为客户所需的格式，然后发送文档。

![](/data/attachment/album/201911/25/000429g88zs828njansexz.jpg)

*图 2：在 LibreOffice 中打开我下载的 Google 文档。*

非小说类作品这样就行了。

### 小说类

这里会稍微变得有点复杂。开始的步骤是相同的，因为我总是在 Google 文档中写小说的每个初稿。完成后，我将文件下载到 Linux 桌面，在 LibreOffice 中打开文件，根据需要设置格式，然后另存为编辑器支持的文件类型（不幸的是，这意味着是 .docx）。

该过程的下一步变得有些琐碎。我的编辑更喜欢使用注释来跟踪更改（因为这使我们俩阅读文档和做出更改一样容易）。因此，一个 60k 的 word 文档可以包含成百上千的注释，这会使 LibreOffice 慢的像爬一样。从前，你可以增加用于文档的内存，但是从 LibreOffice 6 开始，这不再可行。这意味着任何较大的、像小说一样长的、带有大量注释的文档都将无法使用。因此，我不得不采取一些极端的措施，使用 [WPS Office](https://www.wps.com/en-US/)（图 3）。尽管这不是开源解决方案，但 WPS Office 在文档中包含大量注释的情况下做得很好，因此无需处理 LibreOffice 所带来的麻烦（当处理带有数百个注释的大型文件时）。

![](/data/attachment/album/201911/25/000443npovj3pjvtnwintw.jpg)

*图 3：WPS 可以轻松处理大量注释。*

一旦我和我的编辑完成了对书的编辑（所有评论都已删除），我就可以在 LibreOffice 中打开文件进行最终格式化。格式化完成后，我将文件保存为 .html 格式，然后以 [Calibre](https://calibre-ebook.com/) 打开文件以将文件导出为 .mobi 和 .epub 格式。

对于希望在 Amazon、Barnes＆Noble、Smashwords 或其他平台上出版的任何人，Calibre 都是必备工具。Caliber 比其他类似解决方案更好地方是，它使你可以直接编辑 .epub 文件（图 4）。对于 Smashword 来说，这是绝对必要的（因为导出过程将添加 Smashwords 转换工具上不接受的元素）。

![](/data/attachment/album/201911/25/000455yaqwldsythvnnjwh.jpg)

*图 4：直接在 Calibre 中编辑 epub 文件。*

写作过程结束后（或有时在等待编辑完成一校时），我将开始为书制作封面。该任务完全在 Gimp 中处理（图 5）。

![](/data/attachment/album/201911/25/000511u0fucfv6hxd6996s.jpg)

*图 5：在 Gimp 中创建 POTUS 的封面。*

这样就完成了在 Linux 平台上创建小说的过程。由于文档的篇幅以及某些编辑人员的工作方式，与创建非小说类的过程相比，它可能会变得有些复杂，但这远没有挑战性。实际上，在 Linux 上创建小说与其他平台一样简单（并且更可靠）。

### 希望这可以帮助你

我希望这可以帮助有抱负的作家有信心在 Linux 平台上进行写作。还有许多其他工具可供使用，但是多年来我在这里列出的工具很好地服务了我。而且，尽管我确实使用了几个专有的工具，但只要它们在 Linux 上都能正常运行，我觉得是可以的。

---

via: <https://www.linux.com/learn/2018/11/must-have-tools-writers-linux-platform>

作者：[Jack Wallen](https://www.linux.com/users/jlwallen) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


awk 中的字段、记录和变量

Summary: 这个系列的第二篇，我们会学习字段，记录和一些非常有用的 Awk 变量。

这个系列的第二篇，我们会学习字段，记录和一些非常有用的 Awk 变量。

>
> 这个系列的第二篇，我们会学习字段，记录和一些非常有用的 Awk 变量。
>
>
>

![](/data/attachment/album/201911/25/090333m34qx395vwtxx5vx.jpg)

Awk 有好几个变种：最早的 `awk`，是 1977 年 AT&T 贝尔实验室所创。它还有一些重构版本，例如 `mawk`、`nawk`。在大多数 Linux 发行版中能见到的，是 GNU awk，也叫 `gawk`。在大多数 Linux 发行版中，`awk` 和 `gawk` 都是指向 GNU awk 的软链接。输入 `awk`，调用的是同一个命令。[GNU awk 用户手册](https://www.gnu.org/software/gawk/manual/html_node/History.html#History)中，能看到 `awk` 和 `gawk` 的全部历史。

这一系列的[第一篇文章](/article-11543-1.html) 介绍了 `awk` 命令的基本格式：

```
$ awk [选项] '模式 {动作}' 输入文件
```

`awk` 是一个命令，后面要接选项 （比如用 `-F` 来定义字段分隔符）。想让 `awk` 执行的部分需要写在两个单引号之间，至少在终端中需要这么做。在 `awk` 命令中，为了进一步强调你想要执行的部分，可以用 `-e` 选项来突出显示（但这不是必须的）：

```
$ awk -F, -e '{print $2;}' colours.txt
yellow
blue
green
[...]
```

### 记录和字段

`awk` 将输入数据视为一系列*记录*，通常是按行分割的。换句话说，`awk` 将文本中的每一行视作一个记录。每一记录包含多个*字段*。一个字段由*字段分隔符*分隔开来，字段是记录的一部分。

默认情况下，`awk` 将各种空白符，如空格、制表符、换行符等视为分隔符。值得注意的是，在 `awk` 中，多个*空格*将被视为一个分隔符。所以下面这行文本有两个字段：

```
raspberry red
```

这行也是：

```
tuxedo                  black
```

其他分隔符，在程序中不是这么处理的。假设字段分隔符是逗号，如下所示的记录，就有三个字段。其中一个字段可能会是 0 个字节（假设这一字段中不包含隐藏字符）

```
a,,b
```

### awk 程序

`awk` 命令的*程序部分*是由一系列规则组成的。通常来说，程序中每个规则占一行（尽管这不是必须的）。每个规则由一个模式，或一个或多个动作组成：

```
模式 { 动作 }
```

在一个规则中，你可以通过定义模式，来确定动作是否会在记录中执行。模式可以是简单的比较条件、正则表达式，甚至两者结合等等。

这个例子中，程序*只会*显示包含单词 “raspberry” 的记录：

```
$ awk '/raspberry/ { print $0 }' colours.txt
raspberry red 99
```

如果没有文本符合模式，该动作将会应用到所有记录上。

并且，在一条规则只包含模式时，相当于对整个记录执行 `{ print }`，全部打印出来。

Awk 程序本质上是*数据驱动*的，命令执行结果取决于数据。所以，与其他编程语言中的程序相比，它还是有些区别的。

### NF 变量

每个字段都有指定变量，但针对字段和记录，也存在一些特殊变量。`NF` 变量，能存储 `awk` 在当前记录中找到的字段数量。其内容可在屏幕上显示，也可用于测试。下面例子中的数据，来自上篇文章[文本](/article-11543-1.html)：

```
$ awk '{ print $0 " (" NF ")" }' colours.txt
name       color  amount (3)
apple      red    4 (3)
banana     yellow 6 (3)
[...]
```

`awk` 的 `print` 函数会接受一系列参数（可以是变量或者字符串），并将它们拼接起来。这就是为什么在这个例子里，每行结尾处，`awk` 会以一个被括号括起来的整数表示字段数量。

### NR 变量

另外，除了统计每个记录中的字段数，`awk` 也统计输入记录数。记录数被存储在变量 `NR` 中，它的使用方法和其他变量没有任何区别。例如，为了在每一行开头显示行号：

```
$ awk '{ print NR ": " $0 }' colours.txt
1: name       color  amount
2: apple      red    4
3: banana     yellow 6
4: raspberry  red    3
5: grape      purple 10
[...]
```

注意，写这个命令时可以不在 `print` 后的多个参数间添加空格，尽管这样会降低可读性：

```
$ awk '{print NR": "$0}' colours.txt
```

### printf() 函数

为了让输出结果时格式更灵活，你可以使用 `awk` 的 `printf()` 函数。 它与 C、Lua、Bash 和其他语言中的 `printf` 相类似。它也接受以逗号分隔的*格式*参数。参数列表需要写在括号里。

```
$ printf 格式, 项目1, 项目2, ...
```

格式这一参数（也叫*格式符*）定义了其他参数如何显示。这一功能是用*格式修饰符*实现的。`%s` 输出字符，`%d` 输出十进制数字。下面的 `printf` 语句，会在括号内显示字段数量：

```
$ awk 'printf "%s (%d)\n",$0,NF}' colours.txt
name       color  amount (3)
raspberry  red    4 (3)
banana     yellow 6 (3)
[...]
```

在这个例子里，`%s (%d)` 确定了每一行的输出格式，`$0,NF` 定义了插入 `%s` 和 `%d` 位置的数据。注意，和 `print` 函数不同，在没有明确指令时，输出不会转到下一行。出现转义字符 `\n` 时才会换行。

### Awk 脚本编程

这篇文章中出现的所有 `awk` 代码，都在 Bash 终端中执行过。面对更复杂的程序，将命令放在文件（*脚本*）中会更容易。`-f FILE` 选项（不要和 `-F` 弄混了，那个选项用于字段分隔符），可用于指明包含可执行程序的文件。

举个例子，下面是一个简单的 awk 脚本。创建一个名为 `example1.awk` 的文件，包含以下内容：

```
/^a/ {print "A: " $0}
/^b/ {print "B: " $0}
```

如果一个文件包含 `awk` 程序，那么在给文件命名时，最好写上 `.awk` 的扩展名。 这样命名不是强制的，但这么做，会给文件管理器、编辑器（和你）一个关于文件内容的很有用的提示。

执行这一脚本：

```
$ awk -f example1.awk colours.txt
A: raspberry  red    4
B: banana     yellow 6
A: apple      green  8
```

一个包含 `awk` 命令的文件，在最开头一行加上释伴 `#!`，就能变成可执行脚本。创建一个名为 `example2.awk` 的文件，包含以下内容：

```
#!/usr/bin/awk -f
#
# 除了第一行，在其他行前显示行号
#

NR > 1 {
    printf "%d: %s\n",NR,$0
}
```

可以说，脚本中只有一行，大多数情况下没什么用。但在某些情况下，执行一个脚本，比记住，然后打一条命令要容易的多。一个脚本文件，也提供了一个记录命令具体作用的好机会。以 `#` 号开头的行是注释，`awk` 会忽略它们。

给文件可执行权限：

```
$ chmod u+x example2.awk
```

执行脚本：

```
$ ./example2.awk colours.txt
2: apple      red    4
2: banana     yellow 6
4: raspberry red    3
5: grape      purple 10
[...]
```

将 `awk` 命令放在脚本文件中，有一个好处就是，修改和格式化输出会更容易。在终端中，如果能用一行执行多条 `awk` 命令，那么输入多行，才能达到同样效果，就显得有些多余了。

### 试一试

你现在已经足够了解，`awk` 是如何执行指令的了。现在你应该能编写复杂的 `awk` 程序了。试着编写一个 awk 脚本，它需要: 至少包括一个条件模式，以及多个规则。如果你想使用除 `print` 和 `printf` 以外的函数，可以参考在线 [gawk 手册](https://www.gnu.org/software/gawk/manual/)。

下面这个例子是个很好的切入点：

```
#!/usr/bin/awk -f
#
#  显示所有记录 除了出现以下情况
#  如果第一个记录 包含 “raspberry”
#  将 “red” 替换成 “pi”

$1 == "raspberry" {
        gsub(/red/,"pi")
}

{ print }
```

试着执行这个脚本，看看输出是什么。接下来就看你自己的了。

这一系列的下一篇文章，将会介绍更多，能在更复杂(更有用!) 脚本中使用的函数。

这篇文章改编自 [Hacker Public Radio](http://hackerpublicradio.org/eps.php?id=2129) 系列，一个技术社区博客。

---

via: <https://opensource.com/article/19/11/fields-records-variables-awk>

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

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


如何在 Linux 上使用 pkgsrc

Summary: NetBSD 的软件包管理器通用、灵活又容易。下面是如何使用它。

NetBSD 的软件包管理器通用、灵活又容易。下面是如何使用它。

>
> NetBSD 的软件包管理器通用、灵活又容易。下面是如何使用它。
>
>
>

![](/data/attachment/album/201911/26/064538fbktfzxba18wykde.jpg)

NetBSD 以能在几乎所有平台上运行而闻名，但你知道它*第二*有名的 [pkgsrc](http://pkgsrc.org) 包管理器吗？像 NetBSD 一样，pkgsrc 基本上可以在任何系统上运行，或者至少在任意 Unix 和类 Unix 的系统上上运行。你可以在 BSD、Linux、Illumos、Solaris 和 Mac 上安装 pkgsrc。它总共支持 20 多种操作系统。

### 为什么使用 pkgsrc？

除了 MacOS 之外，所有 Unix 操作系统均自带包管理器。你不一定*需要* pkgsrc，但这可能是你想尝试的三个重要原因：

* **打包**。如果你对打包感到好奇，但尚未尝试自己创建一个软件包，那么 pkgsrc 是一个相对简单的系统，尤其是如果你已经熟悉 Makefile 和类似 [GNU Autotools](https://opensource.com/article/19/7/introduction-gnu-autotools) 之类的构建系统时。
* **通用**。如果你使用多个操作系统或发行版，那么可能会遇到每个系统的包管理器。你可以在不同的系统上使用 pkgsrc，以便你为一个系统打包了程序，就为所有系统打包了。
* **灵活**。在许多打包系统中，如何选择二进制包或源码包并不总是很明显。使用 pkgsrc，区别很明显，两种安装方法都一样容易，并且都可以为你解决依赖关系。

### 如何安装 pkgsrc

无论你使用的是 BSD、Linux、Illumos、Solaris 还是 MacOS，安装过程都基本相同：

1. 使用 CVS 检出 pkgsrc 树
2. 引导 pkgsrc 系统
3. 安装软件包

#### 使用 CVS 检出 pkgsrc 树

在 Git 和 Subversion 之前，就有了 [CVS](http://www.netbsd.org/developers/cvs-repos/cvs_intro.html#intro)。要检出代码你无需了解 CVS 太多，如果你习惯 Git，那么可以将<ruby> 检出 <rt>  checkout </rt></ruby>称为<ruby> 克隆 <rt>  clone </rt></ruby>。当你用 CVS 检出 pkgsrc 时，你就下载了详细说明如何构建每个软件包的“<ruby> 配方 <rt>  recipes </rt></ruby>”。它有很多文件，但是它们都很小，因为你实际上并没有拉取每个包的源码，而只有按需构建时需要的构建基础架构和 Makefile。使用 CVS，你可以轻松地在新版本发布时更新 pkgsrc 检出。

pkgsrc 文档建议将其源码树放在 `/usr` 目录下，因此你必须使用 `sudo`（或成为 root）运行此命令：

```
$ cd /usr
$ sudo cvs -q -z2 -d anoncvs@anoncvs.NetBSD.org:/cvsroot checkout -r pkgsrc-2019Q3 -P pkgsrc
```

在我撰写本文时，最新版本是 2019Q3。请检查 [pkgsrc.org](http://pkgsrc.org/) 主页的新闻部分或 [NetBSD文档](http://www.netbsd.org/docs/pkgsrc/getting.html)，以确定最新版本。

#### 引导 pkgsrc

pkgsrc 树复制到你的计算机后，你会看到一个充满构建脚本的 `/usr/pkgsrc` 目录。在使用之前，你必须引导 pkgsrc，以便你可以轻松地访问构建和安装软件所需的相关命令。

引导 pkgsrc 的方式取决于你所使用操作系统。

对于 NetBSD，你只需使用捆绑的引导器：

```
# cd pkgsrc/bootstrap
# ./bootstrap
```

在其他系统上，还有更好的方法，包括一些自定义功能，它是由 Joyent 提供的。要了解运行的确切命令，请访问 [pkgsrc.joyent.com](http://pkgsrc.joyent.com/)。比如，在 Linux（Fedora、Debian、Slackware 等）上：

```
$ curl -O https://pkgsrc.joyent.com/packages/Linux/el7/bootstrap/bootstrap-trunk-x86_64-20170127.tar.gz
$ BOOTSTRAP_SHA="eb0d6911489579ca893f67f8a528ecd02137d43a"
```

尽管路径暗示文件适用于 RHEL 7，但二进制文件往往与所有（最前沿的 Linux 发行版）兼容。如果你发现二进制文件与你的发行版不兼容，你可以选择从源码构建。

验证 SHA1 校验和：

```
$ echo "${BOOTSTRAP_SHA}" bootstrap-trunk*gz > check-shasum
sha1sum -c check-shasum
```

你还可以验证 PGP 签名：

```
$ curl -O https://pkgsrc.joyent.com/packages/Linux/el7/bootstrap/bootstrap-trunk-x86_64-20170127.tar.gz.asc
$ curl -sS https://pkgsrc.joyent.com/pgp/56AAACAF.asc | gpg --import
$ gpg --verify ${BOOTSTRAP_TAR}{.asc,}
```

当你确认你已有正确的引导套件，将其安装到 `/usr/pkg`：

```
sudo tar -zxpf ${BOOTSTRAP_TAR} -C /
```

它为你提供了通常的 pkgsrc 命令。将这些位置添加到[你的 PATH 环境变量中](https://opensource.com/article/17/6/set-path-linux)：

```
$ echo "PATH=/usr/pkg/sbin:/usr/pkg/bin:$PATH" >> ~/.bashrc
$ echo "MANPATH=/usr/pkg/man:$MANPATH" >> ~/.bashrc
```

如果你宁愿使用 pkgsrc 而不依赖于 Joyent 的构建，那么只需运行 pkgsrc 源码树的引导脚本即可。在运行特定于系统的脚本之前，请先阅读 `bootstrap` 目录中相关 `README` 文件。

![Bootstrapping pkgsrc on NetBSD](/data/attachment/album/201911/26/064609piqd1vh8x8j6vhhq.jpg "Bootstrapping pkgsrc on NetBSD")

### 如何使用 pkgsrc 安装软件

使用 pkgsrc 安装预编译的二进制文件（就像使用 DNF 或 Apt 一样）是很容易的。二进制安装的命令是 `pgkin`，它有自己的专门网站 [pkgin.net](http://pkgin.net)。对于任何用过 Linux 的人来说，这个过程应该感觉相当熟悉。

要搜索 `tmux` 包：

```
$ pkgin search tmux
```

要安装 tmux 包：

```
$ sudo pkgin install tmux
```

`pkgin` 命令的目的是模仿典型的 Linux 包管理器的行为，因此有选项可以列出可用的包、查找包提供的特定可执行文件，等等。

### 如何使用 pkgsrc 从源码构建

然而，pkgsrc 真正强大的地方是方便地从源码构建包。你在第一步中检出了所有 20000 多个构建脚本，你可以直接进入 pkgsrc 源码树来访问这些脚本。

例如，要从源码构建 `tcsh`，首先找到构建脚本：

```
$ find /usr/pkgsrc -type d -name "tcsh"
/usr/pkgsrc/shells/tcsh
```

接下来，进入源码目录：

```
$ cd /usr/pgksrc/shells/tcsh
```

构建脚本目录包含许多文件来帮助在你的系统上构建应用，但值得注意的是，这里面有包含了软件说明的 `DESCR` 文件，以及触发构建的 `Makefile`。

```
$ ls
CVS    DESCR     Makefile
PLIST  distinfo  patches
$ cat DESCR
TCSH is an extended C-shell with many useful features like
filename completion, history editing, etc.
$
```

准备就绪后，构建并安装：

```
$ sudo bmake install
```

pkgsrc 系统使用 `bmake` 命令（在第一步检出 pkgsrc 后提供），因此请务必使用 `bmake`（而不是出于习惯使用 `make`）。

如果要为多个系统构建，那么你可以创建一个包，而不是立即安装：

```
$ cd /usr/pgksrc/shells/tcsh
$ sudo bmake package
[...]
=> Creating binary package in /usr/pkgsrc/packages/All/tcsh-X.Y.Z.tgz
```

pkgsrc 创建的包是标准的 tarball，但它可以方便地通过 `pkg_add` 安装：

```
$ sudo pkg_add /usr/pkgsrc/packages/All/tcsh-X.Y.Z.tgz
tcsh-X.Y.Z: adding /usr/pkg/bin/tcsh to /etc/shells
$ tcsh
localhost%
```

pkgsrc 的 pkgtools 集合提供 `pkg_add`、`pkg_info`、`pkg_admin`、`pkg_create` 和 `pkg_delete` 命令，来帮助管理你在系统上构建和维护软件包。

### pkgsrc，易于管理

pkgsrc 系统提供了直接，容易上手的软件包管理方法。如果你正在寻找一个不妨碍你并且可以定制的包管理器，请在任何运行 Unix 或类 Unix 的系统上试试 pkgsrc。

---

via: <https://opensource.com/article/19/11/pkgsrc-netbsd-linux>

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

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


如何在 Linux 上安装 Java

Summary: 在桌面上拥抱 Java 应用程序，然后在所有桌面上运行它们。

在桌面上拥抱 Java 应用程序，然后在所有桌面上运行它们。

>
> 在桌面上拥抱 Java 应用程序，然后在所有桌面上运行它们。
>
>
>

![](/data/attachment/album/201911/26/065307hk22caubakkos0u0.jpg)

无论你运行的是哪种操作系统，通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序，或者使用 Fedora 上的 DNF 或 Mac 上的 Brew 这样的软件包管理器进行安装，而有时你可能会从网站上下载可执行文件或安装程序。因为 Java 是这么多流行的应用程序的后端，所以最好了解安装它的不同方法。好消息是你有很多选择，本文涵盖了所有这些内容。

坏消息是 Java *太大*，我说的不仅仅是文件大小。Java 是一种开放源代码语言和规范，这意味着从理论上讲，任何人都可以创建它的实现版本。这意味着，在安装任何东西之前，必须确定要安装的 Java 发行版。

### 我需要 JVM 还是 JRE 或者 JDK？

Java 大致分为两个下载类别。<ruby> Java 虚拟机 <rt>  Java Virtual Machine </rt></ruby>（JVM）是运行时组件；它是使 Java 应用程序能够在计算机上启动和运行的“引擎”。它包含在 <ruby> Java 运行时环境 <rt>  Java Runtime Environment </rt></ruby>（JRE）中。

<ruby> Java 开发工具包 <rt>  Java Development Kit </rt></ruby>（JDK）是一个开发工具包：你可以将其视为一个车库，修理工可以坐在那里进行调整、修理和改进。JDK 包含 Java 运行时环境（JRE）。

以下载来说，这意味着：

* 如果你是希望运行 Java 应用程序的用户，则只需 JRE（包括了 JVM）。
* 如果你是希望使用 Java 进行编程的开发人员，则需要 JDK（包括 JRE 库，而 JRE 库又包括 JVM）。 ### OpenJDK、IcedTea 和 OracleJDK 有什么不同？

当<ruby> 太阳微系统 <rt>  Sun Microsystems </rt></ruby>被 Oracle 收购时，Java 是该交易的主要部分。幸运的是，Java 是一种开源技术，因此，如果你对 Oracle 维护该项目的方式不满意，则可以选择其他方法。Oracle 将专有组件与 Java 下载捆绑在一起，而 OpenJDK 项目是完全开源的。

IcedTea 项目本质上是 OpenJDK，但其目标是使用户在使用完全自由开源的工具时更容易构建和部署 OpenJDK。

（LCTT 译注：阿里巴巴也有一个它自己维护的 Open JDK 发行版“<ruby> 龙井 <rt>  Dragonwell </rt></ruby>”。以下引自其官网：“Alibaba Dragonwell 是一款免费的，生产就绪型 Open JDK 发行版，提供长期支持，包括性能增强和安全修复。……Alibaba Dragonwell 作为 Java 应用的基石，支撑了阿里经济体内所有的 Java 业务。Alibaba Dragonwell 完全兼容 Java SE 标准，……”）

### 我应该安装哪个 Java？

如果你对这些选择感到不知所措，那么简单的答案就是你应该安装的 Java 实现应该是最容易安装的那个。当应用程序告诉你需要 Java 12，但你的存储库中只有 Java 8 时，可以安装可以从可靠来源中找到的 Java 12 的任何实现。在 Linux 上，你可以一次安装几个不同版本的 Java，它们不会互相干扰。

如果你是需要选择使用哪个版本的开发人员，则应考虑所需的组件。如果选择 Oracle 的版本，请注意，软件包中包含专有的插件和字体，可能会[影响你分发你的应用程序](https://www.oracle.com/technetwork/java/javase/overview/oracle-jdk-faqs.html)。在 IcedTea 或 OpenJDK 上进行开发是最安全的。

### 从存储库安装 OpenJDK？

现在，你已经知道要选择什么了，你可以使用软件包管理器搜索 OpenJDK 或 IcedTea，然后安装所需的版本。有些发行版使用关键字 `latest` 来指示最新版本，这通常是你要运行的应用程序所需要的。根据你使用的软件包管理器，你甚至可以考虑使用 `grep` 过滤搜索结果以仅包括最新版本。例如，在 Fedora 上：

```
$ sudo dnf search openjdk | grep latest | cut -f1 -d':'

java-latest-openjdk-demo.x86_64
java-openjdk.i686
java-openjdk.x86_64
java-latest-openjdk-jmods.x86_64
java-latest-openjdk-src.x86_64
java-latest-openjdk.x86_64
[...]
```

只有当你尝试运行的应用程序坚持要求你使用 Java 的旧版本时，你才应该看看 `latest` 之前的版本。

在 Fedora 或类似系统上安装 Java：

```
$ sudo dnf install java-latest-openjdk
```

如果你的发行版不使用 `latest` 标签，则可以使用其他关键字，例如 `default`。以下是在 Debian 上搜索 OpenJDK 的信息：

```
$ sudo apt search openjdk | less
default-jdk
  Standard Java development kit

default-jre
  Standard Java runtime

openjdk-11-jdk
  OpenJDK development kit (JDK)

[...]
```

在这种情况下，`default-jre` 软件包适合用户，而 `default-jdk` 则适合开发人员。

例如，要在 Debian 上安装 JRE：

```
$ sudo apt install default-jre
```

现在已安装好 Java。

你的存储库中可能有*许多*与 Java 相关的软件包。要搜索 OpenJDK，如果你是用户，则查找最新的 JRE 或 JVM，如果你是开发人员，则查找最新的 JDK。

### 从互联网上安装 Java

如果在存储库中找不到 JRE 或 JDK，或者找不到满足你需求的 JRE 或 JDK，则可以从互联网上下载开源的 Java 软件包。你可以在 [openjdk.java.net](http://openjdk.java.net) 中找到需要手动安装的 tar 形式的 OpenJDK 下载文件，或者可以从 Azul 下载 tar 形式的 [Zulu 社区版](https://www.azul.com/downloads/zulu-community)或其可安装的 RPM 或 DEB 软件包。

#### 从 TAR 文件安装 Java

如果从 Java.net 或 Azul 下载 TAR 文件，则必须手动安装。这通常称为“本地”安装，因为你没有将 Java 安装到“全局”位置。你可以在 `PATH` 中选择一个合适的位置。

如果你不知道 `PATH` 中包含什么，请查看一下以找出：

```
$ echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/seth/bin
```

在此示例 `PATH` 中，位置 `/usr/local/bin` 和 `/home/seth/bin` 是不错的选择。如果你是计算机上的唯一用户，那么你自己的家目录就很有意义。如果你的计算机上有很多用户，则最好选择一个通用位置，例如 `/usr/local` 或 `/opt`。

如果你无权访问需要 `sudo` 权限的 `/usr/local` 之类的系统级目录，则可以在你自己的家目录中创建一个本地 `bin`（意思是 “<ruby> 二进制 <rt>  binary </rt></ruby>”，而不是“<ruby> 垃圾箱 <rt>  waste bin </rt></ruby>”）或 `Applications` 文件夹：

```
$ mkdir ~/bin
```

如果它不在你的 `PATH` 中，请将其添加到其中：

```
$ echo PATH=$PATH:$HOME/bin >> ~/.bashrc
$ source ~/.bashrc
```

最后，将压缩包解压缩到你选择的目录中。

```
$ tar --extract --file openjdk*linux-x64_bin.tar.gz --directory=$HOME/bin
```

Java 现在安装好了。

#### 从 RPM 或 DEB 安装 Java

如果从 Azul.com 下载 RPM 或 DEB 文件，则可以使用软件包管理器进行安装。

对于 Fedora、CentOS、RHEL 等，请下载 RPM 并使用 DNF 进行安装：

```
$ sudo dnf install zulu*linux.x86_64.rpm
```

对于 Debian、Ubuntu、Pop\_OS 和类似发行版，请下载 DEB 软件包并使用 Apt 安装它：

```
$ sudo dpkg -i zulu*linux_amd64.deb
```

Java 现在安装好了。

#### 用 alternatives 安装你的 Java 版本

一些应用程序是为特定版本的 Java 开发的，不能与其他任何版本一起使用。这种情况很少见，但确实会发生，在 Linux 上，你可以使用本地安装方法（请参阅上面“从 TAR 文件安装 Java”一节）或使用 `alternatives` 应用程序来解决此冲突。

`alternatives` 命令会查找 Linux 系统上安装的应用程序，并让你选择要使用的版本。有些发行版，例如 Slackware，不提供 `alternatives` 命令，因此你必须使用本地安装方法。在 Fedora、CentOS 和类似的发行版上，该命令是 `alternatives`。在 Debian、Ubuntu 和类似的系统上，该命令是 `update-alternatives`。

要获取当前已安装在 Fedora 系统上的应用程序的可用版本列表：

```
$ alternatives --list
```

在 Debian 上，你必须指定可供替代的应用程序：

```
$ update-alternatives --list java
```

在 Fedora 上选择要使系统将哪个版本作为默认版本：

```
$ sudo alternatives --config java
```

在 Debian 上：

```
$ sudo updates-alternatives --config java
```

你可以根据需要运行的应用程序，根据需要更改默认的 Java 版本。

### 运行 Java 应用

Java 应用程序通常以 JAR 文件的形式分发。根据你安装 Java 的方式，你的系统可能已经为运行 Java 应用程序配置好了，这使你只需双击应用程序图标（或从应用程序菜单中选择它）即可运行。如果必须执行未与系统其余部分集成的本地 Java 安装，则可以直接从终端启动 Java 应用程序：

```
$ java -jar ~/bin/example.jar &
```

### Java 是个好东西

Java 是少数将跨平台开发放在首位的编程环境之一。没有什么比问一个应用程序是否能在你的平台上运行然后发现该应用程序是用 Java 编写要让人感到松一口气的了。它是如此简单，无论你是开发人员还是用户，你都可以摆脱任何平台上的焦虑。在桌面上拥抱 Java 应用程序，然后在*所有*桌面上运行它们吧。

---

via: <https://opensource.com/article/19/11/install-java-linux>

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

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


谷歌为安卓添加主线 Linux 内核支持

Android Police 指出谷歌正在开发其 Fuchsia 操作系统，这似乎是为了有一天取代谷歌。<br />
很显然,应该是为了取代Android,而非谷歌.

当前的安卓生态系统被数百种不同版本的安卓所污染，每种版本都运行着 Linux 内核的不同变体。每个版本均针对不同的手机和不同的配置而设计。谷歌试图通过将主线 Linux 内核添加到安卓来解决该问题。

### 当前在安卓中是如何处理 Linux 内核的

在到达你的手机之前，你手机上的 Linux 内核经历了[三个主要步骤](https://arstechnica.com/gadgets/2019/11/google-outlines-plans-for-mainline-linux-kernel-support-in-android/)。

首先，谷歌采用了 Linux 内核的 LTS（长期支持）版本，并添加了所有的安卓专用代码。这将成为“安卓通用内核”。

然后，谷歌将此代码发送给创建可运行在手机的片上系统（SoC）的公司。这通常是高通公司。

SoC 制造商添加了支持 CPU 和其他芯片的代码后，便会将该内核传递给实际的设备制造商，例如三星和摩托罗拉。然后，设备制造商添加代码以支持手机的其余部分，例如显示屏和摄像头。

每个步骤都需要一段时间才能完成，并且会导致该内核无法与其他任何设备一起使用。这也意味着内核会非常旧，通常是大约两年前的内核。例如，上个月交付的谷歌 Pixel 4 带有来自 2017 年 11 月的内核，而且它将永远不会得到更新。

谷歌承诺会为较旧的设备创建安全补丁，这意味着他们会一直盯着大量的旧代码。

### 将来

![](/data/attachment/album/201911/27/085706ec16q899q5uzflj9.png)

去年，谷歌宣布[计划](https://lwn.net/Articles/771974/)解决此问题。今年，他们在 2019 Linux Plumbers Conference 上展示了他们取得的进展。

>
> “我们知道运行安卓需要什么，但不一定是在任何给定的硬件上。因此，我们的目标是从根本上找出所有这些，然后将其交给上游，并尝试尽可能接近主线。”
>
>
> Sandeep Patil，[安卓内核团队负责人](https://arstechnica.com/gadgets/2019/11/google-outlines-plans-for-mainline-linux-kernel-support-in-android/)
>
>
>

他们确实炫耀了运行带有合适的 Linux 内核的小米 Poco F1。但是，有些东西[似乎没有工作](https://www.androidpolice.com/2019/11/19/google-wants-android-to-use-regular-linux-kernel-potentially-improving-updates-and-security/)，例如电池电量百分比一直留在 0％。

那么，谷歌计划如何使其工作呢？从他们的 [Treble 项目](https://www.computerworld.com/article/3306443/what-is-project-treble-android-upgrade-fix-explained.html)计划中摘录。在 Treble 项目之前，与设备和安卓本身交互的底层代码是一大堆代码。Treble 项目将两者分开，并使它们模块化，以便可以更快地交付安卓更新，并且在更新时，这些低级代码可以保持不变。

谷歌希望为内核带来同样的模块化。他们的[计划](https://arstechnica.com/gadgets/2019/11/google-outlines-plans-for-mainline-linux-kernel-support-in-android/)“涉及稳定 Linux 的内核 ABI，并为 Linux 内核和硬件供应商提供稳定的接口来进行写入。谷歌希望将 Linux 内核与其硬件支持脱钩。”

因此，这意味着谷歌将交付一个内核，而硬件驱动程序将作为内核模块加载。目前，这只是一个草案。仍然有很多技术问题有待解决。因此，这不会很快有结果。

### 来自开源的反对意见

开源社区不会对将专有代码放入内核的想法感到满意。[Linux 内核准则](https://www.kernel.org/doc/Documentation/process/stable-api-nonsense.rst)指出，驱动程序必须具有 GPL 许可证才能包含在内核中。他们还指出，如果驱动程序的更改导致错误，应由导致该错误的人来解决。从长远来看，这意味着设备制造商的工作量将减少。

### 关于将主线内核包含到安卓中的最终想法

到目前为止，这只是一个草案。谷歌有很大的可能会开始进行该项目，除非他们意识到这将需要多少工作后才会放弃。看看谷歌[已经放弃](https://killedbygoogle.com/)了多少个项目！

[Android Police](https://www.androidpolice.com/2019/11/19/google-wants-android-to-use-regular-linux-kernel-potentially-improving-updates-and-security/) 指出谷歌正在开发其 [Fuchsia 操作系统](https://itsfoss.com/fuchsia-os-what-you-need-to-know/)，这似乎是为了有一天取代谷歌。

那么，问题是谷歌会尝试完成那些艰巨的任务，使安卓以主线 Linux 内核运行，还是完成他们统一的安卓替代产品的工作？只有时间可以回答。

你对此话题有何看法？请在下面的评论中告诉我们。

---

via: <https://itsfoss.com/mainline-linux-kernel-android/>

作者：[John Paul](https://itsfoss.com/author/john/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


bauh：在一个界面中管理 Snap、Flatpak 和 AppImage

Summary: 幸运的是，我偶然发现了一个支持这几种通用包格式的应用程序。

我使用manjaro或Arco安装后，打开bauh需要输入密码，然后点击继续，无响应。无法进入软件主界面。

幸运的是，我偶然发现了一个支持这几种通用包格式的应用程序。

![](/data/attachment/album/201911/27/092926pzzdtytda80yaany.jpg)

[Snap](https://snapcraft.io/)、[Flatpak](https://flatpak.org/) 和 [AppImage](https://appimage.org/) 等通用软件包的最大问题之一就是管理它们。大多数内置的软件包管理器都不能全部支持这些新格式。

幸运的是，我偶然发现了一个支持这几种通用包格式的应用程序。

### bauh：多包装需求的管理器

[bauh](https://github.com/vinifmor/bauh)（LCTT：我给该软件建议一个中文名：“包豪”）最初名为 fpakman，旨在处理 Flatpak、Snap、[AppImage](https://itsfoss.com/use-appimage-linux/) 和 [AUR](https://itsfoss.com/best-aur-helpers/) 软件包。创建者 [vinifmor](https://github.com/vinifmor) 在 2019 年 6 月启动了该项目，[意图](https://forum.manjaro.org/t/bauh-formerly-known-as-fpakman-a-gui-for-flatpak-and-snap-management/96180)“为 Manjaro 用户提供管理 Flatpak 的图形界面”。此后，他扩展了该应用程序，以添加对基于 Debian 的系统的支持。

![Bauh About](/data/attachment/album/201911/27/093003z3kzkpz5hvre5uze.jpg)

首次打开 bauh 时，它将扫描已安装的应用程序并检查更新。如果有任何需要更新的内容，它们将列在前面并居中。更新所有软件包后，你将看到已安装的软件包列表。你可以取消选择不需要更新的软件包，以防止其被更新。你也可以选择安装该应用程序的早期版本。

![With Bauh you can manage various types of packages from one application](/data/attachment/album/201911/27/093005qbd4wi54djbuuf5k.jpg)

你也可以搜索应用程序。bauh 提供了有关已安装和已搜索软件包的详细信息。如果你对一种（或多种）软件包类型不感兴趣，则可以在设置中取消选择它们。

![Bauh Package Info](/data/attachment/album/201911/27/093007sz87nqm8dwnwt1qq.jpg)

![Bauh Updating](/data/attachment/album/201911/27/093008ui7xzzzfaa3yai33.jpg)

![Bauh Search](/data/attachment/album/201911/27/093011mgbebkr9ooyrbmbb.png)

### 在你的 Linux 发行版上安装 bauh

让我们看看如何安装 bauh。

#### 基于 Arch 的发行版

如果你安装的是最近的 [Manjaro](https://manjaro.org/)，则应该一切已经就绪。bauh 默认情况下已安装。如果你安装的是较早版本的 Manjaro（如我一样）或其他基于 Arch 的发行版，则可以在终端中输入以下内容从 [AUR](https://aur.archlinux.org/packages/bauh) 中进行安装：

```
sudo pacman -S bauh
```

#### 基于 Debian/Ubuntu 的发行版

如果你拥有基于 Debian 或 Ubuntu 的 Linux 发行版，则可以使用 `pip` 安装 bauh。首先，请确保[在 Ubuntu 上安装了 pip](https://itsfoss.com/install-pip-ubuntu/)。

```
sudo apt install python3-pip
```

然后使用它来安装 bauh：

```
pip3 install bauh
```

但是，该软件的创建者建议[手动](https://github.com/vinifmor/bauh#manual-installation)安装它，以避免弄乱系统的库。

要手动安装 bauh，你必须先下载其[最新版本](https://github.com/vinifmor/bauh/releases)。下载后，可以[使用图形工具](https://itsfoss.com/unzip-linux/)或 [unzip 命令](https://linuxhandbook.com/unzip-command/)解压缩。接下来，在终端中打开该文件夹。你将需要使用以下步骤来完成安装。

首先，在名为 `env` 的文件夹中创建一个虚拟环境：

```
python3 -m venv env
```

现在在该环境中安装该应用程序的代码：

```
env/bin/pip install .
```

启动该应用程序：

```
env/bin/bauh
```

一旦完成了 bauh 的安装，就可以通过更改环境设置和参数来对其进行[微调](https://github.com/vinifmor/bauh#general-settings)。

### bauh 的未来之路

bauh 在短短的几个月中增长了很多。它有计划继续增长。当前的[路线图](https://github.com/vinifmor/bauh#roadmap)包括：

* 支持其他打包技术
* 每种打包技术一个单独模块
* 内存和性能改进
* 改善用户体验

### 结语

当我尝试 bauh 时，遇到了两个问题。当我第一次打开它时，它告诉我尚未安装 Snap，如果要使用 Snap 软件包，则必须安装它。我知道我已经安装了 Snap，因为我在终端中运行了 `snap list`，并且可以正常工作。我重新启动系统，Snap 才工作正常。

我遇到的另一个问题是我的一个 AUR 软件包无法更新。我可以用 `yay` 更新软件包，而没有任何问题。可能是我的 Manjaro 有问题，我已经使用了它 3 到 4 年。

总体而言，bauh 可以工作。它做到了宣称的功能。我不能要求更多。

你有没有用过 hauh？如果有的话，你最喜欢的用于管理不同打包格式的工具是什么？在下面的评论中让我们知道。

---

via: <https://itsfoss.com/bauh-package-manager/>

作者：[John Paul](https://itsfoss.com/author/john/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


如何使用 TimeShift 备份和还原 Ubuntu Linux

你是否曾经想过如何备份和还原 Ubuntu 或 Debian 系统？Timeshift 是一款自由开源工具，可让你创建文件系统的增量快照。你可以使用 RSYNC 或 BTRFS 两种方式创建快照。

![](/data/attachment/album/201911/27/235959fejmb080e7z0jnu0.jpg)

让我们深入研究并安装 Timeshift。在本教程，我们将安装在 Ubuntu 18.04 LTS 系统上。

### 在 Ubuntu / Debian Linux 上安装 TimeShift

TimeShift 尚未正式托管在 Ubuntu 和 Debian 仓库中。考虑到这一点，我们将运行以下命令来添加 PPA：

```
# add-apt-repository -y ppa:teejee2008/ppa
```

![Add timeshift repository](/data/attachment/album/201911/28/001558mvh2kqjmrzrkvs18.png)

接下来，使用以下命令更新系统软件包：

```
# apt update
```

成功更新系统后，使用以下 `apt` 命令安装 Timeshift：

```
# apt install timeshift
```

![apt install timeshift](/data/attachment/album/201911/28/001417qpv1v3yspypkl9vv.png)

### 准备备份存储设备

最佳实践要求我们将系统快照保存在系统硬盘之外的单独的存储卷上。对于本指南，我们将使用 16GB 闪存作为第二个驱动器，并在该驱动器上保存快照。

```
# lsblk | grep sdb
```

![lsblk sdb ubuntu](/data/attachment/album/201911/28/001421nrarp82frkrxjoqt.png)

为了将闪存用作快照的备份位置，我们需要在设备上创建一个分区表。运行以下命令：

```
# parted /dev/sdb  mklabel gpt
# parted /dev/sdb  mkpart primary 0% 100%
# mkfs.ext4  /dev/sdb1
```

![create partition table on drive ubuntu](/data/attachment/album/201911/28/001425rzbfdn9un4ioditt.jpg)

在 USB 闪存上创建分区表后，我们可以开始创建文件系统的快照！

### 使用 Timeshift 创建快照

要启动 Timeshift，使用应用程序菜单搜索 “Timeshift”。

![Access timeshift](/data/attachment/album/201911/28/001427b5swwersrye5lspd.jpg)

单击 Timeshift 图标，系统将提示你输入管理员密码。提供密码，然后单击验证。

![Authentication required](/data/attachment/album/201911/28/001431smjkwpzm46aa1c0m.jpg)

接下来，选择你喜欢的快照类型。

![Select rsync option](/data/attachment/album/201911/28/001431tfrfor6kizsis4os.jpg)

点击 “Next”。选择快照的目标驱动器。在这里，我的位置是标记为 `/dev/sdb` 的外部 USB 驱动器。

![Select snapshot location](/data/attachment/album/201911/28/001434m1kbo1q11q1qgqj1.png)

接下来，定义快照级别。级别是指创建快照的时间间隔。你可以选择每月、每周、每天或每小时的快照级别。

![Select snapshot levels](/data/attachment/album/201911/28/001437bcz28s8auc7zchf3.jpg)

点击 “Finish”。

在下一个窗口中，单击 “Create” 按钮开始创建快照。此后，系统将开始创建快照。

![Create snapshot](/data/attachment/album/201911/28/001439b32933jyj9z88m95.jpg)

最后，你的快照将显示如下：

![Snapshot created](/data/attachment/album/201911/28/001441qcqklak68c4cqcbk.jpg)

### 从快照还原 Ubuntu / Debian

创建系统快照后，现在让我们看看如何从同一快照还原系统。在同一个 Timeshift 中，单击快照，然后单击 “Restore” 按钮，如图所示。

![Restore snapshot](/data/attachment/album/201911/28/001444z3udab299qd39adi.jpg)

接下来，将提示你选择目标设备。保留默认选择，然后点击 “Next”。

![Select target device](/data/attachment/album/201911/28/001448ax09vvc0wzb96lz0.jpg)

恢复过程开始之前，Timeshift 将会试运行。

![Comparing files dry run](/data/attachment/album/201911/28/001454rico1n1p1112oi3p.jpg)

在下一个窗口中，点击 “Next” 按钮确认显示的操作。

![Confirm actions](/data/attachment/album/201911/28/001459iwaeriwejni8ra3b.jpg)

如图所示，你会看到警告和免责声明。点击 “Next” 初始化恢复过程。

此后，将开始还原过程，最后，系统之后将重新启动到快照定义的早期版本。

![Restoring snapshot](/data/attachment/album/201911/28/001507y44rsxkp7j90s1f7.png)

### 总结

如你所见，使用 TimeShift 从快照还原系统非常容易。在备份系统文件时，它非常方便，并允许你在系统故障时进行恢复。因此，不要害怕修改系统或弄乱系统。TimeShift 使你能够返回到一切运行平稳的时间点。

---

via: <https://www.linuxtechi.com/timeshift-backup-restore-ubuntu-linux/>

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