eDEX-UI：一款科幻风格的酷炫 Linux 终端仿真器

想试试，但仅停留在试试

>
> eDEX-UI 是一个很酷的科幻电影式的终端仿真器，看起来很酷，有大量包括系统监控在内的选项。让我们来快速看看它提供了什么。
>
>
>

你可能已经知道了[大量有趣的 Linux 命令](https://itsfoss.com/funny-linux-commands/)。但你知道当谈到 Linux 命令行时，还有什么有趣的东西吗？那就是终端本身。

是的，终端仿真器（俗称终端）也可以很有趣。还记得 80 年代和 90 年代初的[酷炫复古的终端，给你一个复古的终端](https://itsfoss.com/cool-retro-term/)吗？

那一款深受 《<ruby> <a href="https://www.imdb.com/title/tt1104001/">  创：战纪 </a> <rt>  TRON Legacy </rt></ruby>》 电影特效启发的养眼终端感觉怎么样？

在本文中，让我们来看看一款神奇的跨平台终端模拟器，注意口水！

### eDEX-UI: 一个很酷的终端仿真器

[eDEX-UI](https://github.com/GitSquared/edex-ui) 是一款开源的跨平台终端仿真器，它为你呈现了一个科幻风格的外观和一些有用的功能。

它最初的灵感来自于 [DEX UI](https://github.com/seenaburns/dex-ui) 项目，但该项目已经不再维护。

尽管 eDEX-UI 的重点是外观和未来主义主题的终端，但它也可以作为一个 [Linux 系统监控工具](https://itsfoss.com/linux-system-monitoring-tools/)。怎么说呢？因为当你在终端中工作时，它可以在侧边栏中显示系统的统计信息。事实上，其开发者提到，他的目标是将其改进为一个可用的系统管理工具。

让我们来看看它还提供了什么，以及如何让它安装到你的电脑上。

### eDEX-UI 的特点

![](/data/attachment/album/202009/26/220922te48fl4eodv820ny.png)

eDEX-UI 本质上是一个终端仿真器。你可以像普通终端一样使用它来运行命令和其他任何你可以在终端中做的事情。

它以全屏方式运行，侧边栏和底部面板可以监控系统和网络的统计信息。此外，还有一个用于触摸设备的虚拟键盘。

我做了一个简短的视频，我建议你观看这个视频，看看这个很酷的终端模拟器运行起来是什么样子。**播放视频时要打开声音**（相信我）。

eDEX-UI 的特点：

* 多标签
* 对 curses 的支持
* 目录查看器，显示当前工作目录的内容
* 显示系统信息，包括主板信息、网络状态、IP、网络带宽使用情况、CPU 使用情况、CPU 温度、RAM 使用情况等
* 自定义选项，以改变主题、键盘布局、CSS 注入
* 可选的声音效果，让你有一种黑客的感觉
* 跨平台支持（Windows、macOS 和 Linux）

### 在 Linux 上安装 eDEX

![](/data/attachment/album/202009/26/220933q7qmtm07o0l66172.png)

如前所述，它支持所有主要平台，包括 Windows、macOS，当然还有 Linux。

要在 Linux 发行版上安装它，你可以从它的 [GitHub 发布部分](https://github.com/GitSquared/edex-ui/releases)中抓取 AppImage 文件，或者在包括 [AUR](https://itsfoss.com/aur-arch-linux/) 在内的[可用资源库](https://repology.org/project/edex-ui/versions)中找到它。如果你不知道，我建议你去看一下我们关于[在 Linux 中使用 AppImage](https://itsfoss.com/use-appimage-linux/) 的指南。

你可以访问它的 GitHub 页面，如果你喜欢它，可以随时星标他们的仓库。

* [eDEX-UI](https://github.com/GitSquared/edex-ui)

### 我对 eDEX-UI 的体验

因为它的科幻风格的外观，我喜欢这个终端仿真器。但是，我发现它对系统资源的消耗相当大。我没有[检查我的 Linux 系统的 CPU 温度](https://itsfoss.com/check-laptop-cpu-temperature-ubuntu/)，但 CPU 消耗肯定很高。

所以，如果你需要它运行在后台或单独的工作空间中（像我这样），你可能要注意这一点。除此以外，它是一个令人印象深刻的工具，拥有目录查看器和系统资源监控等有用的选项。

顺便说一句，如果你想模拟黑客来娱乐一下客人和孩子们，[那么可以试试好莱坞工具](https://itsfoss.com/hollywood-hacker-screen/)。

你觉得 eDEX-UI 怎么样？你是想试一试，还是觉得太幼稚/过于复杂？

---

via: <https://itsfoss.com/edex-ui-sci-fi-terminal/>

作者：[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/) 荣誉推出


Nextcloud 如何简化去中心化的注册流程？

>
> Nextcloud 是开源软件，我们不提供托管服务，但我们却能从根本上简化注册体验。
>
>
>

![](/data/attachment/album/202009/27/100351eqd88qub0b3ww0tu.jpg)

我们一直有个列表，上面有几十个 Nextcloud 提供商，然而我听到的最常见的问题，即使是我的技术方面的朋友，也是：

>
> ”嗨，Jan，你推荐哪个 Nextcloud 提供商？”
>
>
>

当然，这也是可以理解的。如果你有一长串的供应商，你怎么选择？托管商在附近？名字可爱？标志最大？

每一个使用服务器的去中心化开源解决方案都在努力解决这些：

* Mastodon 有 [joinmastodon.org](https://joinmastodon.org/) 来选择社区，但很明显主实例是 mastodon.social。
* Diaspora 有[joindiaspora.com](https://joindiaspora.com)，它也是主实例。
* Matrix 有 [matrix.to](https://matrix.to)，还有一个在 [Element.io](http://Element.io) 的应用（多平台）。
* WordPress 有 [wordpress.com](https://wordpress.com)。我不确定是否有提供商能接近它的知名度。
* PeerTube 有一堆实例，都有不同的技术细节。
* Pixelfed 在[beta.joinpixelfed.org](http://beta.joinpixelfed.org) 有一个早期版本的实例选择器，以及在 [pixelfed.social](http://pixelfed.social) 有一个大型实例。
* 还有更多的去中心化开源应用的例子，这里列出了如何通过终端访问它、设置 Rust 实现、或使其在网络打印机上运行。

这就导致了一些问题：

* ? 人们不知道该选哪个，有<ruby> 错失恐惧症 <rt>  Fear Of Missing Out </rt></ruby>（FOMO），然后根本不选。这就是选择的悖论！
* ? 网络并不是真正的去中心化，因为大多数人都在少数服务器上，或者主要是只有一台服务器。

Nextcloud 不存在这些问题。

### 我们的解决方案：简单注册

这是它是如何工作的：

当你下载我们的手机或桌面应用时，你首先看到的是“登录”或“使用供应商注册”的选择。这是任何专有应用都会做的事情：

![Android client](/data/attachment/album/202009/27/100429jg4d6yth84zddwxk.png "Android client")

选择“注册”可打开应用中的[简易注册页面](https://nextcloud.com/signup)。

![Web client](/data/attachment/album/202009/27/100432e6gkxcvixkflkblz.png "Web client")

你输入你的电子邮件地址，然后点击“注册”。

输入密码，就可以了! ?

>
> ”等一下，这怎么这么简单？“
>
>
>

我知道，对吧？✨

事实上，它甚至比很多集中式的应用更简单，在那里你需要输入你的全名和电话号码，然后点击谷歌的验证码。

这基本上可以归结为：

### 我们为你选择

而不是面对一个无法判断什么适合你供应商的名单，我们只向你展示一个选择。就好像我是你的朋友一样，我回答了那个关于我推荐哪家供应商的问题。

很好！?

澄清一下：你可以修改供应商，但默认的应该很适合你。目前，它只是地理位置上离你最近的提供商。

除此之外，我们对通过简单注册列出的提供商有一些要求，以确保无论你选择哪一个提供商都能获得良好的用户体验：

* ? 2 GB 的免费存储空间，而且不仅仅是试用期。
* ? 一套核心应用。文件、日历、联系人、邮件、通话、任务、笔记。有些供应商甚至提供更多。
* ? 最新版本，让你始终保持最新的功能、修复和安全更新。

除此之外，我们还提出了一个尊重隐私的流程。当你点击“注册”时，你的邮件不会发送给我们，而是直接发送给你选择的供应商，这将无缝过渡到他们的设置步骤，在那里你选择一个密码。这意味着在 Nextcloud 不会有任何数据泄露给我们，我们甚至不知道你选择的是哪家提供商！

因此，虽然我们提供这项服务，而且它超级容易使用，但我们只做绝对最低限度的数据处理，以连接你与你的理想供应商。

### 去中心化项目需要简单的注册方式

很多开源软件项目可以从简单注册这样的体验中受益。我们[在最初发布的时候写过有关它的文章](https://nextcloud.com/blog/introducing-simple-signup-you-can-now-get-started-with-nextcloud-in-2-steps/)，我们希望这篇文章能澄清使它成功的设计决策，以便它能被更多项目采用。

去中心化是赋能，但只有当人们即使不知道服务器是什么也能简单注册时，才是真正的革命。

当然，现在也还不完美。比如，如果你已经在 Nextcloud 实例上有了账户，任何一个应用的登录过程都会要求你输入一个服务器地址，而很多人根本不知道那是什么。比如在很多邮件应用中，在这一步会有一个最受欢迎的供应商列表，底部有一个“自定义服务器”的条目。这也可能是一种可能性，但同样带来了系统过于集中的风险，或者让人们对选择什么感到困惑。

所以，我们不断尝试为所有 Nextcloud 桌面和移动应用改进这一点，比如 Nextcloud Talk 或者所有优秀的社区开发的应用。在 Android 上，我们与 DAVx5（Android 上的日历和联系人同步）紧密集成，而且，对于其他 Android 应用，还有一个[单点登录库](https://github.com/nextcloud/Android-SingleSignOn)。不幸的是，在 iOS 上，就没有那么容易了，因为应用必须来自同一个开发者才能共享凭证。

如果你想合作解决类似这些有趣的挑战，[来加入我们的 Nextcloud 设计团队吧](https://nextcloud.com/design)！

---

via: <https://opensource.com/article/20/9/decentralization-signup>

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


#新闻拍一拍# 一个无聊的开发者创造了“DOS 的 Linux 子系统”

Summary: • 福布斯：微软更新 Edge 新功能挑战 Chrome • JetBrains：世界上最大的 Java 程序员集中地在亚洲和德国

![](/data/attachment/album/202009/27/113728gss04253293seeer.jpg)

#### 一个无聊的开发者创造了“DOS 的 Linux 子系统”

墨尔本的一位软件工程师正在通过创建一个 DOS 用户迄今为止所缺乏的工具来消磨城市的封锁时间：一个类似于 Windows 10 用户所享受的集成 Linux 环境。Linux 不但能够从 DOS 命令行启动，而且还可以在 Linux 运行后继续执行 DOS。Linux 会在自己的启动过程中完整地留下第一兆字节的内存（DOS 所在的地方），所以只需跳回正确的地方继续执行 DOS 即可。

来源：[itnews](https://www.itnews.com.au/news/dos-subsystem-for-linux-breaks-cover-553747)

>
> 拍一拍：确实“无聊”，不过这种无聊的探索精神值得鼓励。
>
>
>

#### 福布斯：微软更新 Edge 新功能挑战 Chrome

现在将可以直接在 Edge 浏览器内部直接从地址栏搜索工作文件了。另一个与工作相关的微软 Edge 更新也即将推出，让 IT 管理员管理用户设备上特定的与工作相关的应用，以及管理用户在 Edge 中的工作配置环境中进行的浏览。以及，对微软端点 DLP 原生策略的支持、即将推出的 Sleeping Tabs 可将内存使用率提高 26%，CPU 使用率减少 29% 等。

来源：[forbes](https://www.forbes.com/sites/kateoflahertyuk/2020/09/26/microsoft-updates-edge-with-a-set-of-stellar-new-features-to-beat-chrome/)

>
> 拍一拍：微软希望吸引更多的企业用户使用更新后的 Edge 浏览器。
>
>
>

#### JetBrains：世界上最大的 Java 程序员集中地在亚洲和德国

Java 开发者最集中的地方是亚洲，有 250 万开发者使用它作为主要语言。JetBrains 认为，这可能是由于在中国和印度等国家雇佣离岸开发人员来构建 Android 应用是很常见的。使用 Java 作为主要语言的开发者比例最高的六个国家是：中国、韩国、印度、德国、西班牙和巴西。

来源：[jetbrains](https://blog.jetbrains.com/idea/2020/09/a-picture-of-java-in-2020/)

>
> 拍一拍：不管你喜欢不喜欢，Java 依然那么流行。
>
>
>


通过两个简单的教程来提高你的 awk 技能

Summary: 超越单行的 awk 脚本，学习如何做邮件合并和字数统计。

代码里个别大写小写转义符没有正确显示出来

超越单行的 awk 脚本，学习如何做邮件合并和字数统计。

>
> 超越单行的 awk 脚本，学习如何做邮件合并和字数统计。
>
>
>

![](/data/attachment/album/202009/28/154624jk8w4ez6oujbur8j.jpg)

`awk` 是 Unix 和 Linux 用户工具箱中最古老的工具之一。`awk` 由 Alfred Aho、Peter Weinberger 和 Brian Kernighan（即工具名称中的 A、W 和 K）在 20 世纪 70 年代创建，用于复杂的文本流处理。它是流编辑器 `sed` 的配套工具，后者是为逐行处理文本文件而设计的。`awk` 支持更复杂的结构化程序，是一门完整的编程语言。

本文将介绍如何使用 `awk` 完成更多结构化的复杂任务，包括一个简单的邮件合并程序。

### awk 的程序结构

`awk` 脚本是由 `{}`（大括号）包围的功能块组成，其中有两个特殊的功能块，`BEGIN` 和 `END`，它们在处理第一行输入流之前和最后一行处理之后执行。在这两者之间，块的格式为：

```
模式 { 动作语句 }

```

当输入缓冲区中的行与模式匹配时，每个块都会执行。如果没有包含模式，则函数块在输入流的每一行都会执行。

另外，以下语法可以用于在 `awk` 中定义可以从任何块中调用的函数。

```
function 函数名(参数列表) { 语句 }

```

这种模式匹配块和函数的组合允许开发者结构化的 `awk` 程序，以便重用和提高可读性。

### awk 如何处理文本流

`awk` 每次从输入文件或流中一行一行地读取文本，并使用字段分隔符将其解析成若干字段。在 `awk` 的术语中，当前的缓冲区是一个*记录*。有一些特殊的变量会影响 `awk` 读取和处理文件的方式：

* `FS`（<ruby> 字段分隔符 <rt>  field separator </rt></ruby>）。默认情况下，这是任何空格字符（空格或制表符）。
* `RS`（<ruby> 记录分隔符 <rt>  record separator </rt></ruby>）。默认情况下是一个新行（`n`）。
* `NF`（<ruby> 字段数 <rt>  number of fields </rt></ruby>）。当 `awk` 解析一行时，这个变量被设置为被解析出字段数。
* `$0:` 当前记录。
* `$1`、`$2`、`$3` 等：当前记录的第一、第二、第三等字段。
* `NR`（<ruby> 记录数 <rt>  number of records </rt></ruby>）。迄今已被 `awk` 脚本解析的记录数。

影响 `awk` 行为的变量还有很多，但知道这些已经足够开始了。

### 单行 awk 脚本

对于一个如此强大的工具来说，有趣的是，`awk` 的大部分用法都是基本的单行脚本。也许最常见的 `awk` 程序是打印 CSV 文件、日志文件等输入行中的选定字段。例如，下面的单行脚本从 `/etc/passwd` 中打印出一个用户名列表：

```
awk -F":" '{print $1 }' /etc/passwd

```

如上所述，`$1` 是当前记录中的第一个字段。`-F` 选项将 `FS` 变量设置为字符 `:`。

字段分隔符也可以在 `BEGIN` 函数块中设置：

```
awk 'BEGIN { FS=":" } {print $1 }' /etc/passwd

```

在下面的例子中，每一个 shell 不是 `/sbin/nologin` 的用户都可以通过在该块前面加上匹配模式来打印出来：

```
awk 'BEGIN { FS=":" } ! /\/sbin\/nologin/ {print $1 }' /etc/passwd

```

### awk 进阶：邮件合并

现在你已经掌握了一些基础知识，尝试用一个更具有结构化的例子来深入了解 `awk`：创建邮件合并。

邮件合并使用两个文件，其中一个文件（在本例中称为 `email_template.txt`）包含了你要发送的电子邮件的模板：

```
From: Program committee <pc@event.org>
To: {firstname} {lastname} <{email}>
Subject: Your presentation proposal

Dear {firstname},

Thank you for your presentation proposal:
  {title}

We are pleased to inform you that your proposal has been successful! We
will contact you shortly with further information about the event
schedule.

Thank you,
The Program Committee

```

而另一个则是一个 CSV 文件（名为 `proposals.csv`），里面有你要发送邮件的人：

```
firstname,lastname,email,title
Harry,Potter,hpotter@hogwarts.edu,"Defeating your nemesis in 3 easy steps"
Jack,Reacher,reacher@covert.mil,"Hand-to-hand combat for beginners"
Mickey,Mouse,mmouse@disney.com,"Surviving public speaking with a squeaky voice"
Santa,Claus,sclaus@northpole.org,"Efficient list-making"

```

你要读取 CSV 文件，替换第一个文件中的相关字段（跳过第一行），然后把结果写到一个叫 `acceptanceN.txt` 的文件中，每解析一行就递增文件名中的 `N`。

把 `awk` 程序写在一个叫 `mail_merge.awk` 的文件中。在 `awk` 脚本中的语句用 `;` 分隔。第一个任务是设置字段分隔符变量和其他几个脚本需要的变量。你还需要读取并丢弃 CSV 中的第一行，否则会创建一个以 `Dear firstname` 开头的文件。要做到这一点，请使用特殊函数 `getline`，并在读取后将记录计数器重置为 0。

```
BEGIN {
  FS=",";
  template="email_template.txt";
  output="acceptance";
  getline;
  NR=0;
}

```

主要功能非常简单：每处理一行，就为各种字段设置一个变量 —— `firstname`、`lastname`、`email` 和 `title`。模板文件被逐行读取，并使用函数 `sub` 将任何出现的特殊字符序列替换为相关变量的值。然后将该行以及所做的任何替换输出到输出文件中。

由于每行都要处理模板文件和不同的输出文件，所以在处理下一条记录之前，需要清理和关闭这些文件的文件句柄。

```
{
        # 从输入文件中读取关联字段
        firstname=$1;
        lastname=$2;
        email=$3;
        title=$4;

        # 设置输出文件名
        outfile=(output NR ".txt");

        # 从模板中读取一行，替换特定字段，
        # 并打印结果到输出文件。
        while ( (getline ln < template) > 0 )
        {
                sub(/{firstname}/,firstname,ln);
                sub(/{lastname}/,lastname,ln);
                sub(/{email}/,email,ln);
                sub(/{title}/,title,ln);
                print(ln) > outfile;
        }

        # 关闭模板和输出文件，继续下一条记录
        close(outfile);
        close(template);
}

```

你已经完成了! 在命令行上运行该脚本：

```
awk -f mail_merge.awk proposals.csv

```

或

```
awk -f mail_merge.awk < proposals.csv

```

你会在当前目录下发现生成的文本文件。

### awk 进阶：字频计数

`awk` 中最强大的功能之一是关联数组，在大多数编程语言中，数组条目通常由数字索引，但在 `awk` 中，数组由一个键字符串进行引用。你可以从上一节的文件 `proposals.txt` 中存储一个条目。例如，在一个单一的关联数组中，像这样：

```
        proposer["firstname"]=$1;
        proposer["lastname"]=$2;
        proposer["email"]=$3;
        proposer["title"]=$4;

```

这使得文本处理变得非常容易。一个使用了这个概念的简单的程序就是词频计数器。你可以解析一个文件，在每一行中分解出单词（忽略标点符号），对行中的每个单词进行递增计数器，然后输出文本中出现的前 20 个单词。

首先，在一个名为 `wordcount.awk` 的文件中，将字段分隔符设置为包含空格和标点符号的正则表达式：

```
BEGIN {
        # ignore 1 or more consecutive occurrences of the characters
        # in the character group below
        FS="[ .,:;()<>{}@!\"'\t]+";
}

```

接下来，主循环函数将遍历每个字段，忽略任何空字段（如果行末有标点符号，则会出现这种情况），并递增行中单词数：

```
{
        for (i = 1; i <= NF; i++) {
                if ($i != "") {
                        words[$i]++;
                }
        }
}

```

最后，处理完文本后，使用 `END` 函数打印数组的内容，然后利用 `awk` 的能力，将输出的内容用管道输入 shell 命令，进行数字排序，并打印出 20 个最常出现的单词。

```
END {
        sort_head = "sort -k2 -nr | head -n 20";
        for (word in words) {
                printf "%s\t%d\n", word, words[word] | sort_head;
        }
        close (sort_head);
}

```

在这篇文章的早期草稿上运行这个脚本，会产生这样的输出：

```
[dneary@dhcp-49-32.bos.redhat.com]$ awk -f wordcount.awk < awk_article.txt
the     79
awk     41
a       39
and     33
of      32
in      27
to      26
is      25
line    23
for     23
will    22
file    21
we      16
We      15
with    12
which   12
by      12
this    11
output  11
function        11

```

### 下一步是什么？

如果你想了解更多关于 `awk` 编程的知识，我强烈推荐 Dale Dougherty 和 Arnold Robbins 所著的《[Sed 和 awk](https://www.amazon.com/sed-awk-Dale-Dougherty/dp/1565922255/book)》这本书。

`awk` 编程进阶的关键之一是掌握“扩展正则表达式”。`awk` 为你可能已经熟悉的 sed [正则表达式](https://en.wikibooks.org/wiki/Regular_Expressions/POSIX-Extended_Regular_Expressions)语法提供了几个强大的补充。

另一个学习 `awk` 的好资源是 [GNU awk 用户指南](https://www.gnu.org/software/gawk/manual/gawk.html)。它有一个完整的 `awk` 内置函数库的参考资料，以及很多简单和复杂的 `awk` 脚本的例子。

---

via: <https://opensource.com/article/19/10/advanced-awk>

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


对华为的禁令可能使 5G 部署复杂化

Summary: 对华为、中兴的禁令意味着无线运营商建设 5G 服务的选择减少了。

对华为、中兴的禁令意味着无线运营商建设 5G 服务的选择减少了。

>
> 对华为、中兴的禁令意味着无线运营商建设 5G 服务的选择减少了。
>
>
>

![](/data/attachment/album/202009/28/162741nnea909elmnoex8o.jpg)

随着运营商竞相建设他们的 5G 网络，由于美国联邦政府的压力，在美国购买所需设备的选择比其他国家少，这可能会减缓部署。

在 2018 年的《国防授权法案》中，总部位于中国的华为和中兴通讯都被禁止向美国政府提供设备，此后不久又全面禁止进口。这极大地改变了竞争格局，并引发了美国 5G 的形态可能因此而改变的问题。

Gartner 的分析师 Michael Porowski 表示，虽然还不完全清楚，但运营商可以在哪里购买 5G 设备的限制有可能会减缓部署速度。

他说：”供应商数量仍然很多：爱立信、诺基亚、三星。中兴和华为都是更经济的选择。如果它们可用，你可能会看到更快的采用速度。”

451 Research 的研究总监 Christian Renaud 表示，业界普遍认为，华为设备既成熟又价格低廉，而在没有华为的情况下，运营商也没有明确的替代方案。

他说：“目前，会有采用诺基亚或爱立信标准的运营商。（而且）现在判断谁最成熟还为时过早，因为部署是如此有限。”

这种争论在运营商本身的覆盖地图上可以得到证实。虽然他们很快就大肆宣传 5G 服务在美国的许多市场上已经有了，但实际的地理覆盖范围大多局限于大城市核心区的公共场所。简而言之，大部分地区的 5G 部署还没有到来。

部署缓慢是有充分理由的。5G 接入点的部署密度要远高于上一代的无线技术，这使得该过程更加复杂且耗时。还有一个问题是，目前可用的 5G 用户设备数量还很少。

Renaud 说：“这就好比说，在有人发明汽车之前，说‘我已经有了这条八车道的高速公路’。”

设备供应商目前的部分目标是通过私有部署来展示 5G 的潜力，这些部署将该技术用做<ruby> 信号隧道 <rt>  backhaul </rt></ruby>，用于支持[物联网](https://www.networkworld.com/article/3207535/what-is-iot-the-internet-of-things-explained.html)和其他针对单个企业的链接场景。

Renaud 说：“（设备供应商）都在大力推动私有部署这一块，然后他们可以利用这一点说：你看，我可以用在布鲁克林造船厂或某个私有部署的 5G 网络中，所以，如果可以的话……，它可以用于支持人们观看 YouTube。”

对中国的禁令的一个不幸结果可能是供应商为满足 5G 要求而遵循的规范的分裂。Renaud 称，如果非中国供应商必须为允许华为和中兴的市场制作一个版本，而为不允许华为和中兴的地方制作不同的版本，这可能会给他们带来新的麻烦。

他说：“这将把成本负担转移到设备制造商身上，来试图支持不同的运营商实施。我们会造成非技术性的壁垒。”而这些，反过来又会导致客户体验受到影响。

但是，5G 已经通过[开放无线接入网技术](https://www.networkworld.com/article/3574977/carriers-vendors-work-to-promote-5g-network-flexibility-with-open-standards.html)拥抱了更高的互操作性的趋势，它将 5G 栈各层之间的软件接口标准化。运营商和设备商都接受了这一推动，使得互操作性更有可能，这可能会在未来吸引更多的参与者。

当然，即便是普遍的互操作性，设备制造商仍然会试图建立客户依赖性。他说“在试图锁定客户的供应商和试图保持供应商中立的客户之间，总是会有一场拉锯战。这不会有很大的改变。（但是）我们显然已经看到了试图更加开放的动向。”

---

via: <https://www.networkworld.com/article/3575408/huawei-ban-could-complicate-5g-deployment.html>

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


#新闻拍一拍# 你家的智能咖啡机可能会被黑

Summary: • Emacs 开发者讨论如何构建更“现代”的 Emacs • EdX 的“Linux 入门”免费课程注册人数突破一百万

![](/data/attachment/album/202009/28/172857w5rhq8r5s3s35hb7.jpg)

#### 你家的智能咖啡机可能会被黑

安全公司 Avast 的研究员 Martin Hron 发现 Smarter 公司的联网智能咖啡机与智能手机应用之间的连接是没有加密的，没有身份验证，没有代码验证，最新的固件就储存在手机应用，可以很容易导出进行逆向工程。利用这些漏洞，他能触发咖啡机打开燃烧器，出水，启动磨豆机，显示勒索信息，让咖啡机重复发出哔哔的声音。唯一阻止这一切混乱的方法是拔掉电源插头。

来源：[solidot](https://www.solidot.org/story?sid=65670)

>
> 拍一拍：越来越多的智能产品，可是安全却没有跟上，这有可能带来意想不到的负面后果。
>
>
>

#### Emacs 开发者讨论如何构建更“现代”的 Emacs

开发者认为需要做一些“看得见”的改变让现有的功能更容易接近用户。比如采用更中心化的方法管理颜色主题。改变键盘按键与鼠标绑定方式。Emacs 原作者 Richard Stallman 认为改变基本按键绑定去模仿较新的编辑器是不可取的，将创造一个 Emacs 用户永远不会切换过去的新编辑器。鼠标行为的情况与之类似。Stallman 建议提供一个“重组模式”。他指出现有的鼠标行为是源自 1990 年左右的 X Windows 标准。他不想太仓促，但现在是时候去更新了。

来源：[solidot](https://www.solidot.org/story?sid=65666)

>
> 拍一拍：在讨论了如何让 Emacs 再次流行起来之后，正在准备 Emacs 28 的开发者又热烈讨论如何构建更“现代”的 Emacs。
>
>
>

#### EdX 的“Linux 入门”免费课程注册人数突破一百万

该课程帮助学生使用图形界面和命令行在主要的 Linux 发行系列中学习 Linux 知识。该课程不需要任何知识或经验，因此对于有兴趣从事 IT 行业的个人来说，该课程是受欢迎的第一步。edX 创始人表示，“这是 Linux 基金会的第一个产品，是我们有史以来最受欢迎的十大课程之一。”

来源：[Linux 基金会](https://training.linuxfoundation.org/announcements/free-intro-to-linux-course-surpasses-one-million-enrollments/)

>
> 拍一拍：可喜可贺，Linux 的普及了这么多人了！
>
>
>


如何寻找可行之路？开源律师如是说

>
> 开源法规对成功有不同寻常的要求。一起来了解开源组织的律师如何帮助其雇主找到可行路径。
>
>
>

![](/data/attachment/album/202009/28/210201am63of6hhhw51svo.jpg)

正如我在这个分为两部分的系列文章的[第一部分](/article-12585-1.html)中所分享的那样，我是<ruby> 红帽公司 <rp>  （ </rp> <rt>  Red Hat </rt> <rp>  ） </rp></ruby>的一名开源律师。我工作中的一个重要部分是向其他公司（包括其内部法律顾问）提供有关红帽公司如何采用完全开源的开发模式来构建企业级产品的信息，并回答他们有关开源许可的一般问题。在听了红帽公司的成功经验之后，这些律师与我对话的话题常常转变为其所在组织如何发展成为更具开源意识和能力的组织，他们也经常询问我如何改善他们的做法，以便更熟练地为其组织的雇员提供开源法律咨询。在这两篇文章中，我将分享我通常在这些主题上告诉内部法律顾问的那些内容。

### 总是要找到一条可行路径

我的雇主红帽公司在使用开源软件方面的独特之处在于，我们的开发模式始于具有数千名贡献者的开源社区，并产生了经受过尝试、测试和信任的最终产品。更具体地说，通过我们独特的开发模式，我们从社区创建的开源软件开始，并在每个项目的基础上加强安全性，修复错误，修补漏洞并添加新功能。然后，我们将这些改进回馈给每个项目，以便使整个开源社区受益。此方法的效用非常重要，包括：

1. 通过内部团队与组织外部其他人之间的协作来利用外部创新；
2. 当现有或潜在的社区与您在同一问题上开展工作而且能够合作时，可以避免您自己开发和维护开源解决方案的成本和效率低下的问题；
3. 通过与合作伙伴和上游项目社区的富有成效的合作，能够避免维护主项目下游分支的昂贵代价。
	* 一些公司发现创建上游代码的非公共分支很诱人，因为它是解决特定<ruby> 用例 <rt>  use case </rt></ruby>的快速方法，或者因为它们不愿意在社区中进行协作。然而，由于增加的开发成本、互操作性损失和其他原因，这种分支的长期维护负担可能是令人望而却步的。相比之下，将开发集中在原始上游社区中，可以在所有参与者之间分担开发成本。

除少数例外（例如红帽公司）外，大多数使用开源软件的组织可能都拥有专有软件许可模式（或与 SaaS 等效的概念），并在其业务中对专有软件进行许可。这些组织认为他们拥有可以提供某些竞争优势的软件组件，并且不希望看到这些组件在开源条款下对其他人可用。这是可以理解的。但是，我会鼓励任何咨询此类问题的开源律师来敦促其客户采用开源开发模式，尤其是对于所有无差异且通用的软件。

例如，如果您的公司开发了用于手机的应用程序，并且您需要一个软件模块来读取和写入 PNG 图像文件，那么使用 GitHub 上流行的开源 PNG 软件模块之一将便宜得多。对于您的业务而言，对 PNG 图像进行编码和解码可能是无差别的，那么为什么要花费宝贵的工程时间来编写自己的版本？此外，为此功能编写自己的模块也可能会导致与使用常用开源模块的其他软件的兼容性问题。这是为什么呢？尽管您的模块和开源模块可能已被编写为对已发布的 PNG 规范进行编码和解码，但有时对规范的解释可能会有所不同，也可能会出现实施错误。使用同一模块执行此功能的每个人都可以确保大多数用户的兼容性，并降低开发和维护成本。

还必须意识到，您可能需要某些软件来保持专有性，并且不受开源条款的约束。取决于您系统的软件体系架构和所使用的开源许可证，除非采取某些措施（超出本文的范围），否则打算保留专有权的软件代码可能会受到开源许可证条款的约束。在这里向客户提供一些有关许可证选择和体系架构的咨询服务将变得很有用。

### 寻找灵活的解决方案

之前主要许可专有软件的组织逐渐增加了对开源软件的使用，但审核和批准的要求可能会增长（以我的经验甚至成倍增长）。由于资源限制，这样的组织可能会陷入困境。下面介绍了一些可以采用的灵活的解决方案。

#### 授权并委托他人

律师不能也不应成为看门人。那样效率低下，并且肯定会导致开发和发布时间出现瓶颈，从而产生挫败感和收入损失。相反，应将审批权限授予产品或项目管理和工程领域有能力的个人。这可以让组织有效地保持灵活性。对客户进行教育（请参阅下一节）对于成功实现这一点至关重要。

我采用的一种方法是为整个工程组织提供开源培训，以便他们可以进行基本的许可模式和体系架构选择，同时为软件开发人员提供更专业的培训，以使他们能够提供更复杂的指导和决策。也要考虑在每个级别上对权限进行明确限制，包括哪些类型的问题和决定必须上报给作为他们开源律师的您。您组织的特定授权级别将取决于您团队在开源方面的经验以及对某些开源问题的敏感性。

#### 教育客户

我发现培训是将您的组织迁移到更具开源意识组织的最有效工具之一。培训您的软件工程师和产品经理至关重要。让我详细说明。

尽管您的软件工程师处在最前沿，并且通常可能对开源问题和软件许可非常了解，但是基于您组织的特定要求对他们进行培训仍然很重要。例如，您的公司可能只允许使用宽松的开源许可证，并且可能对其版权声明和源代码可用性有某些要求。为避免开发中出现随后必须纠正的问题（一种昂贵且耗时的实践），最好培训工程师最大程度地减少不当行为的可能性，尤其是在所有开发过程的开始时（请参阅下一节）。

产品经理也必须接受培训。在许多公司中，产品经理负责营销的经典四个环节（产品、价格、定位和促销），并负责产品从生到死的整个生命周期。产品经理工作的方方面面可能会受到使用开源软件的影响。出于上述相同的原因，了解使用开源软件的要求对他们很有用。此外，更重要的是，产品经理在组织中的重要影响，意味着他们通常能够对流程和政策进行必要的更改。产品经理可能会成为您针对“开放”进行流程变更的最强有力的拥护者之一。

#### 早期发现

在开发过程快要结束时解决开源许可问题非常困难且成本很高。随着软件的发布日期临近，体系架构和开源软件模块都是已经选定且难以更改了。如果检测到问题，例如专有软件模块中嵌入的“左版”（copyleft）软件（当该专有模块无意受开源条款约束时），则在该开发阶段修改体系架构或模块可能非常困难且成本昂贵。相反，应该在开发过程中尽早进行架构分析和开源模块选择的过程，以便可以进行成本更低且更有效的更正。

### 开源法规的“奖励”

实践开源法规是一项有益的职业，因为它具有影响组织朝着“开放”方向发展的能力，这具有很大的好处。是否成功取决于您随着组织的成长和成熟而找到具备可行性和灵活性的解决方案的能力。

---

作者简介：Jeffrey R. Kaufman 是全球领先的开源软件解决方案供应商<ruby> 红帽公司 <rp>  （ </rp> <rt>  Red Hat </rt> <rp>  ） </rp></ruby>开源法务团队的资深商务法律顾问，还担任<ruby> 北卡罗来纳大学 <rp>  （ </rp> <rt>  University of North Carolina </rt> <rp>  ） </rp></ruby>的兼职法律教授。在任职红帽公司之前，Jeffrey 曾担任<ruby> 高通公司 <rp>  （ </rp> <rt>  Qualcomm </rt> <rp>  ） </rp></ruby>的专利和开源法律顾问。

译者简介：薛亮，集慧智佳知识产权咨询公司互联网事业部总监，擅长专利分析、专利布局、竞争对手跟踪、FTO 分析、开源软件知识产权风险分析，致力于为互联网企业、高科技公司提供知识产权咨询服务。

![](/data/attachment/album/202009/06/095405uj1gaui7eyuqhs91.png)


11 年后重新打造的 rdiff-backup 2.0 有什么新功能？

>
> 这个老牌 Linux 备份方案迁移到了 Python 3 提供了添加许多新功能的机会。
>
>
>

![](/data/attachment/album/202009/29/094858pb9pa3sppsq9x5z1.jpg)

2020 年 3 月，[rdiff-backup](https://rdiff-backup.net/) 升级到了 2.0 版，这距离上一个主要版本已经过去了 11 年。2020 年初 Python 2 的废弃是这次更新的动力，但它为开发团队提供了整合其他功能和优势的机会。

大约二十年来，`rdiff-backup` 帮助 Linux 用户在本地或远程维护他们的数据的完整备份，而无需无谓地消耗资源。这是因为这个开源解决方案可以进行反向增量备份，只备份从上一次备份中改变的文件。

这次改版（或者说，重生）得益于一个新的、自组织的开发团队（由来自 [IKUS Software](https://www.ikus-soft.com/en/) 的 Eric Zolf 和 Patrik Dufresne，以及来自 [Seravo](https://seravo.fi/english) 的 Otto Kekäläinen 共同领导）的努力，为了所有 `rdiff-backup` 用户的利益，他们齐心协力。

### rdiff-backup 的新功能

在 Eric 的带领下，随着向 Python 3 的迁移，项目被迁移到了一个新的、不受企业限制的[仓库](https://github.com/rdiff-backup/rdiff-backup)，以欢迎贡献。团队还整合了多年来提交的所有补丁，包括对稀疏文件的支持和对硬链接的修复。

#### 用 Travis CI 实现自动化

另一个巨大的改进是增加了一个使用开源 [Travis CI](https://en.wikipedia.org/wiki/Travis_CI) 的持续集成/持续交付（CI/CD）管道。这允许在各种环境下测试 `rdiff-backup`，从而确保变化不会影响方案的稳定性。CI/CD 管道包括集成所有主要平台的构建和二进制发布。

#### 使用 yum 和 apt 轻松安装

新的 `rdiff-backup` 解决方案可以运行在所有主流的 Linux 发行版上，包括 Fedora、Red Hat、Elementary、Debian 等。Frank 和 Otto 付出了艰辛的努力，提供了开放的仓库以方便访问和安装。你可以使用你的软件包管理器安装 `rdiff-backup`，或者按照 GitHub 项目页面上的[分步说明](https://github.com/rdiff-backup/rdiff-backup#installation)进行安装。

#### 新的主页

团队将网站从 Savannah 迁移到了 GitHub Pages，并对 [rdiff-backup.net](https://rdiff-backup.net/) 官网进行了改版，加入了新的内容，让外观和感觉更加到位。

### 如何使用 rdiff-backup

如果你是 `rdiff-backup` 的新手，你可能会对它的易用性感到惊讶。备份方案应该让你对备份和恢复过程感到舒适，而不是吓人。

#### 开始备份

要开始备份到本地驱动器，例如通过 USB 连接的驱动器，输入 `rdiff-backup` 命令，然后输入要备份的驱动器和要存储文件的目标目录。

例如，要备份到名为 `my_backup_drive` 的本地驱动器，请输入：

```
$ rdiff-backup /home/tux/ /run/media/tux/my_backup_drive/

```

要将数据备份到异地存储，请使用远程服务器的位置，并在 `::` 后面指向备份驱动器的挂载点：

```
$ rdiff-backup /home/tux/ tux@example.com::/my_backup_drive/

```

你可能需要[设置 SSH 密钥](https://opensource.com/article/20/8/how-ssh)来使这个过程更轻松。

#### 还原文件

做备份的原因是有时文件会丢失。为了使恢复尽可能简单，你甚至不需要 `rdiff-backup` 来恢复文件（虽然使用 `rdiff-backup` 命令提供了一些方便）。

如果你需要从备份驱动器中获取一个文件，你可以使用 `cp` 将其从备份驱动器复制到本地系统，或者对于远程驱动器使用 `scp` 命令。

对于本地驱动器，使用：

```
$ cp _run_media/tux/my_backup_drive/Documents/example.txt ~/Documents

```

或者用于远程驱动器：

```
$ scp tux@example.com::/my_backup_drive/Documents/example.txt ~/Documents

```

然而，使用 `rdiff-backup` 命令提供了其他选项，包括 `--restore-as-of`。这允许你指定你要恢复的文件的哪个版本。

例如，假设你想恢复一个文件在四天前的版本：

```
$ rdiff-backup --restore-as-of 4D /run/media/tux/foo.txt ~/foo_4D.txt

```

你也可以用 `rdiff-backup` 来获取最新版本：

```
$ rdiff-backup --restore-as-of now /run/media/tux/foo.txt ~/foo_4D.txt`

```

就是这么简单。另外，`rdiff-backup` 还有很多其他选项，例如，你可以从列表中排除文件，从一个远程备份到另一个远程等等，这些你可以在[文档](https://rdiff-backup.net/docs/examples.html)中了解。

### 总结

我们的开发团队希望用户能够喜欢这个改版后的开源 `rdiff-backup` 方案，这是我们不断努力的结晶。我们也感谢我们的贡献者，他们真正展示了开源的力量。

---

via: <https://opensource.com/article/20/9/rdiff-backup-linux>

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


ESR：Windows 最终可能成为 Linux

Summary: 近日，著名开源领袖、写出《大教堂与集市》的 ESR 撰文指出，微软的 Windows 可能最后会切换到 Linux 内核，成为一个保留 Windows 界面的 Linux。

微软的目的是吃掉linux，使之成为Windows下的子系统，进而削弱甚至消灭其它linux发行版。

来自河南南阳的 Chrome Mobile 57.0|Android 10 用户

恐怕不可能

即使可能，也是有生之年了

来自江西南昌的 Chrome Mobile 77.0|Android 10 用户

![](/data/attachment/album/202009/29/110622ueg3gepbsgr3f8rj.jpg)

近日，著名开源领袖、写出《大教堂与集市》的 ESR（Eric S. Raymond）[撰文](http://esr.ibiblio.org/?p=8764)指出，微软的 Windows 可能最后会切换到 Linux 内核，成为一个保留 Windows 界面的 Linux。微软目前的主要收入来源于其云服务，而操作系统业务对微软来说，所能贡献的利润比例会越来越少。Windows 将来可能是运行于 Linux 内核之上的一个桌面环境和一个越来越薄的 Windows 兼容层 —— 以兼容原有的 Windows 二进制程序。

对于 ESR 的这个观点，老王是赞同的：

>
> 这些年来，随着云技术的发展，越来越多的操作系统厂商将业务重点从单机操作系统转向云基础设施。在这一点上，从著名的 Linux 发行版 Ubuntu 的发行商 Canonical 身上也可见一斑。
>
>
> 而另一方面，不说谷歌的 Chromebook 上运行的 Chrome OS，主流的操作系统 macOS 也早已针对 Apple 公司的硬件免费提供。在这种情况下，操作系统已经不是早些年的现金奶牛了。
>
>
> 从微软这几年发布的 Windows 的更新中屡屡出现严重问题所反映出的其内部质控环境的劣化，可见微软已经逐步减少了对操作系统部门的投入。因此，ESR 的这个预测可谓有一定的道理。
>
>
> 看来，我们终将能看到 Linux 打败 Windows 的一天，真正地、彻底地解决 “Ubuntu 第一号 bug”。或许，这不能称之为打败，相对于 Linux 在服务器领域、移动领域、高性能计算领域的高歌猛进，Linux 一直在桌面领域方面进展乏力，这应该说是 Linux 和 Windows 的融合更加合适。
>
>
>

以下是 ESR 的[原文](http://esr.ibiblio.org/?p=8764)译文：

### 桌面战争的最后阶段？

在微软 Windows 操作系统最近的发展中，最令人感兴趣的两个发展是 Windows System for Linux（WSL）和他们将微软 Edge 浏览器移植到 Ubuntu。

对于那些没有注意到的人来说，WSL 允许未经修改的 Linux 二进制文件在 Windows 10 下运行。没有仿真层，没有中间层，它们只需加载就能运行。

微软开发人员现在正在 Linux 内核中提供功能来改进 WSL。而这指向了一个迷人的技术方向。为了理解其中的原因，我们需要注意到自 2010 年推出云服务以来，微软的收入来源是如何变化的。

在十年后，Azure 为微软提供了大部分的创收。Windows 的垄断地位已经成为一个旁观者，传统台式电脑（这是它唯一占据主导地位的市场）的销量在下降。相应地，花在 Windows 开发上的投资回报率也在下降。随着 PC 的销量持续下滑，它不可避免地将不再是利润中心，而变成业务的拖累。

从冷血的利润最大化的角度来看，这意味着继续开发 Windows 是微软宁愿不做的事情。相反，他们最好把更多的资本投入到 Azure 上 —— 据说现在 Azure 运行的 Linux 实例比 Windows 还多。

我们的第三个理由是 Proton。Proton 是一个仿真层，它允许在 Linux 上运行 Steam 上发布的 Windows 游戏。它还不够完美，但已经很接近完美了。我自己就用它在这个巨兽上玩《战舰世界》。

对于游戏而言，它们是对 Windows 仿真层最苛刻的压力测试，比商业软件更苛刻。我们可能已经到了类似 Proton 的技术完全可以在 Linux 上运行 Windows 商业软件的地步。如果还没有，那我们很快就会实现。

那么，如果你是微软公司的战略专家，考虑到所有这些因素，利润最大化的前进道路是什么？

它会是这样：微软 Windows **成为** Linux 内核上的 Proton 一样的模拟层。随着时间的推移，这个层会越来越薄，因为更多的支持会落在主线内核源代码上。而经济上的动机是，由于需要在公司内部完成的开发成本越来越少，因此微软可以减去越来越多的开发成本。

如果你认为这是天方夜谭，那你可以再思考一下。证明这已经是（微软的）计划的最好证据是，微软已经将 Edge 移植到 Linux 下运行。这只有这种方式是有意义的：那就是作为一个试运行，让 Windows 实用程序套件的其他部分摆脱对任何仿真层的依赖。

所以，这一切指向的最终状态是。新的 Windows 将主要是一个 Linux 内核，在它上面有一个旧的 Windows 仿真层，但 Edge 和其余的 Windows 用户空间实用程序**并不使用仿真**。仿真层是为了游戏和其他传统的第三方软件而存在的。

经济上的压力会让微软取消仿真层。部分原因是它完全是一个成本中心。部分原因是因为他们想降低运行 Azure 的复杂性成本。 Windows/Linux 每一点融合都有助于实现这一点 —— 可以减少管理和预期的支持费用。

最终，微软将会宣布结束对 Windows 的仿真。操作系统本身，以及它的用户空间工具，一段时间后，会在一个精心保存的旧 Windows 用户界面下成为 Linux。第三方软件供应商会停止发布 Windows 二进制文件，转而采用原生 Linux API 的 ELF 二进制文件……。

……而 Linux 终将赢得桌面战争，不是通过取代 Windows，而是通过合作。也许这一直是它必须要做的事情。


#新闻拍一拍# 美国国税局要求在纳税申报表上申报加密货币交易

Summary: • OpenSSH 8.4发布 为 FIDO/2FA 密钥带来更好的支持 • 英特尔异构编程器 oneAPI 发布 1.0 正式版

![](/data/attachment/album/202009/29/112324df0oq7pqvfyfsaz8.jpg)

#### 美国国税局要求在纳税申报表上申报加密货币交易

对加密货币态度改变和最近的其他行动表明，美国国税局正在认真对待加密货币对税收制度的威胁，无论对方是不合规的爱好者还是复杂的国际犯罪分子。许多加密货币用户对美国国税局的指导意见感到愤怒，该指南将比特币、以太币及其加密货币视为财产，而非货币。

来源：[cnbeta](https://www.cnbeta.com/articles/tech/1034783.htm)

>
> 拍一拍：加密货币已经从微不足道逐渐变成了正常经济生活的一部分。但是究竟该将加密货币视作货币还是资产，抑或兼而有之，这是一个全新的需要探索的领域。
>
>
>

#### OpenSSH 8.4发布 为 FIDO/2FA 密钥带来更好的支持

对于那些使用 YubiKey 或 Google Titan 安全密钥等 FIDO 密钥处理双因素认证的用户，OpenSSH 8.4 有了更好的支持。OpenSSH 8.4 现在支持每次使用都需要输入 PIN 码的 FIDO 密钥，SSHD 支持“验证-要求”选项，要求 FIDO 签名断言令牌被验证。

来源：[cnbeta](https://www.cnbeta.com/articles/tech/1034669.htm)

>
> 拍一拍：最好的安全就是“不用”密码的安全。
>
>
>

#### 英特尔异构编程器 oneAPI 发布 1.0 正式版

oneAPI 是英特尔推出的开源、基于标准的统一编程模型，旨在为从 CPU 到 GPU，再到 FPGA 等其他加速器的一系列硬件提供支持。oneAPI 的核心是英特尔的 Data Parallel C++（DPC++），是建立在 C++ 和 Khronos SYCL 标准之上的语言。除了基于 LLVM/Clang 的 DPC++ 编译器工具链外，oneAPI 还包含了许多库。

来源：[cnbeta](https://www.cnbeta.com/articles/tech/1034883.htm)

>
> 拍一拍：虽然不懂，但是感觉很厉害。
>
>
>


如何修复 Ubuntu Linux 中的 “Release file is not valid yet” 错误

Summary: 错误的原因是系统上的时间和现实世界的时间不同。

错误的原因是系统上的时间和现实世界的时间不同。


使用 RT-Thread 的 FinSH 对硬件进行编程

>
> 由于物联网（IoT）的兴起，对硬件进行编程变得越来越普遍。RT-Thread 可以让你可以用 FinSH 从 Linux 命令行与设备进行沟通、
>
>
>

![](/data/attachment/album/202009/29/233059w523g55qzvo53h6i.jpg)

RT-Thread 是一个开源的[实时操作系统](https://opensource.com/article/20/6/open-source-rtos)，用于对物联网（IoT）设备进行编程。FinSH 是 [RT-Thread](https://github.com/RT-Thread/rt-thread) 的命令行组件，它提供了一套操作界面，使用户可以从命令行与设备进行沟通。它主要用于调试或查看系统信息。

通常情况下，开发调试使用硬件调试器和 `printf` 日志来显示。但在某些情况下，这两种方法并不是很有用，因为它是从运行的内容中抽象出来的，而且它们可能很难解析。不过 RT-Thread 是一个多线程系统，当你想知道一个正在运行的线程的状态，或者手动控制系统的当前状态时，这很有帮助。因为它是多线程的，所以你能够拥有一个交互式的 shell，你可以直接在设备上输入命令、调用函数来获取你需要的信息，或者控制程序的行为。如果你只习惯于 Linux 或 BSD 等现代操作系统，这在你看来可能很普通，但对于硬件黑客来说，这是极其奢侈的，远超将串行电缆直接连线到电路板上以获取一丝错误的做法。

FinSH 有两种模式。

* C 语言解释器模式，称为 c-style。
* 传统的命令行模式，称为 msh（模块 shell）。

在 C 语言解释器模式下，FinSH 可以解析执行大部分 C 语言的表达式，并使用函数调用访问系统上的函数和全局变量。它还可以从命令行创建变量。

在 msh 模式下，FinSH 的操作与 Bash 等传统 shell 类似。

### GNU 命令标准

当我们在开发 FinSH 时，我们了解到，在编写命令行应用程序之前，你需要熟悉 GNU 命令行标准。这个标准实践的框架有助于给界面带入熟悉感，这有助于开发人员在使用时感到舒适和高效。

一个完整的 GNU 命令主要由四个部分组成。

1. 命令名（可执行文件）：命令行程序的名称；
2. 子命令：命令程序的子函数名称。
3. 选项：子命令函数的配置选项。
4. 参数：子命令函数配置选项的相应参数。

你可以在任何命令中看到这一点。以 Git 为例：

```
git reset --hard HEAD~1

```

这一点可以分解为：

![GNU command line standards](/data/attachment/album/202009/29/233147h3c5qxp3x58qccaz.png "GNU command line standards")

可执行的命令是 `git`，子命令是 `reset`，使用的选项是 `--head`，参数是 `HEAD~1`。

再举个例子：

```
systemctl enable --now firewalld

```

可执行的命令是 `systemctl`，子命令是 `enable`，选项是 `--now`，参数是 `firewalld`。

想象一下，你想用 RT-Thread 编写一个符合 GNU 标准的命令行程序。FinSH 拥有你所需要的一切，并且会按照预期运行你的代码。更棒的是，你可以依靠这种合规性，让你可以自信地移植你最喜欢的 Linux 程序。

### 编写一个优雅的命令行程序

下面是一个 RT-Thread 运行命令的例子，RT-Thread 开发人员每天都在使用这个命令：

```
usage: env.py package [-h] [--force-update] [--update] [--list] [--wizard]
                      [--upgrade] [--printenv]

optional arguments:
  -h, --help      show this help message and exit
  --force-update  force update and clean packages, install or remove the
                  packages by your settings in menuconfig
  --update        update packages, install or remove the packages by your
                  settings in menuconfig
  --list          list target packages
  --wizard        create a new package with wizard
  --upgrade       upgrade local packages list and ENV scripts from git repo
  --printenv      print environmental variables to check

```

正如你所看到的那样，它看起来很熟悉，行为就像你可能已经在 Linux 或 BSD 上运行的大多数 POSIX 应用程序一样。当使用不正确或不充分的语法时，它会提供帮助，它支持长选项和短选项。这种通用的用户界面对于任何使用过 Unix 终端的人来说都是熟悉的。

### 选项种类

选项的种类很多，按长短可分为两大类。

1. 短选项：由一个连字符加一个字母组成，如 `pkgs -h` 中的 `-h` 选项。
2. 长选项：由两个连字符加上单词或字母组成，例如，`scons- --target-mdk5` 中的 `--target` 选项。

你可以把这些选项分为三类，由它们是否有参数来决定。

1. 没有参数：该选项后面不能有参数。
2. 参数必选：选项后面必须有参数。
3. 参数可选：选项后可以有参数，但不是必需的。

正如你对大多数 Linux 命令的期望，FinSH 的选项解析非常灵活。它可以根据空格或等号作为定界符来区分一个选项和一个参数，或者仅仅通过提取选项本身并假设后面的内容是参数（换句话说，完全没有定界符）。

* `wavplay -v 50`
* `wavplay -v50`
* `wavplay --vol=50`

### 使用 optparse

如果你曾经写过命令行程序，你可能会知道，一般来说，你所选择的语言有一个叫做 optparse 的库或模块。它是提供给程序员的，所以作为命令的一部分输入的选项（比如 `-v` 或 `--verbose`）可以与命令的其他部分进行*解析*。这可以帮助你的代码从一个子命令或参数中获取一个选项。

当为 FinSH 编写一个命令时，`optparse` 包希望使用这种格式：

```
MSH_CMD_EXPORT_ALIAS(pkgs, pkgs, this is test cmd.);

```

你可以使用长形式或短形式，或者同时使用两种形式来实现选项。例如：

```
static struct optparse_long long_opts[] =
{
    {"help"        , 'h', OPTPARSE_NONE}, // Long command: help, corresponding to short command h, without arguments.
    {"force-update",  0 , OPTPARSE_NONE}, // Long comman: force-update, without arguments
    {"update"      ,  0 , OPTPARSE_NONE},
    {"list"        ,  0 , OPTPARSE_NONE},
    {"wizard"      ,  0 , OPTPARSE_NONE},
    {"upgrade"     ,  0 , OPTPARSE_NONE},
    {"printenv"    ,  0 , OPTPARSE_NONE},
    { NULL         ,  0 , OPTPARSE_NONE}
};

```

创建完选项后，写出每个选项及其参数的命令和说明：

```
static void usage(void)
{
    rt_kprintf("usage: env.py package [-h] [--force-update] [--update] [--list] [--wizard]\n");
    rt_kprintf("                      [--upgrade] [--printenv]\n\n");
    rt_kprintf("optional arguments:\n");
    rt_kprintf("  -h, --help      show this help message and exit\n");
    rt_kprintf("  --force-update  force update and clean packages, install or remove the\n");
    rt_kprintf("                  packages by your settings in menuconfig\n");
    rt_kprintf("  --update        update packages, install or remove the packages by your\n");
    rt_kprintf("                  settings in menuconfig\n");
    rt_kprintf("  --list          list target packages\n");
    rt_kprintf("  --wizard        create a new package with wizard\n");
    rt_kprintf("  --upgrade       upgrade local packages list and ENV scripts from git repo\n");
    rt_kprintf("  --printenv      print environmental variables to check\n");
}

```

下一步是解析。虽然你还没有实现它的功能，但解析后的代码框架是一样的：

```
int pkgs(int argc, char **argv)
{
    int ch;
    int option_index;
    struct optparse options;

    if(argc == 1)
    {
        usage();
        return RT_EOK;
    }

    optparse_init(&options, argv);
    while((ch = optparse_long(&options, long_opts, &option_index)) != -1)
    {
        ch = ch;

        rt_kprintf("\n");
        rt_kprintf("optopt = %c\n", options.optopt);
        rt_kprintf("optarg = %s\n", options.optarg);
        rt_kprintf("optind = %d\n", options.optind);
        rt_kprintf("option_index = %d\n", option_index);
    }
    rt_kprintf("\n");

    return RT_EOK;
}

```

这里是函数头文件：

```
#include "optparse.h"
#include "finsh.h"

```

然后，编译并下载到设备上。

![Output](/data/attachment/album/202009/29/233154xqmusussoo9vq90s.png "Output")

### 硬件黑客

对硬件进行编程似乎很吓人，但随着物联网的发展，它变得越来越普遍。并不是所有的东西都可以或者应该在树莓派上运行，但在 RT-Thread，FinSH 可以让你保持熟悉的 Linux 感觉。

如果你对在裸机上编码感到好奇，不妨试试 RT-Thread。

---

via: <https://opensource.com/article/20/9/hardware-command-line>

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


#新闻拍一拍# 部分勒索软件攻击时间只需不到 45 分钟

Summary: • CNNIC 发布《第 46 次中国互联网络发展状况统计报告》

• CNNIC 发布《第 46 次中国互联网络发展状况统计报告》

![](/data/attachment/album/202009/30/185944cf34ycxfszbmcmss.jpg)

#### 部分勒索软件攻击时间只需不到 45 分钟

在微软发布的 88 页的《微软数字防御报告》中披露，勒索软件感染一直是该公司事件响应最常见的原因，最让微软头疼的是那些被称为“大型游戏猎人“和“人类操作的勒索软件”的团伙。今年，这些勒索软件团伙特别活跃，并且降低了他们发动攻击的时间。显然，由于 COVID-19 爆发，支付意愿会增加，在某些情况下，网络犯罪分子从最初入侵到赎回的时间在 45 分钟内完成。

来源：[zdnet](https://www.zdnet.com/article/microsoft-some-ransomware-attacks-take-less-than-45-minutes/)

>
> 拍一拍：产业化的勒索攻击正在大行其道，企业/政府需要有效的防御和拯救计划。
>
>
>

#### CNNIC 发布《第 46 次中国互联网络发展状况统计报告》

《报告》显示，截至 2020 年 6 月，我国网民规模达 9.40 亿，在线教育用户规模达 3.81 亿，在线医疗用户规模达 2.76 亿，远程办公用户规模达 1.99 亿。5G 终端连接数已超过 6600 万，已开通 5G 基站超 40 万个。我国已分配 IPv6 地址用户数达 14.42 亿，IPv6 活跃用户数达 3.62亿。网络视频（含短视频）用户规模达 8.88 亿，网络支付用户规模达 8.05 亿。

来源：[新浪科技](https://finance.sina.com.cn/tech/2020-09-29/doc-iivhvpwy9334481.shtml?qq-pf-to=pcqq.c2c)

>
> 拍一拍：这些数据表明，我们整个社会已经浸泡在互联网之中，这在 20 年前简直是不可想象的。
>
>
>


如何在 Linux 中创建/配置 LVM（逻辑卷管理）

Summary: 逻辑卷管理（LVM）在 Linux 系统中扮演着重要的角色，它可以提高可用性、磁盘 I/O、性能和磁盘管理的能力。

![](/data/attachment/album/202010/01/111414m2y0mdhgvd9j1bgv.jpg)

<ruby> 逻辑卷管理 <rt>  Logical Volume Management </rt></ruby>（LVM）在 Linux 系统中扮演着重要的角色，它可以提高可用性、磁盘 I/O、性能和磁盘管理的能力。

LVM 是一种被广泛使用的技术，对于磁盘管理来说，它是非常灵活的。

它在物理磁盘和文件系统之间增加了一个额外的层，允许你创建一个逻辑卷而不是物理磁盘。

LVM 允许你在需要的时候轻松地调整、扩展和减少逻辑卷的大小。

![](/data/attachment/album/202010/01/111230el14fubc4ku55o3k.jpeg)

### 如何创建 LVM 物理卷？

你可以使用任何磁盘、RAID 阵列、SAN 磁盘或分区作为 LVM <ruby> 物理卷 <rt>  Physical Volume </rt></ruby>（PV）。

让我们想象一下，你已经添加了三个磁盘，它们是 `/dev/sdb`、`/dev/sdc` 和 `/dev/sdd`。

运行以下命令来[发现 Linux 中新添加的 LUN 或磁盘](https://www.2daygeek.com/scan-detect-luns-scsi-disks-on-redhat-centos-oracle-linux/)：

```
# ls /sys/class/scsi_host
host0

```

```
# echo "- - -" > /sys/class/scsi_host/host0/scan

```

```
# fdisk -l

```

**创建物理卷 （`pvcreate`） 的一般语法：**

```
pvcreate [物理卷名]

```

当在系统中检测到磁盘，使用 `pvcreate` 命令初始化 LVM PV：

```
# pvcreate /dev/sdb /dev/sdc /dev/sdd
Physical volume "/dev/sdb" successfully created
Physical volume "/dev/sdc" successfully created
Physical volume "/dev/sdd" successfully created

```

**请注意：**

* 上面的命令将删除给定磁盘 `/dev/sdb`、`/dev/sdc` 和 `/dev/sdd` 上的所有数据。
* 物理磁盘可以直接添加到 LVM PV 中，而不必是磁盘分区。

使用 `pvdisplay` 和 `pvs` 命令来显示你创建的 PV。`pvs` 命令显示的是摘要输出，`pvdisplay` 显示的是 PV 的详细输出：

```
# pvs
PV VG Fmt Attr PSize PFree
/dev/sdb lvm2 a-- 15.00g 15.00g
/dev/sdc lvm2 a-- 15.00g 15.00g
/dev/sdd lvm2 a-- 15.00g 15.00g

```

```
# pvdisplay

"/dev/sdb" is a new physical volume of "15.00 GiB"
--- NEW Physical volume ---
PV Name               /dev/sdb
VG Name
PV Size               15.00 GiB
Allocatable           NO
PE Size               0
Total PE              0
Free PE               0
Allocated PE          0
PV UUID               69d9dd18-36be-4631-9ebb-78f05fe3217f

"/dev/sdc" is a new physical volume of "15.00 GiB"
--- NEW Physical volume ---
PV Name               /dev/sdc
VG Name
PV Size               15.00 GiB
Allocatable           NO
PE Size               0
Total PE              0
Free PE               0
Allocated PE          0
PV UUID               a2092b92-af29-4760-8e68-7a201922573b

"/dev/sdd" is a new physical volume of "15.00 GiB"
--- NEW Physical volume ---
PV Name               /dev/sdd
VG Name
PV Size               15.00 GiB
Allocatable           NO
PE Size               0
Total PE              0
Free PE               0
Allocated PE          0
PV UUID               d92fa769-e00f-4fd7-b6ed-ecf7224af7faS

```

### 如何创建一个卷组

<ruby> 卷组 <rt>  Volume Group </rt></ruby>（VG）是 LVM 结构中的另一层。基本上，卷组由你创建的 LVM 物理卷组成，你可以将物理卷添加到现有的卷组中，或者根据需要为物理卷创建新的卷组。

**创建卷组 （`vgcreate`） 的一般语法：**

```
vgcreate [卷组名] [物理卷名]

```

使用以下命令将一个新的物理卷添加到新的卷组中：

```
# vgcreate vg01 /dev/sdb /dev/sdc /dev/sdd
Volume group "vg01" successfully created

```

**请注意：**默认情况下，它使用 4MB 的<ruby> 物理范围 <rt>  Physical Extent </rt></ruby>（PE），但你可以根据你的需要改变它。

使用 `vgs` 和 `vgdisplay` 命令来显示你创建的 VG 的信息：

```
# vgs vg01
VG #PV #LV #SN Attr VSize VFree
vg01 3 0 0 wz--n- 44.99g 44.99g

```

```
# vgdisplay vg01
--- Volume group ---
VG Name              vg01
System ID
Format               lvm2
Metadata Areas       3
Metadata Sequence No 1
VG Access            read/write
VG Status            resizable
MAX LV               0
Cur LV               0
Open LV              0
Max PV               0
Cur PV               3
Act PV               3
VG Size              44.99 GiB
PE Size              4.00 MiB
Total PE             11511
Alloc PE / Size      0 / 0
Free PE / Size       11511 / 44.99 GiB
VG UUID              d17e3c31-e2c9-4f11-809c-94a549bc43b7

```

### 如何扩展卷组

如果 VG 没有空间，请使用以下命令将新的物理卷添加到现有卷组中。

**卷组扩展 （`vgextend`）的一般语法：**

```
vgextend [已有卷组名] [物理卷名]

```

```
# vgextend vg01 /dev/sde
    Volume group "vg01" successfully extended

```

### 如何以 GB 为单位创建逻辑卷？

<ruby> 逻辑卷 <rt>  Logical Volume </rt></ruby>（LV）是 LVM 结构中的顶层。逻辑卷是由卷组创建的块设备。它作为一个虚拟磁盘分区，可以使用 LVM 命令轻松管理。

你可以使用 `lvcreate` 命令创建一个新的逻辑卷。

**创建逻辑卷（`lvcreate`） 的一般语法：**

```
lvcreate –n [逻辑卷名] –L [逻辑卷大小] [要创建的 LV 所在的卷组名称]

```

运行下面的命令，创建一个大小为 10GB 的逻辑卷 `lv001`：

```
# lvcreate -n lv001 -L 10G vg01
Logical volume "lv001" created

```

使用 `lvs` 和 `lvdisplay` 命令来显示你所创建的 LV 的信息：

```
# lvs /dev/vg01/lvol01
LV VG Attr LSize Pool Origin Data% Move Log Cpy%Sync Convert
lv001 vg01 mwi-a-m-- 10.00g lv001_mlog 100.00

```

```
# lvdisplay /dev/vg01/lv001
--- Logical volume ---
LV Path                /dev/vg01/lv001
LV Name                lv001
VG Name                vg01
LV UUID                ca307aa4-0866-49b1-8184-004025789e63
LV Write Access        read/write
LV Creation host, time localhost.localdomain, 2020-09-10 11:43:05 -0700
LV Status              available
# open                 0
LV Size                10.00 GiB
Current LE             2560
Segments               1
Allocation             inherit
Read ahead sectors     auto
- currently set to     256
Block device           253:4

```

### 如何以 PE 大小创建逻辑卷？

或者，你可以使用物理范围（PE）大小创建逻辑卷。

### 如何计算 PE 值？

很简单，例如，如果你有一个 10GB 的卷组，那么 PE 大小是多少？

默认情况下，它使用 4MB 的物理范围，但可以通过运行 `vgdisplay` 命令来检查正确的 PE 大小，因为这可以根据需求进行更改。

```
10GB = 10240MB / 4MB （PE 大小） = 2560 PE

```

**用 PE 大小创建逻辑卷 （`lvcreate`） 的一般语法：**

```
lvcreate –n [逻辑卷名] –l [物理扩展 （PE） 大小] [要创建的 LV 所在的卷组名称]

```

要使用 PE 大小创建 10GB 的逻辑卷，命令如下：

```
# lvcreate -n lv001 -l 2560 vg01

```

### 如何创建文件系统

在创建有效的文件系统之前，你不能使用逻辑卷。

**创建文件系统的一般语法：**

```
mkfs –t [文件系统类型] /dev/[LV 所在的卷组名称]/[LV 名称]

```

使用以下命令将逻辑卷 `lv001` 格式化为 ext4 文件系统：

```
# mkfs -t ext4 /dev/vg01/lv001

```

对于 xfs 文件系统：

```
# mkfs -t xfs /dev/vg01/lv001

```

### 挂载逻辑卷

最后，你需要挂载逻辑卷来使用它。确保在 `/etc/fstab` 中添加一个条目，以便系统启动时自动加载。

创建一个目录来挂载逻辑卷：

```
# mkdir /lvmtest

```

使用挂载命令[挂载逻辑卷](https://www.2daygeek.com/mount-unmount-file-system-partition-in-linux/)：

```
# mount /dev/vg01/lv001 /lvmtest

```

在 [/etc/fstab 文件](https://www.2daygeek.com/understanding-linux-etc-fstab-file/)中添加新的逻辑卷详细信息，以便系统启动时自动挂载：

```
# vi /etc/fstab
/dev/vg01/lv001 /lvmtest xfs defaults 0 0

```

使用 [df 命令](https://www.2daygeek.com/linux-check-disk-space-usage-df-command/)检查新挂载的卷：

```
# df -h /lvmtest
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg01-lv001 15360M 34M 15326M 4% /lvmtest

```

---

via: <https://www.2daygeek.com/create-lvm-storage-logical-volume-manager-in-linux/>

作者：[Magesh Maruthamuthu](https://www.2daygeek.com/author/magesh/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[校对者ID](https://github.com/%E6%A0%A1%E5%AF%B9%E8%80%85ID)

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


10 大静态网站生成工具

这个不错，收藏了

![](/data/attachment/album/202010/01/123903lx1q0w2oh1lxx7wh.jpg)

>
> 在寻找部署静态网页的方法吗？这几个开源的静态网站生成工具可以帮你迅速部署界面优美、功能强大的静态网站，无需掌握复杂的 HTML 和 CSS 技能。
>
>
>

### 静态网站是什么？

技术上来讲，静态网站是指网页不是由服务器动态生成的。HTML、CSS 和 JavaScript 文件就静静地躺在服务器的某个路径下，它们的内容与终端用户接收到的版本是一样的。原始的源码文件已经提前编译好了，源码在每次请求后都不会变化。

[Linux.CN](http://Linux.CN) 是一个依赖多个数据库的动态网站，当有浏览器的请求时，网页就会生成并提供服务。大部分网站是动态的，你与这些网站互动时，大量的内容会经常改变。

静态网站有一些好处，比如加载时间更短，请求的服务器资源更少、更安全（值得商榷）。

传统上，静态网站更适合于创建只有少量网页、内容变化不频繁的小网站。

然而，随着静态网站生成工具出现后，静态网站的适用范围越来越大。你还可以使用这些工具搭建博客网站。

我整理了几个开源的静态网站生成工具，这些工具可以帮你搭建界面优美的网站。

### 最好的开源静态网站生成工具

请注意，静态网站不会提供很复杂的功能。如果你需要复杂的功能，那么你可以参考适用于动态网站的[最佳开源 CMS](https://itsfoss.com/open-source-cms/)列表。

#### 1、Jekyll

![](/data/attachment/album/202010/01/123927e6mgmg878g7nlpeb.jpg)

Jekyll 是用 [Ruby](https://www.ruby-lang.org/en/) 写的最受欢迎的开源静态生成工具之一。实际上，Jekyll 是 [GitHub 页面](https://pages.github.com/) 的引擎，它可以让你免费用 GitHub 托管网站。

你可以很轻松地跨平台配置 Jekyll，包括 Ubuntu。它利用 [Markdown](https://github.com/Shopify/liquid/wiki)、[Liquid](https://github.com/Shopify/liquid/wiki)（模板语言）、HTML 和 CSS 来生成静态的网页文件。如果你要搭建一个没有广告或推广自己工具或服务的产品页的博客网站，它是个不错的选择。

它还支持从常见的 CMS（<ruby> 内容管理系统 <rt>  Content management system </rt></ruby>）如 Ghost、WordPress、Drupal 7 迁移你的博客。你可以管理永久链接、类别、页面、文章，还可以自定义布局，这些功能都很强大。因此，即使你已经有了一个网站，如果你想转成静态网站，Jekyll 会是一个完美的解决方案。你可以参考[官方文档](https://jekyllrb.com/docs/)或 [GitHub 页面](https://github.com/jekyll/jekyll)了解更多内容。

* [Jekyll](https://jekyllrb.com/)

#### 2、Hugo

![](/data/attachment/album/202010/01/123929n3k7ys33k7yz3x3h.jpg)

Hugo 是另一个很受欢迎的用于搭建静态网站的开源框架。它是用 [Go 语言](https://golang.org/)写的。

它运行速度快、使用简单、可靠性高。如果你需要，它也可以提供更高级的主题。它还提供了一些有用的快捷方式来帮助你轻松完成任务。无论是组合展示网站还是博客网站，Hogo 都有能力管理大量的内容类型。

如果你想使用 Hugo，你可以参照它的[官方文档](https://gohugo.io/getting-started/)或它的 [GitHub 页面](https://github.com/gohugoio/hugo)来安装以及了解更多相关的使用方法。如果需要的话，你还可以将 Hugo 部署在 GitHub 页面或任何 CDN 上。

* [Hugo](https://gohugo.io/)

#### 3、Hexo

![](/data/attachment/album/202010/01/123930nl3pb36963w3bbgt.jpg)

Hexo 是一个有趣的开源框架，基于 [Node.js](https://nodejs.org/en/)。像其他的工具一样，你可以用它搭建相当快速的网站，不仅如此，它还提供了丰富的主题和插件。

它还根据用户的每个需求提供了强大的 API 来扩展功能。如果你已经有一个网站，你可以用它的[迁移](https://hexo.io/api/migrator.html)扩展轻松完成迁移工作。

你可以参照[官方文档](https://hexo.io/docs/)或 [GitHub 页面](https://github.com/hexojs/hexo) 来使用 Hexo。

* [Hexo](https://hexo.io/)

#### 4、Gatsby

![](/data/attachment/album/202010/01/124101sjs5woz9ng2gu9z1.png)

Gatsby 是一个越来越流行的开源网站生成框架。它使用 [React.js](https://reactjs.org/) 来生成快速、界面优美的网站。

几年前在一个实验性的项目中，我曾经非常想尝试一下这个工具，它提供的成千上万的新插件和主题的能力让我印象深刻。与其他静态网站生成工具不同的是，你可以使用 Gatsby 生成一个网站，并在不损失任何功能的情况下获得静态网站的好处。

它提供了与很多流行的服务的整合功能。当然，你可以不使用它的复杂的功能，或将其与你选择的流行 CMS 配合使用，这也会很有趣。你可以查看他们的[官方文档](https://www.gatsbyjs.com/docs/)或它的 [GitHub 页面](https://github.com/gatsbyjs/gatsby)了解更多内容。

* [Gatsby](https://www.gatsbyjs.com/)

#### 5、VuePress

![](/data/attachment/album/202010/01/124010jzgtsy7kagj1jh1b.jpg)

VuePress 是由 [Vue.js](https://vuejs.org/) 支持的静态网站生成工具，而 Vue.js 是一个开源的渐进式 JavaScript 框架。

如果你了解 HTML、CSS 和 JavaScript，那么你可以无压力地使用 VuePress。你应该可以找到几个有用的插件和主题来为你的网站建设开个头。此外，看起来 Vue.js 的更新一直很活跃，很多开发者都在关注 Vue.js，这是一件好事。

你可以参照他们的[官方文档](https://vuepress.vuejs.org/guide/)和 [GitHub 页面](https://github.com/vuejs/vuepress)了解更多。

* [VuePress](https://vuepress.vuejs.org/)

#### 6、Nuxt.js

![](/data/attachment/album/202010/01/123943j0cste0w52553sb5.jpg)

Nuxt.js 使用了 Vue.js 和 Node.js，但它致力于模块化，并且有能力依赖服务端而非客户端。不仅如此，它的目标是为开发者提供直观的体验，并提供描述性错误，以及详细的文档等。

正如它声称的那样，在你用来搭建静态网站的所有工具中，Nuxt.js 可以做到功能和灵活性两全其美。他们还提供了一个 [Nuxt 线上沙盒](https://template.nuxtjs.org/)，让你不费吹灰之力就能直接测试它。

你可以查看它的 [GitHub 页面](https://github.com/nuxt/nuxt.js)和[官方网站](https://nuxtjs.org/)了解更多。

* [Nuxt.js](https://nuxtjs.org/)

#### 7、Docusaurus

![](/data/attachment/album/202010/01/123946tfoutihoxte8gbth.jpg)

Docusaurus 是一个有趣的开源静态网站生成工具，为搭建文档类网站量身定制。它还是 [Facebook 开源计划](https://opensource.facebook.com/)的一个项目。

Docusaurus 是用 React 构建的。你可以使用所有的基本功能，像文档版本管理、文档搜索和翻译大多是预先配置的。如果你想为你的产品或服务搭建一个文档网站，那么可以试试 Docusaurus。

你可以从它的 [GitHub 页面](https://github.com/facebook/docusaurus)和它的[官网](https://docusaurus.io/)获取更多信息。

* [Docusaurus](https://docusaurus.io/)

#### 8、Eleventy

![](/data/attachment/album/202010/01/123946wuzyaya8gdn8n8ey.png)

Eleventy 自称是 Jekyll 的替代品，旨在以更简单的方法来制作更快的静态网站。

它似乎很容易上手，而且它还提供了适当的文档来帮助你。如果你想找一个简单的静态网站生成工具，Eleventy 似乎会是一个有趣的选择。

你可以参照它的 [GitHub 页面](https://github.com/11ty/eleventy/)和[官网](https://www.11ty.dev/)来了解更多的细节。

* [Eleventy](https://www.11ty.dev/)

#### 9、Publii

![](/data/attachment/album/202010/01/124013psqkz5wsblsqbdy2.jpg)

Publii 是一个令人印象深刻的开源 CMS，它能使生成一个静态网站变得很容易。它是用 [Electron](https://www.electronjs.org) 和 Vue.js 构建的。如果有需要，你也可以把你的文章从 WorkPress 网站迁移过来。此外，它还提供了与 GitHub 页面、Netlify 及其它类似服务的一键同步功能。

如果你利用 Publii 生成一个静态网站，你还可以得到一个所见即所得的编辑器。你可以从[官网](https://getpublii.com/)下载它，或者从它的 [GitHub 页面](https://github.com/GetPublii/Publii)了解更多信息。

* [Publii](https://getpublii.com/)

#### 10、Primo

![](/data/attachment/album/202010/01/123954n50uuj0b151xsj1h.jpg)

一个有趣的开源静态网站生成工具，目前开发工作仍很活跃。虽然与其他的静态生成工具相比，它还不是一个成熟的解决方案，有些功能还不完善，但它是一个独特的项目。

Primo 旨在使用可视化的构建器帮你构建和搭建网站，这样你就可以轻松编辑和部署到任意主机上。

你可以参照[官网](https://primo.af/)或查看它的 [GitHub 页面](https://github.com/primo-app/primo-desktop)了解更多信息。

* [Primo](https://primo.af/)

### 结语

还有很多文章中没有列出的网站生成工具。然而，我试图提到最好的静态生成器，为您提供最快的加载时间，最好的安全性和令人印象深刻的灵活性。

列表中没有你最喜欢的工具？在下面的评论中告诉我。

---

via: <https://itsfoss.com/open-source-static-site-generators/>

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

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


#新闻拍一拍# 泄露的 Windows XP 代码经用户编译为可运行的系统

Summary: • Khronos 开源新 OpenCL SDK，并发布 OpenCL 3.0 规范 • 开发者再次尝试让摩托罗拉 68000 系列进入 LLVM 上游

![](/data/attachment/album/202010/01/133351fzc4pcv8rzkn84vb.jpg)

#### 泄露的 Windows XP 代码经用户编译为可运行的系统

上周在 4chan 网上泄露的 Windows XP 和 Windows Server 2003 源代码，在一位用户将代码编译成可运行的操作系统后，已被证实是真实的。NTDEV 是上周下载代码的数百万用户之一，他决定编译代码，自己去判断代码真假。根据网上分享的视频，这位业余 IT 技术人员在周末成功编译了 Windows XP 代码，昨天又成功编译了 Windows Server 2003。

来源：[zdnet](https://www.zdnet.com/article/windows-xp-leak-confirmed-after-user-compiles-the-leaked-code-into-a-working-os/)

>
> 拍一拍：这种实锤方法令人惊叹！
>
>
>

#### Khronos 开源新 OpenCL SDK，并发布 OpenCL 3.0 规范

OpenCL 3.0 主要变化还是让 OpenCL 2 的功能成为可选，所以 OpenCL 3.0 适合更多的设备和环境，但也增加了新的扩展，围绕着能够在给定的 OpenCL 驱动/设备上获得 UUID，还增加了异步 DMA 功能，有助于 OpenCL 3.0 对嵌入式处理器的工作。

来源：[cnbeta](https://www.cnbeta.com/articles/tech/1035659.htm)

>
> 拍一拍：以防你不知道，OpenCL 是第一个面向异构系统通用目的并行编程的开放式、免费标准，也是一个统一的编程环境。
>
>
>

#### 开发者再次尝试让摩托罗拉 68000 系列进入 LLVM 上游

摩托罗拉 68000 系列（M68k）处理器在 80 年代就已经出现了，这要归功于早期苹果 Macintosh 电脑。到 2020 年，M68k 仍然是复古电脑爱好者和业余爱好者的热门目标。这已经不是第一次提出对 LLVM 的 M68k 支持了。

来源：[phoronix](https://www.phoronix.com/scan.php?page=news_item&px=LLVM-Motorola-6800-Series-2020)

>
> 拍一拍：这是一种计算机考古需要。
>
>
>


如何在 Linux 中扩展/增加 LVM 大小（逻辑卷调整）

Summary: 扩展逻辑卷非常简单，只需要很少的步骤，而且不需要卸载某个逻辑卷就可以在线完成。

我觉得文章没有很好地区分卷组和卷的概念，新手初看有点迷糊。<br />
我现在觉得vg代表卷组，lv代表卷

扩展逻辑卷非常简单，只需要很少的步骤，而且不需要卸载某个逻辑卷就可以在线完成。

![](/data/attachment/album/202010/01/234018qgnwilmmzom8xarb.jpg)

扩展逻辑卷非常简单，只需要很少的步骤，而且不需要卸载某个逻辑卷就可以在线完成。

LVM 的主要目的是灵活的磁盘管理，当你需要的时候，可以很方便地调整、扩展和缩小逻辑卷的大小。

如果你是逻辑卷管理（LVM） 新手，我建议你从我们之前的文章开始学习。

* **第一部分：[如何在 Linux 中创建/配置 LVM（逻辑卷管理）](/article-12670-1.html)**

![](/data/attachment/album/202010/01/233946ybwbnw4zanjbn00e.jpeg)

扩展逻辑卷涉及到以下步骤：

* 检查逻辑卷（LV）所在的卷组中是否有足够的未分配磁盘空间
* 如果有，你可以使用这些空间来扩展逻辑卷
* 如果没有，请向系统中添加新的磁盘或 LUN
* 将物理磁盘转换为物理卷（PV）
* 扩展卷组
* 增加逻辑卷大小
* 扩大文件系统
* 检查扩展的文件系统大小

### 如何创建 LVM 物理卷？

使用 `pvcreate` 命令创建 LVM 物理卷。

当在操作系统中检测到磁盘，使用 `pvcreate` 命令初始化 LVM 物理卷：

```
# pvcreate /dev/sdc
Physical volume "/dev/sdc" successfully created

```

**请注意：**

* 上面的命令将删除磁盘 `/dev/sdc` 上的所有数据。
* 物理磁盘可以直接添加到 LVM 物理卷中，而不是磁盘分区。

使用 `pvdisplay` 命令来显示你所创建的物理卷：

```
# pvdisplay /dev/sdc

"/dev/sdc" is a new physical volume of "10.00 GiB"
--- NEW Physical volume ---
PV Name               /dev/sdc
VG Name
PV Size               10.00 GiB
Allocatable           NO
PE Size               0
Total PE              0
Free PE               0
Allocated PE          0
PV UUID               69d9dd18-36be-4631-9ebb-78f05fe3217f

```

### 如何扩展卷组

使用以下命令在现有的卷组（VG）中添加一个新的物理卷：

```
# vgextend vg01 /dev/sdc
Volume group "vg01" successfully extended

```

使用 `vgdisplay` 命令来显示你所创建的物理卷：

```
# vgdisplay vg01
--- Volume group ---
VG Name              vg01
System ID
Format               lvm2
Metadata Areas       2
Metadata Sequence No 1
VG Access            read/write
VG Status            resizable
MAX LV               0
Cur LV               0
Open LV              0
Max PV               0
Cur PV               2
Act PV               2
VG Size              14.99 GiB
PE Size              4.00 MiB
Total PE             3840
Alloc PE / Size      1280 / 4.99
Free PE / Size       2560 / 9.99 GiB
VG UUID              d17e3c31-e2c9-4f11-809c-94a549bc43b7

```

### 如何扩展逻辑卷？

使用以下命令增加现有逻辑卷大小。

**逻辑卷扩展（`lvextend`）的常用语法：**

```
lvextend [要增加的额外空间] [现有逻辑卷名称]

```

使用下面的命令将现有的逻辑卷增加 10GB：

```
# lvextend -L +10G /dev/mapper/vg01-lv002
Rounding size to boundary between physical extents: 5.90 GiB
Size of logical volume vg01/lv002 changed from 5.00 GiB (1280 extents) to 15.00 GiB (3840 extents).
Logical volume var successfully resized

```

使用 PE 大小来扩展逻辑卷：

```
# lvextend -l +2560 /dev/mapper/vg01-lv002

```

要使用百分比（%）扩展逻辑卷，请使用以下命令：

```
# lvextend -l +40%FREE /dev/mapper/vg01-lv002

```

现在，逻辑卷已经扩展，你需要调整文件系统的大小以扩展逻辑卷内的空间：

对于基于 ext3 和 ext4 的文件系统，运行以下命令：

```
# resize2fs /dev/mapper/vg01-lv002

```

对于 xfs 文件系统，使用以下命令：

```
# xfs_growfs /dev/mapper/vg01-lv002

```

使用 [df 命令](https://www.2daygeek.com/linux-check-disk-space-usage-df-command/)查看文件系统大小：

```
# df -h /lvmtest1
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/vg01-lv002 15360M 34M 15326M 4% /lvmtest1

```

---

via: <https://www.2daygeek.com/extend-increase-resize-lvm-logical-volume-in-linux/>

作者：[Magesh Maruthamuthu](https://www.2daygeek.com/author/magesh/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


用 testdisk 恢复 Linux 上已删除的文件

>
> 这篇文章介绍了 testdisk，这是恢复最近删除的文件（以及用其他方式修复分区）的工具之一，非常方便。
>
>
>

![](/data/attachment/album/202010/02/003801ijb4e4lqrzqeanjz.jpg)

当你在 Linux 系统上删除一个文件时，它不一定会永远消失，特别是当你最近才刚刚删除了它的时候。

除非你用 `shred` 等工具把它擦掉，否则数据仍然会放在你的磁盘上 —— 而恢复已删除文件的最佳工具之一 `testdisk` 可以帮助你拯救它。虽然 `testdisk` 具有广泛的功能，包括恢复丢失或损坏的分区和使不能启动磁盘可以重新启动，但它也经常被用来恢复被误删的文件。

在本篇文章中，我们就来看看如何使用 `testdisk` 恢复已删除的文件，以及该过程中的每一步是怎样的。由于这个过程需要不少的步骤，所以当你做了几次之后，你可能会觉得操作起来会更加得心应手。

### 安装 testdisk

可以使用 `apt install testdisk` 或 `yum install testdisk` 等命令安装 `testdisk`。有趣的是，它不仅是一个 Linux 工具，而且还适用于 MacOS、Solaris 和 Windows。

文档可在 [cgsecurity.org](https://www.cgsecurity.org/testdisk.pdf) 中找到。

### 恢复文件

首先，你必须以 `root` 身份登录，或者有 `sudo` 权限才能使用 `testdisk`。如果你没有 `sudo` 访问权限，你会在这个过程一开始就被踢出，而如果你选择创建了一个日志文件的话，最终会有这样的消息：

```
TestDisk exited normally.
jdoe is not in the sudoers file.  This incident will be reported.

```

当你用 `testdisk` 恢复被删除的文件时，你最终会将恢复的文件放在你启动该工具的目录下，而这些文件会属于 `root`。出于这个原因，我喜欢在 `/home/recovery` 这样的目录下启动。一旦文件被成功地还原和验证，就可以将它们移回它们的所属位置，并将它们的所有权也恢复。

在你可以写入的选定目录下开始：

```
$ cd /home/recovery
$ testdisk

```

`testdisk` 提供的第一页信息描述了该工具并显示了一些选项。至少在刚开始，创建个日志文件是个好主意，因为它提供的信息可能会被证明是有用的。下面是如何做的：

```
Use arrow keys to select, then press Enter key:
>[ Create ] Create a new log file
 [ Append ] Append information to log file
 [ No Log ] Don’t record anything

```

左边的 `>` 以及你看到的反转的字体和背景颜色指出了你按下回车键后将使用的选项。在这个例子中，我们选择了创建日志文件。

然后会提示你输入密码（除非你最近使用过 `sudo`）。

下一步是选择被删除文件所存储的磁盘分区（如果没有高亮显示的话）。根据需要使用上下箭头移动到它。然后点两次右箭头，当 “Proceed” 高亮显示时按回车键。

```
Select a media (use Arrow keys, then press Enter):
 Disk /dev/sda - 120 GB / 111 GiB - SSD2SC120G1CS1754D117-551
>Disk /dev/sdb - 500 GB / 465 GiB - SAMSUNG HE502HJ
 Disk /dev/loop0 - 13 MB / 13 MiB (RO)
 Disk /dev/loop1 - 101 MB / 96 MiB (RO)
 Disk /dev/loop10 - 148 MB / 141 MiB (RO)
 Disk /dev/loop11 - 36 MB / 35 MiB (RO)
 Disk /dev/loop12 - 52 MB / 49 MiB (RO)
 Disk /dev/loop13 - 78 MB / 75 MiB (RO)
 Disk /dev/loop14 - 173 MB / 165 MiB (RO)
 Disk /dev/loop15 - 169 MB / 161 MiB (RO)
>[Previous]  [  Next  ]  [Proceed ]  [  Quit  ]

```

在这个例子中，被删除的文件在 `/dev/sdb` 的主目录下。

此时，`testdisk` 应该已经选择了合适的分区类型。

```
Disk /dev/sdb - 500 GB / 465 GiB - SAMSUNG HE502HJ

Please select the partition table type, press Enter when done.
 [Intel  ] Intel/PC partition
>[EFI GPT] EFI GPT partition map (Mac i386, some x86_64...)
 [Humax  ] Humax partition table
 [Mac    ] Apple partition map (legacy)
 [None   ] Non partitioned media
 [Sun    ] Sun Solaris partition
 [XBox   ] XBox partition
 [Return ] Return to disk selection

```

在下一步中，按向下箭头指向 “[ Advanced ] Filesystem Utils”。

```
[ Analyse  ] Analyse current partition structure and search for lost partitions
>[ Advanced ] Filesystem Utils
 [ Geometry ] Change disk geometry
 [ Options  ] Modify options
 [ Quit     ] Return to disk selection

```

接下来，查看选定的分区。

```
Partition                  Start        End    Size in sectors
> 1 P Linux filesys. data         2048  910155775  910153728 [drive2]

```

然后按右箭头选择底部的 “[ List ]”，按回车键。

```
[  Type  ]  [Superblock] >[  List  ]  [Image Creation]  [  Quit  ]

```

请注意，它看起来就像我们从根目录 `/` 开始，但实际上这是我们正在工作的文件系统的基点。在这个例子中，就是 `/home`。

```
Directory /   <== 开始点

>drwxr-xr-x     0     0      4096 23-Sep-2020 17:46 .
 drwxr-xr-x     0     0      4096 23-Sep-2020 17:46 ..
 drwx———     0     0     16384 22-Sep-2020 11:30 lost+found
 drwxr-xr-x  1008  1008      4096  9-Jul-2019 14:10 dorothy
 drwxr-xr-x  1001  1001      4096 22-Sep-2020 12:12 nemo
 drwxr-xr-x  1005  1005      4096 19-Jan-2020 11:49 eel
 drwxrwxrwx     0     0      4096 25-Sep-2020 08:08 recovery
...

```

接下来，我们按箭头指向具体的主目录。

```
drwxr-xr-x  1016  1016      4096 17-Feb-2020 16:40 gino
>drwxr-xr-x  1000  1000     20480 25-Sep-2020 08:00 shs

```

按回车键移动到该目录，然后根据需要向下箭头移动到子目录。注意，如果选错了，可以选择列表顶部附近的 `..` 返回。

如果找不到文件，可以按 `/`（就像在 `vi` 中开始搜索时一样），提示你输入文件名或其中的一部分。

```
Directory /shs     <== current location
                                                   Previous
...
 -rw-rw-r—  1000  1000       426  8-Apr-2019 19:09 2-min-topics
>-rw-rw-r—  1000  1000     24667  8-Feb-2019 08:57 Up_on_the_Roof.pdf

```

一旦你找到需要恢复的文件，按 `c` 选择它。

注意：你会在屏幕底部看到有用的说明：

```
Use Left arrow to go back, Right to change directory, h to hide deleted files
    q to quit, : to select the current file, a to select all files
    C to copy the selected files, c to copy the current file <==

```

这时，你就可以在起始目录内选择恢复该文件的位置了（参见前面的说明，在将文件移回原点之前，先在一个合适的地方进行检查）。在这种情况下，`/home/recovery` 目录没有子目录，所以这就是我们的恢复点。

注意：你会在屏幕底部看到有用的说明：

```
Please select a destination where /shs/Up_on_the_Roof.pdf will be copied.
Keys: Arrow keys to select another directory
      C when the destination is correct
      Q to quit
Directory /home/recovery    <== 恢复位置

```

一旦你看到 “Copy done! 1 ok, 0 failed” 的绿色字样，你就会知道文件已经恢复了。

在这种情况下，文件被留在 `/home/recovery/shs` 下（起始目录，附加所选目录）。

在将文件移回原来的位置之前，你可能应该先验证恢复的文件看起来是否正确。确保你也恢复了原来的所有者和组，因为此时文件由 root 拥有。

**注意：** 对于文件恢复过程中的很多步骤，你可以使用退出（按 `q` 或“[ Quit ]”）来返回上一步。如果你愿意，可以选择退出选项一直回到该过程中的第一步，也可以选择按下 `^c` 立即退出。

#### 恢复训练

使用 `testdisk` 恢复文件相对来说没有痛苦，但有些复杂。在恐慌时间到来之前，最好先练习一下恢复文件，让自己有机会熟悉这个过程。

---

via: <https://www.networkworld.com/article/3575524/recovering-deleted-files-on-linux-with-testdisk.html>

作者：[Sandra Henry-Stocker](https://www.networkworld.com/author/Sandra-Henry_Stocker/) 选题：[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: • Outlook 服务瘫痪故障开始影响其全球用户 • IBM 将开源太空垃圾防撞系统

![](/data/attachment/album/202010/02/062130njeoj3cc8cgk9cz1.jpg)

#### 美财政部：向勒索软件交付赎金可能被罚

美国财政部警告说，如果从攻击中获利的勒索者已经受到了美国经济制裁，那么受到勒索软件伤害的公司和帮忙与勒索软件勒索者谈判的公司可能面临美国联邦政府的巨额罚款。美国财政部对一些人和机构实施了经济制裁，有效冻结了这些人（受美国管辖）的所有财产和利益，并将与他们交易定为犯罪。这包括朝鲜、伊朗、俄罗斯等一些人或机构。而向这些被制裁对象缴纳赎金可能面临违反 OFAC（外国资产控制办公室）法规的风险。

来源：[krebsonsecurity](https://krebsonsecurity.com/2020/10/ransomware-victims-that-pay-up-could-incur-steep-fines-from-uncle-sam/)

>
> 拍一拍：这就面临更糟糕的局面，有可能遭受勒索后，你只能被“撕票”。
>
>
>

#### Outlook 服务瘫痪故障开始影响其全球用户

在印度之后，世界各地的 Outlook 在线用户都报告了访问微软服务的问题。微软最初表示，印度的用户是受影响的主要群体。不过，该公司随后在微软 365 状态推特账号上证实，该问题正在影响全球用户。微软正在调查最近对其服务的更新，以确定问题的潜在来源。然而，没有迹象表明何时会解决这个问题。

来源：[cnbeta](https://www.cnbeta.com/articles/tech/1035967.htm)

>
> 拍一拍：大规模云技术服务如火如荼的发展的背后，就是可能会造成大规模的服务故障。
>
>
>

#### IBM 将开源太空垃圾防撞系统

太空已经是一个相当混乱的地方，有成千上万的人造物体以每秒 8000 米的速度飞行在地球上空飞来飞去。这使得在发射前很久就能预测人造物体的轨迹并做出相应的计划变得极为重要。IBM 和德克萨斯大学 Moriba Jah 博士的一个空间态势感知（SSA）开源项目，目的是确定和预测这些太空垃圾的位置。这一项目背后的想法是，当物理模型错误地预测未来位置时，机器学习可以创建学习模型。

来源：[zdnet](https://www.zdnet.com/article/ibm-to-open-source-space-junk-collision-avoidance/)

>
> 拍一拍：AI 应用到这个这个领域是很有意义的。
>
>
>


使用 qemu-kvm 安装和运行 Vagrant

![](/data/attachment/album/202010/03/094655p9j204bt8em0plje.png)

Vagrant 是一个出色的工具，DevOps 专业人员、程序员、系统管理员和普通极客来使用它来建立可重复的基础架构来进行开发和测试。引用自它的网站：

>
> Vagrant 是用于在单工作流程中构建和管理虚拟机环境的工具。凭借简单易用的工作流程并专注于自动化，Vagrant 降低了开发环境的设置时间，提高了生产效率，并使“在我的机器上可以工作”的借口成为过去。
>
>
> 如果你已经熟悉 Vagrant 的基础知识，那么该文档为所有的功能和内部结构提供了更好的参考。
>
>
> Vagrant 提供了基于行业标准技术构建的、易于配置、可复制、可移植的工作环境，并由一个一致的工作流程控制，帮助你和你的团队最大限度地提高生产力和灵活性。
>
>
> <https://www.vagrantup.com/intro>
>
>
>

本指南将逐步介绍使 Vagrant 在基于 Fedora 的计算机上工作所需的步骤。

我从最小化安装 Fedora 服务器开始，因为这样可以减少宿主机操作系统的内存占用，但如果你已经有一台可以使用的 Fedora 机器，无论是服务器还是工作站版本，那么也没问题。

### 检查机器是否支持虚拟化

```
$ sudo lscpu | grep Virtualization

```

```
Virtualization:                  VT-x
Virtualization type:             full

```

### 安装 qemu-kvm

```
sudo dnf install qemu-kvm libvirt libguestfs-tools virt-install rsync

```

### 启用并启动 libvirt 守护进程

```
sudo systemctl enable --now libvirtd

```

### 安装 Vagrant

```
sudo dnf install vagrant

```

### 安装 Vagrant libvirtd 插件

```
sudo vagrant plugin install vagrant-libvirt

```

### 添加一个 box

```
vagrant box add fedora/32-cloud-base --provider=libvirt

```

（LCTT 译注：以防你不知道，box 是 Vagrant 中的一种包格式，Vagrant 支持的任何平台上的任何人都可以使用盒子来建立相同的工作环境。）

### 创建一个最小化的 Vagrantfile 来测试

```
$ mkdir vagrant-test
$ cd vagrant-test
$ vi Vagrantfile

```

```
Vagrant.configure("2") do |config|
  config.vm.box = "fedora/32-cloud-base"
end

```

**注意文件名和文件内容的大小写。**

### 检查文件

```
vagrant status

```

```
Current machine states:

default not created (libvirt)

The Libvirt domain is not created. Run 'vagrant up' to create it.

```

### 启动 box

```
vagrant up

```

### 连接到你的新机器

```
vagrant ssh

```

完成了。现在你的 Fedora 机器上 Vagrant 可以工作了。

要停止该机器，请使用 `vagrant halt`。这只是简单地停止机器，但保留虚拟机和磁盘。 要关闭并删除它，请使用 `vagrant destroy`。这将删除整个机器和你在其中所做的任何更改。

### 接下来的步骤

在运行 `vagrant up` 命令之前，你不需要下载 box。你可以直接在 Vagrantfile 中指定 box 和提供者，如果还没有的话，Vagrant 会下载它。下面是一个例子，它还设置了内存量和 CPU 数量：

```
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "fedora/32-cloud-base"
  config.vm.provider :libvirt do |libvirt|
    libvirt.cpus = 1
    libvirt.memory = 1024
  end
end

```

关于使用 Vagrant、创建你自己的机器和使用不同 box 的更多信息，请参见官方文档 <https://www.vagrantup.com/docs>。

有一个庞大的仓库，你可以随时下载使用这些 box，它们的的官方仓库是 Vagrant Cloud - <https://app.vagrantup.com/boxes/search>。这里有些是基本的操作系统，有些提供完整的功能，如数据库、网络服务器等。

---

via: <https://fedoramagazine.org/vagrant-qemukvm-fedora-devops-sysadmin/>

作者：[Andy Mott](https://fedoramagazine.org/author/amott/) 选题：[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/) 荣誉推出


微软帮我在 Windows 10 电脑上安装了 Ubuntu Linux

配图好评，哈哈，笑死我了

![](/data/attachment/album/202010/03/111045ecl3b81041jbi0ib.jpg)

>
> 很多读过我们的技术文章评论的人都知道，每个技术问题的答案都是“切换到 Linux”。而如果你对 Linux 是什么以及它是如何工作的感到好奇，微软可以提供帮助。
>
>
>

如果你曾经经历过痛苦的 Windows 更新，或者不敢置信地看着你的 MacBook 慢到像爬行，并将其风扇切换到巨型喷气式飞机起飞模式，你知道有一个也只有一个答案来解决你的困境：“切换到 Linux”。

当然，我是开玩笑的，但如果你浏览一下这些技术评论，你会发现这个建议是认真的，有一支开源布道者大军定期宣讲圣莱纳斯的福音，以回应关于其他平台的哪怕是最模糊的相关新闻。

你知道吗？我认为那些评论者的观点是合理的。任何有志于了解现代计算环境的人都应该对他们经常使用的操作系统平台以外的平台有一些经验，因为今天你在 Windows、MacOS 和 Linux 中看到的很多东西都来自于相同的 DNA。

为了跟上 Linux 的新动向，我自己每隔一两年就会进行一次这样的练习。所以，想象一下我的惊讶吧，今年我能够在几分钟内搭建一个功能完善的 Ubuntu Linux 机器，而不干扰我当前的 Windows 10 设置。更令人惊讶的是，微软为此做了大部分的工作。

使这一切成为可能的魔法是每台运行 Windows 10 专业版或企业版的 PC 所包含的 Hyper-V 虚拟化软件。（对不起，Windows 10 家庭用户，如果你想玩这些，你得先升级）。用 Hyper-V 的“快速创建”陈列栏，只需点击几下就可以建立一个新的虚拟机，其中包括了独立的 Ubuntu 镜像，而且不是一个而是三个，包括新的 Ubuntu 20.04 版本。

![](/data/attachment/album/202010/03/111247iy21ei64nxqqay6y.jpg)

*Hyper-V 快速创建工具包括了三个 Ubuntu Linux 版本*

最重要的是，这些自定义镜像能够在 Hyper-V 增强会话中运行，这意味着你可以选择自定义的显示分辨率，或者在全屏中运行，甚至跨越多个显示器，其性能接近于在裸机上运行的性能。在增强型会话中，你的虚拟机可以共享主机上的 Windows 剪贴板、本地存储和音频硬件。

一旦你把一切都弄好了，你就可以在全屏模式下启动 Ubuntu 虚拟机，并与它一起工作，就像 Windows 10 宿主机不存在一样。

唉，关于所有东西都能正常工作的那部分话并不是说说而已。好消息是，两年前的 Ubuntu 18.04.3 长期支持（LTS）版本工作得很完美，不需要任何操作。但两个较新的版本却让我欲哭无泪。我需要手动编辑一个受保护的 Linux 配置文件，然后才能让增强的会话在最新的 Ubuntu 版本（20.04）中工作，19.10 版本的虚拟机挂了好几次，至少需要重启十几次（包括几次硬重置）才能如期工作。

不过，在一切都结束后，我还是有了三个可以工作的虚拟机，让我对 Ubuntu Linux 中的新功能有了一个相当不错的印象。

* 补充更新，2020 年 6 月 5 日。[通过 Twitter](https://twitter.com/unixterminal/status/1268918077141528579)，@Canonical 的 Ubuntu on WSL 和 Hyper-V 的开发布道师 Hayden Barnes 说，“我们知道 19.10 和 20.04 中的 xrdp bug。20.04 镜像将在即将到来的 20.04.1 LTS 更新中进行修补。19.10 已经接近 EOL，将被放弃。"
* 补充更新 2，2020 年 10 月 1 日。20.04.1 LTS 桌面 Ubuntu 镜像于 2020 年 7 月 31 日发布，但截至 10 月 1 日，它还没有被整合到 Hyper-V 中的快速创建镜像中。

另外，正如我的同事 Mary Branscombe 所指出的那样，包括 Home 在内的所有版本的 Windows 10 都提供了对 Windows Subsystem for Linux（WSL）的访问，该系统在轻量级虚拟机中运行 Linux 内核，并且从 Windows 10 的 2004 版本开始，该系统已经全新升级为 WSL2。正如 WSL2 文档中明确指出的那样，这并不是传统的虚拟机体验，它最适合那些希望获得命令行体验并能够运行 Bash shell 脚本和 GNU/Linux 命令行应用程序的开发者。在 WSL2 环境中运行图形应用程序的能力已列入微软的路线图，应该会在 2020 年底或 2021 年初由 Windows Insiders 进行测试。

如果你想尝试在 Windows 10 中设置一个或多个 Ubuntu 虚拟机进行自己的实验，请看我的另外一篇文章。

---

via: <https://www.zdnet.com/article/microsoft-helped-me-install-ubuntu-linux-on-my-windows-10-pc-and-its-actually-pretty-good/>

作者：[Ed Bott](https://www.zdnet.com/meet-the-team/us/ed-bott/) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](/article-12674-1.html) 荣誉推出


如何在 Windows 10 中设置 Ubuntu 虚拟机

![](/data/attachment/album/202010/03/114551tnlr0guvn0vlnsg0.jpg)

如果你想尝试在 Windows 10 中设置一个或多个 Ubuntu 虚拟机进行自己的实验，请按照以下步骤进行。

### 1、启用 Hyper-V 平台

所有 64 位 Windows 10 专业版、企业版和教育版都内置了这个<ruby> 管理程序 <rp>  （ </rp> <rt>  hypervisor </rt> <rp>  ） </rp></ruby>，但默认情况下并没有启用。在在旧电脑上启用该功能之前，你可能需要打开一些固件选项。有关完整说明，请参阅“[Windows 10：如何启用 Hyper-V 并创建虚拟机](https://www.zdnet.com/article/windows-10-tip-how-to-enable-hyper-v-and-create-virtual-machines/)”。

### 2、使用快速创建工具来下载并安装 Ubuntu 虚拟机

Hyper-V 提供了两种创建新虚拟机的方法。老式的方法是通过一个繁琐的多步骤向导来点击。更简单的解决方案是使用 Hyper-V 的“快速创建”工具。你可以在 Hyper-V 管理器的右侧窗格中找到一个快捷方式，或者你可以直接在 Windows 10 搜索框搜索 `Vmcreate.exe` 命令打开它。

从左侧的陈列栏中选择一个 Ubuntu 版本，然后点击“创建虚拟机”按钮。这将需要下载 1.6GB（18.04.3 LTS）到 2GB（更近的版本）。下载完成后，该“快速创建”功能会提取镜像、配置虚拟机，然后显示一个对话框，告诉你虚拟机已成功创建。

### 3、调整一些设置

虽然你可以通过点击“连接”立即开始，但如果你的 Windows 10 主机上有足够的资源，我建议你点击“编辑设置”，以便你可以先进行一些调整。

![](/data/attachment/album/202010/03/112217zw8enlyfekwdl570.jpg)

*我建议你在第一次运行虚拟机之前编辑一些设置*

从新虚拟机的硬件列表中选择“内存”，然后将分配的 RAM 从其默认值 2048 改为 4096，这应该会带来更好的性能。如果你想要一个固定数量的虚拟内存，你也可以禁用动态内存。你可能还希望将系统磁盘的大小从其默认的 12GB 提升到更多。单击“硬盘驱动器”，然后单击“编辑”，并按照提示操作，直到进行到“扩展”命令那一步。

![](/data/attachment/album/202010/03/112747gbpcictiqcioz6uk.jpg)

*根据需要调整分配给你的 Linux 虚拟机的内存*

最后，在“管理”标题下，单击“检查点”，然后清除“使用自动检查点”框。（如果你希望能够回滚更改，可以随时手动创建检查点。）

这些调整完毕后，可以关闭“设置”对话框，并首次打开虚拟机。

### 4、设置你的用户账户

运行 Ubuntu 的系统配置，选择你的默认语言、时区等。当你进入创建登录凭证的界面时，将默认设置保留在“需要我的密码才能登录”。

虽然听起来很方便，但不要启用自动登录功能。如果你选择此选项，将无法使用增强型会话登录，并且你的虚拟机将被迫在一个相对较小的窗口中运行，其默认分辨率无法轻松更改。

这也是运行“系统更新管理器”应用程序并获取最新 Ubuntu 更新的好时机。（点击左下角的 Ubuntu 图标，使用搜索框找到该应用。）

### 5、使用增强版会话登录

如果你在“虚拟机连接”窗口中看到这个对话框，你就会知道已经启动了一个增强的会话：

![](/data/attachment/album/202010/03/113335lyfsypf8sfaaaa8p.jpg)

*将该滑块移至最右侧，以全屏方式运行虚拟机*

滑动开关来选择你的显示分辨率。我喜欢将滑块向右移动，到全屏位置。如果你有多个显示器，并希望虚拟机跨越它们，请选择 “使用所有我的显示器”复选框。单击“连接”继续，打开 Xrdp 登录对话框，如图所示：

![](/data/attachment/album/202010/03/113335l7f9ce1ffw3trwf7.jpg)

*登录到 Xrdp 以启动增强型 Hyper-V 会话*

输入你在创建 Ubuntu Linux 账户时设置的用户名和密码，然后点击“确定”。假设大神们没有生气，你就会登录到你的虚拟机上，可以开始工作了。如果你还没有在你要连接的会话上登录，可能需要第二次输入密码。（如果你得到一个黑屏，尝试按回车键。）

如果你使用 18.04.3 LTS 虚拟机，所有这些都应该可以正常工作。如果你使用的是 Ubuntu 19.10，你可能要经历我所做的同样令人沮丧的一次次重启，然后一切才能正常工作。

对于 Ubuntu 20.04 来说，在基本会话中一切都很正常，但是在我编辑 `/etc/xrdp` 中的 `Xrdp.ini` 文件之前，运行增强会话的选项是不可用的。这是一个受保护的位置，所以我不得不用管理员的凭证打开一个编辑器，这个任务既不直观也不容易。

我必须修改 ini 文件中的两行内容。首先，把 `port=3389` 改为 `port=vsock://-1:3389`。然后把 `use_vsock=true` 改为 `use_vsock=false`。关闭虚拟机连接窗口并再次连接到虚拟机，你应该可以启动增强型会话。但你可能还需要召唤一些小神灵，烧一些香。毕竟，这是 Linux。

（正如我之前所提到的，Canonical/微软团队将在 20.04.1 更新中对该镜像进行修复，所以到时候 xrdp.ini 文件不需要编辑了。）

而你现在就只能靠自己了。你必须找到能让你完成实际工作的生产力软件。如果你像我一样，主要使用微软的服务，准备好接受一大堆的挫折吧。

微软目前没有任何适用于 Linux 的 Office 应用程序，所以你需要从 Web 浏览器访问你的文件。(是的，有 Linux 的 Office 克 隆版，包括 LibreOffice 套件，它是微软 Office 2007 的忠实克隆版。它……勉强够用。)

在 Ubuntu Linux 20.04 中，我可以直接连接 Google Drive，这样那些云端备份的文件就直接出现在文件应用中。还可以使用一个易于安装的 Dropbox 应用。但是 OneDrive 呢？那就不一定了。有一个开源的第三方同步客户端，但安装时需要半打以上的依赖包，我没有耐心也没有信心完成这个过程。

Ubuntu Linux 中的默认浏览器是 Mozilla Firefox，它在 18.04.3 LTS 版本上工作得足够好，但在 20.04 版本中却很卡顿。微软表示，它计划为 Linux 发布基于 Chromium 的新 Edge，但到目前为止，这还只是一个排期。从好的方面来看，有一个做得非常好的微软 Teams 客户端，它可以提供一个原生的 .deb 包来安装。

虽然整体体验出乎意料的流畅，但有太多的时刻，运行 Linux 对我来说感觉略显磕磕绊绊。比如在 20.04 版本中，Firefox 会将包文件下载到一个临时文件夹，在那里无法正常打开。当我弄清楚我必须把这些文件移到下载目录下，一切都好了。（请注意， Google Chrome 浏览器的下载会默认到正确的位置。）

另一方面，如果你的工作日是在谷歌生态系统中度过的，Linux 可能会感到非常舒适。在安装 Chrome 浏览器和设置 G Suite 之后，这个虚拟机基本上就是一个略显廉价的 Chromebook。

我是不是要改用 Linux 作为主要操作系统？呃，不会。但我会让这些虚拟机继续服务，并定期检查。因为你永远不知道会……

---

via: <https://www.zdnet.com/article/microsoft-helped-me-install-ubuntu-linux-on-my-windows-10-pc-and-its-actually-pretty-good/>

作者：[Ed Bott](https://www.zdnet.com/meet-the-team/us/ed-bott/) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](/article-12677-1.html) 荣誉推出


#新闻拍一拍# IonQ 宣称打造了迄今最强大的量子计算机

Summary: • Sourcegraph：开发人员现在管理的代码比 2010 年多 100 倍 • PHP 8.0 RC1 发布

![](/data/attachment/album/202010/03/130821qo2z6027ifei6omz.jpg)

#### IonQ 宣称打造了迄今最强大的量子计算机

IonQ 今日宣布，其已推出“具有 32 个完美的量子比特、且门误差相当低”的最新款量子计算机。参考 IBM 首选的量子基准测试，IonQ 有望达成 400 万的量子体积，因而可将之视作迄今最强大的量子计算机。该公司将当代硬件的量子比特从 11 位提升到了 32 位。不过更重要的是，他们提升了使用全部 32 个量子比特所需的保真度。根据不同应用，客户需要 80~150 个之间的高保真量子位和逻辑门才能体现量子优势。IonQ 的目标是让量子比特数年均至少翻一番。

来源：[cnbeta](https://www.cnbeta.com/articles/tech/1036117.htm)

>
> 拍一拍：虽然还距离实用尚有距离，但是量子技术的进步也太快了。
>
>
>

#### Sourcegraph：开发人员现在管理的代码比 2010 年多 100 倍

Sourcegraph CEO 接受媒体采访时表示，一个典型的组织或开发人员管理的代码量在过去十年中出现增长这并不奇怪，超一半的被调查开发者报告增长（以兆字节计算）超过了一百倍，大部分都来自于所使用的平台和工具的多样性的增加。

来源：[cnbeta](https://www.cnbeta.com/articles/tech/1036227.htm)

>
> 拍一拍：代码会越来越庞大复杂，必然会出现更好的管理工具，跟不上时代人类程序员，只能将工作交给 AI 来编程和管理。
>
>
>

#### PHP 8.0 RC1 发布

PHP 8.0 继续提升性能，引入了 PHP JIT 和其他优化，现在 PHP 已经是一头与 PHP 5.x 的迟钝时代截然不同的野兽了。PHP 8.0 还将 JSON 支持引入 PHP 核心而不是可选的扩展，改进了 GD 图像库、OpenSSL 加密消息语法支持、Zip 改进，以及其他无数的增强功能。预计 11 月底左右完成一般可用性版本。

来源：[cnbeta](https://www.cnbeta.com/articles/tech/1036089.htm)

>
> 拍一拍：PHP 的活力十足，不再是当年那个橡皮小鸭子了。
>
>
>


构建一个即时消息应用（四）：消息

![](/data/attachment/album/202010/04/114458z1p1188epequ686p.jpg)

本文是该系列的第四篇。

* [第一篇: 模式](/article-11396-1.html)
* [第二篇: OAuth](/article-11510-1.html)
* [第三篇: 对话](/article-12056-1.html)

在这篇文章中，我们将对端点进行编码，以创建一条消息并列出它们，同时还将编写一个端点以更新参与者上次阅读消息的时间。 首先在 `main()` 函数中添加这些路由。

```
router.HandleFunc("POST", "/api/conversations/:conversationID/messages", requireJSON(guard(createMessage)))
router.HandleFunc("GET", "/api/conversations/:conversationID/messages", guard(getMessages))
router.HandleFunc("POST", "/api/conversations/:conversationID/read_messages", guard(readMessages))

```

消息会进入对话，因此端点包含对话 ID。

### 创建消息

该端点处理对 `/api/conversations/{conversationID}/messages` 的 POST 请求，其 JSON 主体仅包含消息内容，并返回新创建的消息。它有两个副作用：更新对话 `last_message_id` 以及更新参与者 `messages_read_at`。

```
func createMessage(w http.ResponseWriter, r *http.Request) {
	var input struct {
		Content string `json:"content"`
	}
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	errs := make(map[string]string)
	input.Content = removeSpaces(input.Content)
	if input.Content == "" {
		errs["content"] = "Message content required"
	} else if len([]rune(input.Content)) > 480 {
		errs["content"] = "Message too long. 480 max"
	}
	if len(errs) != 0 {
		respond(w, Errors{errs}, http.StatusUnprocessableEntity)
		return
	}

	ctx := r.Context()
	authUserID := ctx.Value(keyAuthUserID).(string)
	conversationID := way.Param(ctx, "conversationID")

	tx, err := db.BeginTx(ctx, nil)
	if err != nil {
		respondError(w, fmt.Errorf("could not begin tx: %v", err))
		return
	}
	defer tx.Rollback()

	isParticipant, err := queryParticipantExistance(ctx, tx, authUserID, conversationID)
	if err != nil {
		respondError(w, fmt.Errorf("could not query participant existance: %v", err))
		return
	}

	if !isParticipant {
		http.Error(w, "Conversation not found", http.StatusNotFound)
		return
	}

	var message Message
	if err := tx.QueryRowContext(ctx, `
		INSERT INTO messages (content, user_id, conversation_id) VALUES
			($1, $2, $3)
		RETURNING id, created_at
	`, input.Content, authUserID, conversationID).Scan(
		&message.ID,
		&message.CreatedAt,
	); err != nil {
		respondError(w, fmt.Errorf("could not insert message: %v", err))
		return
	}

	if _, err := tx.ExecContext(ctx, `
		UPDATE conversations SET last_message_id = $1
		WHERE id = $2
	`, message.ID, conversationID); err != nil {
		respondError(w, fmt.Errorf("could not update conversation last message ID: %v", err))
		return
	}

	if err = tx.Commit(); err != nil {
		respondError(w, fmt.Errorf("could not commit tx to create a message: %v", err))
		return
	}

	go func() {
		if err = updateMessagesReadAt(nil, authUserID, conversationID); err != nil {
			log.Printf("could not update messages read at: %v\n", err)
		}
	}()

	message.Content = input.Content
	message.UserID = authUserID
	message.ConversationID = conversationID
	// TODO: notify about new message.
	message.Mine = true

	respond(w, message, http.StatusCreated)
}

```

首先，它将请求正文解码为包含消息内容的结构。然后，它验证内容不为空并且少于 480 个字符。

```
var rxSpaces = regexp.MustCompile("\\s+")

func removeSpaces(s string) string {
	if s == "" {
		return s
	}

	lines := make([]string, 0)
	for _, line := range strings.Split(s, "\n") {
		line = rxSpaces.ReplaceAllLiteralString(line, " ")
		line = strings.TrimSpace(line)
		if line != "" {
			lines = append(lines, line)
		}
	}
	return strings.Join(lines, "\n")
}

```

这是删除空格的函数。它遍历每一行，删除两个以上的连续空格，然后回非空行。

验证之后，它将启动一个 SQL 事务。首先，它查询对话中的参与者是否存在。

```
func queryParticipantExistance(ctx context.Context, tx *sql.Tx, userID, conversationID string) (bool, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	var exists bool
	if err := tx.QueryRowContext(ctx, `SELECT EXISTS (
		SELECT 1 FROM participants
		WHERE user_id = $1 AND conversation_id = $2
	)`, userID, conversationID).Scan(&exists); err != nil {
		return false, err
	}
	return exists, nil
}

```

我将其提取到一个函数中，因为稍后可以重用。

如果用户不是对话参与者，我们将返回一个 `404 NOT Found` 错误。

然后，它插入消息并更新对话 `last_message_id`。从这时起，由于我们不允许删除消息，因此 `last_message_id` 不能为 `NULL`。

接下来提交事务，并在 goroutine 中更新参与者 `messages_read_at`。

```
func updateMessagesReadAt(ctx context.Context, userID, conversationID string) error {
	if ctx == nil {
		ctx = context.Background()
	}

	if _, err := db.ExecContext(ctx, `
		UPDATE participants SET messages_read_at = now()
		WHERE user_id = $1 AND conversation_id = $2
	`, userID, conversationID); err != nil {
		return err
	}
	return nil
}

```

在回复这条新消息之前，我们必须通知一下。这是我们将要在下一篇文章中编写的实时部分，因此我在那里留一了个注释。

### 获取消息

这个端点处理对 `/api/conversations/{conversationID}/messages` 的 GET 请求。 它用一个包含会话中所有消息的 JSON 数组进行响应。它还具有更新参与者 `messages_read_at` 的副作用。

```
func getMessages(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	authUserID := ctx.Value(keyAuthUserID).(string)
	conversationID := way.Param(ctx, "conversationID")

	tx, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
	if err != nil {
		respondError(w, fmt.Errorf("could not begin tx: %v", err))
		return
	}
	defer tx.Rollback()

	isParticipant, err := queryParticipantExistance(ctx, tx, authUserID, conversationID)
	if err != nil {
		respondError(w, fmt.Errorf("could not query participant existance: %v", err))
		return
	}

	if !isParticipant {
		http.Error(w, "Conversation not found", http.StatusNotFound)
		return
	}

	rows, err := tx.QueryContext(ctx, `
		SELECT
			id,
			content,
			created_at,
			user_id = $1 AS mine
		FROM messages
		WHERE messages.conversation_id = $2
		ORDER BY messages.created_at DESC
	`, authUserID, conversationID)
	if err != nil {
		respondError(w, fmt.Errorf("could not query messages: %v", err))
		return
	}
	defer rows.Close()

	messages := make([]Message, 0)
	for rows.Next() {
		var message Message
		if err = rows.Scan(
			&message.ID,
			&message.Content,
			&message.CreatedAt,
			&message.Mine,
		); err != nil {
			respondError(w, fmt.Errorf("could not scan message: %v", err))
			return
		}

		messages = append(messages, message)
	}

	if err = rows.Err(); err != nil {
		respondError(w, fmt.Errorf("could not iterate over messages: %v", err))
		return
	}

	if err = tx.Commit(); err != nil {
		respondError(w, fmt.Errorf("could not commit tx to get messages: %v", err))
		return
	}

	go func() {
		if err = updateMessagesReadAt(nil, authUserID, conversationID); err != nil {
			log.Printf("could not update messages read at: %v\n", err)
		}
	}()

	respond(w, messages, http.StatusOK)
}

```

首先，它以只读模式开始一个 SQL 事务。检查参与者是否存在，并查询所有消息。在每条消息中，我们使用当前经过身份验证的用户 ID 来了解用户是否拥有该消息（`mine`）。 然后，它提交事务，在 goroutine 中更新参与者 `messages_read_at` 并以消息响应。

### 读取消息

该端点处理对 `/api/conversations/{conversationID}/read_messages` 的 POST 请求。 没有任何请求或响应主体。 在前端，每次有新消息到达实时流时，我们都会发出此请求。

```
func readMessages(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	authUserID := ctx.Value(keyAuthUserID).(string)
	conversationID := way.Param(ctx, "conversationID")

	if err := updateMessagesReadAt(ctx, authUserID, conversationID); err != nil {
		respondError(w, fmt.Errorf("could not update messages read at: %v", err))
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

```

它使用了与更新参与者 `messages_read_at` 相同的函数。

---

到此为止。实时消息是后台仅剩的部分了。请等待下一篇文章。

* [源代码](https://github.com/nicolasparada/go-messenger-demo)

---

via: <https://nicolasparada.netlify.com/posts/go-messenger-messages/>

作者：[Nicolás Parada](https://nicolasparada.netlify.com/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[gxlct008](https://github.com/gxlct008) 校对：[wxy](https://github.com/wxy)

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


使用 gosec 检查 Go 代码中的安全问题

Summary: 来学习下 Go 语言的安全检查工具 gosec。

来学习下 Go 语言的安全检查工具 gosec。

>
> 来学习下 Go 语言的安全检查工具 gosec。
>
>
>

![](/data/attachment/album/202010/04/125129bh4qxxsyqpvqjtx4.jpg)

[Go 语言](https://golang.org/)写的代码越来越常见，尤其是在容器、Kubernetes 或云生态相关的开发中。Docker 是最早采用 Golang 的项目之一，随后是 Kubernetes，之后大量的新项目在众多编程语言中选择了 Go。

像其他语言一样，Go 也有它的长处和短处（如安全缺陷）。这些缺陷可能会因为语言本身的缺陷加上程序员编码不当而产生，例如，C 代码中的内存安全问题。

无论它们出现的原因是什么，安全问题都应该在开发过程的早期修复，以免在封装好的软件中出现。幸运的是，静态分析工具可以帮你以更可重复的方式处理这些问题。静态分析工具通过解析用某种编程语言写的代码来找到问题。

这类工具中很多被称为 linter。传统意义上，linter 更注重的是检查代码中编码问题、bug、代码风格之类的问题，它们可能不会发现代码中的安全问题。例如，[Coverity](https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html) 是一个很流行的工具，它可以帮助寻找 C/C++ 代码中的问题。然而，也有一些工具专门用来检查源码中的安全问题。例如，[Bandit](https://pypi.org/project/bandit/) 可以检查 Python 代码中的安全缺陷。而 [gosec](https://github.com/securego/gosec) 则用来搜寻 Go 源码中的安全缺陷。`gosec` 通过扫描 Go 的 AST（<ruby> 抽象语法树 <rt>  abstract syntax tree </rt></ruby>）来检查源码中的安全问题。

### 开始使用 gosec

在开始学习和使用 `gosec` 之前，你需要准备一个 Go 语言写的项目。有这么多开源软件，我相信这不是问题。你可以在 GitHub 的 [热门 Golang 仓库](https://github.com/trending/go)中找一个。

本文中，我随机选了 [Docker CE](https://github.com/docker/docker-ce) 项目，但你可以选择任意的 Go 项目。

#### 安装 Go 和 gosec

如果你还没安装 Go，你可以先从仓库中拉取下来。如果你用的是 Fedora 或其他基于 RPM 的 Linux 发行版本：

```
$ dnf install golang.x86_64

```

如果你用的是其他操作系统，请参照 [Golang 安装](https://golang.org/doc/install)页面。

使用 `version` 参数来验证 Go 是否安装成功：

```
$ go version
go version go1.14.6 linux/amd64

```

运行 `go get` 命令就可以轻松地安装 `gosec`：

```
$ go get github.com/securego/gosec/cmd/gosec

```

上面这行命令会从 GitHub 下载 `gosec` 的源码，编译并安装到指定位置。在仓库的 `README` 中你还可以看到[安装该工具的其他方法](https://github.com/securego/gosec#install)。

`gosec` 的源码会被下载到 `$GOPATH` 的位置，编译出的二进制文件会被安装到你系统上设置的 `bin` 目录下。你可以运行下面的命令来查看 `$GOPATH` 和 `$GOBIN` 目录：

```
$ go env | grep GOBIN
GOBIN="/root/go/gobin"
$ go env | grep GOPATH
GOPATH="/root/go"

```

如果 `go get` 命令执行成功，那么 `gosec` 二进制应该就可以使用了：

```
$ ls -l ~/go/bin/
total 9260
-rwxr-xr-x. 1 root root 9482175 Aug 20 04:17 gosec

```

你可以把 `$GOPATH` 下的 `bin` 目录添加到 `$PATH` 中。这样你就可以像使用系统上的其他命令一样来使用 `gosec` 命令行工具（CLI）了。

```
$ which gosec
/root/go/bin/gosec
$

```

使用 `gosec` 命令行工具的 `-help` 选项来看看运行是否符合预期：

```
$ gosec -help

gosec - Golang security checker

gosec analyzes Go source code to look for common programming mistakes that
can lead to security problems.

VERSION: dev
GIT TAG:
BUILD DATE:

USAGE:

```

之后，创建一个目录，把源码下载到这个目录作为实例项目（本例中，我用的是 Docker CE）：

```
$ mkdir gosec-demo
$ cd gosec-demo/
$ pwd
/root/gosec-demo
$ git clone https://github.com/docker/docker-ce.git
Cloning into 'docker-ce'...
remote: Enumerating objects: 1271, done.
remote: Counting objects: 100% (1271/1271), done.
remote: Compressing objects: 100% (722/722), done.
remote: Total 431003 (delta 384), reused 981 (delta 318), pack-reused 429732
Receiving objects: 100% (431003/431003), 166.84 MiB | 28.94 MiB/s, done.
Resolving deltas: 100% (221338/221338), done.
Updating files: 100% (10861/10861), done.

```

代码统计工具（本例中用的是 `cloc`）显示这个项目大部分是用 Go 写的，恰好迎合了 `gosec` 的功能。

```
$ ./cloc /root/gosec-demo/docker-ce/
   10771 text files.
    8724 unique files.
    2560 files ignored.

-----------------------------------------------------------------------------------
Language                         files          blank        comment           code
-----------------------------------------------------------------------------------
Go                                7222         190785         230478        1574580
YAML                                37           4831            817         156762
Markdown                           529          21422              0          67893
Protocol Buffers                   149           5014          16562          10071

```

### 使用默认选项运行 gosec

在 Docker CE 项目中使用默认选项运行 `gosec`，执行 `gosec ./...` 命令。屏幕上会有很多输出内容。在末尾你会看到一个简短的 “Summary”，列出了浏览的文件数、所有文件的总行数，以及源码中发现的问题数。

```
$ pwd
/root/gosec-demo/docker-ce
$ time gosec ./...
[gosec] 2020/08/20 04:44:15 Including rules: default
[gosec] 2020/08/20 04:44:15 Excluding rules: default
[gosec] 2020/08/20 04:44:15 Import directory: /root/gosec-demo/docker-ce/components/engine/opts
[gosec] 2020/08/20 04:44:17 Checking package: opts
[gosec] 2020/08/20 04:44:17 Checking file: /root/gosec-demo/docker-ce/components/engine/opts/address_pools.go
[gosec] 2020/08/20 04:44:17 Checking file: /root/gosec-demo/docker-ce/components/engine/opts/env.go
[gosec] 2020/08/20 04:44:17 Checking file: /root/gosec-demo/docker-ce/components/engine/opts/hosts.go

# End of gosec run

Summary:
   Files: 1278
   Lines: 173979
   Nosec: 4
  Issues: 644

real    0m52.019s
user    0m37.284s
sys     0m12.734s
$

```

滚动屏幕你会看到不同颜色高亮的行：红色表示需要尽快查看的高优先级问题，黄色表示中优先级的问题。

#### 关于误判

在开始检查代码之前，我想先分享几条基本原则。默认情况下，静态检查工具会基于一系列的规则对测试代码进行分析，并报告出它们发现的*所有*问题。这是否意味着工具报出来的每一个问题都需要修复？非也。这个问题最好的解答者是设计和开发这个软件的人。他们最熟悉代码，更重要的是，他们了解软件会在什么环境下部署以及会被怎样使用。

这个知识点对于判定工具标记出来的某段代码到底是不是安全缺陷至关重要。随着工作时间和经验的积累，你会慢慢学会怎样让静态分析工具忽略非安全缺陷，使报告内容的可执行性更高。因此，要判定 `gosec` 报出来的某个问题是否需要修复，让一名有经验的开发者对源码做人工审计会是比较好的办法。

#### 高优先级问题

从输出内容看，`gosec` 发现了 Docker CE 的一个高优先级问题，它使用的是低版本的 TLS（<ruby> 传输层安全 <rt>  Transport Layer Security </rt> <rt> </rt></ruby>）。无论什么时候，使用软件和库的最新版本都是确保它更新及时、没有安全问题的最好的方法。

```
[/root/gosec-demo/docker-ce/components/engine/daemon/logger/splunk/splunk.go:173] - G402 (CWE-295): TLS MinVersion too low. (Confidence: HIGH, Severity: HIGH)
    172:
  > 173:        tlsConfig := &tls.Config{}
    174:

```

它还发现了一个弱随机数生成器。它是不是一个安全缺陷，取决于生成的随机数的使用方式。

```
[/root/gosec-demo/docker-ce/components/engine/pkg/namesgenerator/names-generator.go:843] - G404 (CWE-338): Use of weak random number generator (math/rand instead of crypto/rand) (Confidence: MEDIUM, Severity: HIGH)
    842: begin:
  > 843:        name := fmt.Sprintf("%s_%s", left[rand.Intn(len(left))], right[rand.Intn(len(right))])
    844:        if name == "boring_wozniak" /* Steve Wozniak is not boring */ {

```

#### 中优先级问题

这个工具还发现了一些中优先级问题。它标记了一个通过与 `tar` 相关的解压炸弹这种方式实现的潜在的 DoS 威胁，这种方式可能会被恶意的攻击者利用。

```
[/root/gosec-demo/docker-ce/components/engine/pkg/archive/copy.go:357] - G110 (CWE-409): Potential DoS vulnerability via decompression bomb (Confidence: MEDIUM, Severity: MEDIUM)
    356:
  > 357:                        if _, err = io.Copy(rebasedTar, srcTar); err != nil {
    358:                                w.CloseWithError(err)

```

它还发现了一个通过变量访问文件的问题。如果恶意使用者能访问这个变量，那么他们就可以改变变量的值去读其他文件。

```
[/root/gosec-demo/docker-ce/components/cli/cli/context/tlsdata.go:80] - G304 (CWE-22): Potential file inclusion via variable (Confidence: HIGH, Severity: MEDIUM)
    79:         if caPath != "" {
  > 80:                 if ca, err = ioutil.ReadFile(caPath); err != nil {
    81:                         return nil, err

```

文件和目录通常是操作系统安全的最基础的元素。这里，`gosec` 报出了一个可能需要你检查目录的权限是否安全的问题。

```
[/root/gosec-demo/docker-ce/components/engine/contrib/apparmor/main.go:41] - G301 (CWE-276): Expect directory permissions to be 0750 or less (Confidence: HIGH, Severity: MEDIUM)
    40:         // make sure /etc/apparmor.d exists
  > 41:         if err := os.MkdirAll(path.Dir(apparmorProfilePath), 0755); err != nil {
    42:                 log.Fatal(err)

```

你经常需要在源码中启动命令行工具。Go 使用内建的 exec 库来实现。仔细地分析用来调用这些工具的变量，就能发现安全缺陷。

```
[/root/gosec-demo/docker-ce/components/engine/testutil/fakestorage/fixtures.go:59] - G204 (CWE-78): Subprocess launched with variable (Confidence: HIGH, Severity: MEDIUM)
    58:
  > 59:              cmd := exec.Command(goCmd, "build", "-o", filepath.Join(tmp, "httpserver"), "github.com/docker/docker/contrib/httpserver")
    60:                 cmd.Env = append(os.Environ(), []string{

```

#### 低优先级问题

在这个输出中，gosec 报出了一个 `unsafe` 调用相关的低优先级问题，这个调用会绕开 Go 提供的内存保护。再仔细分析下你调用 `unsafe` 的方式，看看是否有被别人利用的可能性。

```
[/root/gosec-demo/docker-ce/components/engine/pkg/archive/changes_linux.go:264] - G103 (CWE-242): Use of unsafe calls should be audited (Confidence: HIGH, Severity: LOW)
    263:        for len(buf) > 0 {
  > 264:                dirent := (*unix.Dirent)(unsafe.Pointer(&buf[0]))
    265:                buf = buf[dirent.Reclen:]

[/root/gosec-demo/docker-ce/components/engine/pkg/devicemapper/devmapper_wrapper.go:88] - G103 (CWE-242): Use of unsafe calls should be audited (Confidence: HIGH, Severity: LOW)
    87: func free(p *C.char) {
  > 88:         C.free(unsafe.Pointer(p))
    89: }

```

它还标记了源码中未处理的错误。源码中出现的错误你都应该处理。

```
[/root/gosec-demo/docker-ce/components/cli/cli/command/image/build/context.go:172] - G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW)
    171:                err := tar.Close()
  > 172:                os.RemoveAll(dockerfileDir)
    173:                return err

```

### 自定义 gosec 扫描

使用 `gosec` 的默认选项会带来很多的问题。然而，经过人工审计，随着时间推移你会掌握哪些问题是不需要标记的。你可以自己指定排除和包含哪些测试。

我上面提到过，`gosec` 是基于一系列的规则从 Go 源码中查找问题的。下面是它使用的完整的[规则](https://github.com/securego/gosec#available-rules)列表：

* G101：查找硬编码凭证
* G102：绑定到所有接口
* G103：审计 `unsafe` 块的使用
* G104：审计未检查的错误
* G106：审计 `ssh.InsecureIgnoreHostKey` 的使用
* G107: 提供给 HTTP 请求的 url 作为污点输入
* G108: `/debug/pprof` 上自动暴露的剖析端点
* G109: `strconv.Atoi` 转换到 int16 或 int32 时潜在的整数溢出
* G110: 潜在的通过解压炸弹实现的 DoS
* G201：SQL 查询构造使用格式字符串
* G202：SQL 查询构造使用字符串连接
* G203：在 HTML 模板中使用未转义的数据
* G204：审计命令执行情况
* G301：创建目录时文件权限分配不合理
* G302：使用 `chmod` 时文件权限分配不合理
* G303：使用可预测的路径创建临时文件
* G304：通过污点输入提供的文件路径
* G305：提取 zip/tar 文档时遍历文件
* G306: 写到新文件时文件权限分配不合理
* G307: 把返回错误的函数放到 `defer` 内
* G401：检测 DES、RC4、MD5 或 SHA1 的使用
* G402：查找错误的 TLS 连接设置
* G403：确保最小 RSA 密钥长度为 2048 位
* G404：不安全的随机数源（`rand`）
* G501：导入黑名单列表：crypto/md5
* G502：导入黑名单列表：crypto/des
* G503：导入黑名单列表：crypto/rc4
* G504：导入黑名单列表：net/http/cgi
* G505：导入黑名单列表：crypto/sha1
* G601: 在 `range` 语句中使用隐式的元素别名

#### 排除指定的测试

你可以自定义 `gosec` 来避免对已知为安全的问题进行扫描和报告。你可以使用 `-exclude` 选项和上面的规则编号来忽略指定的问题。

例如，如果你不想让 `gosec` 检查源码中硬编码凭证相关的未处理的错误，那么你可以运行下面的命令来忽略这些错误：

```
$ gosec -exclude=G104 ./...
$ gosec -exclude=G104,G101 ./...

```

有时候你知道某段代码是安全的，但是 `gosec` 还是会报出问题。然而，你又不想完全排除掉整个检查，因为你想让 `gosec` 检查新增的代码。通过在你已知为安全的代码块添加 `#nosec` 标记可以避免 `gosec` 扫描。这样 `gosec` 会继续扫描新增代码，而忽略掉 `#nosec` 标记的代码块。

#### 运行指定的检查

另一方面，如果你只想检查指定的问题，你可以通过 `-include` 选项和规则编号来告诉 `gosec` 运行哪些检查：

```
$ gosec -include=G201,G202 ./...

```

#### 扫描测试文件

Go 语言自带对测试的支持，通过单元测试来检验一个元素是否符合预期。在默认模式下，`gosec` 会忽略测试文件，你可以使用 `-tests` 选项把它们包含进来：

```
gosec -tests ./...

```

#### 修改输出的格式

找出问题只是它的一半功能；另一半功能是把它检查到的问题以用户友好同时又方便工具处理的方式报告出来。幸运的是，`gosec` 可以用不同的方式输出。例如，如果你想看 JSON 格式的报告，那么就使用 `-fmt` 选项指定 JSON 格式并把结果保存到 `results.json` 文件中：

```
$ gosec -fmt=json -out=results.json ./...

$ ls -l results.json
-rw-r--r--. 1 root root 748098 Aug 20 05:06 results.json
$

         {
             "severity": "LOW",
             "confidence": "HIGH",
             "cwe": {
                 "ID": "242",
                 "URL": "https://cwe.mitre.org/data/definitions/242.html"
             },
             "rule_id": "G103",
             "details": "Use of unsafe calls should be audited",
             "file": "/root/gosec-demo/docker-ce/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go",
             "code": "304: \t// Cast to []byte\n305: \theader := *(*reflect.SliceHeader)(unsafe.Pointer(\u0026buf))\n306: \theader.      Len *= 8\n",
             "line": "305",
             "column": "36"
         },

```

### 用 gosec 检查容易被发现的问题

静态检查工具不能完全代替人工代码审计。然而，当代码量变大、有众多开发者时，这样的工具往往有助于以可重复的方式找出容易被发现的问题。它对于帮助新开发者识别和在编码时避免引入这些安全缺陷很有用。

---

via: <https://opensource.com/article/20/9/gosec>

作者：[Gaurav Kamathe](https://opensource.com/users/gkamathe) 选题：[lujun9972](https://github.com/lujun9972) 译者：[lxbowlf](https://github.com/lxbwolf) 校对：[wxy](https://github.com/wxy)

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


Windows 的 Linux 子系统（WSL）即将带来图形化应用

Summary: 按照现在的发展速度，我对 Windows 11 可能会运行在 Linux 之上的“疯狂”预测，也许会成为现实!

>
> Windows 上的 Linux 正在继续发展，功能越来越强大。现在，图形化的 Linux 程序正在被整合到 WSL 中。
>
>
>

![](/data/attachment/album/202010/04/224625e5kvrjksnkkzlhos.jpg)

在微软 Build 2020 虚拟开发者大会上，微软 CEO 萨提亚·纳德拉宣布 Windows 的 Linux 子系统（WSL）2.0 [即将支持 Linux GUI 和应用程序](https://www.zdnet.com/article/microsoft-linux-gui-apps-coming-to-windows-10-wsl-along-with-gpu-access/)。现在这一天比以往任何时候都要近。在最近的 X.Org 开发者大会（XDC）上，微软合作伙伴开发者负责人 Steve Pronovost 透露，微软已经可以在 WSL 中运行图形化的 Linux 应用。

一直以来，虽然都可以在 WSL 上运行 [GIMP 图形编辑器](https://www.gimp.org/)、[Evolution 电子邮件客户端](https://wiki.gnome.org/Apps/Evolution)和 [LibreOffice](https://www.libreoffice.org/) 等 Linux 图形程序，但这并不容易。你必须安装一个第三方 X Window 显示服务器，比如 Windows 10 中的 [VcXsrv Windows X Server](https://sourceforge.net/projects/vcxsrv/)，然后[对 Windows 和 Linux 做一些调整，让它们顺利地一起工作](https://techcommunity.microsoft.com/t5/windows-dev-appconsult/running-wsl-gui-apps-on-windows-10/ba-p/1493242)。[X Window 系统](http://www.opengroup.org/tech/desktop/x-window-system/)几乎是所有 Linux 图形用户界面的基础。

现在，微软已经将 [Wayland 显示服务器](https://www.linux-magazine.com/Online/Features/Is-Wayland-the-New-X)移植到 WSL 中。Wayland 是最流行的 X Window 兼容的显示服务器。在 WSL2 中，它通过远程桌面协议（RDP）连接将图形化的 Linux 应用程序连接到主 Windows 显示器上。这意味着你可以在同一个桌面屏幕上同时运行 Linux 和 Windows GUI 应用程序。

Pronovost 解释道：

>
> WSL 本质上是在 Windows 托管的虚拟机里面运行 Linux，我们将应用程序（控制台程序，现在还有 GUI 程序）整合回你的 Windows 桌面，这样你就可以在统一的体验里面同时运行 Win32 和 Linux 应用程序。由于 Linux 是在虚拟机中运行，我们不能运行期望直接访问 GPU 的原生 GPU 驱动程序（除非我们做一些类似于离散设备分配的事情，并将其中一个宿主机 GPU 分配给虚拟机......但那样宿主机将失去对该 GPU 的访问！）。有了[GPU-PV（GPU 准虚拟化）](https://petri.com/microsoft-announces-gpu-hardware-acceleration-and-gui-app-support-for-wsl-2-at-build-2020)，我们基本上可以在 Linux 中投射宿主机 GPU，让 Linux 和 Windows 进程共享同一个物理 GPU，而不需要固定的资源分区。
>
>
>

微软 WSL 项目经理 Craig Loewen 在 Twitter 上补充道，使用第三方 X 服务器和内置 Wayland 服务器的关键区别在于。“[你不需要启动显示服务器](https://twitter.com/craigaloewen/status/1308452901266751488)，我们会为你处理。”此外，它还带有“与 Windows 的完美集成”，例如投影和 Linux 图标支持。

Loewen 还表示，你可以在其中运行 Linux Web 浏览器。“我们还没有用完整的桌面环境（DE）对它进行充分测试，因为我们想先专注于运行经常被问及的应用，主要是 IDE（集成开发环境），这样你就可以在一个完整的 Linux 环境中运行这些应用，”他说。

不过，先别太兴奋。Loewen 继续说道：“我们还没有制定测试通道的时间表，不过，这项工作通常会在接下来几个月内提供给 Insiders 试用。”

[微软将 Linux 整合到 Windows 中](https://www.zdnet.com/article/2020-will-be-the-year-of-linux-on-the-windows-desktop/)已经有一段时间了。四年前，微软推出了 WSL，[将 Linux Bash shell 带到了 Windows 10 中](https://www.zdnet.com/article/microsoft-to-show-bash-on-linux-running-on-windows-10/)。通过 Bash 和 WSL，你可以运行大多数 [Linux shell 工具](http://blog.dustinkirkland.com/2016/03/ubuntu-on-windows.html)和流行的 Linux 编程语言。

随着时间的推移，Linux 更成为 Windows 桌面上的一等公民。多个 Linux 发行版，从 Ubuntu 开始，随后是红帽 Fedora 和 SUSE Linux 企业桌面版（SLED） 都进入了 Windows 商店。随后，微软用 WSL 2 取代了将 Linux 内核调用转换为 Windows 调用的 WSL 翻译层。这一更新是在精简版 Hyper-V 管理程序上运行的[微软自己的 Linux 内核](https://www.zdnet.com/article/hell-freezing-over-microsoft-releases-its-own-linux-for-windows/)附带的。

最近，[从 Windows 10 Insider Preview build 20211 开始，Windows 用户可以访问 Linux 文件系统](https://devblogs.microsoft.com/commandline/access-linux-filesystems-in-windows-and-wsl-2/)。这包括访问 Windows 本身不支持的 Linux 文件系统，例如 ext4。这也意味着，如果你用不同的磁盘双启动 Windows 和 Linux，现在可以从 Windows 访问 Linux 文件。有了这个功能，你可以通过管理权限从 Windows 文件资源管理器和 PowerShell 窗口访问 Linux 文件。

按照现在的发展速度，我对 [Windows 11 可能会运行在 Linux 之上](https://www.computerworld.com/article/3438856/call-me-crazy-but-windows-11-could-run-on-linux.html)的“疯狂”预测，也许会成为现实!


构建一个即时消息应用（五）：实时消息

Summary: 对于实时消息，我们将使用 服务器发送事件。

对于实时消息，我们将使用 服务器发送事件。

![](/data/attachment/album/202010/05/091113edbuavorm89looja.jpg)

本文是该系列的第五篇。

* [第一篇: 模式](/article-11396-1.html)
* [第二篇: OAuth](/article-11510-1.html)
* [第三篇: 对话](/article-12056-1.html)
* [第四篇: 消息](/article-12680-1.html)

对于实时消息，我们将使用 <ruby> <a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events">  服务器发送事件 </a> <rt>  Server-Sent Events </rt></ruby>。这是一个打开的连接，我们可以在其中传输数据流。我们会有个端点，用户会在其中订阅发送给他的所有消息。

### 消息户端

在 HTTP 部分之前，让我们先编写一个<ruby> 映射 <rt>  map </rt></ruby> ，让所有客户端都监听消息。 像这样全局初始化：

```
type MessageClient struct {
    Messages chan Message
    UserID   string
}

var messageClients sync.Map

```

### 已创建的新消息

还记得在 [上一篇文章](/article-12680-1.html) 中，当我们创建这条消息时，我们留下了一个 “TODO” 注释。在那里，我们将使用这个函数来调度一个 goroutine。

```
go messageCreated(message)

```

把这行代码插入到我们留注释的位置。

```
func messageCreated(message Message) error {
    if err := db.QueryRow(`
 SELECT user\_id FROM participants
 WHERE user\_id != $1 and conversation\_id = $2
 `, message.UserID, message.ConversationID).
    Scan(&message.ReceiverID); err != nil {
        return err
    }

    go broadcastMessage(message)

    return nil
}

func broadcastMessage(message Message) {
    messageClients.Range(func(key, \_ interface{}) bool {
        client := key.(\*MessageClient)
        if client.UserID == message.ReceiverID {
            client.Messages <- message
        }
        return true
    })
}

```

该函数查询接收者 ID（其他参与者 ID），并将消息发送给所有客户端。

### 订阅消息

让我们转到 `main()` 函数并添加以下路由：

```
router.HandleFunc("GET", "/api/messages", guard(subscribeToMessages))

```

此端点处理 `/api/messages` 上的 GET 请求。请求应该是一个 [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) 连接。它用一个事件流响应，其中的数据是 JSON 格式的。

```
func subscribeToMessages(w http.ResponseWriter, r \*http.Request) {
    if a := r.Header.Get("Accept"); !strings.Contains(a, "text/event-stream") {
        http.Error(w, "This endpoint requires an EventSource connection", http.StatusNotAcceptable)
        return
    }

    f, ok := w.(http.Flusher)
    if !ok {
        respondError(w, errors.New("streaming unsupported"))
        return
    }

    ctx := r.Context()
    authUserID := ctx.Value(keyAuthUserID).(string)

    h := w.Header()
    h.Set("Cache-Control", "no-cache")
    h.Set("Connection", "keep-alive")
    h.Set("Content-Type", "text/event-stream")

    messages := make(chan Message)
    defer close(messages)

    client := &MessageClient{Messages: messages, UserID: authUserID}
    messageClients.Store(client, nil)
    defer messageClients.Delete(client)

    for {
        select {
        case <-ctx.Done():
            return
        case message := <-messages:
            if b, err := json.Marshal(message); err != nil {
                log.Printf("could not marshall message: %v\n", err)
                fmt.Fprintf(w, "event: error\ndata: %v\n\n", err)
            } else {
                fmt.Fprintf(w, "data: %s\n\n", b)
            }
            f.Flush()
        }
    }
}

```

首先，它检查请求头是否正确，并检查服务器是否支持流式传输。我们创建一个消息通道，用它来构建一个客户端，并将其存储在客户端映射中。每当创建新消息时，它都会进入这个通道，因此我们可以通过 `for-select` 循环从中读取。

<ruby> 服务器发送事件 <rt>  Server-Sent Events </rt></ruby>使用以下格式发送数据：

```
data: some data here\n\n

```

我们以 JSON 格式发送：

```
data: {"foo":"bar"}\n\n

```

我们使用 `fmt.Fprintf()` 以这种格式写入响应<ruby> 写入器 <rt>  writter </rt></ruby>，并在循环的每次迭代中刷新数据。

这个循环会一直运行，直到使用请求上下文关闭连接为止。我们延迟了通道的关闭和客户端的删除，因此，当循环结束时，通道将被关闭，客户端不会收到更多的消息。

注意，<ruby> 服务器发送事件 <rt>  Server-Sent Events </rt></ruby>（EventSource）的 JavaScript API 不支持设置自定义请求头?，所以我们不能设置 `Authorization: Bearer <token>`。这就是为什么 `guard()` 中间件也会从 URL 查询字符串中读取令牌的原因。

---

实时消息部分到此结束。我想说的是，这就是后端的全部内容。但是为了编写前端代码，我将再增加一个登录端点：一个仅用于开发的登录。

* [源代码](https://github.com/nicolasparada/go-messenger-demo)

---

via: <https://nicolasparada.netlify.com/posts/go-messenger-realtime-messages/>

作者：[Nicolás Parada](https://nicolasparada.netlify.com/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[gxlct008](https://github.com/gxlct008) 校对：[wxy](https://github.com/wxy)

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


awk 实用学习指南

Summary: 下载我们的电子书，学习如何更好地使用 awk。

awk 三位作者写的 <<awk 程序设计语言>> 也挺不错的, 有人还把它翻译成了中文, github.com / wuzhouhui / awk

下载我们的电子书，学习如何更好地使用 awk。

>
> 下载我们的电子书，学习如何更好地使用 `awk`。
>
>
>

![](/data/attachment/album/202010/05/100648zcubcpww627627nw.jpg)

在众多 [Linux](https://opensource.com/resources/linux) 命令中，`sed`、`awk` 和 `grep` 恐怕是其中最经典的三个命令了。它们引人注目或许是由于名字发音与众不同，也可能是它们无处不在，甚至是因为它们存在已久，但无论如何，如果要问哪些命令很有 Linux 风格，这三个命令是当之无愧的。其中 `sed` 和 `grep` 已经有很多简洁的标准用法了，但 `awk` 的使用难度却相对突出。

在日常使用中，通过 `sed` 实现字符串替换、通过 `grep` 实现过滤，这些都是司空见惯的操作了，但 `awk` 命令相对来说是用得比较少的。在我看来，可能的原因是大多数人都只使用 `sed` 或者 `grep` 的一些变化实现某些功能，例如：

```
$ sed -e 's/foo/bar/g' file.txt
$ grep foo file.txt

```

因此，尽管你可能会觉得 `sed` 和 `grep` 使用起来更加顺手，但实际上它们还有更多更强大的作用没有发挥出来。当然，我们没有必要在这两个命令上钻研得很深入，但我有时会好奇自己“学习”命令的方式。很多时候我会记住一整串命令“咒语”，而不会去了解其中的运作过程，这就让我产生了一种很熟悉命令的错觉，我可以随口说出某个命令的好几个选项参数，但这些参数具体有什么作用，以及它们的相关语法，我都并不明确。

这大概就是很多人对 `awk` 缺乏了解的原因了。

### 为使用而学习 awk

`awk` 并不深奥。它是一种相对基础的编程语言，因此你可以把它当成一门新的编程语言来学习：使用一些基本命令来熟悉语法、了解语言中的关键字并实现更复杂的功能，然后再多加练习就可以了。

### awk 是如何解析输入内容的

`awk` 的本质是将输入的内容看作是一个数组。当 `awk` 扫描一个文本文件时，会把每一行作为一条<ruby> 记录 <rt>  record </rt></ruby>，每一条记录中又分割为多个<ruby> 字段 <rt>  field </rt></ruby>。`awk` 记录了各条记录各个字段的信息，并通过内置变量 `NR`（记录数） 和 `NF`（字段数） 来调用相关信息。例如一下这个命令可以查看文件的行数：

```
$ awk 'END { print NR;}' example.txt
36

```

从上面的命令可以看出 `awk` 的基本语法，无论是一个单行命令还是一整个脚本，语法都是这样的：

```
模式或关键字 { 操作 }

```

在上面的例子中，`END` 是一个关键字而不是模式，与此类似的另一个关键字是 `BEGIN`。使用 `BEGIN` 或 `END` 可以让 `awk` 在解析内容前或解析内容后执行大括号中指定的操作。

你可以使用<ruby> 模式 <rt>  pattern </rt></ruby>作为过滤器或限定符，这样 `awk` 只会对匹配模式的对应记录执行指定的操作。以下这个例子就是使用 `awk` 实现 `grep` 命令在文件中查找“Linux”字符串的功能：

```
$ awk '/Linux/ { print $0; }' os.txt
OS: CentOS Linux (10.1.1.8)
OS: CentOS Linux (10.1.1.9)
OS: Red Hat Enterprise Linux (RHEL) (10.1.1.11)
OS: Elementary Linux (10.1.2.4)
OS: Elementary Linux (10.1.2.5)
OS: Elementary Linux (10.1.2.6)

```

`awk` 会将文件中的每一行作为一条记录，将一条记录中的每个单词作为一个字段，默认情况下会以空格作为<ruby> 字段分隔符 <rt>  field separator </rt></ruby>（`FS`）切割出记录中的字段。如果想要使用其它内容作为分隔符，可以使用 `--field-separator` 选项指定分隔符：

```
$ awk --field-separator ':' '/Linux/ { print $2; }' os.txt
 CentOS Linux (10.1.1.8)
 CentOS Linux (10.1.1.9)
 Red Hat Enterprise Linux (RHEL) (10.1.1.11)
 Elementary Linux (10.1.2.4)
 Elementary Linux (10.1.2.5)
 Elementary Linux (10.1.2.6)

```

在上面的例子中，可以看到在 `awk` 处理后每一行的行首都有一个空格，那是因为在源文件中每个冒号（`:`）后面都带有一个空格。和 `cut` 有所不同的是，`awk` 可以指定一个字符串作为分隔符，就像这样：

```
$ awk --field-separator ': ' '/Linux/ { print $2; }' os.txt
CentOS Linux (10.1.1.8)
CentOS Linux (10.1.1.9)
Red Hat Enterprise Linux (RHEL) (10.1.1.11)
Elementary Linux (10.1.2.4)
Elementary Linux (10.1.2.5)
Elementary Linux (10.1.2.6)

```

### awk 中的函数

可以通过这样的语法在 `awk` 中自定义函数：

```
函数名称(参数) { 操作 }

```

函数的好处在于只需要编写一次就可以多次复用，因此函数在脚本中起到的作用会比在构造单行命令时大。同时 `awk` 自身也带有很多预定义的函数，并且工作原理和其它编程语言或电子表格一样。你只需要了解函数需要接受什么参数，就可以放心使用了。

`awk` 中提供了数学运算和字符串处理的相关函数。数学运算函数通常比较简单，传入一个数字，它就会传出一个结果：

```
$ awk 'BEGIN { print sqrt(1764); }'
42

```

而字符串处理函数则稍微复杂一点，但 [GNU awk 手册](https://www.gnu.org/software/gawk/manual/gawk.html)中也有充足的文档。例如 `split()` 函数需要传入一个待分割的单一字段、一个用于存放分割结果的数组，以及用于分割的<ruby> 定界符 <rt>  delimiter </rt></ruby>。

例如前面示例中的输出内容，每条记录的末尾都包含了一个 IP 地址。由于变量 `NF` 代表的是每条记录的字段数量，刚好对应的是每条记录中最后一个字段的序号，因此可以通过引用 `NF` 将每条记录的最后一个字段传入 `split()` 函数：

```
$ awk --field-separator ': ' '/Linux/ { split($NF, IP, "."); print "subnet: " IP[3]; }' os.txt
subnet: 1
subnet: 1
subnet: 1
subnet: 2
subnet: 2
subnet: 2

```

还有更多的函数，没有理由将自己限制在每个 `awk` 代码块中。你可以在终端中使用 `awk` 构建复杂的管道，也可以编写 `awk` 脚本来定义和使用你自己的函数。

### 下载电子书

使用 `awk` 本身就是一个学习 `awk` 的过程，即使某些操作使用 `sed`、`grep`、`cut`、`tr` 命令已经完全足够了，也可以尝试使用 `awk` 来实现。只要熟悉了 `awk`，就可以在 Bash 中自定义一些 `awk` 函数，进而解析复杂的数据。

[下载我们的这本电子书](https://opensource.com/downloads/awk-ebook)（需注册）学习并开始使用 `awk` 吧！

---

via: <https://opensource.com/article/20/9/awk-ebook>

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

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