《代码英雄》第三季（6）：Bash Shell 中的英雄

>
> 代码英雄讲述了开发人员、程序员、黑客、极客和开源反叛者如何彻底改变技术前景的真实史诗。
>
>
>

>
> **什么是《代码英雄》**
>
>
> <ruby> 代码英雄 <rt>  Command Line Heroes </rt></ruby>是世界领先的企业开源软件解决方案供应商红帽（Red Hat）精心制作的原创音频播客，讲述开发人员、程序员、黑客、极客和开源反叛者如何彻底改变技术前景的真实史诗。该音频博客邀请到了谷歌、NASA 等重量级企业的众多技术大牛共同讲述开源、操作系统、容器、DevOps、混合云等发展过程中的动人故事。
>
>
>

![](/data/attachment/album/202012/24/164517puraibr9z9gtbwzb.png)

本文是《[代码英雄](https://www.redhat.com/en/command-line-heroes)》系列播客[《代码英雄》第三季（6）：Bash Shell 中的英雄](https://www.redhat.com/en/command-line-heroes/season-3/heroes-in-a-bash-shell)的[音频](https://cdn.simplecast.com/audio/a88fbe/a88fbe81-5614-4834-8a78-24c287debbe6/1a66178b-d531-4b54-b217-6c0db423325c/CLH_S3_EP6_Heroes_in_a_Bash_Shell_vFINAL_1_tc.mp3)脚本。

>
> 导语：Shell 使得大规模 IT 成为可能。它们是现代计算的必要组成部分。但是，如果没有<ruby> 自由软件基金会 <rt>  Free Software Foundation </rt></ruby>一位名叫 Brian Fox 的开发者的辛勤工作，它可能不会变成这样。现在，世界上几乎每台电脑都有 Bash shell。
>
>
> 在上世纪 70 年代，<ruby> 贝尔实验室 <rt>  Bell Labs </rt></ruby>希望将重复的、复杂的命令序列自动化。Chet Ramey 描述了贝尔实验室是如何开发出几个 shell 的 —— 但 UNIX 只能有一个官方支持的 shell。获选的是 Bourne shell。尽管 Bourne shell 是这些之中最好的一个 shell，但它也有其局限性。而且它只有在受到限制的 UNIX 许可证下才能使用。Brian J. Fox 讲述了他在自由软件基金会的工作，他需要创建一个自由的 Bourne shell 版本。它必须兼容但不使用任何原始源代码的元素。这个 Bourne-Again Shell，即 Bash，可能是这个星球上使用最广泛的软件。而 Taz Brown 描述了它是如何成为一个开发者可以学习使用的最重要的工具之一。
>
>
>

**00:00:07 - Saron Yitbarek**：

那是 1987 年。里根总统治下的美国正蓬勃发展，一个怀揣远大梦想的人正驱车前往他位于<ruby> 圣巴巴拉 <rt>  Santa Barbara </rt></ruby>的新家。这个人名叫 Brian Fox，27 岁，是高中辍学生。在他车的后备箱里，有两盒巨大的磁带，里面载满了他当时正在编写的代码。

**00:00:28**：

Fox 多年来一直以程序员的身份工作在所谓的自由软件运动中。他相信他锁在这个后备箱里的代码，可以带来一场革命，这是一种全新的软件范例。他的社区正在一点一点地使之成为现实。

**00:00:49**：

那年，<ruby> 理查德•斯托曼 <rt>  Richard Stallman </rt></ruby>（RMS）的<ruby> 自由软件基金会 <rt>  Free Software Foundation </rt></ruby>的一组程序员，正在想尽办法给计算机界带来自由。他们想要构建一个 UNIX 的替代品，以取代自从 70 年代以来就主导编程的 UNIX 操作系统。他们的 GNU（表示 GNU's not UNIX）将成为公众的操作系统，任何人都可以使用它，无需担心许可费用或版权问题。

**00:01:18**：

多年以来，基金会一直在努力制造这个崭新的系统。那么 Brian Fox 汽车后备箱里的那两盒装着代码的巨型磁带是什么？它们存储着这个系统一个至关重要的组成部分。这是一个自由的，而且可更改的 shell，它能够使 GNU 操作系统变得完整。这是 Brian Fox 送给自由软件运动的礼物。他称之为 Bash。

**00:01:46**：

我是 Saron Yitbarek，这里是<ruby> 代码英雄 <rt>  Command Line Heroes </rt></ruby>，一档来自<ruby> 红帽 <rt>  Red Hat </rt></ruby>的原创播客节目。在这一集中，我们将来看看 Bash shell 中的英雄们。我们将探索 shell 的历史，以及它们为什么对我们如今的工作如此重要。大家可以将 shell 看作要给演员的剧本。它们提供了完整的命令序列，然后 shell 可以快速地运行，就像演员可以一行接一行地读她的台词一样。这是对于实现重复且复杂的代码的，是最终的解决方案，也是自动化的关键。你可能会说，shell 脚本是我们开发的一大助力。但是，是否可以编写一个，能给所有人带来帮助的 shell？这就是挑战所在。

>
> **00:02:38 - Ken Thompson**：
>
>
> 让我们回到 1969 年。那时候贝尔实验室的几位计算机科学家，正在根据自己的需求开发程序。
>
>
>

**00:02:48 - Saron Yitbarek**：

这位是代码英雄先驱 Ken Thompson。由贝尔实验室设计的 UNIX 操作系统，在一开始确实是供他们个人使用的。最初，它只是一个内部系统。UNIX 鼓励程序员之间进行密切的交流，不过目的并不是要改变整个世界，而是改变贝尔实验室。

>
> **00:03:13 - Ken Thompson**：
>
>
> 到现在，几乎整个贝尔实验室都在使用这个系统。我们公司拥有近两万个计算机终端，其中大多数使用 UNIX 系统。
>
>
>

**00:03:25 - Saron Yitbarek**：

一款由 Ken Thompson 所设计的 UNIX shell 在 1971 年发布。 虽然 Thompson shell 被设计为命令行解释器，但是它却不能很好地支持脚本。所以直到六年后的 1977 年，脚本才开始兴起。

**00:03:44 - Chet Ramey**：

Shell 参数、特殊参数以及我们如今认为理所当然的变量，起源于 Steve Bourne 和 Bourne shell。

**00:03:57 - Saron Yitbarek**：

这位是 Chet Ramey，Case Western Reserve 大学的 IT 架构师。Chet 致力于维护 Bash，他也为我们讲述了许多 Bash 的起源故事。他描述了贝尔实验室当时研究 UNIX shell 样子时的情景。

**00:04:13 - Chet Ramey**：

我们如今使用的编程结构起源于 Steve Bourne，他的 shell 赢得了这场比赛。当时有大量使用 Mashey shell 的用户社区，也有大量用户开始使用 Bourne shell。那时候成立了一个委员会来决定哪一个将会获胜，哪一个将会成为从那时候起得到官方支持的 UNIX shell， Bourne 的 shell 赢了。而其他的 shell，正如他们所说，成为了历史。

**00:04:54 - Saron Yitbarek**：

不过，这还不是历史的终结。当然，Bourne shell 是一个巨大的飞跃。它打开了一扇通向更高自动化水平的大门。但是尽管有一段时间 Bourne 占据了上风，但是 Bourne shell 并不能解决我们所有的脚本需求。

**00:05:14 - Chet Ramey**：

Bourne 撰写自己的 shell 时所受到的限制，几乎是现在的你我难以想象的。显然，当你遇到这些限制时，你不得不放弃很多东西，Bourne 就放弃了很多。考虑到他所处理的空间、内存和 CPU 限制，他能够让 Bourne shell 包含那么多东西，这相当了不起。

**00:05:42 - Saron Yitbarek**：

请记住，Bourne shell 仍然是贝尔实验室 UNIX 系统的一部分。它仍然与 UNIX 许可证绑定。这意味着它不是自由的，不是开放的。这款 shell 是私有的。

**00:05:55 - Chet Ramey**：

如果你不在大学里，获取 UNIX 源码将会非常困难。显然，这对 Berkeley UNIX 的普及产生了影响。Berkeley UNIX 始于大学中，在大学社区中成长，并走了一条阻力最小的道路。因此，如果你在正确的地方，访问到 Bourne shell 的源码并不困难，但是总的来说，这并不是大众都能够认可的方案。

**00:06:36 - Saron Yitbarek**：

Chet Ramey 是 Bash shell 的维护者。

**00:06:41**：

因此，我们有了 shell 的雏形，可以着手写这些关键的组成部分，但是目前为止，最好的 shell 的许可证却有个大大的问题，它是闭源的。对于理查德•斯托曼和他的自由软件基金会而言，这是绝对无法接受的事情。我们所需要的是一个不与任何公司绑定的 shell，一个面向所有人的 shell。

**00:07:05**：

但这就带来了问题。这意味着我们需要编写某种，能做到 Bourne shell 所能做到的一切，而又不会侵犯到版权的东西。如果逐字复制 Bourne shell 的代码，你会被起诉。

**00:07:20**：

为了使人们摆脱 Bourne shell 的束缚，你必须找到一位能在没看过 Bourne shell 任何源代码的情况下，编写这款复杂程序的程序员。你必须找到这样的一位局外人天才。而理查德•斯托曼找到了完成这项工作的程序员。

**00:07:46**：

Brian Fox 是一名 20 来岁的高中辍学生，比贝尔实验室的大多数人更懂代码。他从来没有见过任何 Bourne shell 的源代码，这使得他非常适合手头的任务。

**00:08:02 - Brian Fox**：

我是 Brian Fox。

**00:08:04 - Saron Yitbarek**：

为什么不直接问问这个年轻人，这个故事是什么样的呢？现如今，Fox 是一位开源倡导者以及 Opus Logica 的 CEO。但是早在 80 年代后期，他只是一个信仰开源软件运动的年轻人。我们聊了聊过去的日子，以及 Bash 是如何从那时演变过来的。

**00:08:23**：

所以那时候理查德•斯托曼请你为 UNIX 开发一款 shell。那将会是一款自由的 shell，并且是 Bourne shell 的替代品。你是如何回应的呢？

**00:08:38 - Brian Fox**：

“我们就不能做个更棒的吗？”

**00:08:41 - Saron Yitbarek**：

我喜欢这个。再多跟我说说。

**00:08:45 - Brian Fox**：

我为斯托曼所做的第一件事，其实就是编写个信息技术文档系统。我让理查德惊讶于我做这种编程的速度。他是个优秀的程序员而且工作的很快，但是他不认为其他人也能写得那么快。

**00:09:00 - Brian Fox**：

因此，在第一周内，我完成了一款名为 GNU Info 的程序的第一版实现，理查德对此有点儿震惊。我说：“我的下一个项目是什么？我的下一个项目是什么？”他说：“好吧，现在给它做个编译器吧。”我就做了，一周时间之内就完成了。然后我说：“我的下一个项目是什么？我的下一个项目是什么？”他说：“好吧，另一个家伙一直在研究那个 shell，但他还没有太多进展。”我说了“好的”，九个月后， Bourne shell 的替代品完成了。

**00:09:29 - Saron Yitbarek**：

九个月，哇。再多告诉我一些。为什么它如此具有挑战性？

**00:09:33 - Brian Fox**：

这真是个有趣的问题。它之所以如此具有挑战性，是因为我们必须忠实地模仿 Stephen Bourne 最初的 Bourne shell 的所有行为，同时对其进行扩展，让它成为人们能使用的、更好的工具。

**00:09:51**：

那时候，我和 Korn shell 的作者 David Korn 私下进行了秘密争论。POSIX 委员会，也就是规定了什么是标准 UNIX 的委员会，他们也参与了进来，并说：“哦，很好，我们需要知道 shell 到底要包含些什么。”而这方面最重要的两个人是我和 David Korn。David Korn 已经写了一个名为 KSH 的 shell。对于他所加入到 KSH 中的每一个功能，他都说：“这应该是一个标准功能。”是这样吗？对他来说这比起拥有最完美的 POSIX shell 要容易得多，如果这仅仅是他的 shell 的话。

**00:10:31**：

其中的一些功能并不是很好的功能，不是很好的选择，而且使得这款 shell 与 Bourne shell 有些不兼容，或者我觉得缺少功能，对此我们进行了一些讨论和争论，因此构建一个兼容 POSIX 的 shell 与过去为 Bourne shell 所编写的每个 shell 脚本都完全兼容花了超过 3 个月时间。

**00:10:54 - Saron Yitbarek**：

因此，如果你正在设计的产品不仅可以取代 Bourne shell，而且还试图模仿 Bourne shell 的每个部分，听起来你可能会遇到一些版权问题。你是如何处理的？

**00:11:08 - Brian Fox**：

为了构建真正开源而自由的软件，你必须得在一个干净的空间里，开始做这项工作。你不能从查看别人的代码开始然后重新实现它。因此，我从未见过与任何贝尔的系统、UNIX 或者甚至 Berkeley UNIX 相关的任何软件，也从未见过这些东西的源代码。

**00:11:29**：

当我开始构建 Bash shell 时，我使用了一个名为 Bison 的解析器，理查德已经将其整合到自由软件基金会里，并且与之前任何的其他程序完全不同。因此，我已经知道我所要构建的东西，绝对不会侵犯任何先前构建的东西的版权。

**00:11:55 - Saron Yitbarek**：

创建 Bash 的工作有很多小插曲，对于那些硬核的代码英雄来说，这只是其中一个例子。

**00:12:03 - Brian Fox**：

有一次，我正致力于在 shell 中实现<ruby> 通配扩展 <rt>  globbing </rt></ruby>。举例来说，这是允许你匹配大量文件的通配符扩展。你可以给出 `*.c`，而这会匹配所有带有 `.c` 扩展名的文件。

**00:12:17**：

因此我在通配扩展上忙活了几个小时，并且使其生效了，对此我感到很兴奋。这是一个很好的实现。而在创建这一版实现的过程中，我在我的目录里创建了一个名为 `*.c` 的文件，然后我想：“好吧，我应该删掉这个文件”，然后我输入了 RM、空格、引号、星号点 C、闭合引号，在现代 shell 中当你使用了括号，这意味着“不要扩展这个”，然后我按下了回车，提示符过了很长时间才重新出现，因为我们正在使用 Sun 350s，运行缓慢。我意识到，之所以花了很长时间是因为它要删除这个目录里的所有源文件。

**00:12:58 - Saron Yitbarek**：

哦，不！

**00:12:59 - Brian Fox**：

是的。所以我当时删掉了 Bash 的源代码。

**00:13:01 - Saron Yitbarek**：

哦，不要。

**00:13:04 - Brian Fox**：

这 ——

**00:13:05 - Saron Yitbarek**：

哦我的天哪，嗯。

**00:13:06 - Brian Fox**：

这件事让我笑了很久，笑的很大声。我甚至没有感到一丝沮丧。然后在接下来的几天里，我重新输入了全部。这份代码在我脑海里是完全是崭新的。

**00:13:20 - Saron Yitbarek**：

哇。

**00:13:20 - Brian Fox**：

问题解决了。只需将其记录到文件中即可。

**00:13:25 - Saron Yitbarek**：

好的。因此，大多数人会在那一刻完全惊慌失措。而你笑了，只是说：“哦，我想我必须重新做一遍了。”为什么你当时那么冷静呢？

**00:13:35 - Brian Fox**：

这让我感到疯狂很荒唐，也非常好笑，我正在打造这个工具，而要确保自己能搞好，确保该工具正常工作，你得在构建它的过程中就使用它。但是该工具无法正常工作。我还没有实现引号，并且因为我还没实现引号，所以我输入的命令没有按照我所预期的去执行，我觉得这真的很滑稽。

**00:14:06 - Saron Yitbarek**：

太神奇了。

**00:14:08**：

不过，甚至是关于错误的这个故事也能说明 Fox 的才华。他们说莫扎特在头脑中完成了交响曲，然后只需要在完成后写下来即可。Fox 也有类似的天赋。

**00:14:23**：

因此，当你最终完成并交付 Bash 时，感觉如何呢？

**00:14:27 - Brian Fox**：

呵，其实感觉很壮观。那么这里有一个故事，其实我一般不讲的。构建这款 shell 花了大约 8 个月的时候，当时我知道，我大概还需要大约一个月时间才能完成工作，然后另一个 shell 发布了 —— ASH，一个开源的 shell 被发布了，我很沮丧，因为我们还没有向任何人发布 Bash shell，所以只有少数人在使用它。我知道这还需一个月的工作量，于是我想：“哦，这太糟糕了。我投入的全部能量和精力都不会得到赞许，甚至可能都不会被看见。”所以我非常沮丧。这次我没有笑。

**00:15:13 - Saron Yitbarek**：

然而，布丁好不好，吃了才知道。GNU 的 Bash 发布于 1989 年并且变成了 Linux 的默认 shell。如今，它是计算机中不可或缺的一部分。

**00:15:25**：

它无处不在。如此多的人每天都在使用它。它遍布于每一台计算机上。作为 Bash 的作者感觉如何？

**00:15:34 - Brian Fox**：

大多数时候，我甚至都没有注意到 Bash 是比工具更加重要的东西。我真的没有经常想这件事。每隔一段时间，我会走进一家苹果商店，环顾四周然后想：“哇，这里的每台计算机不仅运行着我 27 年前编写的软件，甚至上面还包含有我的名字。”然后我想：“互联网上的每台计算机、每台服务器都在运行着 Bash shell，并且其中包含有我的名字。”然后 Windows 在去年还是前年推出了 Power shell，就是 Bash，当时我想：“哦，天哪。我的名字遍及地球上的每台计算机了。”

**00:16:21 - Saron Yitbarek**：

不过，我想让你们能仔细听听 Fox 接下来告诉我的内容，因为它是很重要。他从未想过，它的程序会这样统治全球。他试图提供帮助，试图帮助他所置身其中的编程文化。

**00:16:37 - Brian Fox**：

我并没有打算去实现出现在每个人的计算机上这样的宏伟目标。我对此一点都不感兴趣。我想制作一款有用的软件，我希望它有典型的 3 到 5 年软件寿命，而不是像现在这样疯狂的 30 年的寿命。

**00:16:58 - Saron Yitbarek**：

难道你对于你在计算机领域有如此巨大影响力的事情，一直反应那么平淡吗？

**00:17:06 - Brian Fox**：

我为自己写了 Bash 而感到骄傲，而且它让我意识到了我的价值，所以有时候我会做一些事情，诸如接受播客邀约谈论 shell 之类的事情。

**00:17:14 - Saron Yitbarek**：

非常感谢你。

**00:17:15 - Brian Fox**：

谢谢。但这不是存在于我日常生活中的东西。幸运的是，我只是一个默默无闻的人，对吧？的确，我的软件正运行在每家每户的计算机上，不过也确实没有人知道这一点，对吧？因此我保持了许多个人隐私，而这个 shell 以及某个住在圣芭芭拉的人编写了它这一事实正越来越广为人知，我开始在生活中越来越多地注意到它。人们有时候来看我演奏音乐，然后告诉我说：“你是写了 shell 的那个家伙。”我感觉有点儿像 Keanu Reeves。

**00:17:54 - Saron Yitbarek**：

很酷。所以你说过你不指望 Bash 出现在每台计算机上。你打算做的是什么呢？你对 Bash 有什么期望？

**00:18:04 - Brian Fox**：

一个有用的替代工具，成为 GNU 项目的一部分，并帮助创建这个自由的开源操作系统。我实际上以为一旦我们完成了该开源操作系统的创建，该系统上的软件就可以升级，并且我将有机会创建自己想要创建的那种 shell，以帮助人们在某种程度上促进计算机科学的发展。

**00:18:35 - Brian Fox**：

我最终意识到，Bash 被创建的原因实际上是与已经存在的 UNIX 世界向后兼容，并且这种势头使其保持了活力，这是另一个独一无二的地位，你的工具如此基础，几乎是一副不可或缺的螺母和螺栓。

**00:19:01 - Saron Yitbarek**：

确实是这样。

**00:19:01 - Brian Fox**：

知道我创造了世界上某种有价值的、别人仍然还在使用的东西，这真的是一种很棒的感觉。然后当我注意到这是怎么回事时，我意识到，更重要的是，“自由软件”和“开源”这些词存在于日常英语和全世界的日常语言之中了，而最初并不是这样的。这是我和理查德•斯托曼还有其他人所投入努力的产物。作为这一运动的一部分，我很幸运能这么早参与，但让我回过头来看时，也感到非常满意，我想：“哇，开源软件已经存在，而且我就是其中的一部分。”

**00:19:50 - Saron Yitbarek**：

Brian Fox 是 Bash shell 的创建者和 Opus Logica 的 CEO。

**00:20:01 - Steve Bourne**：

事实上，我确实听说过 Bash。

**00:20:03 - Saron Yitbarek**：

这位就是被 Brian Fox 的工作所替代的 Bourne shell 的创建者 Steve Bourne。我们想知道 Bourne 对 Fox 的工作有何看法。他是否将重生的 shell Bash 视为自己作品的开源复制品？我的意思是，他觉得 Bash 怎么样？

**00:20:20 - Steve Bourne**：

有一天，写了 Bash 的那个人在一次会议上找我，给了我一件 T 恤，前面印着 “Bourne again” 的字样。

**00:20:26 - Saron Yitbarek**：

那就是 Brian Fox。

**00:20:29 - Steve Bourne**：

那是一种友好的情绪，当时是：“好吧，希望您不介意，但我只是重写了您的 shell”，而我说：“听起来不错”，然后他给了我一件 T 恤。

**00:20:38 - Saron Yitbarek**：

如果我在编程领域学到了一件事，那就是每个人都喜欢意外之喜。事实证明，Stephen Bourne 认为 Bash 是他和其他人在贝尔实验室所做工作的必要扩展。一点儿都不为此苦恼。

**00:20:52 - Steve Bourne**：

曾经有一些人们想要，但是我没做的特性，例如变量替换和字符串管理，但是这些都被加入到了 Bash 中，现如今人们经常会用到。Bash 和原始 shell 之间的关系，我当时的印象是，它只是对语言的重新实现，并且随着时间的推移，它确实添加了功能，因此它确实取得了超越我所写作品的进步，当然是在字符串管理领域。我现在一直在用它。

**00:21:21 - Saron Yitbarek**：

Steve Bourne 是 Bourne shell 的创建者和 Rally Ventures 的 CTO。

**00:21:32 - Saron Yitbarek**：

自从 Bash 在前往圣芭芭拉的长途车程中被塞进 Brian Fox 的卡车以来，已经过去了很多年。 2019 年，版本 5.0 被发布，就像 Fox 提到的那样，Bash 现在被内置进了 Linux 中、macOS 中，甚至微软 Windows 中。Bash 已经成了开源世界中脚本编写的基石。这是我们自动化的基础。

**00:22:02 - Taz Brown**：

随着组织规模的扩大，使用能够使我们更快完成工作的工具变得至关重要。它成为了必需品。

**00:22:16 - Saron Yitbarek**：

Taz Brown 是 Red Hat 的资深 Ansible 自动化顾问，因此她非常了解 Bash 的价值。

**00:22:24 - Taz Brown**：

我绝对认为人们在职业生涯初期就应该使用 Bash。与其使用 GUI 或者说是图形用户界面，不如将自己视为管理员或 DevOps 人员。

**00:22:39 - Saron Yitbarek**：

而这是因为作为一名 Bash 程序员，你将会掌握能让你晋升的核心价值。

**00:22:45 - Taz Brown**：

学习写脚本有一定的价值，因为这可以让你从自动化的角度，为程序的长期运行做打算。你可以看到脚本的运行方式，然后可以说：“好吧，我可以做到，我可以使这项任务自动化执行。”它开始使你成为与之前不一样的思想家和技术专家。

**00:23:09 - Saron Yitbarek**：

对于运维而言，自动化已经变得不可或缺。复杂的程序、应用和工具均由优雅的 Bash 代码实现。

**00:23:21 - Taz Brown**：

如果你愿意的话，你不必重复造轮子。你可以从 GitHub 库或是其他任何你存储这些特定文件的地方拉取它们。Bash 允许你这么做。Bash 允许你执行这些常见任务，并且可以从 10 台服务器扩展到 1000 台服务器。

**00:23:42**：

关于自动化的伟大之处在于，一旦你制定了计划，就可以以一种非常有效的方式执行。它允许你执行那些，无法手动执行的操作。

**00:23:56 - Saron Yitbarek**：

最近 Taz Brown 所从事开发的 Ansible® 这样的最新产品可以始终与 Bash 集成在一起，完成了工作。

**00:24:04 - Taz Brown**：

虽然时代在不停前进，但是我认为 Bash 永远都会是管理员会去选择使用的工具，特别是他们想要快速自动化的情况下。

**00:24:14 - Saron Yitbarek**：

最后，这一切的成功，都可以追溯到它是一个自由的、允许所有人加以改进的软件这件事上。它是 Brian Fox 提供给世界的，某种没有许可证和限制的东西。满足了人们一直的需求，所以是 Bash 成功的关键。实际上，他甚至已经不再主管 Bash 开发已经很长一段时间了。这位是 Chet Ramey，他维护了 Bash 数十年。

**00:24:38 - Chet Ramey**：

我想，Brian 在发布 1.05 版本后就已经决定了他想要继续去从事其他工作。他曾在自由软件基金会负责过其他任务，他想做除了 Bash 以外的事情，而我是 Bash 最活跃的贡献者。他和我一起开发了许多新功能。我们共同努力解决了许多 bug，因此当到了需要其他人接手时，我是最佳人选。

**00:25:16 - Saron Yitbarek**：

就像 Fox 一样，Ramey 也必须继续努力，因为 Bash 比任何一位维护者都重要。

**00:25:25 - Chet Ramey**：

我是从 23 岁开始贡献的，有点儿像是我和 Bash 共同成长。在某些时刻，我会需要征集一个团队。我需要征集那些愿意并且有能力投入时间推动 shell 发展向前的人们。

**00:25:46 - Saron Yitbarek**：

Bash，这款再次降生的 shell 明年将迎来 30 岁（LCTT 译注：Bash 发布于 1989 年，至本译文发表时，已经 31 岁了），并且没有衰老的迹象。Bash 乘着自由软件浪潮，然后是开源浪潮，直到传播至编程世界的每个角落。但曾经，它只是存储在 Brian Fox 汽车后备箱里磁带上的代码。它只是一些程序员，想要带给大家的 shell 语言。几乎偶然的，Brian Fox 在此过程中成为了一名伟大的代码英雄。

**00:26:23**：

顺便说一句，有些事情始终困扰着我， Brian Fox 驱车将所有 Bash 代码载到了 Santa Barbara。为什么要转移呢？我的意思是，他在某家科技公司找到了新工作吗？

**00:26:34 - Brian Fox**：

我想要继续我的音乐生涯，而我认为做到这一点的最佳去处就是气温总在 72 华氏度左右、天空没有乌云、海滩很美的地方。

**00:26:45 - Saron Yitbarek**：

很好，我更喜欢这个理由。

**00:26:49**：

现在让我们向 Wayne A. Lee 致敬，是他向我们建议了这一集标题《Bash Shell 中的英雄》。干得好，Wayne。

**00:26:57**：

在下一集中，我们对于自动化的兴趣，将提升到一个全新的高度，并且着眼于 AI 语言，特别是 John McCarthy 创造的 LISP。

**00:27:11**：

《代码英雄》是 Red Hat 的原创播客节目。如果你访问节目的网站 [redhat.com/commandlineheroes](file:///Users/xingyuwang/develop/LCRH-wxy/translated/www.redhat.com/en/command-line-heroes)，你将更深入了解到有关 Bash 或是我们本季所介绍的任何编程语言的故事。

**00:27:28 - Saron Yitbarek**：

我是 Saron Yitbarek。下棋之前，坚持编程。

>
> **什么是 LCTT SIG 和 LCTT LCRH SIG**
>
>
> LCTT SIG 是 LCTT <ruby> 特别兴趣小组 <rt>  Special Interest Group </rt></ruby>，LCTT SIG 是针对特定领域、特定内容的翻译小组，翻译组成员将遵循 LCTT 流程和规范，参与翻译，并获得相应的奖励。LCRH SIG 是 LCTT 联合红帽（Red Hat）发起的 SIG，当前专注任务是《代码英雄》系列播客的脚本汉化，已有数十位贡献者加入。敬请每周三、周五期待经过我们精心翻译、校对和发布的译文。
>
>
> 欢迎[加入 LCRH SIG](/article-12436-1.html) 一同参与贡献，并领取红帽（Red Hat）和我们联合颁发的专属贡献者证书。
>
>
>

---

via: <https://www.redhat.com/en/command-line-heroes/season-3/heroes-in-a-bash-shell>

作者：[Red Hat](https://www.redhat.com/en/command-line-heroes) 选题：[bestony](https://github.com/bestony) 译者：[JonnieWayy](https://github.com/JonnieWayy) 校对：[acyanbird](https://github.com/acyanbird), [wxy](https://github.com/wxy)

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


Web 开发人员喜欢 Brackets 文本编辑器的原因

>
> 这个基础的编辑器是面向 Web 开发人员的，它支持多种编程语言，并提供了大量的扩展，使其成为你自己的编辑器。
>
>
>

![](/data/attachment/album/202012/25/092259zy4xvxs4axmysuf9.jpg)

Brackets 文本编辑器是主要面向 Web 开发人员的编辑器。恰如其分的是，它的“编辑”菜单中充满了对 Web 编程语言用户特别有用的功能，主要是 HTML、CSS 和 Javascript 的经典组合。

但是，它还支持许多与互联网相关的语言和格式，包括 XML、Markdown、YAML 和 JSON、PHP、Lua、Java 和 Python，以及一些常见的通用语言，例如 C、C++，甚至是 `diff` 命令的输出。

### 安装 Brackets

Brackets 可以从 [Brackets 网站](http://brackets.io/)安装到 Linux、Windows 和 macOS 上。

另外，在 Linux 上，你可以从 [flathub.org](https://flathub.org/apps/details/io.brackets.Brackets) 以 Flatpak 安装。

![Brackets editor](/data/attachment/album/202012/25/092338m5qgtvtjbg4vj3bx.png "Brackets editor")

### 使用 Brackets

在大多数时候，Brackets 是一个“普通”的文本编辑器，其功能类似于 [jEdit](/article-12942-1.html) 或 [Medit](/article-12938-1.html)。有语法高亮、可配置的制表符间距、字符编码设置等等。这些都可以在窗口底部的状态栏中找到。

在“视图”菜单中，有主题设置、行号、自动换行，甚至还有分割窗口的选项，这样你可以在一个窗口中看到两个文件。

然而，在“编辑”菜单中，有一些的特别用于编程的功能。以下是我最喜欢的一些功能：

* 使用 `Ctrl+[` 或 `Ctrl+]` 键盘快捷键来缩进和取消缩进文本块，这不仅对保持 HTML、CSS 和 Javascript 的整洁很有用，而且对 Python 代码也很重要。
* 用 `Ctrl+/` 把一行变成注释。Brackets 标记注释的方式取决于你所使用的语言，所以无论你的文档是否使用斜线、破折号、箭头、井号或其他任何类型注释，这个功能都可以使用。
* 用 `Shift+Ctrl+Up` 或 `Shift+Ctrl+Down` 在文档中将一行向上或向下移动。
* 用 `Shift+Ctrl+D` 删除整个一行。
* 用 `Ctrl+D` 复制整个一行。

这些都是看似小众的功能，你可能认为不会经常使用，但一旦你拥有了它们，你就会对它们产生依赖。

### 扩展

Brackets 还可以接受扩展，因此你和其他编码者可以添加扩展到功能中。要查看有哪些可用的扩展，请单击“文件”菜单并选择“扩展管理器”。有各种各样的扩展，包括用于调整代码格式的 Beautify、用于其他语言的多个支持包、用于转到标签开头或结尾的功能等等。

无论编辑器是否适合你，扩展可以使一个编辑器变得不同，所以如果你尝试 Brackets 并享受它的一切，但缺少一些重要的功能，在你放弃它之前，请浏览一下可用的扩展。

### 尝试 Brackets

Brackets 是一个有点低调的编辑器。虽然它宣传自己是 “Web 代码编辑器”，但实际上它是一个不错的通用编辑器，并为常见的 Web 工具链加入了一些额外的功能。如果你喜欢 Brackets 的外观和它所提供的功能，不妨一试！

---

via: <https://opensource.com/article/20/12/brackets>

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

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


除了操作系统，openEuler 还可以是什么

Summary: openEuler 的过去，是我们熟悉的 Linux 发行版，是我们所不熟悉的创新平台。而未来，openEuler 可能是什么？

![](/data/attachment/album/202012/25/192009frxqrnvxi9yr7q4y.jpg)

openEuler 是什么？在 2019 年 7 月 19 日，华为宣布要在年底正式开源 openEuler 操作系统；在半年后的 12 月 31 日，华为正式[开源了 openEuler 操作系统](/article-11787-1.html)，邀请社区开发者共同来贡献。

一年后，截止到 2020 年12 月 25日，[openEuler](https://openeuler.org/) 已经拥有了 3 万社区用户，2 万多个合入的<ruby> 拉取请求 <rp>  （ </rp> <rt>  Pull Request </rt> <rp>  ） </rp></ruby>，2000 多名社区贡献者，7032 款社区软件，75 个特别兴趣组（SIG）以及 7 个商业发行版。不仅如此，openEuler 还在系统主体之外，开源了虚拟化平台 StratoVirt、[容器引擎 iSula](/article-12789-1.html) 等重量级软件。

![](/data/attachment/album/202012/25/201614jhuhnb1n0w671mmk.jpg)

### openEuler 是发行版，还是...

和其他发行版不同，openEuler 的开场并不是以发行版开场的，而是从一个更加深刻的问题开始的——“**操作系统有创新么？**”。这个答案是肯定的，近些年来，无论是内核的架构还是应用组织的方式，都在不断的发生着创新和变化。但这些创新又好似离我们很遥远，也没有听到谁在实际生产环境中应用。这背后不是开发者的不努力，而是操作系统开发和交付的问题。

openEuler 技术委员会主席胡欣蔚拿线下的物流供应链举例，一条供应链是以满足客户需求为目的，包含了所有与满足客户需求相关的环节，从生产、运输、仓储、零售一直到最终的顾客，而与之类比的软件开发供应链，则由软件组成的相互依赖（构建、运行、代码的复制粘贴、定制化）形成的复杂关系网络被称为软件供应链。过去的开源软件把软件的交付终结于某一个特定的发行版，这带来了一些便利，简化了供应链的管理，相应的，也为软件开发的整体流程带来了单点故障的可能。

![openEuler 技术委员会主席胡欣蔚](/data/attachment/album/202012/25/192159r81l6lmmmrlm564y.jpg)

为了解决这个问题，openEuler 不是先做操作系统，而是**先对软件开发供应链进行梳理，并将整个供应链梳理开源出来**，让开发者的软件可以更好的交付给用户，让用户可以更好的把需求反馈到开源软件的上游社区。整个生态中开发者的需求、用户的需求都可以在这个供应链中得到满足。

而也正是这样的供应链，为 openEuler 带来了更多的可能，各个开源社区、合作伙伴，可以根据自己的需求，结合所在的行业和领域，打造出一款专精于领域的发行版。

不仅如此，供应链的思维，也让胡欣蔚可以重新思考云原生这个问题——“**只有云才需要云原生么？**”，答案显然是否定的。所有的数字化转型领域，都会需要云原生的这些特性。如何让这些边缘计算设备、端设备享受到云原生中的交付、迭代的性能，也是 openEuler 在关注的。**openEuler 所特有的供应链，让操作系统的打包和精简变的更简单**，可以根据设备不同的类型、场景整合出适合相应场景的操作系统，从而让这些新特性，可以不只交付给云，更可以交付给边和端，云边端一体为行业和业务创造价值。

### openEuler 是操作系统，还是....

openEuler 广为人知的是一个新的发行版，一个 Linux 操作系统，但对于 openEuler 自己来说，操作系统不过是一个创新的产品的承载平台。在胡欣蔚来看，如果一个平台没有创新，则这个平台没有未来；如果一个创新没有好的平台去落地，那这个创新不过是无根浮萍，毫无意义。对于开发者来说，**openEuler 就是这样一个孵化和培养创新的平台**。

胡欣蔚做了个比喻，“**openEuler 就是 Apache 基金会 + CentOS 操作系统**”。 CentOS 操作系统是一个著名的服务器系统，而 Apache 基金会是一个非常善于对新项目进行培育的基金会。openEuler 的操作系统，只是为了让创新可以有一个落地的平台，让创新有价值，而 openEuler 背后，是一个创新的平台、一个创新的土壤。

在这片土壤中，诞生了一些非常有意思，同时有具备使用价值的特性，比如跟随 openEuler 一同开源的 A-Tune，将 AI 的技术引入到系统的调教和优化过程中，用机器智能进行优化；比如开源的容器引擎 iSula ，让容器的运行可以更加的轻量和简单，从过去的只能运行于 x86 服务器，到现在可以应用在不同的边缘计算设备；比如 Bisheng JDK ，基于 specjbb 基准测试，相对 openJDK 性能提升了 20%；比如 StratoVirt，是基于 rust 语言开发的轻量级虚拟机，相对 QEMU 资源占用减少了80%，启动速度提升了 10 倍。

这些小的创新，让 openEuler 从一个普通的发行版，变成了一个远超过去的操作系统；而 openEuler 的孵化机制，可以让更多的有用的特性，从需求的收集，到发布到用户端，更加快速和方便。

行业在演进，操作系统和应用之间的分界线，开始变的更加的模糊，操作系统要做什么？应用要做什么？很难有一个一概而论的回答，但可以肯定的是，无论什么样的变更，都是希望这个行业可以有更大的进展，每一个行业中的开发者，都可以有更多的时间和精力去做更加核心的业务逻辑的开发。

### openEuler 是软件，还是....

openEuler 不仅仅是一款软件产品，为什么 openEuler 会出现？有了数百款发行版的 Linux 世界，真的缺这样一款操作系统么？

答案是肯定的。

提及 openEuler 的诞生，胡欣蔚回顾了自己的过去，早在 2013 年，他就开始参与 ARM 服务器的构建，彼时 Linus Torvalds 对于 ARM v7 架构的评价刚刚过去不久，ARM 芯片应用在通用计算领域也只是刚刚开始，整个行业方兴未艾。他通过研究发现，整个行业的操作系统都存在一个普遍的问题：只适配于自家的芯片和计算平台，这使得应用开发者在开发的时候，需要根据不同的芯片来进行适配，大大的降低了开发者的效率，将更多的精力放在适配，而不是业务逻辑的研发上。

在他看来，这样薄的操作系统，无法为业务创新和行业的创新提供价值，而想要促进行业的前进，操作系统的变厚、变强是必不可少的，必须要像 x86 服务器一样，一个版本足以支撑所有厂商的 ARM 服务器，才能真正的促进 ARM 在通用计算领域的蓬勃发展。

也正是因为从那时开始的努力，经过了多年的耕耘，如今在 openEuler 上可以有所收获，当年的选择也无疑是正确的。如今的 openEuler 可以完美的运行在华为自家的鲲鹏处理器上，更是可以支撑多家的 ARM 服务器。不仅如此，一些科研院所，比如国科大的“一生一芯”项目，也被 openEuler 很好的支持了。对于开发者来说，使用的是 RISC-V 架构的芯片，也可以完美的支持 openEuler。未来，openEuler 将会从系统软件的角度，打通不同算力，让软件开发者可以在一个更加简单的操作系统之上，进行技术的创新。

### openEuler 还可以是什么？

**openEuler 的过去，是我们熟悉的 Linux 发行版，是我们所不熟悉的创新平台**。而未来，openEuler 可能是什么？

胡欣蔚也向我们介绍了他的一些想法，在未来，openEuler 会在当前已有的基础之上，投入更多的精力去做一些普通开发者、厂商所无法实现的特性。比如在操作系统层面，提供秒级内核切换能力，让那些过去不敢升级、不愿升级内核的老旧系统，可以通过 openEuler 提供的特性，实现秒级的内核切换。在系统几乎不受影响的情况下，完成底层内核的切换，让老系统也可以享受到新内核提供的特性。也会花更多的精力，在 openEuler 社区的治理上，让 openEuler 社区可以有更多的用户，以及更多的开发者，让 openEuler 造福更多的企业和个人用户。

未来，openEuler 会出现在我们所熟悉的云计算和边缘计算上，到时候，我们再来看看，openEuler 还可以是什么。


为什么 Vim 用户会喜欢 Kakoune 文本编辑器？

Summary: 这个编辑器可能会让人联想到 Vim，但它也提供了很多自己独特的功能和特性。

好像kakoune只支持英文。

这个编辑器可能会让人联想到 Vim，但它也提供了很多自己独特的功能和特性。

>
> 这个编辑器可能会让人联想到 Vim，但它也提供了很多自己独特的功能和特性。
>
>
>

![](/data/attachment/album/202012/25/233039wpnwnwakzn1zwa33.jpg)

[Kakoune](https://kakoune.org/) 文本编辑器的灵感来源于 Vi。它拥有简约的界面、简短的键盘快捷键以及独立的编辑和插入模式，乍一看确实[看起来和感觉很像 Vi](/article-12947-1.html)。然而，Kakoune 编辑器在设计和功能上都有自己独特的风格，与其说是另一个 Vim，不如说是它是它自己。

### 安装

在 Linux 和 BSD 上，你可以从你的发行版的软件仓库或 port 树上安装 Kakoune。例如，在 Fedora、CentOS 或 RHEL 上：

```
$ sudo dnf install kakoune

```

在 Debian、Ubuntu 或类似的系统上：

```
$ sudo apt install kakoune

```

在 macOS 上，你可以使用 Homebrew：

```
$ brew install kakoune

```

或者，你也可以[从源码构建](https://github.com/mawww/kakoune)。

启动 Kakoune 的命令是 `kak`。你可以启动 Kakoune 打开空文件，也可以在启动时包含文件名让它打开：

```
$ kak example.txt

```

### 使用 Kakoune

当你启动 Kakoune（不带文件名）时，除了在窗口底部有一个小的状态栏外，它在你的终端中打开的大部分是空的缓冲区。像 Vim 一样，Kakoune 以“正常”模式启动，它把按键作为命令，不向缓冲区输入文本。要进入*插入模式*，你必须按 `i`（代表<ruby> 插入 <rt>  Insert </rt></ruby>）或 `a`（代表<ruby> 追加 <rt>  Append </rt></ruby>）。

在插入模式下，Kakoune 的操作和其他编辑器一样。你在键盘上输入，然后你输入的字符就会显示在缓冲区里。在插入模式下，你可以使用方向键来浏览缓冲区。

### 正常模式

在正常模式下，你可以发出导航和文本编辑命令。这是从 Vi 传统中借用的最明显的功能。编辑命令包括复制、剪切（在传统的 Unix 编辑术语中，称为 “<ruby> 猛拉 <rt>  yank </rt></ruby>”）、粘贴单词和行、撤销、转换字符大小写等功能。下面是一些基础：

* `d`：复制并删除当前选择（现代术语中的“剪切”）
* `c`：复制并删除当前选择，并进入插入模式
* `Esc+Alt+d`：删除当前选择
* `y`：复制选择
* `p`：粘贴
* `<`：取消所选行的缩进
* `u`：撤消
* `U`：重做
* `：转为小写
* `~`：转换为大写

### 选择

在 Kakoune 中，你的光标是一个单字符的移动选区。除非你扩展你的选区，否则任何影响选区的命令都只适用当前光标位置。例如，如果你的光标悬停在字母 `n` 上，那么复制命令（正常模式下的 `c`）会将字母 `n` 复制到剪贴板，而粘贴命令（正常模式下的 `p`）则会将字母 `n` 粘贴到缓冲区。

从单个字符扩展选区的最简单方法是进入正常模式，按下 `Shift` 键，同时用方向键移动光标。然而，有几种方法可以根据某些标准来扩展选区。例如，`Alt+l` 将选区从光标扩展到当前行的末端。

完整的文档可以在 <https://github.com/mawww/kakoune/blob/master/README.asciidoc> 中找到。

### 函数

除了这些基本的交互，你还可以执行命令来调用 Kakoune 的内置功能。要访问 Kakoune 的命令行，在普通模式下输入 `:`。在命令行中，你可以执行命令，包括打开文件的 `edit` 命令，保存缓冲区到文件的 `write` 命令，当然还有退出应用的 `quit`。

还有更多的函数，包括针对特定编程语言和文件格式的特殊选项、使用 [Ranger 文件浏览器](https://opensource.com/article/20/3/ranger-file-navigator)浏览文件系统的选项、改变颜色主题、搜索和替换文本等等。

![Kakoune](/data/attachment/album/202012/25/233225xyoiic61kycw25kz.png "Kakoune")

### 尝试 Kakoune

如果你是一个有经验的 Vim 用户，或者甚至是一个只是略知一二的人，你可能会发现 Kakoune 一开始会让你感到迷惑。它与 Vim 的相似度足以让你陷入一种虚假的熟悉感。一切都与 Vim 一模一样，直到你发现了完全不同的地方。不过，如果你是一个刚接触 Vim 编辑器的新手，或者你是一个正在寻找新挑战的 Vim 用户，那么 Kakoune 可能是你的理想编辑器。

你自己试试吧！

---

via: <https://opensource.com/article/20/12/kakoune>

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

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


在 Ubuntu Linux 上安装 Deb 文件的 3 种方法

几个方法里面提到的几个工具，用apt安装<br />
那直接sudo apt install deb文件路径（如果是当前路径必须加./）不就能安装了吗，还能解决依赖问题

>
> 这篇初学者文章解释了如何在 Ubuntu 中安装 deb 软件包。它稍后也向你展示如何移除这些 deb 软件包。
>
>
>

这是 Ubuntu 初学者系列的另一篇文章。如果你对 Ubuntu 很陌生，你可能会想知道 [如何安装应用程序](https://itsfoss.com/remove-install-software-ubuntu/)。

最简单的方法是使用 Ubuntu 软件中心。在软件中心中搜索应用程序的名称并安装它。如果你能在软件中心中找到所有的应用程序，那么生活就太惬意了。但是，不幸的是，这是不可能的发生的。

一些软件包可以通过 DEB 软件包的形式获得。它们是以 .deb 扩展名为结尾的存档文件。你可以把 .deb 文件看作为 Windows 中的 .exe 文件。在 Windows 中，你双击 .exe 文件，它将开始安装过程。DEB 软件包也是非常类似的。

你可以从软件提供商网站的下载区域找到这些 DEB 软件包。例如，如果你想 [在 Ubuntu 上安装 Google Chrome](https://itsfoss.com/install-chrome-ubuntu/) ，你可以从它的网站下载 Chrome 的 DEB 软件包。

现在问题产生了，你将如何安装 deb 文件呢？在 Ubuntu 中有多种安装 DEB 软件包的方法。在这篇教程中，我将向你依次地展示它们。

![在 Ubuntu 中安装 deb 文件](/data/attachment/album/202012/25/234722wyxuanovuv2xvgey.png)

### 在 Ubuntu 和基于 Debian 的 Linux 发行版中安装 .deb 文件

你可以选择一个 GUI 工具或一个命令行工具来安装一个 deb 软件包。你拥有选择权。

让我们继续看看如何安装 deb 文件。

#### 方法 1: 使用默认的软件中心

在 Ubuntu 中，最简单的方法是使用默认的软件中心。在这里你不必要做任何特别的事。只需要转到你所下载的 .deb 文件的文件夹下（它应该是 Downloads 文件夹），并在这个文件上双击。

![在 Ubuntu 上的 Google Chrome 的 deb 文件](/data/attachment/album/202012/25/234722u3krz1r8mihg414i.jpg)

在已下载的 .deb 文件上双击以开始安装。

它将打开软件中心，并且你将看到安装软件时的选项。你所需要做的全部工作就是：点击安装按钮并输入你的登录密码。

![在 Ubuntu 软件中心中安装 Google Chrome](/data/attachment/album/202012/25/234722zakezpkok16rq1ak.jpg)

.deb 文件的安装将通过软件中心来执行。

看，它甚至比在 Windows 中安装一个 .exe 文件更简单，不是吗？

#### 方法 2: 使用 Gdebi 应用程序来安装 deb 软件包及其依赖项

再提一次，如果事情总是进展顺利，生活可能会更简单一些。但是生活并不是我们熟知的那样。

现在，你已经知道 .deb 文件可以简单地通过软件中心来安装，接下来，让我告诉你一些软件包可能会遇到的依赖项的错误。

发生错误的原因是，一个程序可能依赖于另外一个软件包（库）。当开发者为你准备 DEB 软件包时，他/她可能会假设你的系统中已经有了所依赖的软件包（库）。

但是如果情况并不是这样的，你的系统没有这些所需要的软件包（库），你将遇到臭名昭著的“依赖项错误”。

软件中心不能处理这样的错误，因此你不得不使用另外一个名称为 [gdebi](https://itsfoss.com/gdebi-default-ubuntu-software-center/) 的工具。

gdebi 是一个轻量级的 GUI 应用程序，它只有安装 deb 软件包的一个用途。

它将识别依赖项，并尝试在安装 .deb 文件的同时安装这些依赖项。

![gdebi 在安装 deb 软件包时处理依赖项](/data/attachment/album/202012/25/234723mqwijbnzczwiqbcj.jpg)

就我个人而言，我更喜欢使用 gdebi 而不是使用软件包中心来安装 deb 文件。它是一个轻量级应用程序，因此安装过程看起来更快一点。更多的信息，你可以阅读[使用 gDebi ，并使其成为安装 DEB 软件包的默认设置](https://itsfoss.com/gdebi-default-ubuntu-software-center/)。

你可以从软件中心或使用下面的命令来安装 gdebi ：

```
sudo apt install gdebi

```

#### 方法 3: 在命令行中使用 dpkg 安装 .deb 文件

如果你想在命令行中安装 deb 软件包，你可以使用 `apt` 命令或者 `dpkg` 命令。实际上，`apt` 命令在底层上使用 [dpkg](https://help.ubuntu.com/lts/serverguide/dpkg.html.en) 命令，但是 `apt` 却更流行和易于使用。

如果你想对 deb 文件使用 `apt` 命令，像这样使用它：

```
sudo apt install path_to_deb_file

```

如果你想对将要安装的 deb 软件包使用 `dpkg` 命令，在这里是如何完成它：

```
sudo dpkg -i path_to_deb_file

```

在这两个命令中，你应该使用你已下载的 deb 文件的路径和名称来替换 `path_to_deb_file` 。

![在 Ubuntu 中使用 dpkg 命令安装 deb 文件](/data/attachment/album/202012/25/234724j4eewerlidyc7xc1.png)

如果你在安装 deb 软件包的过程中得到一个依赖项的错误，你可以使用下面的命令来修复依赖项的问题：

```
sudo apt install -f

```

### 如何移除 deb 软件包

移除一个 deb 软件包也不是一件什么大事。并且，你不需要用于安装程序的原始的 deb 文件。

#### 方法 1: 使用 apt 命令移除 deb 软件包

你所需要的全部东西就是你所已安装程序的名称，接下来你可以使用 `apt` 或 `dpkg` 来移除这个程序。

```
sudo apt remove program_name

```

现在，问题来了，在移除命令中，你如何找到你所需要使用的准确的程序名称？为此，`apt` 命令也有一个解决方案。

你可以使用 `apt` 命令找到所有已安装文件的列表，但是手动完成这一过程将会是一件令人头疼的事。因此，你可以使用 `grep` 命令来搜索你的软件包。

例如，在先前的部分中，我已安装 AppGrid 应用程序，但是如果我想知道准确的程序名称，我可以像这样使用一些东西：

```
sudo apt list --installed | grep grid

```

这将给予我全部的名称中含有 “grid” 的软件包，从这里，我可以得到准确的程序名称。

```
apt list --installed | grep grid
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
appgrid/now 0.298 all [installed,local]

```

正如你所看到的，一个名称为 “appgrid” 的软件包已经安装。现在，你可以在 `apt remove` 命令中使用这个程序名称。

#### 方法2: 使用 dpkg 命令移除 deb 软件包

你可以使用 `dpkg` 来找到已安装程序的名称：

```
dpkg -l | grep grid

```

该输出将给予所有的名称中有 “grid” 的软件包。

```
dpkg -l | grep grid

ii appgrid 0.298 all Discover and install apps for Ubuntu

```

在上面的命令输出中的 `ii` 意味着软件包已经被正确地安装。

现在，你有了程序名称，你可以使用 `dpkg` 命令来移除它：

```
dpkg -r program_name

```

**提示：更新 deb 软件包**

一些 deb 软件包 （像 Chrome）通过系统更新来提供其更新，但是对于大多数的其它的程序，你将不得不先移除已存在的程序，并在接下来安装更新的版本。

我希望这篇初学者指南能够帮助你在 Ubuntu 上安装 deb 软件包。我添加了移除部分，以便你可以更好地控制你所安装的程序。

---

via: <https://itsfoss.com/install-deb-files-ubuntu>

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

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


如何在 Fedora Linux 上安装 RPM 文件

>
> 这篇初学者文章介绍如何在 Fedora 和 Red Hat Linux 上安装 RPM 软件包。它也随后向你展示如何移除这些 RPM 软件包。
>
>
>

![](/data/attachment/album/202012/26/182851bxi1lstdz13siuif.jpg)

当你开始使用 Red Hat 系的 Fedora Linux 时，你早晚会偶然发现 .rpm 文件。就像在 Windows 中的 .exe 文件，以及在 Ubuntu 和 Debian 中的 .deb 文件一样，一个 rpm 文件能够使你在 [Fedora](https://getfedora.org/) 上快速安装一个软件。

你可以从软件中心中找到并安装大量的软件，特别是 [如果你在 Fedora 中启用附加的存储库的话](https://itsfoss.com/fedora-third-party-repos/)。但是有时你会在它们的网站上找到可用的 RPM 格式的软件包。

就像在 Windows 中的 .exe 文件一样，你可以通过 **下载 .rpm 文件并双击它来安装**。不用担心，我将向你展示详细的步骤。

### 在 Fedora 和 Red Hat Linux 上安装 RPM 文件

我将向你展示安装 RPM 文件的三个方法：

* [使用软件中心安装 RPM 文件](file:///Users/xingyuwang/develop/TranslateProject-wxy/translated/tech/tmp.TvkJtlRJ6T#gui-method)（GUI 方法）
* [使用 DNF 命令安装 RPM 文件](file:///Users/xingyuwang/develop/TranslateProject-wxy/translated/tech/tmp.TvkJtlRJ6T#use-dnf)（CLI 方法）
* [使用 Yum 命令安装 RPM 文件](file:///Users/xingyuwang/develop/TranslateProject-wxy/translated/tech/tmp.TvkJtlRJ6T#use-yum)（Red Hat 的 CLI 方法）

#### 方法 1: 使用软件中心

在 Fedora 中使用默认的软件中心是最简单的方法。它真地很简单。转到你下载的 .rpm 文件的文件夹位置。它通常在 “Downloads” 文件夹。

只需要 **双击 RPM 文件，它将会在软件中心中打开**。

或者，你可以在 RPM 文件上右键单击并选择通过软件中心来安装它。

![或者双击或者右键并选择软件安装](/data/attachment/album/202012/26/182904r8zjjox8tb8bt4fz.png)

当它在软件中心打开时，你应该会看到“安装”选项。只需要点击“安装”按钮并在提示时输入你的账号密码。

![通过 Fedora 软件中心安装 RPM](/data/attachment/album/202012/26/182904r3qdd2ii3tgqdqpd.jpg)

它很简单，对吗？

#### 方法 2: 使用 DNF 命令来安装 RPM 文件

这是命令行方法。Fedora 使用新的 `dnf` [软件包管理器](https://itsfoss.com/package-manager/) ，你也可以使用它来安装下载的 RPM 文件。

打开一个终端并切换到你下载 RPM 文件的目录下。你也可以通过到 RPM 文件的路径。像这样使用 `dnf` 命令：

```
sudo dnf install rpm_file_name

```

这是一个我 [在 Fedora 上使用 dnf 命令安装 Google Chrome](https://itsfoss.com/install-google-chrome-fedora/) 屏幕截图：

![使用 DNF 命令安装 RPM 文件](/data/attachment/album/202012/26/182905n5t05irh0sz13kxw.jpg)

#### 方法 3: 在 Red Hat 中使用 Yum 命令安装 RPM 文件

不像 Fedora ，Red Hat 仍然使用很好的旧式的 Yum 软件包管理器。在这里你还不能找到 `dnf` 命令。

这个过程与 `dnf` 命令相同。转到 RPM 文件所在的目录或提供它的路径。

```
sudo yum install path_to_RPM_file

```

就是这样。没有一点异常花哨的东西。

### 如何移除 RPM 软件包

移除一个 RPM 软件包也不是一个什么大的问题。并且，你不需要原始的用来安装程序的 rpm 文件。

你可以在软件中心中找到已安装的软件包，并从其中移除应用程序。

![移除 RPM 软件包](/data/attachment/album/202012/26/182905n7uxgepdqxk83j31.jpg)

或者，你可以使用带有 `remove` 选项的 `dnf` 或 `yum` 命令。

使用 `dnf` ，使用这个命令：

```
sudo dnf remove rpm_package_name

```

使用 `yum` ，使用这个命令：

```
sudo yum remove rpm_package_name

```

你可能不记得准确的软件包名称，没有关系。你可以做的是输入软件包的前几个字母，然后敲击 `tab` 按键。这是假设你已经启用 `tab` 按键补全，通常是这样的。

这就是你需要做的全部。相当简单，对吧？作为一个初学者，你可能会为这样一个简单的任务而挣扎，我希望像这样的快速教程会让你对 Fedora 更自信一些。

---

via: <https://itsfoss.com/install-rpm-files-fedora/>

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

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


初次登录 Linux 服务器马上要做的 9 件事

Summary: 在将新配置的服务器投入工作之前，请确保你知道你正在使用什么。

很实用，虽然有些命令如lshw没有找。马克<br />
（另外本文是基于红帽系列linux系统的，作者应该说明一下）

在将新配置的服务器投入工作之前，请确保你知道你正在使用什么。

>
> 在将新配置的服务器投入工作之前，请确保你知道你正在使用什么。
>
>
>

![](/data/attachment/album/202012/27/112042zuz1lpudkypeppeu.jpg)

当我在 Linux 上测试软件时（这是我工作中的一个常规部分），我需要使用多台运行 Linux 的不同架构的服务器。我整备机器，安装所需的软件包，运行我的测试，收集结果，并将机器返回到仓库中，以便其他人可以使用它进行测试。

由于我经常这样做（甚至一天多次），我初次登录 Linux 服务器的前 10 分钟内的工作已经成为每天的仪式。当我初次登录 Linux 服务器时，我会使用命令来收集我需要的信息来寻找某些东西。我将在本文中介绍我的过程，但请注意，在大多数情况下，我只给出命令名称，所以你需要确定这些命令的具体选项，以获得你需要的信息。阅读命令的手册页是一个很好的起点。

### 1、第一次接触

当我登录到一台服务器时，我做的第一件事就是检查它是否拥有我将要运行的测试所需的操作系统、内核和硬件架构。我经常会检查一台服务器已经运行了多久。虽然这对测试系统来说并不重要，因为它会被多次重启，但我还是发现这些信息很有帮助。

使用下面的命令来获取这些信息。我主要使用 Red Hat Linux 进行测试，所以如果你使用其他 Linux 发行版，请在文件名中使用 `*-release` 而不是 `redhat-release`：

```
cat /etc/redhat-release
uname -a
hostnamectl
uptime

```

### 2、有人登录在上面吗？

一旦我知道这台机器符合我的测试需求，我需要确保没有其他人同时登录该系统运行他们自己的测试。虽然考虑到整备系统会帮我处理好这个问题，这种可能性很小，但偶尔检查一下还是有好处的 —— 尤其是当我第一次登录服务器的时候。我还会检查是否有其他用户（除了 root）可以访问系统。

使用下面的命令来查找这些信息。最后一条命令是查找 `/etc/passwd` 文件中具有 shell 访问权限的用户；它会跳过文件中没有 shell 访问权限或 shell 设置为 `nologin` 的其他服务：

```
who
who -Hu
grep sh$ /etc/passwd

```

### 3、物理机还是虚拟机

现在我有了属于自己的机器，我需要确定它是一台物理机还是一台虚拟机（VM）。如果是我自己整备的这台机器，我可以确定这是我要求的东西。但是，如果你使用的是一台不是你自己整备的机器，你应该检查该机器是物理机还是虚拟机。

使用以下命令来识别这些信息。如果是物理系统，你会看到供应商的名称（如 HP、IBM 等）以及服务器的品牌和型号；而在虚拟机中，你应该看到 KVM、VirtualBox 等，这取决于创建虚拟机时使用了什么虚拟化软件：

```
dmidecode -s system-manufacturer
dmidecode -s system-product-name
lshw -c system | grep product | head -1
cat /sys/class/dmi/id/product_name
cat /sys/class/dmi/id/sys_vendor

```

### 4、硬件

因为我经常测试连接到 Linux 机器的硬件，所以我通常使用物理服务器，而不是虚拟机。在物理机器上，我的下一步是确定服务器的硬件能力 —— 例如，运行的是什么类型的 CPU，它有多少个核心，启用了哪些标志，以及有多少内存可用于运行测试。如果我正在运行网络测试，我会检查连接到服务器的以太网或其他网络设备的类型和容量。

使用以下命令来显示连接到 Linux 服务器的硬件。其中一些命令在新的操作系统版本中可能会被废弃，但你仍然可以从 yum 存储库中安装它们或切换到它们的等效新命令：

```
lscpu or cat /proc/cpuinfo
lsmem or cat /proc/meminfo
ifconfig -a
ethtool <devname>
lshw
lspci
dmidecode

```

### 5、已安装的软件

测试软件总是需要安装额外的依赖包、库等。然而，在安装任何东西之前，我都会检查已经安装了什么（包括是什么版本），以及配置了哪些存储库，这样我就知道软件来自哪里，并可以调试任何软件包安装问题。

使用下面的命令来确定安装了什么软件：

```
rpm -qa
rpm -qa | grep <pkgname>
rpm -qi <pkgname>
yum repolist
yum repoinfo
yum install <pkgname>
ls -l /etc/yum.repos.d/

```

### 6、运行的进程和服务

检查了安装的软件之后，自然就会检查系统上有哪些进程在运行。当在系统上运行性能测试时，这一点至关重要 —— 如果一个正在运行的进程、守护进程、测试软件等占用了大部分 CPU/RAM，那么在运行测试之前停止该进程是有意义的。这也可以检查测试所需的进程或守护进程是否已经启动并运行。例如，如果测试需要 `httpd` 运行，那么即使安装了软件包，启动守护进程的服务也可能没有运行。

使用以下命令来识别系统上正在运行的进程和已启用的服务：

```
pstree -pa 1
ps -ef
ps auxf
systemctl

```

### 7、网络连接

如今的机器网络化程度很高，它们需要与网络上的其他机器或服务进行通信。我会识别服务器上哪些端口是开放的，是否有到测试机器的任何网络连接，是否启用了防火墙，如果启用了，是否屏蔽了任何端口，以及机器与哪些 DNS 服务器对话。

使用以下命令来识别网络服务相关信息。如果一个过时的命令不可用，请从 yum 存储库中安装它或使用等效的新命令：

```
netstat -tulpn
netstat -anp
lsof -i
ss
iptables -L -n
cat /etc/resolv.conf

```

### 8、内核

在进行系统测试时，我发现了解内核相关的信息是很有帮助的，比如内核版本和加载了哪些内核模块。我还会列出任何[可调整的内核参数](https://www.oreilly.com/library/view/red-hat-enterprise/9781785283550/ch10s05.html)以及它们的设置，并检查启动运行中的内核时使用的选项。

使用以下命令来识别这些信息：

```
uname -r
cat /proc/cmdline
lsmod
modinfo <module>
sysctl -a
cat /boot/grub2/grub.cfg

```

### 9、日志

现在，我已经对服务器有了很好的了解，包括安装了哪些软件，运行了哪些进程。还有一件事我无法逃避，那就是日志文件 —— 我需要知道在哪里可以查看不断更新的信息。

使用以下命令查看系统的日志：

```
dmesg
tail -f /var/log/messages
journalctl

```

### 接下来的步骤

虽然命令和实用程序会发生变化，但它们所显示的基本信息大致不变。在你专注于掌握哪些命令之前，你需要对你要寻找的信息以及它属于什么类别有一个宏观的看法。

由于 Linux 将大部分信息保存在文件中，这些命令基本上是从文件中读取信息，并以一种易于理解的方式呈现出来。下一步的好做法是找出每个命令用来获取信息显示的文件。一个提示：寻找这些信息的方法是 `strace` 命令。

---

via: <https://opensource.com/article/20/12/linux-server>

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


了解如何使用 Norka 编辑器阅读文本

Summary: 这是一个素颜朝天的文本编辑器，旨在让你的写作能被更好地阅读和理解。

这是一个素颜朝天的文本编辑器，旨在让你的写作能被更好地阅读和理解。

>
> 这是一个素颜朝天的文本编辑器，旨在让你的写作能被更好地阅读和理解。
>
>
>

![](/data/attachment/album/202012/28/003505f3h3pppkg7enpxi9.jpg)

有些文本编辑器是为编程而设计的，有些是为专门的文本格式而设计的，有些是为文档设计而设计的。Norka 文本编辑器是为阅读而设计的。创建一个为*阅读*而设计的文本编辑器似乎很奇怪，但实际上，如果你仔细想想，这是很有意义的。你的文字已经写了一次或三次，这取决于你个人对迭代的容忍度，但它的目的是为了在未来几年内被阅读。Norka 让你轻松地专注于你的文字如何被阅读。

### 安装

Norka 文本编辑器是 MIT 授权的，可以[作为 Flatpak 包安装在 Linux上](https://flathub.org/apps/details/com.github.tenderowl.norka)。它是开源的，所以如果你想尝试在不同的平台上安装它，你可以[克隆它的 Github 仓库](https://github.com/TenderOwl/Norka)。

### Norka 的界面

Norka 的界面很简单。一个回到 Norka 文档集的按钮，一个导出按钮和一个偏好菜单。窗口的其余部分是供你写文字的空白空间，在右下角，有一个阅读时间计算器来帮助你了解读者可能需要多长时间才能看完你写的东西。

![Dark Norka terminal box with green and white text](/data/attachment/album/202012/28/003540drnnr66cxz6c6cr1.png "Dark Norka terminal box with green and white text")

这几乎就是 Norka 的全部内容。没有分页符或行号，没有代码折叠或正则搜索。这是一个用于文字书写的编辑器，而不设置文档样式或跟踪复杂的数据模式。

当然，它还有一些额外的功能。如预期那样，可以使用 `Ctrl+C` 和 `Ctrl+V` 复制和粘贴。你可以用 `Ctrl+Z` 撤销，用 `Ctrl+F` 查找。你甚至可以用 `Ctrl+:` 插入表情符号。

![Norka terminal box with pop up box of emoji search menu](/data/attachment/album/202012/28/003540qpmmb2i4puvh2hap.png "Norka terminal box with pop up box of emoji search menu")

#### 样式文本

虽然 Norka 绝对没有兴趣帮你设计，比如说，一本小册子或传单，但它确实有一些能力来指示你想要的文本样式。它通过 [Markdown](https://opensource.com/article/19/9/introduction-markdown) 来实现这一点，这是一个简单的纯文本书写的约定，但用特殊的符号来指示文本应该如何在 HTML、EPUB 或 PDF 或任何你的目标格式中呈现。

在大多数编辑器中，你必须知道 Markdown 才能使用 Markdown，但 Norka 翻译了常见的文字处理器键盘快捷键来为你生成 Markdown。例如，要使一个单词加粗，你可以按 `Ctrl+B`，它会在光标的两边插入四个星号。当你输入下一个单词时，它的两边都会有两个星号，这就是 Markdown 对粗体（默认为粗体）文本的标记。你可以在 Norka 窗口右上角的汉堡样式的菜单中查看所有的 Markdown 快捷方式。

#### 保存和导出

你可以把 Norka 想象成一个笔记本。你在 Norka 中打开的所有文档都会保留在 Norka 的内部数据库中，所有的文档都会默认自动保存。要在 Norka 外使用文件，你可以在打开的文件中点击右上角的**共享**按钮。另外，你也可以在 Norka 的文件视图中右击任何文件，选择**导出**。你可以将文档导出（或**共享**，Norka 可互换使用这两个术语）为**文本**、**HTML** 或 **Markdown**。

### 尝试 Norka

Norka 便于尝试，也易于使用。它通过保持界面简单，几乎到了受限的程度，帮助你专注于写作。但限制有时也是一种强大的创意工具。

Norka 可能不是你进行大量修改或文本处理的最佳选择。它没有让人激动的功能，比如大小写转换、集成 `sed` 命令、字符交换等等。它是一个为读者服务的文本编辑器。如果你觉得有用，那么你可能正是 Norka 正在寻找的受众。

感谢你花 2 分 39 秒阅读本文！

---

via: <https://opensource.com/article/20/12/norka>

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

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


通过编写互动游戏学习 Bash

Summary: 编程一个简单的游戏是练习一门新语言并与其他你掌握的语言进行比较的好方法。

请教一下 ， 为什么要使用 0$guess 这样的形式？ 直接用 $guess 不行么？

可能是为了预防用户输入空值

编程一个简单的游戏是练习一门新语言并与其他你掌握的语言进行比较的好方法。

>
> 编程一个简单的游戏是练习一门新语言并与其他你掌握的语言进行比较的好方法。
>
>
>

![](/data/attachment/album/202012/28/010432yq3vgsqtiol9zzi6.jpg)

学习一门新的编程语言是很有趣的。每当我尝试学习一门新的语言时，我都会专注于定义变量、编写语句和评估表达式。一旦我对这些概念有了大致的了解，我通常可以自己弄清楚其余的概念。大多数编程语言都有一些相似之处，所以一旦你了解了一种编程语言，学习下一种编程语言就是要弄清楚其独特的细节，认识到其中的差异。

为了帮助我练习一种新的编程语言，我喜欢写一些测试程序。我经常写的一个示例程序是一个简单的“猜数字”程序，电脑在 1 到 100 之间选一个数字，让我猜这个数字。程序会一直循环，直到我猜对为止。

“猜数字”程序锻炼了编程语言中的几个概念：如何给变量赋值，如何写语句，如何进行条件判断和循环。对于学习一门新的编程语言来说，这是一个很好的实践实验。

### 用 Bash 猜数字

[Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) 是大多数 Linux 系统的标准 shell。除了提供丰富的命令行用户界面外，Bash 还以*脚本*的形式支持完整的编程语言。

如果你对 Bash 不熟悉，我推荐你看这些介绍：

* [什么是 Bash？](https://opensource.com/resources/what-bash)。
* [开始使用 Bash 编程](https://opensource.com/article/20/4/bash-programming-guide)
* [系统管理员的 Bash 脚本入门](https://opensource.com/article/20/4/bash-sysadmins-ebook)
* [如何在 Bash 中编写函数](https://opensource.com/article/20/6/bash-functions)
* [阅读更多关于 Bash 的信息](https://opensource.com/tags/bash)

你可以通过编写一个 Bash 版本的“猜数字”游戏来探索它。这是我的实现：

```
#!/bin/bash

number=$(( $RANDOM % 100 + 1 ))

echo "Guess a number between 1 and 100"

guess=0

while [ "0$guess" -ne $number ] ; do
        read guess
        [ "0$guess" -lt $number ] && echo "Too low"
        [ "0$guess" -gt $number ] && echo "Too high"
done

echo "That's right!"
exit 0

```

### 拆解这个脚本

脚本中的第一行，`#!/bin/bash` 告诉 Linux 使用 Bash shell 来运行这个脚本。每个脚本都以 `#!` 字符对（LCTT 译注：释伴）开始，这表示它是一个 shell 脚本。紧跟在`#!` 后面的是要运行的 shell。在本例中，`/bin/bash` 是指 Bash shell。

要给一个变量赋值，在变量名后面列出 `=` 号。例如，语句 `guess=0` 给 `guess` 变量分配一个零值。

你也可以使用 `read` 语句提示用户输入一个值。如果你写了 `read guess` 语句，Bash 会等待用户输入一些文本，然后把这个值存储在 `guess` 变量中。

要引用一个变量的值，在变量名前使用 `$`。所以, 在 `guess` 变量中存储了一个值后, 你可以使用 `$guess` 来检索它。

你可以使用任何你喜欢的变量名称，但是 Bash 为自己保留了一些特殊的变量名称。一个特殊的变量是 `RANDOM`，每次引用它都会产生一个很大的随机数。

如果你想在存储一个值的同时执行一个操作，你需要用特殊的括号把语句括起来。这将告诉 Bash 先执行该语句，而 `=` 则将结果值存储在变量中。要评估一个数学表达式，使用 `$(())` 围在你的语句上。双括号表示一个*算术表达式*。在我的例子中，`number=$(( $RANDOM % 100 + 1 ))` 评估表达式 `$RANDOM % 100 + 1`，然后将值存储在 `number` 变量中。

标准的算术运算符，如 `+`（加）、`-`（减）、`*`（乘）、`/`（除）和 `%`（模）都适用。

这意味着语句 `number=$(( $RANDOM % 100 + 1 ))` 产生一个 1 到 100 之间的随机数。模数运算符（`%`）返回两个数相除后的余数。在这种情况下，Bash 将一个随机数除以 100，剩下的余数范围是 0 到 99，通过在这个值上加 1，你可以得到一个介于 1 和 100 之间的随机数。

Bash 支持像循环这样的*条件表达式*和*流程控制*。在“猜数字”的游戏中，只要 `guess` 中的值不等于 `number`，Bash 就会继续循环。如果猜的数小于随机数，Bash 就会打印“太低”，如果猜的数大于数字，Bash 就会打印“太高”。

### 它是如何工作的

现在你已经写好了你的 Bash 脚本，你可以运行它来玩“猜数字”游戏。一直猜，直到你找到正确的数字：

```
Guess a number between 1 and 100
50
Too high
30
Too high
20
Too high
10
Too low
15
Too high
13
Too low
14
That's right!

```

每次运行这个脚本，Bash 都会随机选择一个不同的数字。

这个“猜数字”游戏是学习新的编程语言时的一个很好的入门程序，因为它以一种很直接的方式锻炼了几个常见的编程概念。通过在不同的编程语言中实现这个简单的游戏，你可以展示一些核心概念，并比较每种语言的细节。

你有喜欢的编程语言吗？你会如何用它来写“猜数字”游戏呢？请关注本系列文章，看看你可能感兴趣的其他编程语言的例子。

---

via: <https://opensource.com/article/20/12/learn-bash>

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


如何使用这个 KDE Plasma 文本编辑器？

轻量版的 kate ？不过感觉标签页功能还是需要的

有个KWrite，又有个kate

kate慢死了

来自河南南阳的 Chrome Mobile 94.0|Android 11 用户

>
> 作为流行的 KDE Plasma 桌面的一部分，KWrite 在一个简单易用的界面中隐藏了大量有用的功能。
>
>
>

![](/data/attachment/album/202012/29/010557o53b649j66a1snjv.jpg)

KWrite 是一款适用于 KDE Plasma 桌面的文本编辑器。它的目的是成为一个通用的应用，任何人都可以在他们需要快速做笔记、写一篇学校论文、做一些编程，和/或任何其他你能想到的文本编辑器能做的事时用上它。它使用 [Kate 编辑器](https://opensource.com/article/20/12/kate-text-editor)的组件来创建一个简单的界面，但它利用这些相同的组件来提供了大量有用的功能。

### 安装

KWrite 不可用于所有环境，它是 [KDE Plasma 桌面](https://opensource.com/article/19/12/linux-kde-plasma)的一个组件，所以如果你正在运行 Plasma，那么你已经有了 KWrite。

如果你没有运行 Plasma，那么你可以安装 Plasma，这样你可以将它和 KWrite 一起开始使用，或者根据需要使用 KWrite。然而，它是作为 Plasma 桌面的默认文本编辑器，所以如果你真的想把它作为一个独立应用使用，那么安装 Kate 可能更容易。

### 使用 KWrite

当你启动 KWrite 时，你会看到期望的编辑器的样子：一大块用于输入的区域，顶部有一个菜单栏和工具栏，底部有一个状态栏。这就是你在开始之前需要了解的全部内容。KWrite 是一个直观的应用，工具栏按钮用于重要的动作，如打开和保存文件，简单的菜单系统用于更高级的编辑任务。

![Kwrite terminal containing dark gray html code on white background](/data/attachment/album/202012/29/010627ost4vnk4u44lu3ns.png "Kwrite terminal containing dark gray html code on white background")

KWrite 的许多功能都是潜在的，不需要你自己去激活它们就会发生。例如，如果你打开一个用 HTML 编写的文件，那么 KWrite 会高亮显示关键字（如 `class` 和 `id`）和识别代码标签（如 `<p>` 或 `<div>`），并将它们与自然语言的单词区别对待。当你加载一个用 Python 编写的文件时，也会发生同样的情况，而对于主要用自然语言编写的文件，则不会发生任何事情。

当然，你不必只选择 HTML、Python 和你的母语。KWrite 支持很多语言和格式（对于很多语言和格式，它甚至有自动完成选项）。

对于那些想要除了自动加载功能之外更多功能的用户，在编辑、视图和工具菜单中都有一些选项。例如，你可以激活动态的拼写检查、运行脚本、调出命令行、注释或取消注释一行、调整缩进、显示行号等等。

当从终端启动 KWrite 时，也有一些有趣的选项。例如，如果你知道要到文件中的哪一行，你可以用行号参数启动 KWrite：

```
$ kwrite --line 101 example.txt

```

你也可以使用 `--stdin` （或简写 `-i`）选项方便地将命令的输出通过管道到 KWrite。例如，这个命令下载 [example.com](http://example.com) 的首页，并在一个新的 KWrite 窗口中显示 HTML：

```
$ curl http://example.com | kwrite --stdin

```

### 尝试 KWrite

我一直觉得 KDE 的优势之一就是它的复杂性很灵活。如果你想要一个简单的桌面，你基本上可以选择忽略任何你不想要的功能。KWrite 就是这种灵活性也适用于开发人员的一个例子。由于 Kate 具有许多功能，所以开发者有能够重用这些功能的一个子集来创建一个更干净、更专注的应用版本。

KWrite 是一个单文档编辑器。它没有标签，也没有任何“项目”的意识。它的目的是为那些想一次只处理一个文档的人准备的，他们希望基本的功能在默认情况下是激活的，在需要的时候可以选择强大的编辑工具。安装优秀的 Plasma 桌面，今天就来试试吧！

---

via: <https://opensource.com/article/20/12/kwrite-kde-plasma>

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

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


Linux 内核如何处理中断

Summary: 中断是计算机处理数据的关键部分。

中断是计算机处理数据的关键部分。

>
> 中断是计算机处理数据的关键部分。
>
>
>

![](/data/attachment/album/202012/29/063805o5taf82ftgz85e22.jpg)

中断是现代 CPU 工作方式中重要的部分。例如：当你每次在键盘上按下一个按键后，CPU 会被中断以使得 PC 读取用户键盘的输入。这个过程发生得相当快，以致于在使用体验上你不会感到任何变化或损害。

此外，键盘并不是导致中断的唯一组件。一般来说，有三种类型的事件会导致 CPU 发生中断：硬件中断、软件中断和异常。在具体介绍不同类型的中断前，我需要先定义一些术语。

### 定义

<ruby> 中断请求 <rt>  interrupt request </rt></ruby>（IRQ）是由<ruby> 可编程的中断控制器 <rt>  programmable interrupt controlle </rt></ruby>（PIC）发起的，其目的是为了中断 CPU 和执行<ruby> 中断服务程序 <rt>  interrupt service routine </rt></ruby>（ISR）。中断服务程序（ISR）是一个小的程序，用来处理具体的数据，其具体的处理方式依赖于造成中断请求（IRQ）的原因。之前正在运行的进程在中断服务程序（ISR）运行结束前都会被中断。

在过去，中断请求由单独的芯片处理（中断控制器芯片 PIC），I/O 设备直接与中断控制器（PIC）相连。中断控制器（PIC）管理着多种硬件的中断请求（IRQ），并且可以直接与 CPU 通信。当一个中断请求（IRQ）产生后，中断控制器（PIC）向 CPU 写入数据，并且触发中断请求引脚（INTR）。

现如今，中断请求（IRQ）由 CPU 中的<ruby> 高级可编程中断控制器 <rt>  advanced programmable interrupt controller </rt></ruby>（APIC）部分来处理。每个核中都拥有属于自己的高级可编程中断控制器。

### 中断的类型

正如我前文中提到的，中断可以根据其来源分为三种类型。

#### 硬件中断

当一个硬件设备想要告诉 CPU 某一需要处理的数据已经准备好后（例如：当键盘被按下或者一个数据包到了网络接口处），它将会发送一个中断请求（IRQ）来告诉 CPU 数据是可用的。接下来会调用在内核启动时设备驱动注册的对应的中断服务程序（ISR）。

#### 软件中断

当你在播放一个视频时，音频和视频是同步播放是相当重要的，这样音乐的速度才不会变化。这是由软件中断实现的，由精确的计时器系统（称为 [jiffies](https://elinux.org/Kernel_Timer_Systems)）重复发起的。这个计时器会使得你的音乐播放器同步。软件中断也可以被特殊的指令所调用，来读取或写入数据到硬件设备。

当系统需要实时性时（例如在工业应用中），软件中断会变得重要。你可以在 Linux 基金会的文章中找到更多相关信息：[面向嵌入式开发者的实时 Linux 介绍](https://www.linuxfoundation.org/blog/2013/03/intro-to-real-time-linux-for-embedded-developers/)。

#### 异常

<ruby> 异常 <rt>  exception </rt></ruby>是你可能之前就知道的中断类型。当 CPU 执行一些将会导致除零或缺页错误的指令时，任何其他运行中的程序都会被中断。在这种情况下，你会被一个弹窗提醒，或在控制台输出中看到\*\*<ruby> 段错误 <rt>  segmentation fault </rt></ruby>（<ruby> 核心已转储 <rt>  core dumped </rt></ruby>）\*\*。但并不是所有异常都是由指令错误引起的。

异常可以进一步分为<ruby> 错误 <rt>  Fault </rt></ruby>、<ruby> 陷阱 <rt>  Trap </rt></ruby>和<ruby> 终止 <rt>  Abort </rt></ruby>。

* **错误**：错误是系统可以纠正的异常。例如当一个进程尝试访问某个已经被换出到硬盘的页时。当请求的地址在进程的地址空间中，并且满足访问权限时，如果页不在内存（RAM）中，将会产生一个中断请求（IRQ），并开始启用**缺页异常处理程序**把所需的页加载到内存中。如果操作成功执行，程序将继续运行。
* **陷阱**：陷阱主要用在调试中。如果你在某个程序中设置了一个断点，你就插入了一条可以触发陷阱执行的特殊指令。陷阱可以触发上下文切换来允许你的调试器读取和展示局部变量的值。之后程序可以继续运行。陷阱同样也是运行系统调用的方式（如杀死一个进程）
* **终止**：终止是由系统表中的硬件错误或值不一致而导致的。终止不会报告造成异常的指令的所在位置。这是最严重的中断，终止将会调用系统的**终止异常处理程序**来结束造成异常的进程。

### 动手实践

中断请求按照高级可编程中断控制器（APIC）中的优先级高低排序（0是最高优先级）。前 32 个中断（0~31）是由 CPU 指定的固定序列。你可以在 [OsDev 异常](https://wiki.osdev.org/Exceptions) 页面找到关于它们的概述。随后的中断请求可以以不同的方式进行分配。<ruby> 中断描述表 <rt>  interrupt descriptor table </rt></ruby>（IDT）中记录了中断请求（IRQ）和中断服务程序（ISR）的对应关系。Linux 中定义了从 0 到 256 的 IRQ 向量。

为了打印出在你的系统中已注册的中断，打开一个终端并输入：

```
cat /proc/interrupts

```

你应该会看到类似如下图的结果：

![注册的中断列表](/data/attachment/album/202012/29/063827i0teo4lpxi6rveed.png "Registered interrupts list")

*内核版本为5.6.6中注册的中断 (Stephan Avenwedde, [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/))*

从左到右各列的含义依次为：中断向量号、每个 CPU（0~n）中断发生次数、硬件来源、硬件源通道信息、以及造成中断请求的设备名。

在表的末尾，有一些非数字的中断。它们是特定于体系结构的中断，如<ruby> 本地计时器中断 <rt>  local timer interrupt </rt></ruby>（LOC）的中断请求（IRQ）号为 236。其中一些在 Linux 内核源树中的[Linux IRQ 向量布局](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/irq_vectors.h)中指定。

![特定于体系结构的中断](/data/attachment/album/202012/29/063827dnvkcchkcjvyvaav.png "Architecture-specific interrupts")

*特定于体系结构的中断 (Stephan Avenwedde, [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/))*

如果要实时获取该表，请运行如下命令：

```
watch -n1 "cat /proc/interrupts"

```

### 总结

正确的中断请求（IRQ）处理对于硬件、驱动和软件的正常交互是必要的。幸运地是，Linux 内核很好地完成了它，一个 PC 的普通用户几乎不会注意到内核的整个中断处理过程。

中断相当复杂，本文仅仅是一个关于中断的概述。如果想要深入了解该主题可以阅读 [Linux Inside 电子书](https://0xax.gitbooks.io/linux-insides/content/Interrupts/)（CC BY-NC-SA 4.0）和 [Linux 内核教程](https://linux-kernel-labs.github.io/refs/heads/master/lectures/interrupts.html#) 仓库。

---

via: <https://opensource.com/article/20/10/linux-kernel-interrupts>

作者：[Stephan Avenwedde](https://opensource.com/users/hansic99) 选题：[lujun9972](https://github.com/lujun9972) 译者：[萌新阿岩](https://github.com/mengxinayan) 校对：[wxy](https://github.com/wxy)

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


2020 年企业采用开源的 4 个原因

Summary: 根据 Tidelift 的第三次开源管理调查，根据公司规模，出现了差异。

根据 Tidelift 的第三次开源管理调查，根据公司规模，出现了差异。

>
> 根据 Tidelift 的第三次开源管理调查，根据公司规模，出现了差异。
>
>
>

![](/data/attachment/album/202012/30/062849dn8amhiait1cvnne.jpg)

Tidelift 的[第三次开源管理调查](https://www.tidelift.com/subscription/2020-managed-open-source-survey)发现，企业在大流行期间正在转向开源，44% 的组织报告他们将增加使用开源进行应用开发。

我们以前见过类似现象。在以前的经济衰退中，组织转向开源[以节省成本](https://blog.tidelift.com/the-third-wave-of-open-source-migration?utm_source=opensource&utm_medium=referral&utm_campaign=2020-survey)，并因[其它一些转型收益](https://blog.tidelift.com/theres-one-thing-stopping-developers-from-using-open-source-even-more?utm_source=opensource&utm_medium=referral&utm_campaign=2020-survey)而留下来。我们想了解哪些长期收益对不同规模的组织最有帮助。以下是我们发现的摘要。

**开源正在推动成本和时间的节约，同时提高效率。**68% 的组织提到的一个关键驱动力是节约资金和开发时间，因为使用开源减少了开发人员从头开始编写新代码的时间。近半数（48%）报告称，它提高了应用开发和维护效率。拥有超过 1000 名员工的组织更有可能将此作为鼓励使用更多开源的原因（61%，而少于 1000 人的组织为 41%）。

![Graph showing reasons for using open source](/data/attachment/album/202012/30/062904wduoiqx16a1dddi4.png "Graph showing reasons for using open source")

*（Tidelift ©2020）*

**在组织使用更多的开源的原因中，消除供应商锁定是一个重要原因。** 我们发现 40％ 的受访者将这视为主要原因。用开源软件取代昂贵的专有软件，可以确保组织更加灵活，避免对供应商的依赖。同样，规模较大的组织也倾向于这个原因。在拥有 1000 名以上员工的组织中，有 50% 的组织将此作为主要优势。

**增加开发人员的满意度是使用更多开源的另一个原因，有 31% 的组织提到了这一点。** 随着企业对人才的激烈竞争，他们了解确保开发人员在工作中和使用的工具中感到快乐的价值。调查发现，开发人员使用的前三种语言是 JavaScript（78%）、Python（52%）和 Java（41%）。

**此外，随着开源使用量的增加，83% 的组织继续对其贡献，近一半的组织制定了管理贡献的政策。** 这些政策包括：在工作时间对组织使用但不赞助或管理的项目的贡献、对他们赞助或管理的项目的贡献、在个人时间对与工作无关的（个人）项目的贡献、以及在工作时间对与工作无关的（个人）项目的贡献。

虽然向开源的长期迁移仍在继续，但很明显，COVID-19 的影响可能正在加速这一进程，组织继续从使用和贡献中获得更深层次的价值。

更多信息，请查看 [2020 年开源管理调查](https://www.tidelift.com/subscription/2020-managed-open-source-survey)的所有调查结果。

---

via: <https://opensource.com/article/20/12/open-source-survey>

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


Font Manager：一个简单的 GTK+ 桌面的开源应用

Summary: 一个非常简单的字体管理器应用，让你专注于调整 Linux 系统上的字体。

挺不错，gtk+简洁明了，也越来越成熟了。

一个非常简单的字体管理器应用，让你专注于调整 Linux 系统上的字体。

![](/data/attachment/album/202012/30/065754mx4363qxabe8y9z0.jpg)

>
> 一个非常简单的字体管理器应用，让你专注于调整 Linux 系统上的字体。
>
>
>

如果你是一个有经验的 Linux 用户，你可能会利用终端或 [调整工具](https://itsfoss.com/gnome-tweak-tool/)来管理你的 Linux 系统的字体。

老实说，不管 GNOME 调整工具有多有用，但是用来管理字体可能会不太够用。因此，一个单独的应用可以很好地帮助你管理字体。

### Font Manager：一个帮助管理字体的开源应用

![](/data/attachment/album/202012/30/065802uagfmpgfxzcplaga.png)

Font Manager（这就是应用的字面名称）是一个专门帮助你管理字体的应用。

你可以获得字体族的详细信息、可用的变体，以及根据字体的高度、宽度、间距等进行过滤和调整的功能。考虑到它是一个简单的应用，因此你找不到很多功能，但是我将在下面简要介绍一些功能。

### Font Manager 的功能

![](/data/attachment/album/202012/30/065803y9ixvig7ez3mpe0c.jpg)

* 可以添加字体
* 可以删除字体
* 根据字体族、供应商、间距、高度等因素轻松筛选字体
* 调整字体的缩放系数
* 调整字体的抗锯齿（软度/锐度）
* 添加字体源，以便在安装前进行预览
* 提供快速管理的键盘快捷键
* 开箱即用的谷歌字体集成
* 获取关于字体族中可用字符的详细信息、许可证、字体大小、供应商、文件类型、间距、宽度和样式

![](/data/attachment/album/202012/30/065804vo091010mm0lojsz.png)

总的来说，你可以轻松安装或删除字体。但是，当你管理字体时，你会得到很多帮助，如上面的截图所示。

### 在 Linux 上安装 Font Manager

你有多种选择（取决于你使用的 Linux 发行版）进行安装。

如果你使用的是基于 Ubuntu 的发行版，你可以通过下面的命令轻松添加 PPA 来安装 Font Manager：

```
sudo add-apt-repository ppa:font-manager/staging
sudo apt update
sudo apt install font-manager

```

如果你不喜欢 [PPA](https://itsfoss.com/ppa-guide/)（我更喜欢这样安装），你也可以在任何 Linux 发行版上安装一个[可用的 Flatpak 包](https://flathub.org/apps/details/org.gnome.FontManager)。

你只需要在你的 Linux 系统上启用 Flatpak，然后在你的软件中心搜索它（如果它支持 Flatpak 集成的话），或者直接输入下面的命令安装它：

```
flatpak install flathub org.gnome.FontManager

```

如果你是 Arch 用户，你可以在 [AUR](https://itsfoss.com/aur-arch-linux/) 中找到[包](https://aur.archlinux.org/packages/font-manager/)。

更多的安装说明，你可以参考它的[官网](https://fontmanager.github.io/)和 [GitHub 页面](https://github.com/FontManager/font-manager)。

* [下载 Font Manager](https://fontmanager.github.io/)

### 总结

Font Manager 是一个简单的解决方案，适用于任何基于 GTK+ 的桌面环境。主要用于 GNOME，但你在其他桌面环境使用它。

你可以得到很多有用的信息，同时可以添加或删除字体，我想这显然是一个真正的字体管理器。

你对 Font Manager 有什么看法？在下面的评论中告诉我你的想法吧!

---

via: <https://itsfoss.com/font-manager/>

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


20 分钟建立一个 Ansible 实验室

Summary: 建立一个支持学习和实验新软件的环境。

建立一个支持学习和实验新软件的环境。

>
> 建立一个支持学习和实验新软件的环境。
>
>
>

![](/data/attachment/album/202012/31/112636h6ck5qd60d44t0mm.jpg)

能够构建和拆解公有云环境是非常有用的，但我们大多数人都不能轻松访问公有云。退而求其次的最好办法就是在本地机器上建立一个实验室，但即使在本地机器上运行也会带来性能、灵活性和其他挑战。大多数时候，本地机器上额外的工作负载会干扰我们日常的工作，它们当然也会影响你提供一个现成的环境来玩耍和实验新软件。

几年前，当我和我的团队开始学习 [Ansible](https://opensource.com/resources/what-ansible) 时，我们就遇到了这个挑战。我们找不到一个可以单独使用的环境，我们对这种情况的失望导致我们中的一些人停止了实验。我们知道需要找到一个解决方案。

我们花了很多时间研究各种方案，得出了一套工具，使我们的好奇心能够在我们完全控制的环境中学习。我们可以在本地机器上轮换和拆解实验室环境，而不需要访问内部实验室或公共云。

本文将解释如何在 20 分钟内以完全自动化的方式在本地机器上部署自己的实验室环境。

你可以在我的 [GitHub 仓库](https://github.com/mikecali/ansible-labs-101)中找到这个练习的所有代码。

### 工具和软件

本方案使用以下工具和软件：

* [Ansible](https://www.ansible.com/) 是我们选择的自动化工具，因为它易于使用，而且足够灵活，可以满足实验室的要求。
* [Vagrant](https://www.vagrantup.com/) 易于使用，用于构建和维护虚拟机。
* [VirtualBox](https://www.virtualbox.org/) 是一个托管管理程序，可以在 Windows 和 Linux 环境中使用。
* [Fedora v30+](https://getfedora.org/) 是我本地机器上的操作系统。

你必须进行以下设置才能建立环境：

* 一个互联网连接
* 在 BIOS 中启用虚拟化技术支持（以下是在我的联想笔记本上的[过程](https://support.lenovo.com/pt/en/solutions/ht500006)）
* Vagrant v2.2.9
* 最新版本的 Ansible
* 最新版本的 VirtualBox
* Fedora v30+ 宿主机操作系统

### 这个实验室环境有什么？

这个项目旨在部署一个带有 Ansible 引擎和多个 Linux 节点的 Ansible 主机，以及一些预加载和预配置的应用程序（httpd 和 MySQL）。它还启用了 [Cockpit](https://opensource.com/article/20/11/cockpit-server-management)，这样你就可以在测试过程中监控虚拟机（VM）的状态。使用预部署的应用程序的原因是为了提高效率（所以你不必花时间安装这些组件）。这样你就可以专注于创建角色和剧本，并针对上述工具部署的环境进行测试。

我们确定，对于我们的用例来说，最好的方案是多机 Vagrant 环境。Vagrant 文件创建了三个 CentOS 虚拟机，以模拟两个目标主机和一个 Ansible 控制机。

* Host1: 没有图形用户界面（GUI），安装 httpd 和 MySQL
* Host2: 没有 GUI，安装了 httpd 和 MySQL
* Ansible-host：没有 GUI，安装了 Ansible 引擎

### 启用多个管理程序

如果使用了多个管理程序，一些管理程序可能不允许你拉起虚拟机。要解决这个问题，请遵循以下步骤（基于 Vagrant 的[安装](https://www.vagrantup.com/docs/installation)说明）。

首先，找出管理程序的名称：

```
$ lsmod | grep kvm
kvm_intel             204800  6
kvm                   593920  1 kvm_intel
irqbypass              16384  1 kvm

```

我感兴趣的是 `kvm_intel`，但你可能需要另一个（比如 `kvm_amd`）。

以 root 身份运行以下内容，将该管理程序列入黑名单：

```
$ echo 'blacklist kvm-intel' >> /etc/modprobe.d/blacklist.conf

```

重新启动你的机器并尝试再次运行 Vagrant。

### Vagrant 文件

```
cat Vagrantfile

```

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

Vagrant.configure("2") do |config|
# Define VMs with static private IP addresses, vcpu, memory and vagrant-box.
  boxes = [
    {
      :name => "web1.demo.com", ⇒ Host1 this is one of the target nodes
      :box => "centos/8",             ⇒ OS version
      :ram => 1024,                   ⇒ Allocated memory
      :vcpu => 1,               ⇒  Allocated CPU
      :ip => "192.168.29.2"     ⇒ Allocated IP address of the node
    },
    {
      :name => "web2.demo.com", ⇒ Host2 this is one of the target nodes
      :box => "centos/8",
      :ram => 1024,
      :vcpu => 1,
      :ip => "192.168.29.3"
    },
    {
      :name => "ansible-host", ⇒ Ansible Host with Ansible Engine
      :box => "centos/8",
      :ram => 8048,
      :vcpu => 1,
      :ip => "192.168.29.4"
    }
  ]

  # Provision each of the VMs.
  boxes.each do |opts|
    config.vm.define opts[:name] do |config|
#   Only Enable this if you are connecting to Proxy server
#      config.proxy.http    = "http://usernam:password@x.y:80"⇒ Needed if you have a proxy
#      config.proxy.https   = "http://usernam:password@x.y:80"
#      config.proxy.no_proxy = "localhost,127.0.0.1"
      config.vm.synced_folder ".", "/vagrant", id: "vagrant-root", disabled: true
      config.ssh.insert_key = false
      config.vm.box = opts[:box]
      config.vm.hostname = opts[:name]
      config.vm.provider :virtualbox do |v| ⇒  Defines the vagrant provider
        v.memory = opts[:ram]
        v.cpus = opts[:vcpu]
      end
      config.vm.network :private_network, ip: opts[:ip]
      config.vm.provision :file do |file|
         file.source     = './keys/vagrant' ⇒ vagrant keys to allow access to the nodes
         file.destination    = '/tmp/vagrant' ⇒ the location to copy the vagrant key
      end
      config.vm.provision :shell, path: "bootstrap-node.sh" ⇒ script that copy hosts entry
      config.vm.provision :ansible do |ansible| ⇒ declaration to run ansible playbook
        ansible.verbose = "v"
        ansible.playbook = "playbook.yml" ⇒ the playbook used to configure the hosts
      end
        end
  end
end

```

这些是你需要注意的重要文件。

* `inventory-test.yaml`：连接到节点的清单文件
* `playbook.yaml`：Vagrant 供应者调用的用于配置节点的剧本文件
* `Vagrantfile'：Vagrant 用来部署环境的文件
* Vagrant 密钥文件：连接实验室环境中各节点的 Vagrant 密钥

你可以根据你的需要调整这些文件。Ansible 的灵活性使你有能力根据你的需要声明性地改变你的环境。

### 部署你的实验室环境

首先，克隆这个 [GitHub 仓库](https://github.com/mikecali/ansible-labs-101.git) 中的代码：

```
$ git clone https://github.com/mikecali/ansible-labs-101.git
Cloning into 'ansible-labs-101'...
remote: Enumerating objects: 15, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 15 (delta 2), reused 10 (delta 0), pack-reused 0
Unpacking objects: 100% (15/15), 6.82 KiB | 634.00 KiB/s, done.

```

接下来，将你的目录改为 `vagrant-session-2`，并查看其内容：

```
$ ls
Bootstrap-node.sh   inventory   keys   playbook.yml   README.md Vagrantfile

```

现在你已经拥有了实验室环境所需的所有工件和配置文件。要部署环境，请运行：

```
$ vagrant up

```

只要有一个像样的网络连接，只需要 20 分钟左右就可以得到一个运行环境：

```
$ vagrant up
Bringing machine 'web1.demo.com' up with 'virtualbox' provider...
Bringing machine 'web2.demo.com' up with 'virtualbox' provider...
Bringing machine 'ansible-host' up with 'virtualbox' provider...
==> web1.demo.com: Importing base box 'centos/8'...
==> web1.demo.com: Matching MAC address for NAT networking...
==> web1.demo.com: Checking if box 'centos/8' version '1905.1' is up to date...
==> web1.demo.com: Setting the name of the VM: ansible-labs_web1democom_1606434176593_70913
==> web1.demo.com: Clearing any previously set network interfaces...
==> web1.demo.com: Preparing network interfaces based on configuration...
    web1.demo.com: Adapter 1: nat
    web1.demo.com: Adapter 2: hostonly
==> web1.demo.com: Forwarding ports...
    web1.demo.com: 22 (guest) => 2222 (host) (adapter 1)
==> web1.demo.com: Running 'pre-boot' VM customizations...
==> web1.demo.com: Booting VM...
==> web1.demo.com: Waiting for machine to boot. This may take a few minutes...
    web1.demo.com: SSH address: 127.0.0.1:2222
    web1.demo.com: SSH username: vagrant
    web1.demo.com: SSH auth method: private key
[...]

```

一旦该剧本执行完成，你会看到这样的输出：

```
PLAY RECAP *********************************
Ansible-host     : ok=20 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=3

Real 18m14.288s
User 2m26.978s
Sys 0m26.849s

```

确认所有虚拟机都在运行：

```
$ vagrant status
Current machine states:

Web1.demo.com    running (virtualbox)
Web2.demo.com    running (virtualbox)
ansible-host     running (virtualbox)
[...]

```

你可以通过登录其中一个虚拟机进一步调查。访问 `ansible-host`：

```
> vagrant ssh ansible-host
Activate the web console with: systemctl enable --now cockpit.socket

Last login: Thu Nov 26 12:21:23 2020 from 10.0.2.2
[vagrant@ansible-host ~] uptime
16:46:42 up 1:24, 1 user, load average: 0.00, 0.01, 0.04

```

最后，你可以使用 Ansible 模块来 ping 你创建的其他节点：

```
[vagrant@ansible-host]$ ansible -i inventory-test.yaml \
webservers -m ping -u vagrant
192.168.29.2 | SUCCESS => {
  "Ansible-facts": {
      "Discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "Changed": false;
    "Ping": "pong"
}
[...]

```

### 清理

运行如下命令来清理环境：

```
$ vagrant destroy [vagrant machine name]

```

你的输出会像这样：

![Output from cleaning up environment](/data/attachment/album/202012/31/112723b72f4jj76v8z886s.png "Output from cleaning up environment")

### 有创意的学习

在自己的实验室里利用自己的时间学习 Ansible 这样的软件是一个好习惯，但由于受到无法控制的限制，可能会很困难。

有时候，你需要发挥创意，找到另一种方法。在开源社区中，你可以选择很多方案；我们选择这些工具的主要原因之一是，它们是许多人常用和熟悉的。

另外，请注意，这些剧本并没有按照我的要求进行优化。请随时改进它们，并在评论中分享你的工作。

---

via: <https://opensource.com/article/20/12/ansible-lab>

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


COPR 仓库中 4 个很酷的新项目（2020.12）

Summary: 本文介绍了 COPR 中一些有趣的新项目。

本文介绍了 COPR 中一些有趣的新项目。

![](/data/attachment/album/202012/31/121320eq7cfcj5vdjufvfg.jpg)

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

本文介绍了 COPR 中一些有趣的新项目。如果你第一次使用 COPR，请参阅 [COPR 用户文档](https://docs.pagure.org/copr.copr/user_documentation.html)。

### Blanket

[Blanket](https://github.com/rafaelmardojai/blanket) 是一款播放背景声音的应用，它可能会提高你的注意力，提高你的工作效率。另外，它还可以帮助你在嘈杂的环境中放松和入睡。无论何时何地，Blanket 都可以让你在鸟鸣中醒来，在咖啡店聊天声或远离城市交通喧嚣的友好氛围下工作，然后在外面淅淅沥沥的雨声中像木头一样沉睡在壁炉旁边。还有其他流行的背景音选择，如粉色和白色噪音。

![](/data/attachment/album/202012/31/121321obmbre8mbae7lzkm.png)

#### 安装说明

目前[仓库](https://copr.fedorainfracloud.org/coprs/tuxino/blanket/)为 Fedora 32 和 33 提供了 Blanket。要安装它，请使用以下命令：

```
sudo dnf copr enable tuxino/blanket
sudo dnf install blanket

```

### k9s

[k9s](https://k9scli.io/) 是一个管理 Kubernetes 集群的命令行工具。它可以让你列出正在运行的 pod 并与之交互，读取它们的日志，挖掘已使用的资源，并总体上使操作 Kubernetes 更轻松。通过插件和可定制的用户界面的可扩展性，k9s 受到有经验用户的欢迎。

![](/data/attachment/album/202012/31/121322nmfr18mrfo818r1u.png)

有关[更多预览截图](https://k9scli.io/#-previews)，请参见[项目页面](https://k9scli.io/)。

#### 安装说明

目前[仓库](https://copr.fedorainfracloud.org/coprs/luminoso/k9s/)为 Fedora 32、33、Fedora Rawhide 以及 EPEL 7、8、Centos Stream 等提供 k9s。要安装它，请使用以下命令：

```
sudo dnf copr enable luminoso/k9s
sudo dnf install k9s

```

### rhbzquery

[rhbzquery](https://github.com/juhp/rhbzquery) 是一个简单的查询 Fedora Bugzilla 的工具。它提供了一个指定搜索查询的界面，但它并不在命令行中列出结果，而是由 rhbzquery 生成 Bugzilla 的 URL，并在浏览器中打开。

![](/data/attachment/album/202012/31/121323bpd6mmqnzsgp0msq.png)

#### 安装说明

目前[仓库](https://copr.fedorainfracloud.org/coprs/petersen/rhbzquery/)为 Fedora 32、33 和 Fedora Rawhide 提供 rhbzquery。要安装它，请使用以下命令：

```
sudo dnf copr enable petersen/rhbzquery
sudo dnf install rhbzquery

```

### gping

[gping](https://github.com/orf/gping) 是一个比标准的 `ping` 命令更有视觉吸引力的选择，因为它以图表的形式显示结果。也可以同时 ping 多个主机，以方便比较它们的响应时间。

![](/data/attachment/album/202012/31/121324n0ezkeh33kgbpohk.png)

#### 安装说明

目前[仓库](https://copr.fedorainfracloud.org/coprs/atim/gping)为 Fedora 32、33、Fedora Rawhide 以及 EPEL 7 和 8 提供了 gping。要安装它，请使用以下命令：

```
sudo dnf copr enable atim/gping
sudo dnf install gping

```

---

via: <https://fedoramagazine.org/4-cool-new-projects-to-try-in-copr-from-december/>

作者：[Jakub Kadlčík](https://fedoramagazine.org/author/frostyx/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


编写一个简单的游戏来学习 Python

Summary: 通过编写一个“猜数字”游戏来探索 Python（和其他编程语言）。

通过编写一个“猜数字”游戏来探索 Python（和其他编程语言）。

>
> 通过编写一个“猜数字”游戏来探索 Python（和其他编程语言）。
>
>
>

![](/data/attachment/album/202101/01/105156uwwgx3tw2otntoow.jpg)

在这个系列中，我们要用不同的编程语言编写相同的应用，以比较各种语言是如何工作的，并说明使用标准测试程序是学习新编程好方法。

当你学习一门新的编程语言时，关注它们的共同点是件好事。变量、表达式和语句是大多数编程语言的基础。一旦你理解了这些概念，你就可以开始弄清楚其余的东西。

因为编程语言有许多相似之处，一旦你知道一种语言，你通常可以通过观察它与你所知道的语言的不同之处来学习另一种语言的基础知识。使用你用其他语言编写的标准测试程序，可以让你专注于语言，而不是程序的逻辑。

为了证明这点，我们正在测试如何用多种语言编写一个“猜数字”程序。计算机选择一个 1 到 100 之间的数字，然后让你猜。程序循环，直到你猜出正确答案。

“猜数字”程序练习了编程语言的几个概念：

* 变量
* 输入
* 输出
* 条件判断
* 循环

这是一个很好的学习新编程语言的实践实验。

### 用 Python 猜数字

用 [Python 软件基金会](https://docs.python.org/3/faq/general.html#general-information)的话来说。“Python 是一种解释性的、交互式的、面向对象的程序设计语言，它包含了模块、异常、动态类型、非常高层的动态数据类型和类。”它是一种很好的通用编程语言，从简单的脚本到复杂的 GUI 应用都很适用。

你可以通过编写一个版本的“猜数字”游戏来探索 Python。这是我的实现：

```
import random as randomlib
random = randomlib.randint(1, 100)
print("Guess a number between 1 and 100")
while True:
    guess = int(input())
    if guess < random:
        print("Too low")
    elif guess > random:
        print("Too high")
    else:
        print("That's right!")
        break

```

要给一个变量赋值，请列出变量的名称，然后是 `=` 号。例如，语句 `random = 0` 给 `random` 变量分配了一个零值。

脚本的第一行就导入了 `random` 模块。由于本系列中的所有程序都使用 `random` 作为变量的名称，你可以使用 `import random as randomlib` 以别名导入它，以避免命名冲突。

很少有函数被内置到 Python 中，大多数函数必须从标准库中显式导入。`random` 标准库模块有生成各种随机值的功能。

脚本的第二行读取函数 `randint()` 的结果，并将其赋值给名为 `random` 的变量。函数需要两个参数：一个最小值和一个最大值。在本例中，范围是 `1` 到 `100`，以使游戏具有足够的挑战性。

你可以使用 `input()` 函数提示用户输入一个值。如果你写 `guess = int(input())`，Python 会等待用户输入一些文本，将其转换为一个整数，然后将值存储在 `guess` 变量中。

Python 支持条件表达式和循环等流程控制。在“猜数字”游戏中，只要 `guess` 中的值不等于 `random`，Python 就会继续循环。

如果猜测值小于随机数，Python 会打印 `Too low`，如果猜测值大于这个数字，Python 会打印 `Too high`。

### 示例输出

现在你已经写好了 Python 程序，运行它来玩“猜数字”游戏。每次运行程序，Python 都会随机选取一个不同的数字。为了完成这个游戏，你需要猜测，直到找到正确的数字：

```
$ python guess.py
Guess a number between 1 and 100
 50
Too high
 25
Too high
 12
Too high
 7
Too high
 3
Too low
 5
Too low
 6
That's right!

```

在学习一门新的编程语言时，这个“猜数字”游戏是一个很好的入门程序，因为它以一种相当直接的方式练习了几个常见的编程概念。通过在不同的编程语言中实现这个简单的游戏，你可以展示不同编程语言的一些核心概念，并比较每种语言的细节。

你有喜欢的编程语言吗？你会如何编写“猜数字”游戏？请关注本系列文章，看看你可能感兴趣的其他编程语言的例子吧！

---

via: <https://opensource.com/article/20/12/learn-python>

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

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


用 Linux 让旧 MacBook 重获新生

Summary: 花上一小时，用 Fedora 让一台过时的 Mac 重新有用。

第一张图明显是带bar的MacBook Pro

看不懂看不懂

花上一小时，用 Fedora 让一台过时的 Mac 重新有用。

>
> 花上一小时，用 Fedora 让一台过时的 Mac 重新有用。
>
>
>

![](/data/attachment/album/202101/02/102156tjj8g7r272j74huj.jpg)

最近，我偶然找到了一台 2011 年底的老款 13 英寸 MacBook Pro，有 125GB SSD 和 8GB 内存。我曾带着这台机器去世界各地旅行，当年，我开了很多场会议、研讨会或演示，分享 JBoss 技术带来的各种 AppDev 优势。

在验证了它的电池能用，充了电，重新安装了一个新的 OS X 之后，我发现 Safari 浏览器的版本受限于旧的安全规范，这意味着它现在无法连接到很多 HTTPS 网站。这就使得这个解决方案失效了。

这个老伙计该怎么处理呢？

自从我作为开发人员专门在 Linux 工作站上工作以来已经有几年了。我只使用 Fedora，所以我决定尝试在这台 MacBook Pro 上安装它的最新版本。

我只花了一个多小时就用下面的步骤让 [Fedora 33](https://getfedora.org/en/) 在这台笔记本上工作了。

### 下载 Fedora 33 并创建一个临场 USB

第一步是找到正确的安装 Fedora 的方法。这台机器有一个 CD 插槽，所以可以刻录一个 ISO 并从它启动，但我选择直接使用可启动的 USB 方式。

我登上了另一台 MacBook，访问了 [Fedora Workstation 网站](https://getfedora.org/en/workstation/download/)，它有 Fedora Media Writer 的链接。点击你的机器类型的图标（在我的例子中是苹果标志），你会得到一个安装包。

![Fedora Media Writer 下载界面](/data/attachment/album/202101/02/102214er9gtirp1f96nnmz.png "Fedora Media Writer download screen")

开始安装，可以看到一个引导你完成安装过程的图形用户界面（GUI）。选择 Fedora Workstation 33 选项。

![在 Fedora Media Writer 中下载 Fedora Workstation](/data/attachment/album/202101/02/102214ga184lakyj89jll4.png "Fedora Workstation download in Fedora Media Writer")

接下来，选择右上角的“Create Live USB”选项。

![创建 Live USB 的按钮](/data/attachment/album/202101/02/102215byrydjhndezjndhe.png "Create Live USB button")

镜像将开始下载，你将看到一个下拉菜单来选择安装位置。

![下载 Fedora Workstation](/data/attachment/album/202101/02/102215ljeh5zehchf598sg.png "Downloading Fedora Workstation")

插入一个有足够空间的 U 盘，下载完成后，就可以选择它并在上面安装镜像。完成后，关闭 GUI，取出 U 盘。

### 安装 Linux

将你创建的 U 盘插入 MacBook Pro 左侧的端口，并按住 `Cmd` 键左侧的 `Option`（或 `Alt`）键的同时重新启动。这将打开一个启动机器的选项菜单：使用 EFI 选项，因为那是 USB 镜像。

笔记本电脑将从 USB 设备启动，你可以按照[正常的 Fedora 安装](https://docs.fedoraproject.org/en-US/fedora/f33/install-guide/install/Booting_the_Installation/)过程进行。如果你能将 MacBook Pro 插入网线连接，会有帮助，因为它的 Broadcom WiFi 设备无法开箱即用。

![MacBook Pro](/data/attachment/album/202101/02/102215v6hhnbq2s83hobqz.jpg "MacBook Pro")

你现在也可以将 Fedora 安装到你的硬盘上，并将它永久地放在你的机器上。

![在 MacBook Pro 上安装 Fedora](/data/attachment/album/202101/02/102216z4dh4b4jpj3xzwdw.jpg "Installing Fedora on MacBook Pro")

一旦安装程序完成，重新启动机器，Fedora 33 现在应该是启动选项。

![MacBook Pro 启动到 Fedora](/data/attachment/album/202101/02/102216zmmmmk170lzjwppd.jpg "MacBook Pro booting into Fedora")

唯一缺少的就是 WiFi 驱动，所以要保持网线连接，安装你正在运行的内核的开发包，并为该内核构建 `broadcom-wl` 驱动。

验证你需要用于 WiFi 的卡。

```
$ lspci -vnn -d 14e4:

```

在输出中会有几项，包括如下内容：

```
Network controller [0280]: Broadcom Inc. and subsidiaries....

Subsystem: Apple Inc. AirPort Extreme...

```

安装一个仓库来拉取 Broadcom 相关的部分：

```
$ su -c 'dnf install -y http://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm'

```

接下来的部分很有趣：如果你查看正在运行的内核时，你会看到 `v5.9.8-200.fc33`，但是你要使用开发内核包来构建你的 Broadcom 无线驱动。所以，你需要安装 `v5.8.15-301.fc33`（在写这篇文章的时候可用）。使用 `uname -r` 检查它们，并使用 `sudo dnf list kernel` 列出已安装的内核包：

```
$ sudo dnf list kernel
kernel.x86_64                     5.8.15-301.fc33
kernel.x86_64                     5.9.8-200.fc33

```

安装开发包：

```
$ sudo dnf install -y akmods kernel-devel-5.8.15-301.fc33

```

![安装开发包](/data/attachment/album/202101/02/102216k92232xuzm9axrum.jpg "Installing development packages")

安装 Broadcom 无线软件包：

```
$ sudo dnf install -y broadcom-wl

```

构建内核模块：

```
$ sudo akmods

```

![构建内核模块](/data/attachment/album/202101/02/102217f3z54al4ff3waf54.jpg "Building the kernel module")

重新启动你的机器，你应该可以看到无线驱动（`wl`）。

```
$ lsmod | grep wl

```

在 Fedora 中设置你的无线连接：

![设置无线连接](/data/attachment/album/202101/02/102217hepdin3if5ghhafr.jpg "Set up wireless connection")

这篇文章对我来说有些出乎意料，但我希望它能帮助别人在周末享受一些老硬件的乐趣！

>
> 现在要走不寻常路了……在 2011 年的 Macbook Pro 上安装 [#Fedora](https://twitter.com/hashtag/Fedora?src=hash&ref_src=twsrc%5Etfw)。祝我好运! [pic.twitter.com/zlsESnq2Px](https://t.co/zlsESnq2Px)。
>
>
> * Eric D. Schabell (@ericschabell) [2020 年 11 月 22 日](https://twitter.com/ericschabell/status/1330434517883121665?ref_src=twsrc%5Etfw)
>
>
>

*此文原载于 [Schabell.org](https://www.schabell.org/2020/11/installing-fedora33-on-macbook-pro-13inch-late-2011.html)，经许可转载。*

---

via: <https://opensource.com/article/20/12/linux-macbook>

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


如何使用 heredoc 作为一个文本编辑器

Summary: 这个不起眼的终端功能在紧要关头提供一个文本编辑器。

这个不起眼的终端功能在紧要关头提供一个文本编辑器。

>
> 这个不起眼的终端功能在紧要关头提供一个文本编辑器。
>
>
>

![](/data/attachment/album/202101/03/094710uucu3150at905t15.jpg)

在 Linux 和 Unix 的 shell 中有一个不为人知的功能，它能让你用 [cat](https://opensource.com/article/19/2/getting-started-cat-command) 命令打开一个 do-while 循环。它被称为 heredoc，无论你使用什么 shell，它都能让你或多或少地拥有一个文本编辑器。它的语法是：

```
$ cat << EOF >> example.txt

```

中间的字符串（`EOF`），本质上是一个停止循环的条件。也就是说，如果你在一行中单独输入它，循环就会结束。在循环过程中，无论你在终端中输入什么，都会被管道传送到目标文件中（在本例中）。

### 安装

只要你有一个终端，你就能够启动 heredoc。我在 [Bash](https://opensource.com/article/20/4/bash-sysadmins-ebook)、[tsh](https://opensource.com/article/20/8/tcsh) 和 Korn shell 中使用过这个语法技巧。

### 使用 heredoc

要打开一个 heredoc “会话”，你可以使用带重定向的 `cat` 命令。首先用终止字符串（常见约定是 `EOF`，代表 “End Of File”，但它实际上可以是任何字符串）指向 `cat` 命令。在终止字符串之后，将输出重定向到一个目标文件。然后，你就可以直接在终端中输入了，可以使用最常见的 shell 键盘快捷键来处理你的工作。当你在一行上输入你指定的终止字符串时，你的会话就结束了。你可以通过唯一的提示符（通常是 `>`）知道你是在一个 heredoc 循环中。

```
$ cat << EOF >> example.txt
> Everything you type here will be placed into example.txt when I type EOF on a line by itself. Until then, you can type...
>
> whatever...
>
> you want to type.
>
> EOF
$

```

在终端等待 `EOF` 时，你输入的所有内容都会被放入目标文件中，提示符被忽略，`EOF` 本身也不是文件的一部分。

```
Everything you type here will be placed into example.txt when I type EOF on a line by itself. Until then, you can type...

whatever...

you want to type.

```

在现实中，你可能不会用 heredoc 语法来代替一个正常的文本编辑器。它是一个很好的快速处理方式，可以输入多行，但超过 10 行左右就开始限制它的作用了。例如，如果不触发你 shell 的 [history](https://opensource.com/article/20/6/bash-history-commands) 功能，你就不能编辑以前的行。根据你的 shell 和配置，你可能需要先按向上键，然后按向下键来找回你的文本，然后用 `Ctrl+B` 来后退。

你的 shell 的大部分功能都能正常工作，但可能没有撤销功能，也没有什么错误恢复功能。

此外，即使是最简安装的系统，可能也至少安装了 [Vi](https://opensource.com/article/19/3/getting-started-vim) 或 [ed](https://opensource.com/article/20/12/gnu-ed)。

然而 heredoc 还是很有用的！它比 `echo` 更灵活，当你在编写 shell 脚本时，它是不可缺少的。例如，想象一下你正在编写一个安装脚本，以便你可以自动安装一组自定义应用。其中一个应用没有生成 `.dekstop` 文件，所以它不会出现在你的应用菜单中。为了解决这个问题，你决定在安装时生成一个 `.desktop` 文件。

与其编写一个 `.desktop` 文件，然后作为安装脚本的外部依赖，不如在安装脚本中使用 heredoc：

```
#!/bin/sh

VERSION=${VERSION:-x.y.z}
PKGNAM=${VERSION:-example}
PKG="${PKGNAM}"-"${VERSION}"-`arch`.tgz

# download package
wget "${PKG}"
tar txvf "${PKG}"

# use here doc to create missing .desktop file
cat << EOF >> $HOME/.local/share/applications/example.desktop
[Desktop Entry]
Version=1.0
Type=Application
Name="${PKGNAM}"
Comment="${PKGNAM}"
Exec="${PKGNAM}" %F
EOF

# insert the rest of an install script...

```

你自动地将文本输入到了一个文件中，而不需要文本编辑器（当然，除了你用来写脚本的那个）。下面是生成的 `.desktop` 文件的样子：

```
[Desktop Entry]
Version=1.0
Type=Application
Name=example
Comment=example
Exec=example %F

```

正如你所看到的，你可以在 heredoc 中使用变量，而且它们得到了正确的解析。`EOF` 字符串并没有出现在文件中，它只是标志着 heredoc 的结束。

### 比 echo 更好

heredoc 技术通常被认为比 `echo` 或 [printf](https://opensource.com/article/20/8/printf) 更容易，因为一旦你“进入”了文档，你就可以自由地做任何你想做的事情。从这个意义上说，它是自由的，但与合适的文本编辑器相比，它是有限的。

使用 heredoc 来做快速笔记和 shell 脚本，再也不用为如何动态生成配置文件而烦恼了。

---

via: <https://opensource.com/article/20/12/heredoc>

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

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


通过编写一个简单的游戏来学习 Rust

Summary: 你可以尝试以多种语言编程一个简单的游戏来开始编程之路。

你可以尝试以多种语言编程一个简单的游戏来开始编程之路。

>
> 你可以尝试以多种语言编程一个简单的游戏来开始编程之路。
>
>
>

![海底的螃蟹 Ferris，Rust 编程语言的非官方标志](/data/attachment/album/202101/03/101054axmmaxrpmsmopkac.png "Ferris the crab under the sea, unofficial logo for Rust programming language")

当你想学习一门新的编程语言时，不妨关注一下编程语言的共同点。

* 变量
* 表达式
* 语句

这些概念是大多数编程语言的基础。一旦你理解了它们，你就可以开始弄清楚其余的东西。

因为编程语言通常具有相似性，一旦你懂了一种语言，你就可以通过理解其差异来学习另一种语言的基础知识。

学习新语言的一个好方法是使用一个你可以用来练习的标准程序。这可以让你专注于语言，而不是程序的逻辑。我在这一系列文章中使用了一个“猜数字”的程序，在这个程序中，电脑会在 1 到 100 之间选一个数字让你猜。程序一直循环，直到你猜对数字为止。

这个程序锻炼了编程语言的几个概念:

* 变量
* 输入
* 输出
* 条件评估
* 循环

这是学习一门新编程语言的很好的实践实验。

### 安装 Rust

你可以[使用 Rustup 安装一个 Rust 工具链](https://www.rust-lang.org/learn/get-started)，或者你可以[在线尝试 Rust](https://play.rust-lang.org/) 而不在本地安装它。

如果你在本地安装，你应该定期用 `rustup update` 来更新它，以保持你的工具链的新鲜，并使用 `cargo update` 来保持你的库的最新版本。

### Rust 语言版本的猜数字

[Rust](https://www.rust-lang.org/) 是一门赋予任何人构建可靠和高效的软件能力的语言。你可以通过编写一个 Rust 版本的“猜数字”游戏来探索 Rust。

第一步是编写一个 `Cargo.toml` 文件。你可以使用 `cargo new` 命令生成一个骨架 `Cargo.toml`。这几乎是开始一个 Rust 项目的最佳方式。

```
$ cargo new guess
$ cd guess
$ ls -1
Cargo.toml
src/

```

`Cargo.toml` 文件为你的包命名，并给它一些元数据，最重要的是，指明了它依赖于 `rand` [crate](https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html)。

```
[package]
name = "guess"
version = "2020.11.0"
authors = ["Moshe Zadka <moshez@opensource.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "*"

```

Rust 中的许多东西不是由语言或标准库提供的。取而代之的是，你可以从某个外部 crate 得到它们，这些 crate 可以做许多事情。

程序逻辑在 `src/main.rs` 中：

```
use rand::Rng;
use std::io::BufRead;

fn main() {
    let mut rng = rand::thread_rng();
    let random = rng.gen_range(1..101);
    println!("Guess a number between 1 and 100");
    for line in std::io::stdin().lock().lines() {
        let parsed = line.ok().as_deref().map(str::parse::<i64>);
        if let Some(Ok(guess)) = parsed {
            match guess {
                _ if guess < random => println!("Too low"),
                _ if guess > random => println!("Too high"),
                _ => {
                    println!("That's right");
                    break;
                }
            }
        }
    }
}

```

代码的前两行声明你要做什么。在本例中，`rand::Rng` 生成一个猜测值，而 [trait](https://doc.rust-lang.org/rust-by-example/trait.html) `std::io::BufRead` 使得可以从标准输入中读取。

Rust 代码的入口在 `main()` 函数中，所以下一步就是定义 `main()`。

要给一个变量赋值，先放 `let`，再放变量的名字，后面放 `=` 号。这样就创建了一个[不可变](https://en.wikipedia.org/wiki/Immutable_object)变量。

Rust 中大多数变量都是不可变的，但是 `rng` 对象必须是可变的（`mut`）。例如，语句 `let random = 0` 给`random` 变量分配一个零值。

函数的第一行创建了一个线程安全的 `Rng` 对象，并将其分配给变量 `rng`。Rust 是建立在线程和内存安全的基础上的，所以你必须在开始写代码时就考虑到这些事情。

程序的下一行读取函数 `gen_range()` 的结果，并将其分配给名为 `random` 的变量。该函数取一个最小值（包含）和一个上界（不包含）。为了也包含上界，你可以用一个等号来标记较大的数字（例如，`1...=100`），或者你也可以像我在代码中做的那样，将上界设置为比你的预期最大值大 1。在这种情况下，该范围是 `1` 到 `100`，使得游戏刚好有足够的挑战性。

中央循环在 `std::io::stdin()` 中迭代行。由于有各种可能导致行不能读取的例外情况，Rust 要求你用一个 `Result` 来包裹一行。有时候可能无法将一行解析为一个整数。

这段代码使用条件模式匹配来忽略所有会导致错误的行：

```
        let parsed = line.ok().as_deref().map(str::parse::<i64>);
        if let Some(Ok(guess)) = parsed {
            // ...
        }

```

第一行创建了一个 `Result<Option<i64>, ...>` 对象，因为它可能在读取或解析步骤中失败。由于下一行只在 `Some(Ok(guess))` 上匹配，每当一行的结果是一个不匹配的值时，它就会跳过 `if` 语句。这是一种强大的忽略错误的方法。

Rust 支持条件表达式和流程控制，比如循环。在“猜数字”游戏中，只要猜中的值不等于 `random`，Rust 就会继续循环。

`if` 语句的主体包含一个 Rust 的 `match` 语句的三向分支。虽然 `match` 最常用于模式匹配，但它也可以检查任意条件。在这种情况下是打印一个适当的信息，如果猜测是正确的，则中断（`break`）循环。

### 示例输出

现在你已经写好了你的 Rust 程序，你可以运行它来玩“猜数字”游戏。每次运行程序时，Rust 都会选择一个不同的随机数，所以继续猜，直到找到正确的数字。

```
$ cargo run
   Compiling guess v2020.11.0 (/Users/mzadka/src/guess)
    Finished dev [unoptimized + debuginfo] target(s) in 0.70s
     Running `target/debug/guess`
Guess a number between 1 and 100
50
Too high
25
Too high
12
Too low
18
Too high
15
Too high
13
Too low
14
That's right

```

典型的做法是用 `cargo run` 来测试程序。最终，你可能会使用 `cargo build` 分成两个独立的步骤[构建一个可执行文件并运行它](https://opensource.com/article/20/3/rust-cargo)。

### 学习 Rust

这个“猜数字”游戏是学习一门新的编程语言的一个很好的入门程序，因为它以一种相当直接的方式锻炼了几个常见的编程概念。通过在不同的编程语言中实现这个简单的游戏，你可以展示语言的一些核心概念，并比较它们的细节。

你有喜欢的编程语言吗？你会如何用它来写“猜数字”游戏呢？请关注本系列文章，看看你可能感兴趣的其他编程语言的例子吧！

---

via: <https://opensource.com/article/20/12/learn-rust>

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


让你爱上 Git 的 10 篇文章

>
> 你对 Git 了解得越多，使用 Git 就会越容易。一起来回顾下年度最佳 Git 文章。
>
>
>

![](/data/attachment/album/202101/04/120824ue7jvn9nj4n2s4vh.jpg)

Git 是开源开发者工具箱中最基本的工具。这个强大的版本控制系统有很多复杂的功能。使用 Git 不需要了解它所有的功能，但是对 Git 了解得越多，使用 Git 就会越容易。

下面每篇文章都提供了一些奇技淫巧来提升和增强你的 Git 技能。

### 怎么解决 git 合并时的冲突

Brian Breniser 的这篇教程从 `git merge` 的定义以及解释什么是冲突开始。然后他详细解释了在合并时如果有冲突[如何解决冲突](https://opensource.com/article/20/4/git-merge-conflict)。Breniser 还提了一些能学习更多关于解决冲突和其他 Git 功能的建议。

### 4 个不可或缺的 Git 脚本

Vince Power 分享了他[最重要的 Git 脚本](/article-12180-1.html)。这些脚本可以从 Git Extras 包中获得，该包提供了 60 多个 Git 增强脚本。Power 最爱的脚本有：在无需打开文本编辑器的情况下编辑 `.git-ignore` 的 `git-ignore` ；用于提供 Git 仓库的摘要的 `git-info`；用来处理 GitLab 的合并请求（MR）和 GitHub 的拉取请求（PR）的 `git-pr`；把 Git 的提交（`commit`）、标签（`tag`）和推送（`push`）合为一体的 `git-release`。

### 完美生活：git rebase -i

在 Dave Neary 的这篇文章中可以学习[使用 git rebase -i 来修改你的 Git 提交历史](/article-12231-1.html)。Neary 从解释 Git 是如何把提交历史记录到仓库中的以及 `git commit` 和 `git rebase` 的区别。之后，他又解释了如何使用 `git rebase -i` 让 Git 仓库的提交历史变得简洁。这个命令能让你把“修复书写错误”的提交合进其它提交里，把几个相似的小提交合并成一个大的提交。

### Git Cola 让使用 Git 变得简单

Seth Kenlon 演示了[如何使用 Git Cola](https://opensource.com/article/20/3/git-cola)。Git 是个命令行工具，这对于有些人来说是有学习门槛的。Git Cola 提供了一个图形界面，因此不习惯用命令行的用户也可以使用 Git。在此文中，Kenlon 展示了如何安装 Git Cola，并使用 Git Cola 的图形用户界面完成了很多 Git 提交任务。

### 6 个在团队中使用 Git 的最佳实践

从设计上讲，Git 是个协同工具，但是关于如何协同的很多细节是由团队自行决定的。Ravi Chandran 提了一些建议，团队应该采用这些建议[更高效地使用 Git](/article-12621-1.html)。Chandran 在文中列出的 6 个最佳实践是：“使约定正式化”，“正确地合并修改”，“经常变基你的功能分支”，“在合并之前把压扁你的提交”，“使用标签”，“让软件的可执行程序打印标签”。

### 改变我使用 Git 工作方式的七个技巧

Rajeev Bera 分享了 [7 个 Git 技巧](/article-12894-1.html)，这些技巧能提升 Git 的用户体验。文章探讨了 Git 的自动更正、提交计数、仓库优化、备份未追踪的文件、了解 `.git` 目录、在另一个分支查看文件以及在 Git 下搜索。

### 使用 tmux 和 Git 定制化我的 Linux 终端

Moshe Zadka 展示了他是如何[使用 tmux 和 Git](/article-12450-1.html)定制化他的 Linux 终端的。Zadka 的文章是个人工作流的优秀探索。他使用 GNOME 终端，用 tmux 和一些能让他快速查看 Git 仓库状态的功能来增强终端。他只需要用一个字母就可以提交文件或把提交推送到远程仓库。

### 使用 Lazygit 让复杂的 Git 任务简单化

Jesse Duffield 解释了如何使用[Lazygit，一个能让使用 Git 变得简单的终端界面](https://opensource.com/article/20/3/lazygit)。Lazygit 的开发者 Duffield 详细阐述了如何使用这个界面来暂存文件、以交互方式变基、进行筛选、搜索提交以及创建一个 PR。

### 使用子模块和子树来管理 Git 项目

子模块和子树是两种在 Git 仓库中引入嵌套的子项目的方式。在[使用子模块和子树来管理 Git 项目](/article-12244-1.html)中，Manaswini Das 解释了两个选项的工作原理和区别。

### 不喜欢 diff？那么试试 Meld

Ben Nuttall 展示了如何[使用 Meld 代替 diff](/article-12067-1.html)来进行对比和合并修改。Meld 是图形化的 `diff`，输出更容易理解。Nuttall 演示了使用 `diff` 和 Meld 进行对比的区别。他还解释了 Meld 是如何识别 Git 项目的，这意味着在 Git 中一个文件被提交之后，可以用 Meld 来搜索修改。

你想学习关于 Git 的什么内容？请在评论去分享你的想法。

---

via: <https://opensource.com/article/20/12/git>

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


试试 GNU nano，一个轻量级的 Vim 替代品

Summary: 轻巧而直接，nano 提供了一个简单、直观的编辑器，没有额外的麻烦。

轻巧而直接，nano 提供了一个简单、直观的编辑器，没有额外的麻烦。

>
> 轻巧而直接，nano 提供了一个简单、直观的编辑器，没有额外的麻烦。
>
>
>

![](/data/attachment/album/202101/04/124300oklykg26z6wyhjey.jpg)

许多 Linux 发行版都捆绑了 [Vim](https://opensource.com/article/20/12/vi-text-editor) 作为默认的文本编辑器。这吸引了很多长期使用 Linux 的用户，反正那些不喜欢它的用户也可以在安装后及时更换。不过 Vim 是一个很有趣的编辑器，因为它是少数几个打开时的模式不允许输入文字的编辑器之一。这对任何用户来说都是一个令人费解的选择，对一个新用户来说也是很困惑的。

多亏了 GNU nano，才有了 Vim 之外的另一种轻量级终端文本编辑器，而且使用起来非常方便 —— 它的窗口底部列出了最重要的命令。

![Black nano terminal with white text](/data/attachment/album/202101/04/124350m35qdl2vk6p9576f.png "Black nano terminal with white text")

### 安装

在 Linux 和 macOS 上，你可能已经安装了 GNU nano。你可以用 `which` 命令来验证：

```
$ which nano
/bin/nano

```

如果你没有安装它，你可以从你的软件库中安装，或者你可以自己[下载它的源代码并编译](http://nano-editor.org)。

在 Windows 上，你可以使用 [Chocolatey](https://opensource.com/article/20/3/chocolatey) 来 [安装 GNU nano](https://opensource.com/article/20/12/%C2%A0https://chocolatey.org/packages/nano)。

### 启动 nano

从终端启动 nano，要么单独打开它：

```
$ nano

```

或者你也可以在你的命令后面加上一个文件的路径来打开一个特定的文件。如果你命名的文件还不存在，它就会被创建：

```
$ nano example.txt

```

### 使用 nano

nano，是个只要稍看一下，就会发现它是一个非常自明的东西。当你启动它的时候，nano 会打开一个空的缓冲区或者你要打开的文件。在屏幕下方，有一个功能列表和相应的键盘快捷键。更多的功能可以按 `Ctrl+G` 获取帮助。

以下是最重要的应用程序命令：

* `Ctrl+S` 保存你的工作
* `Ctrl+W` 另存为
* `Ctrl+R` 加载文件（读取）
* `Ctrl+X` 退出
* `Ctrl+G` 获得帮助

以下是最常用的编辑命令：

* `Alt+A` 选择（标记）一个区域
* `Ctrl+K` 剪切标记的文字
* `Ctrl+U` 粘贴（不剪切）
* `Alt+F` 撤销
* `Alt+E` 重做

### 可定制

nano 不像 Emacs 或 Vim 那样可扩展，但你可以在一个名为 `~/.nanorc` 的文件中进行一些重要的定制。在这个文件中，你可以设置全局的偏好，包括文字折行设置、颜色方案、行号等。你也可以创建你自己的键绑定，所以如果你想用 `Ctrl+V` 来代替 nano 默认的 `Ctrl+U` 来粘贴，你可以改变分配给 `paste` 函数的绑定。

```
bind ^V paste all

```

你可以在 [GNU nano 文档](https://www.nano-editor.org/dist/latest/nanorc.5.html)中获得所有可用函数的列表。

### 简单而有效

GNU nano 是一款简单明了的文本编辑器。它易于使用，并提供了你所期望的所有文本编辑器的功能。试试吧，享受直观编辑的简单性。

---

via: <https://opensource.com/article/20/12/gnu-nano>

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

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


Go 通道是糟糕的，你应该也觉得很糟糕

Summary: 如果你在大街上问一个有名的程序员，Go 有什么特别之处？ 她很可能会告诉你 Go 最出名的是通道和 goroutine。

golang:看我理你吧

自己用不习惯就来黑golang，不喜欢不要用啊，

![](/data/attachment/album/202101/05/101049y2995vz8l9mdg4bz.jpg)

更新：如果你是从一篇题为 《[糟糕的 Go 语言](https://github.com/ksimka/go-is-not-good)》 的汇编文章看到这篇博文的话，那么我想表明的是，我很惭愧被列在这样的名单上。Go 绝对是我使用过的最不糟糕的的编程语言。在我写作本文时，我是想遏制我所看到的一种趋势，那就是过度使用 Go 的一些较复杂的部分。我仍然认为 <ruby> 通道 <rt>  Channel </rt></ruby>可以更好，但是总体而言，Go 很棒。这就像你最喜欢的工具箱中有 [这个工具](https://blog.codinghorror.com/content/images/uploads/2012/06/6a0120a85dcdae970b017742d249d5970d-800wi.jpg)；它可以有用途（甚至还可能有更多的用途），它仍然可以成为你最喜欢的工具箱！

更新 2：如果我没有指出这项对真实问题的优秀调查，那我将是失职的：《[理解 Go 中的实际并发错误](https://songlh.github.io/paper/go-study.pdf)》。这项调查的一个重要发现是...Go 通道会导致很多错误。

从 2010 年中后期开始，我就断断续续地在使用 Google 的 [Go 编程语言](https://golang.org/)，自 2012 年 1 月开始（在 Go 1.0 之前！），我就用 Go 为 [Space Monkey](http://www.spacemonkey.com/) 编写了合规的产品代码。我对 Go 的最初体验可以追溯到我在研究 Hoare 的 [通信顺序进程](https://en.wikipedia.org/wiki/Communicating_sequential_processes) 并发模型和 [Matt Might](http://matt.might.net) 的 [UCombinator 研究组](http://www.ucombinator.org/) 下的 [π-演算](https://en.wikipedia.org/wiki/%CE%A0-calculus) 时，作为我（[现在已重定向](https://www.jtolio.com/writing/2015/11/research-log-cell-states-and-microarrays/)）博士工作的一部分，以更好地支持多核开发。Go 就是在那时发布的（多么巧合啊！），我当即就开始学习尝试了。

它很快就成为了 Space Monkey 开发的核心部分。目前，我们在 Space Monkey 的生产系统有超过 42.5 万行的纯 Go 代码（*不* 包括我们所有的 vendored 库中的代码量，这将使它接近 150 万行），所以也并不是你见过的最多的 Go 代码，但是对于相对年轻的语言，我们是重度用户。我们之前 [写了我们的 Go 使用情况](https://www.jtolio.com/writing/2014/04/go-space-monkey/)。也开源了一些使用率很高的库；许多人似乎是我们的 [OpenSSL 绑定](https://godoc.org/github.com/spacemonkeygo/openssl)（比 [crypto/tls](https://golang.org/pkg/crypto/tls/) 更快，但请保持 openssl 本身是最新的！）、我们的 [错误处理库](https://godoc.org/github.com/spacemonkeygo/errors)、[日志库](https://godoc.org/github.com/spacemonkeygo/spacelog) 和 [度量标准收集库/zipkin 客户端](https://godoc.org/gopkg.in/spacemonkeygo/monitor.v1) 的粉丝。我们使用 Go、我们热爱 Go、我们认为它是目前为止我们使用过的最不糟糕的、符合我们需求的编程语言。

尽管我也不认为我能说服自己不要提及我的广泛避免使用 [goroutine-local-storage 库](https://github.com/jtolds/gls) （尽管它是一个你不应该使用的魔改技巧，但它是一个漂亮的魔改），希望我的其他经历足以证明我在解释我故意煽动性的帖子标题之前知道我在说什么。

![](/data/attachment/album/202101/05/101255fuq9vximbz7rbjvh.jpg)

### 等等，什么？

如果你在大街上问一个有名的程序员，Go 有什么特别之处？ 她很可能会告诉你 Go 最出名的是<ruby> 通道 <rt>  Channels </rt></ruby> 和 goroutine。 Go 的理论基础很大程度上是建立在 Hoare 的 CSP（<ruby> 通信顺序进程 <rt>  Communicating Sequential Processes </rt></ruby>）模型上的，该模型本身令人着迷且有趣，我坚信，到目前为止，它产生的收益远远超过了我们的预期。

CSP（和 π-演算）都使用通信作为核心同步原语，因此 Go 会有通道是有道理的。Rob Pike 对 CSP 着迷（有充分的理由）[相当深](https://en.wikipedia.org/wiki/Newsqueak) 已经有一段时间了。（[当时](https://en.wikipedia.org/wiki/Alef_%28programming_language%29) 和 [现在](https://en.wikipedia.org/wiki/Limbo_%28programming_language%29)）。

但是从务实的角度来看（也是 Go 引以为豪的），Go 把通道搞错了。在这一点上，通道的实现在我的书中几乎是一个坚实的反模式。为什么这么说呢？亲爱的读者，让我细数其中的方法。

#### 你可能最终不会只使用通道

Hoare 的 “通信顺序进程” 是一种计算模型，实际上，唯一的同步原语是在通道上发送或接收的。一旦使用 <ruby> 互斥量 <rt>  mutex </rt></ruby>、<ruby> 信号量 <rt>  semaphore </rt></ruby> 或 <ruby> 条件变量 <rt>  condition variable </rt></ruby>、bam，你就不再处于纯 CSP 领域。 Go 程序员经常通过高呼 “[通过交流共享内存](https://blog.golang.org/share-memory-by-communicating)” 的 [缓存的思想](https://lesswrong.com/lw/k5/cached_thoughts/) 来宣扬这种模式和哲学。

那么，让我们尝试在 Go 中仅使用 CSP 编写一个小程序！让我们成为高分接收者。我们要做的就是跟踪我们看到的最大的高分值。如此而已。

首先，我们将创建一个 `Game` 结构体。

```
type Game struct {
  bestScore int
  scores    chan int
}

```

`bestScore` 不会受到<ruby> 互斥量 <rt>  mutex </rt></ruby>的保护！这很好，因为我们只需要一个 goroutine 来管理其状态并通过通道来接收新的分值即可。

```
func (g *Game) run() {
  for score := range g.scores {
    if g.bestScore < score {
      g.bestScore = score
    }
  }
}

```

好的，现在我们将创建一个有用的构造函数来开始 `Game`。

```
func NewGame() (g *Game) {
  g = &Game{
    bestScore: 0,
    scores:    make(chan int),
  }
  go g.run()
  return g
}

```

接下来，假设有人给了我们一个可以返回分数的 `Player`。它也可能会返回错误，因为可能传入的 TCP 流可能会死掉或发生某些故障，或者玩家退出。

```
type Player interface {
  NextScore() (score int, err error)
}

```

为了处理 `Player`，我们假设所有错误都是致命的，并将获得的比分向下传递到通道。

```
func (g *Game) HandlePlayer(p Player) error {
  for {
    score, err := p.NextScore()
    if err != nil {
      return err
    }
    g.scores <- score
  }
}

```

好极了！现在我们有了一个 `Game` 类型，可以以线程安全的方式跟踪 `Player` 获得的最高分数。

你圆满完成了自己的开发工作，并开始拥有客户。你将这个游戏服务器公开，就取得了令人难以置信的成功！你的游戏服务器上也许正在创建许多游戏。

很快，你发现人们有时会离开你的游戏。许多游戏不再有任何玩家在玩，但没有任何东西可以阻止游戏运行的循环。死掉的 `(*Game).run` goroutines 让你不知所措。

**挑战：** 在无需互斥量或 panics 的情况下修复上面的 goroutine 泄漏。实际上，可以滚动到上面的代码，并想出一个仅使用通道来解决此问题的方案。

我等着。

就其价值而言，它完全可以只通过通道来完成，但是请观察以下解决方案的简单性，它甚至没有这个问题：

```
type Game struct {
  mtx sync.Mutex
  bestScore int
}

func NewGame() *Game {
  return &Game{}
}

func (g *Game) HandlePlayer(p Player) error {
  for {
    score, err := p.NextScore()
    if err != nil {
      return err
    }
    g.mtx.Lock()
    if g.bestScore < score {
      g.bestScore = score
    }
    g.mtx.Unlock()
  }
}

```

你想选择哪一个？不要被欺骗了，以为通道的解决方案可以使它在更复杂的情况下更具可读性和可理解性。<ruby> 拆解 <rt>  Teardown </rt></ruby>是非常困难的。这种拆解若用<ruby> 互斥量 <rt>  mutex </rt></ruby>来做那只是小菜一碟，但最困难的是只使用 Go 专用通道来解决。另外，如果有人回复说发送通道的通道更容易推理，我马上就是感到头疼。

重要的是，这个特殊的情况可能真的 **很容易** 解决，而通道有一些运行时的帮助，而 Go 没有提供！不幸的是，就目前的情况来看，与 Go 的 CSP 版本相比，使用传统的<ruby> 同步原语 <rt>  synchronization primitives </rt></ruby>可以更好地解决很多问题，这是令人惊讶的。稍后，我们将讨论 Go 可以做些什么来简化此案例。

**练习：** 还在怀疑？ 试着让上面两种解决方案（只使用通道与只使用互斥量channel-only vs mutex-only）在一旦 `bestScore` 大于或等于 100 时，就停止向 `Players` 索要分数。继续打开你的文本编辑器。这是一个很小的玩具问题。

这里的总结是，如果你想做任何实际的事情，除了通道之外，你还会使用传统的同步原语。

#### 通道比你自己实现要慢一些

Go 如此重视 CSP 理论，我认为其中一点就是，运行时应该可以通过通道做一些杀手级的调度优化。也许通道并不总是最直接的原语，但肯定是高效且快速的，对吧？

![](/data/attachment/album/202101/05/101257f9gnh028922k909h.jpg)

正如 [Dustin Hiatt](https://twitter.com/HiattDustin) 在 [Tyler Treat’s post about Go](http://bravenewgeek.com/go-is-unapologetically-flawed-heres-why-we-use-it/) 上指出的那样，

>
> 在幕后，通道使用锁来序列化访问并提供线程安全性。 因此，通过使用通道同步对内存的访问，你实际上就是在使用锁。 被包装在线程安全队列中的锁。 那么，与仅仅使用标准库 `sync` 包中的互斥量相比，Go 的花式锁又如何呢？ 以下数字是通过使用 Go 的内置基准测试功能，对它们的单个集合连续调用 Put 得出的。
>
>
>

```
> BenchmarkSimpleSet-8 3000000 391 ns/op
> BenchmarkSimpleChannelSet-8 1000000 1699 ns/o
>

```

无缓冲通道的情况与此类似，甚至是在争用而不是串行运行的情况下执行相同的测试。

也许 Go 调度器会有所改进，但与此同时，良好的旧互斥量和条件变量是非常好、高效且快速。如果你想要提高性能，请使用久经考验的方法。

#### 通道与其他并发原语组合不佳

好的，希望我已经说服了你，有时候，你至少还会与除了通道之外的原语进行交互。标准库似乎显然更喜欢传统的同步原语而不是通道。

你猜怎么着，正确地将通道与互斥量和条件变量一起使用，其实是有一定的挑战性的。

关于通道的一个有趣的事情是，通道发送是同步的，这在 CSP 中是有很大意义的。通道发送和通道接收的目的是为了成为同步屏蔽，发送和接收应该发生在同一个虚拟时间。如果你是在执行良好的 CSP 领域，那就太好了。

![](/data/attachment/album/202101/05/101259tqqfqthyzlidzn0m.jpg)

实事求是地说，Go 通道也有多种缓冲方式。你可以分配一个固定的空间来考虑可能的缓冲，以便发送和接收是不同的事件，但缓冲区大小是有上限的。Go 并没有提供一种方法来让你拥有任意大小的缓冲区 —— 你必须提前分配缓冲区大小。 *这很好*，我在邮件列表上看到有人在争论，*因为无论如何内存都是有限的*。

What。

这是个糟糕的答案。有各种各样的理由来使用一个任意缓冲的通道。如果我们事先知道所有的事情，为什么还要使用 `malloc` 呢？

没有任意缓冲的通道意味着在 *任何* 通道上的幼稚发送可能会随时阻塞。你想在一个通道上发送，并在互斥下更新其他一些记账吗？小心！你的通道发送可能被阻塞！

```
// ...
s.mtx.Lock()
// ...
s.ch <- val // might block!
s.mtx.Unlock()
// ...

```

这是哲学家晚餐大战的秘诀。如果你使用了锁，则应该迅速更新状态并释放它，并且尽可能不要在锁下做任何阻塞。

有一种方法可以在 Go 中的通道上进行非阻塞发送，但这不是默认行为。假设我们有一个通道 `ch := make(chan int)`，我们希望在其上无阻塞地发送值 `1`。以下是在不阻塞的情况下你必须要做的最小量的输入：

```
select {
case ch <- 1: // it sent
default:      // it didn't
}

```

对于刚入门的 Go程序员来说，这并不是自然而然就能想到的事情。

综上所述，因为通道上的很多操作都会阻塞，所以需要对哲学家及其就餐仔细推理，才能在互斥量的保护下，成功地将通道操作与之并列使用，而不会造成死锁。

#### 严格来说，回调更强大，不需要不必要的 goroutines

![](/data/attachment/album/202101/05/101301wofz5979ap6xod7d.jpg)

每当 API 使用通道时，或者每当我指出通道使某些事情变得困难时，总会有人会指出我应该启动一个 goroutine 来读取该通道，并在读取该通道时进行所需的任何转换或修复。

呃，不。如果我的代码位于热路径中怎么办？需要通道的实例很少，如果你的 API 可以设计为使用<ruby> 互斥量 <rt>  mutexes </rt></ruby>、<ruby> 信号量 <rt>  semaphores </rt></ruby>和<ruby> 回调 <rt>  callbacks </rt></ruby>，而不使用额外的 goroutine （因为所有事件边缘都是由 API 事件触发的），那么使用通道会迫使我在资源使用中添加另一个内存分配堆栈。是的，goroutine 比线程轻得多，但更轻量并不意味着是最轻量。

正如我以前 [在一篇关于使用通道的文章的评论中争论过的](http://www.informit.com/articles/article.aspx?p=2359758#comment-2061767464)（呵呵，互联网），如果你使用回调而不是通道，你的 API *总是* 可以更通用，*总是* 更灵活，而且占用的资源也会大大减少。“总是” 是一个可怕的词，但我在这里是认真的。有证据级的东西在进行。

如果有人向你提供了一个基于回调的 API，而你需要一个通道，你可以提供一个回调，在通道上发送，开销不大，灵活性十足。

另一方面，如果有人提供了一个基于通道的 API 给你，而你需要一个回调，你必须启动一个 goroutine 来读取通道，*并且* 你必须希望当你完成读取时，没有人试图在通道上发送更多的东西，这样你就会导致阻塞的 goroutine 泄漏。

对于一个超级简单的实际例子，请查看 [context 接口](https://godoc.org/golang.org/x/net/context)（顺便说一下，它是一个非常有用的包，你应该用它来代替 [goroutine 本地存储](https://github.com/jtolds/gls)）。

```
type Context interface {
  ...
  // Done returns a channel that closes when this work unit should be canceled.
  // Done 返回一个通道，该通道在应该取消该工作单元时关闭。
  Done() <-chan struct{}

  // Err returns a non-nil error when the Done channel is closed
  // 当 Done 通道关闭时，Err 返回一个非 nil 错误
  Err() error
  ...
}

```

想象一下，你要做的只是在 `Done()` 通道触发时记录相应的错误。你该怎么办？如果你没有在通道中选择的好地方，则必须启动 goroutine 进行处理：

```
go func() {
  <-ctx.Done()
  logger.Errorf("canceled: %v", ctx.Err())
}()

```

如果 `ctx` 在不关闭返回 `Done()` 通道的情况下被垃圾回收怎么办？哎呀！这正是一个 goroutine 泄露！

现在假设我们更改了 `Done` 的签名：

```
// Done calls cb when this work unit should be canceled.
Done(cb func())

```

首先，现在日志记录非常容易。看看：`ctx.Done(func() { log.Errorf ("canceled:%v", ctx.Err()) })`。但是假设你确实需要某些选择行为。你可以这样调用它：

```
ch := make(chan struct{})
ctx.Done(func() { close(ch) })

```

瞧！通过使用回调，不会失去表现力。 `ch` 的工作方式类似于用于返回的通道 `Done()`，在日志记录的情况下，我们不需要启动整个新堆栈。我必须保留堆栈跟踪信息（如果我们的日志包倾向于使用它们）；我必须避免将其他堆栈分配和另一个 goroutine 分配给调度程序。

下次你使用通道时，问问你自己，如果你用互斥量和条件变量代替，是否可以消除一些 goroutine ？ 如果答案是肯定的，那么修改这些代码将更加有效。而且，如果你试图使用通道只是为了在集合中使用 `range` 关键字，那么我将不得不请你放下键盘，或者只是回去编写 Python 书籍。

![more like Zooey De-channel, amirite](/data/attachment/album/202101/05/101303lz58y83goqjj14qh.jpg)

#### 通道 API 不一致，只是 cray-cray

在通道已关闭的情况下，执行关闭或发送消息将会引发 panics！为什么呢？ 如果想要关闭通道，你需要在外部同步它的关闭状态（使用互斥量等，这些互斥量的组合不是很好！），这样其他写入者才不会写入或关闭已关闭的通道，或者只是向前冲，关闭或写入已关闭的通道，并期望你必须恢复所有引发的 panics。

这是多么怪异的行为。 Go 中几乎所有其他操作都有避免 panic 的方法（例如，类型断言具有 `, ok =` 模式），但是对于通道，你只能自己动手处理它。

好吧，所以当发送失败时，通道会出现 panic。我想这是有一定道理的。但是，与几乎所有其他带有 nil 值的东西不同，发送到 nil 通道不会引发 panic。相反，它将永远阻塞！这很违反直觉。这可能是有用的行为，就像在你的除草器上附加一个开罐器，可能有用（在 Skymall 可以找到）一样，但这肯定是意想不到的。与 nil 映射（执行隐式指针解除引用），nil 接口（隐式指针解除引用），未经检查的类型断言以及其他所有类型交互不同，nil 通道表现出实际的通道行为，就好像为该操作实例化了一个全新的通道一样。

接收的情况稍微好一点。在已关闭的通道上执行接收会发生什么？好吧，那会是有效操作——你将得到一个零值。好吧，我想这是有道理的。奖励！接收允许你在收到值时进行 `, ok =` 样式的检查，以确定通道是否打开。谢天谢地，我们在这里得到了 `, ok =`。

但是，如果你从 nil 渠道接收会发生什么呢？ *也是永远阻塞！* 耶！不要试图利用这样一个事实：如果你关闭了通道，那么你的通道是 nil！

### 通道有什么好处？

当然，通道对于某些事情是有好处的（毕竟它们是一个通用容器），有些事情你只能用它们来做（比如 `select`）。

#### 它们是另一种特殊情况下的通用数据结构

Go 程序员已经习惯于对泛型的争论，以至于我一提起这个词就能感觉到 PTSD（创伤后应激障碍）的到来。我不是来谈论这件事的，所以擦擦额头上的汗，让我们继续前进吧。

无论你对泛型的看法是什么，Go 的映射、切片和通道都是支持泛型元素类型的数据结构，因为它们已经被特殊封装到语言中了。

在一种不允许你编写自己的泛型容器的语言中，任何允许你更好地管理事物集合的东西都是有价值的。在这里，通道是一个支持任意值类型的线程安全数据结构。

所以这很有用！我想这可以省去一些陈词滥调。

我很难把这算作是通道的胜利。

#### Select

使用通道可以做的主要事情是 `select` 语句。在这里，你可以等待固定数量的事件输入。它有点像 epoll，但你必须预先知道要等待多少个套接字。

这是真正有用的语言功能。如果不是 `select`，通道将被彻底清洗。但是我的天呐，让我告诉你，第一次决定可能需要在多个事物中选择，但是你不知道有多少项，因此必须使用 `reflect.Select`。

### 通道如何才能更好？

很难说 Go 语言团队可以为 Go 2.0 做的最具战术意义的事情是什么（Go 1.0 兼容性保证很好，但是很费劲），但这并不能阻止我提出一些建议。

#### 在条件变量上的 Select ！

我们可以不需要通道！这是我提议我们摆脱一些“<ruby> 圣牛 <rt>  sacred cows </rt></ruby>”（LCTT 译注：神圣不可质疑的事物）的地方，但是让我问你，如果你可以选择任何自定义同步原语，那会有多棒？（答：太棒了。）如果有的话，我们根本就不需要通道了。

#### GC 可以帮助我们吗？

在第一个示例中，如果我们能够使用定向类型的通道垃圾回收（GC）来帮助我们进行清理，我们就可以轻松地解决通道的高分服务器清理问题。

![](/data/attachment/album/202101/05/101305wnt2tqv2zq4n2umb.jpg)

如你所知，Go 具有定向类型的通道。 你可以使用仅支持读取的通道类型（`<-chan`）和仅支持写入的通道类型（`chan<-`）。 这太棒了！

Go 也有垃圾回收功能。 很明显，某些类型的记账方式太繁琐了，我们不应该让程序员去处理它们。 我们清理未使用的内存！ 垃圾回收非常有用且整洁。

那么，为什么不帮助清理未使用或死锁的通道读取呢？ 与其让 `make(chan Whatever)` 返回一个双向通道，不如让它返回两个单向通道（`chanReader, chanWriter:= make(chan Type)`）。

让我们重新考虑一下最初的示例：

```
type Game struct {
  bestScore int
  scores    chan<- int
}

func run(bestScore *int, scores <-chan int) {
  // 我们不会直接保留对游戏的引用，因为这样我们就会保留着通道的发送端。
  for score := range scores {
    if *bestScore < score {
      *bestScore = score
    }
  }
}

func NewGame() (g *Game) {
  // 这种 make(chan) 返回风格是一个建议
  scoreReader, scoreWriter := make(chan int)
  g = &Game{
    bestScore: 0,
    scores:    scoreWriter,
  }
  go run(&g.bestScore, scoreReader)
  return g
}

func (g *Game) HandlePlayer(p Player) error {
  for {
    score, err := p.NextScore()
    if err != nil {
      return err
    }
    g.scores <- score
  }
}

```

如果垃圾回收关闭了一个通道，而我们可以证明它永远不会有更多的值，那么这个解决方案是完全可行的。是的，是的，`run` 中的评论暗示着有一把相当大的枪瞄准了你的脚，但至少现在这个问题可以很容易地解决了，而以前确实不是这样。此外，一个聪明的编译器可能会做出适当的证明，以减少这种脚枪造成的损害。

#### 其他小问题

* **Dup 通道吗？** —— 如果我们可以在通道上使用等效于 `dup` 的系统调用，那么我们也可以很容易地解决多生产者问题。 每个生产者可以关闭自己的 `dup` 版通道，而不会破坏其他生产者。
* **修复通道 API！** —— 关闭不是幂等的吗？ 在已关闭的通道上发送信息引起的 panics 没有办法避免吗？ 啊！
* **任意缓冲的通道** —— 如果我们可以创建没有固定的缓冲区大小限制的缓冲通道，那么我们可以创建非阻塞的通道。

### 那我们该怎么向大家介绍 Go 呢？

如果你还没有，请看看我目前最喜欢的编程文章：《[你的函数是什么颜色](http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/)》。虽然不是专门针对 Go，但这篇博文比我更有说服力地阐述了为什么 goroutines 是 Go 最好的特性（这也是 Go 在某些应用程序中优于 Rust 的方式之一）。

如果你还在使用这样的一种编程语言写代码，它强迫你使用类似 `yield` 关键字来获得高性能、并发性或事件驱动的模型，那么你就是活在过去，不管你或其他人是否知道这一点。到目前为止，Go 是我所见过的实现 M：N 线程模型（非 1：1 ）的语言中最好的入门者之一，而且这种模型非常强大。

所以，跟大家说说 goroutines 吧。

如果非要我选择 Go 的另一个主要特性，那就是接口。静态类型的 <ruby> <a href="https://en.wikipedia.org/wiki/Duck_typing">  鸭子模型 </a> <rt>  duck typing </rt></ruby> 使得扩展、使用你自己或他人的项目变得如此有趣而令人惊奇，这也许值得我改天再写一组完全不同的文章来介绍它。

### 所以…

我一直看到人们争先恐后冲进 Go，渴望充分利用通道来发挥其全部潜力。这是我对你的建议。

**够了！**

当你在编写 API 和接口时，尽管“绝不”的建议可能很糟糕，但我非常肯定，通道从来没有什么时候好过，我用过的每一个使用通道的 Go API，最后都不得不与之抗争。我从来没有想过“哦 太好了，这里是一个通道；”它总是被一些变体取代，***这是什么新鲜的地狱？***

所以，*请在适当的地方，并且只在适当的地方使用通道。*

在我使用的所有 Go 代码中，我可以用一只手数出有多少次通道真的是最好的选择。有时候是这样的。那很好！那就用它们吧。但除此之外，就不要再使用了。

![](/data/attachment/album/202101/05/101306aje6ztzntb872izz.jpg)

*特别感谢我的校对读者 Jeff Wendling、[Andrew Harding](https://github.com/azdagron)、[George Shank](https://twitter.com/taterbase) 和 [Tyler Treat](http://bravenewgeek.com) 提供的宝贵反馈。*

如果你想和我们一起用 Go 在 Space Monkey 项目工作，请[给我打个招呼](https://www.jtolio.com/contact/)！

---

via: <https://www.jtolio.com/2016/03/go-channels-are-bad-and-you-should-feel-bad>

作者：[jtolio.com](https://www.jtolio.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/) 荣誉推出


编写一个简单的游戏来练习用 C++ 编程

Summary: C++ 语言很复杂，但它可以教会你很多关于数据类型、内存管理和代码链接的知识。

C++ 语言很复杂，但它可以教会你很多关于数据类型、内存管理和代码链接的知识。

>
> C++ 语言很复杂，但它可以教会你很多关于数据类型、内存管理和代码链接的知识。
>
>
>

![](/data/attachment/album/202101/05/110821dwzztxchazhto8ko.jpg)

学习一门编程语言有几种方法。如果你是编码新手，你通常会学习一些基本的计算机编码概念，并尝试应用它们。如果你已经知道如何用另一种语言进行编码，你可以重新学习编码概念在新语言中是如何表达的。

不管是哪种情况，学习这些新原理的便捷方法是创建一个简单的猜谜游戏。这会迫使你了解一门语言如何接收输入和发送输出，如何比较数据，如何控制程序的流程，以及如何利用条件来影响结果。它还确保你知道一门语言是如何组织其代码的；例如，Lua 或 [Bash](https://opensource.com/article/20/12/learn-bash) 可以很容易地作为脚本运行，而 [Java](https://opensource.com/article/20/12/learn-java-writing-guess-number-game) 则需要你创建一个类。

在本文中，我将演示如何用 [C++](https://www.cplusplus.com/) 在终端上实现猜谜游戏。

### 安装依赖关系

要跟上本文的步伐，你需要 C++ 和一个编译器。

在 Linux 上，你可以通过从你的发行版软件仓库中安装 Qt Creator IDE 来获得你所需要的一切。

在 Fedora、CentOS 或 RHEL 上：

```
$ sudo dnf install qt-creator

```

在 Debian、Ubuntu、Chromebook 或类似的系统上：

```
$ sudo apt install qtcreator

```

本文并没有使用 Qt Creator IDE，但它是一个安装你所需要的一切的简单方法，对于复杂的 C++ 项目（包括那些带有 GUI 的项目），它是一个必不可少的工具。在 macOS 或 Windows 上，按照 Qt 网站上的[安装说明](https://www.qt.io/product/development-tools)进行安装。

### 设置包含和命名空间

C++ 的核心语言是精简的。即使是一个简单的应用程序也需要使用额外的库。这个应用程序使用 [iostream](http://www.cplusplus.com/reference/iostream/) 来获得对 `cout` 和 `cin` 关键字的访问。

另外，确保程序使用 `std` 命名空间：

```
#include <iostream>

using namespace std;

```

这并不是绝对必要，但如果不将命名空间设置为 `std`，所有来自 `iostream` 库的关键字都需要一个命名空间前缀。例如，我不能写作 `cout`，而是要写作 `std::cout`。

C++ 中的语句以分号结束。

### 创建一个函数

每个 C++ 应用程序至少需要一个函数。一个 C++ 应用程序的主函数必须称为 `main`，它必须返回一个整数（`int`），这符合 [POSIX](https://opensource.com/article/19/7/what-posix-richard-stallman-explains) 的期望，即一个进程在成功时返回 0，而在失败时返回其他值。你可以通过为它提供返回类型和名称来创建一个新函数。

```
int main() {
 // code goes here
}

```

### 实现程序逻辑

游戏代码必须首先产生一个随机数供玩家猜测。在 C++ 中，你可以通过建立一个用于生成伪随机数的*种子*来实现。一个简单的种子就是当前的时间。一旦有了种子，你就可以得到一个在 1 和 100 之间的数字。通过调用 `rand` 函数，并设置上限值 100 来产生一个从 0 到 99 的随机数，所以无论选择了什么数字都要加 1，并将结果分配给一个名为 `number` 的变量。你还必须声明一个变量来保存玩家的猜测值。为了清楚起见，我称这个变量为 `guess`。

这个示例代码还包括一个调试语句，告诉你随机数到底是什么。这对于猜测游戏来说不是很好，但它使测试速度快了很多。以后，你可以删除这一行，或者直接在行前面用 `//` 注释出来：

```
 srand (time(NULL));
 int number = rand() % 100+1;
 int guess = 0;

 cout << number << endl; //debug

```

### 增加 do-while 和 if 语句

C++ 中的 `do-while` 语句以关键字 `do` 开头，并将你希望 C++ 做的所有事情用括号括起来。用 `while` 关键字结束语句，后面是必须满足的条件（括号内）：

```
do {
 // code here
} while ( number != guess );

```

游戏代码出现在 `if`、`else if` 和 `else` 语句之间，为玩家提供提示。

首先，用 `cout` 语句提示玩家猜测。`cout` 函数将输出打印到 `stdout` 上。因为 `cout` 语句没有连着 `endl`（endline）函数，所以没有换行。紧接着这个 `cout` 语句，通过使用 `cin` 函数告诉 C++ 等待输入。正如你所猜测的那样，`cin` 等待来自 `stdin` 的输入。

接下来，程序进入 `if` 控制语句。如果玩家的猜测大于 `number` 变量中包含的伪随机数，那么程序就会打印出一个提示，后面是一个换行符。这就中断了 `if` 语句，但 C++ 仍然被困在 `do-while` 循环中，因为它的条件（`number` 变量等于 `guess`）还没有满足。

如果玩家的猜测小于 `number` 变量中包含的伪随机数，那么程序就会打印出一个提示，后面是一个换行符。这再次中断了 `if` 语句，但程序仍然被困在 `do-while` 循环中。

当 `guess` 等于 `number` 时，最终满足关键条件，触发 `else` 语句，`do-while` 循环结束，程序结束：

```
do {
  cout << "Guess a number between 1 and 100: ";
  cin >> guess;

  if ( guess > number) { cout << "Too high.\n" << endl; }
  else if ( guess < number ) { cout << "Too low.\n" << endl; }
  else {
    cout << "That's right!\n" << endl;
    exit(0);
  } // fi
 } while ( number != guess );
return 0;
} // main

```

### 构建代码和玩游戏

你可以用 GCC 构建你的应用程序：

```
$ g++ -o guess.bin guess.cpp

```

运行二进制文件试试：

```
$ ./guess.bin
74
Guess a number between 1 and 100: 76
Too high.

Guess a number between 1 and 100: 1
Too low.

Guess a number between 1 and 100: 74
That's right!

```

成功了！

### 试试 C++ 吧

C++ 语言很复杂。为终端编写 C++ 应用程序可以让你学到很多关于数据类型、内存管理和代码链接的知识。试着用 C++ 编写一个有用的实用程序，看看你能发现什么！

---

via: <https://opensource.com/article/20/12/learn-c-game>

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

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