mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-25 23:11:02 +08:00
commit
b78249f002
171
published/20180116 Command Line Heroes- Season 1- OS Wars_2.md
Normal file
171
published/20180116 Command Line Heroes- Season 1- OS Wars_2.md
Normal file
@ -0,0 +1,171 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lujun9972)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11296-1.html)
|
||||
[#]: subject: (Command Line Heroes: Season 1: OS Wars)
|
||||
[#]: via: (https://www.redhat.com/en/command-line-heroes/season-1/os-wars-part-2-rise-of-linux)
|
||||
[#]: author: (redhat https://www.redhat.com)
|
||||
|
||||
《代码英雄》第一季(2):操作系统战争(下)Linux 崛起
|
||||
======
|
||||
|
||||
> 代码英雄讲述了开发人员、程序员、黑客、极客和开源反叛者如何彻底改变技术前景的真实史诗故事。
|
||||
|
||||
![](https://www.redhat.com/files/webux/img/bandbg/bkgd-clh-ep2-2000x950.png)
|
||||
|
||||
本文是《[代码英雄](https://www.redhat.com/en/command-line-heroes)》系列播客[第一季(2):操作系统战争(下)](https://www.redhat.com/en/command-line-heroes/season-1/os-wars-part-2-rise-of-linux) 的[音频](https://dts.podtrac.com/redirect.mp3/audio.simplecast.com/2199861a.mp3)脚本。
|
||||
|
||||
> 微软帝国控制着 90% 的用户。操作系统的完全标准化似乎是板上钉钉的事了。但是一个不太可能的英雄出现在开源反叛组织中。戴着眼镜,温文尔雅的<ruby>林纳斯·托瓦兹<rt>Linus Torvalds</rt></ruby>免费发布了他的 Linux® 程序。微软打了个趔趄,并且开始重整旗鼓而来,战场从个人电脑转向互联网。
|
||||
|
||||
**Saron Yitbarek:** 这玩意开着的吗?让我们进一段史诗般的星球大战的开幕吧,开始了。
|
||||
|
||||
配音:第二集:Linux® 的崛起。微软帝国控制着 90% 的桌面用户。操作系统的全面标准化似乎是板上钉钉的事了。然而,互联网的出现将战争的焦点从桌面转向了企业,在该领域,所有商业组织都争相构建自己的服务器。*[00:00:30]*与此同时,一个不太可能的英雄出现在开源反叛组织中。固执、戴着眼镜的 <ruby>林纳斯·托瓦兹<rt>Linus Torvalds</rt></ruby>免费发布了他的 Linux 系统。微软打了个趔趄,并且开始重整旗鼓而来。
|
||||
|
||||
**Saron Yitbarek:** 哦,我们书呆子就是喜欢那样。上一次我们讲到哪了?苹果和微软互相攻伐,试图在一场争夺桌面用户的战争中占据主导地位。*[00:01:00]* 在第一集的结尾,我们看到微软获得了大部分的市场份额。很快,由于互联网的兴起以及随之而来的开发者大军,整个市场都经历了一场地震。互联网将战场从在家庭和办公室中的个人电脑用户转移到拥有数百台服务器的大型商业客户中。
|
||||
|
||||
这意味着巨量资源的迁移。突然间,所有相关企业不仅被迫为服务器空间和网站建设付费,而且还必须集成软件来进行资源跟踪和数据库监控等工作。*[00:01:30]* 你需要很多开发人员来帮助你。至少那时候大家都是这么做的。
|
||||
|
||||
在操作系统之战的第二部分,我们将看到优先级的巨大转变,以及像林纳斯·托瓦兹和<ruby>理查德·斯托尔曼<rt>Richard Stallman</rt></ruby>这样的开源反逆者是如何成功地在微软和整个软件行业的核心地带引发恐惧的。
|
||||
|
||||
我是 Saron Yitbarek,你现在收听的是代码英雄,一款红帽公司原创的播客节目。*[00:02:00]* 每一集,我们都会给你带来“从码开始”改变技术的人的故事。
|
||||
|
||||
好。想象一下你是 1991 年时的微软。你自我感觉良好,对吧?满怀信心。确立了全球主导的地位感觉不错。你已经掌握了与其他企业合作的艺术,但是仍然将大部分开发人员、程序员和系统管理员排除在联盟之外,而他们才是真正的步兵。*[00:02:30]* 这时出现了个叫林纳斯·托瓦兹的芬兰极客。他和他的开源程序员团队正在开始发布 Linux,这个操作系统内核是由他们一起编写出来的。
|
||||
|
||||
坦白地说,如果你是微软公司,你并不会太在意 Linux,甚至不太关心开源运动,但是最终,Linux 的规模变得如此之大,以至于微软不可能不注意到。*[00:03:00]* Linux 第一个版本出现在 1991 年,当时大概有 1 万行代码。十年后,变成了 300 万行代码。如果你想知道,今天则是 2000 万行代码。
|
||||
|
||||
*[00:03:30]* 让我们停留在 90 年代初一会儿。那时 Linux 还没有成为我们现在所知道的庞然大物。这个奇怪的病毒式的操作系统只是正在这个星球上蔓延,全世界的极客和黑客都爱上了它。那时候我还太年轻,但有点希望我曾经经历过那个时候。在那个时候,发现 Linux 就如同进入了一个秘密社团一样。就像其他人分享地下音乐混音带一样,程序员与朋友们分享 Linux CD 集。
|
||||
|
||||
开发者 Tristram Oaten *[00:03:40]* 讲讲你 16 岁时第一次接触 Linux 的故事吧。
|
||||
|
||||
**Tristram Oaten:** 我和我的家人去了红海的 Hurghada 潜水度假。那是一个美丽的地方,强烈推荐。第一天,我喝了自来水。也许,我妈妈跟我说过不要这么做。我整个星期都病得很厉害,没有离开旅馆房间。*[00:04:00]* 当时我只带了一台新安装了 Slackware Linux 的笔记本电脑,我听说过这玩意并且正在尝试使用它。所有的东西都在 8 张 cd 里面。这种情况下,我只能整个星期都去了解这个外星一般的系统。我阅读手册,摆弄着终端。我记得当时我甚至不知道一个点(表示当前目录)和两个点(表示前一个目录)之间的区别。
|
||||
|
||||
*[00:04:30]* 我一点头绪都没有。犯过很多错误,但慢慢地,在这种强迫的孤独中,我突破了障碍,开始理解并明白命令行到底是怎么回事。假期结束时,我没有看过金字塔、尼罗河等任何埃及遗址,但我解锁了现代世界的一个奇迹。我解锁了 Linux,接下来的事大家都知道了。
|
||||
|
||||
**Saron Yitbarek:** 你会从很多人那里听到关于这个故事的不同说法。访问 Linux 命令行是一种革命性的体验。
|
||||
|
||||
**David Cantrell:** *[00:05:00]* 它给了我源代码。我当时的感觉是,“太神奇了。”
|
||||
|
||||
**Saron Yitbarek:** 我们正在参加一个名为 Flock to Fedora 的 2017 年 Linux 开发者大会。
|
||||
|
||||
**David Cantrell:** ……非常有吸引力。我觉得我对这个系统有了更多的控制力,它越来越吸引我。我想,从 1995 年我第一次编译 Linux 内核那时起,我就迷上了它。
|
||||
|
||||
**Saron Yitbarek:** 开发者 David Cantrell 与 Joe Brockmire。
|
||||
|
||||
**Joe Brockmeier:** *[00:05:30]* 我在 Cheap Software 转的时候发现了一套四张 CD 的 Slackware Linux。它看起来来非常令人兴奋而且很有趣,所以我把它带回家,安装在第二台电脑上,开始摆弄它,有两件事情让我感到很兴奋:一个是,我运行的不是 Windows,另一个是 Linux 的开源特性。
|
||||
|
||||
**Saron Yitbarek:** *[00:06:00]* 某种程度上来说,对命令行的使用总是存在的。在开源真正开始流行还要早的几十年前,人们(至少在开发人员中是这样)总是希望能够做到完全控制。让我们回到操作系统大战之前的那个时代,在苹果和微软为他们的 GUI 而战之前。那时也有代码英雄。<ruby>保罗·琼斯<rt>Paul Jones</rt></ruby>教授(在线图书馆 ibiblio.org 的负责人)在那个古老的时代,就是一名开发人员。
|
||||
|
||||
**Paul Jones:** *[00:06:30]* 从本质上讲,互联网在那个时候客户端-服务器架构还是比较少的,而更多的是点对点架构的。确实,我们会说,某种 VAX 到 VAX 的连接(LCTT 译注:DEC 的一种操作系统),某种科学工作站到科学工作站的连接。这并不意味着没有客户端-服务端的架构及应用程序,但这的确意味着,最初的设计是思考如何实现点对点,*[00:07:00]* 它与 IBM 一直在做的东西相对立。IBM 给你的只有哑终端,这种终端只能让你管理用户界面,却无法让你像真正的终端一样为所欲为。
|
||||
|
||||
**Saron Yitbarek:** 图形用户界面在普通用户中普及的同时,在工程师和开发人员中总是存在着一股相反的力量。早在 Linux 出现之前的二十世纪七八十年代,这股力量就存在于 Emacs 和 GNU 中。有了斯托尔曼的自由软件基金会后,总有某些人想要使用命令行,但上世纪 90 年代的 Linux 提供了前所未有的东西。
|
||||
|
||||
*[00:07:30]* Linux 和其他开源软件的早期爱好者是都是先驱。我正站在他们的肩膀上。我们都是。
|
||||
|
||||
你现在收听的是代码英雄,一款由红帽公司原创的播客。这是操作系统大战的第二部分:Linux 崛起。
|
||||
|
||||
**Steven Vaughan-Nichols:** 1998 年的时候,情况发生了变化。
|
||||
|
||||
**Saron Yitbarek:** *[00:08:00]* Steven Vaughan-Nichols 是 zdnet.com 的特约编辑,他已经写了几十年关于技术商业方面的文章了。他将向我们讲述 Linux 是如何慢慢变得越来越流行,直到自愿贡献者的数量远远超过了在 Windows 上工作的微软开发人员的数量的。不过,Linux 从未真正追上微软桌面客户的数量,这也许就是微软最开始时忽略了 Linux 及其开发者的原因。Linux 真正大放光彩的地方是在服务器机房。当企业开始线上业务时,每个企业都需要一个满足其需求的独特编程解决方案。
|
||||
|
||||
*[00:08:30]* WindowsNT 于 1993 年问世,当时它已经在与其他的服务器操作系统展开竞争了,但是许多开发人员都在想,“既然我可以通过 Apache 构建出基于 Linux 的廉价系统,那我为什么要购买 AIX 设备或大型 Windows 设备呢?”关键点在于,Linux 代码已经开始渗透到几乎所有网上的东西中。
|
||||
|
||||
**Steven Vaughan-Nichols:** *[00:09:00]* 令微软感到惊讶的是,它开始意识到,Linux 实际上已经开始有一些商业应用,不是在桌面环境,而是在商业服务器上。因此,他们发起了一场运动,我们称之为 FUD - <ruby>恐惧、不确定和怀疑<rt>fear, uncertainty and double</rt></ruby>。他们说,“哦,Linux 这玩意,真的没有那么好。它不太可靠。你一点都不能相信它”。
|
||||
|
||||
**Saron Yitbarek:** 这种软宣传式的攻击持续了一段时间。微软也不是唯一一个对 Linux 感到紧张的公司。这其实是整个行业在对抗这个奇怪新人的挑战。*[00:09:30]* 例如,任何与 UNIX 有利害关系的人都可能将 Linux 视为篡夺者。有一个案例很著名,那就是 SCO 组织(它发行过一种 UNIX 版本)在过去 10 多年里发起一系列的诉讼,试图阻止 Linux 的传播。SCO 最终失败而且破产了。与此同时,微软一直在寻找机会,他们必须要采取动作,只是不清楚具体该怎么做。
|
||||
|
||||
**Steven Vaughan-Nichols:** *[00:10:00]* 让微软真正担心的是,第二年,在 2000 年的时候,IBM 宣布,他们将于 2001 年投资 10 亿美元在 Linux 上。现在,IBM 已经不再涉足个人电脑业务。(那时)他们还没有走出去,但他们正朝着这个方向前进,他们将 Linux 视为服务器和大型计算机的未来,在这一点上,剧透警告,IBM 是正确的。*[00:10:30]* Linux 将主宰服务器世界。
|
||||
|
||||
**Saron Yitbarek:** 这已经不再仅仅是一群黑客喜欢他们对命令行的绝地武士式的控制了。金钱的投入对 Linux 助力极大。<ruby>Linux 国际<rt>Linux International</rt></ruby>的执行董事 John “Mad Dog” Hall 有一个故事可以解释为什么会这样。我们通过电话与他取得了联系。
|
||||
|
||||
**John Hall:** *[00:11:00]* 我有一个名叫 Dirk Holden 的朋友,他是德国德意志银行的系统管理员,他也参与了个人电脑上早期 X Windows 系统图形项目的工作。有一天我去银行拜访他,我说:“Dirk,你银行里有 3000 台服务器,用的都是 Linux。为什么不用 Microsoft NT 呢?”*[00:11:30]* 他看着我说:“是的,我有 3000 台服务器,如果使用微软的 Windows NT 系统,我需要 2999 名系统管理员。”他继续说道:“而使用 Linux,我只需要四个。”这真是完美的答案。
|
||||
|
||||
**Saron Yitbarek:** 程序员们着迷的这些东西恰好对大公司也极具吸引力。但由于 FUD 的作用,一些企业对此持谨慎态度。*[00:12:00]* 他们听到开源,就想:“开源。这看起来不太可靠,很混乱,充满了 BUG”。但正如那位银行经理所指出的,金钱有一种有趣的方式,可以说服人们克服困境。甚至那些只需要网站的小公司也加入了 Linux 阵营。与一些昂贵的专有选择相比,使用一个廉价的 Linux 系统在成本上是无法比拟的。如果你是一家雇佣专业人员来构建网站的商店,那么你肯定想让他们使用 Linux。
|
||||
|
||||
让我们快进几年。Linux 运行每个人的网站上。Linux 已经征服了服务器世界,然后智能手机也随之诞生。*[00:12:30]* 当然,苹果和他们的 iPhone 占据了相当大的市场份额,而且微软也希望能进入这个市场,但令人惊讶的是,Linux 也在那,已经做好准备了,迫不及待要大展拳脚。
|
||||
|
||||
作家兼记者 James Allworth。
|
||||
|
||||
**James Allworth:** 肯定还有容纳第二个竞争者的空间,那本可以是微软,但是实际上却是 Android,而 Andrid 基本上是基于 Linux 的。众所周知,Android 被谷歌所收购,现在运行在世界上大部分的智能手机上,谷歌在 Linux 的基础上创建了 Android。*[00:13:00]* Linux 使他们能够以零成本从一个非常复杂的操作系统开始。他们成功地实现了这一目标,最终将微软挡在了下一代设备之外,至少从操作系统的角度来看是这样。
|
||||
|
||||
**Saron Yitbarek:** *[00:13:30]* 这可是个大地震,很大程度上,微软有被埋没的风险。John Gossman 是微软 Azure 团队的首席架构师。他还记得当时困扰公司的困惑。
|
||||
|
||||
**John Gossman:** 像许多公司一样,微软也非常担心知识产权污染。他们认为,如果允许开发人员使用开源代码,那么他们可能只是将一些代码复制并粘贴到某些产品中,就会让某种病毒式的许可证生效从而引发未知的风险……他们也很困惑,*[00:14:00]* 我认为,这跟公司文化有关,很多公司,包括微软,都对开源开发的意义和商业模式之间的分歧感到困惑。有一种观点认为,开源意味着你所有的软件都是免费的,人们永远不会付钱。
|
||||
|
||||
**Saron Yitbarek:** 任何投资于旧的、专有软件模型的人都会觉得这里发生的一切对他们构成了威胁。当你威胁到像微软这样的大公司时,是的,他们一定会做出反应。*[00:14:30]* 他们推动所有这些 FUD —— 恐惧、不确定性和怀疑是有道理的。当时,商业运作的方式基本上就是相互竞争。不过,如果是其他公司的话,他们可能还会一直怀恨在心,抱残守缺,但到了 2013 年的微软,一切都变了。
|
||||
|
||||
微软的云计算服务 Azure 上线了,令人震惊的是,它从第一天开始就提供了 Linux 虚拟机。*[00:15:00]* <ruby>史蒂夫·鲍尔默<rt>Steve Ballmer</rt></ruby>,这位把 Linux 称为癌症的首席执行官,已经离开了,代替他的是一位新的有远见的首席执行官<ruby>萨提亚·纳德拉<rt>Satya Nadella</rt></ruby>。
|
||||
|
||||
**John Gossman:** 萨提亚有不同的看法。他属于另一个世代。比保罗、比尔和史蒂夫更年轻的世代,他对开源有不同的看法。
|
||||
|
||||
**Saron Yitbarek:** 还是来自微软 Azure 团队的 John Gossman。
|
||||
|
||||
**John Gossman:** *[00:15:30]* 大约四年前,处于实际需要,我们在 Azure 中添加了 Linux 支持。如果访问任何一家企业客户,你都会发现他们并不会才试着决定是使用 Windows 还是使用 Linux、 使用 .net 还是使用 Java ^TM 。他们在很久以前就做出了决定 —— 大约 15 年前才有这样的一些争论。*[00:16:00]* 现在,我见过的每一家公司都混合了 Linux 和 Java、Windows 和 .net、SQL Server、Oracle 和 MySQL —— 基于专有源代码的产品和开放源代码的产品。
|
||||
|
||||
如果你打算运维一个云服务,允许这些公司在云上运行他们的业务,那么你根本不能告诉他们,“你可以使用这个软件,但你不能使用那个软件。”
|
||||
|
||||
**Saron Yitbarek:** *[00:16:30]* 这正是萨提亚·纳德拉采纳的哲学思想。2014 年秋季,他站在舞台上,希望传递一个重要信息。“微软爱 Linux”。他接着说,“20% 的 Azure 业务量已经是 Linux 了,微软将始终对 Linux 发行版提供一流的支持。”没有哪怕一丝对开源的宿怨。
|
||||
|
||||
为了说明这一点,在他们的背后有一个巨大的标志,上面写着:“Microsoft ❤️ Linux”。哇噢。对我们中的一些人来说,这种转变有点令人震惊,但实际上,无需如此震惊。下面是 Steven Levy,一名科技记者兼作家。
|
||||
|
||||
**Steven Levy:** *[00:17:00]* 当你在踢足球的时候,如果草坪变滑了,那么你也许会换一种不同的鞋子。他们当初就是这么做的。*[00:17:30]* 他们不能否认现实,而且他们里面也有聪明人,所以他们必须意识到,这就是这个世界的运行方式,不管他们早些时候说了什么,即使他们对之前的言论感到尴尬,但是让他们之前关于开源多么可怕的言论影响到现在明智的决策那才真的是疯了。
|
||||
|
||||
**Saron Yitbarek:** 微软低下了它高傲的头。你可能还记得苹果公司,经过多年的孤立无援,最终转向与微软构建合作伙伴关系。现在轮到微软进行 180 度转变了。*[00:18:00]* 经过多年的与开源方式的战斗后,他们正在重塑自己。要么改变,要么死亡。Steven Vaughan-Nichols。
|
||||
|
||||
**Steven Vaughan-Nichols:** 即使是像微软这样规模的公司也无法与数千个开发着包括 Linux 在内的其它大项目的开源开发者竞争。很长时间以来他们都不愿意这么做。前微软首席执行官史蒂夫·鲍尔默对 Linux 深恶痛绝。*[00:18:30]* 由于它的 GPL 许可证,他视 Linux 为一种癌症,但一旦鲍尔默被扫地出门,新的微软领导层说,“这就好像试图命令潮流不要过来,但潮水依然会不断涌进来。我们应该与 Linux 合作,而不是与之对抗。”
|
||||
|
||||
**Saron Tiebreak:** 事实上,互联网技术史上最大的胜利之一就是微软最终决定做出这样的转变。*[00:19:00]* 当然,当微软出现在开源圈子时,老一代的铁杆 Linux 支持者是相当怀疑的。他们不确定自己是否能接受这些家伙,但正如 Vaughan-Nichols 所指出的,今天的微软已经不是以前的微软了。
|
||||
|
||||
**Steven Vaughan-Nichols:** 2017 年的微软既不是史蒂夫·鲍尔默的微软,也不是比尔·盖茨的微软。这是一家完全不同的公司,有着完全不同的方法,而且,一旦使用了开源,你就无法退回到之前。*[00:19:30]* 开源已经吞噬了整个技术世界。从未听说过 Linux 的人可能对它并不了解,但是每次他们访问 Facebook,他们都在运行 Linux。每次执行谷歌搜索时,你都在运行 Linux。
|
||||
|
||||
*[00:20:00]* 每次你用 Android 手机,你都在运行 Linux。它确实无处不在,微软无法阻止它,而且我认为以为微软能以某种方式接管它的想法,太天真了。
|
||||
|
||||
**Saron Yitbarek:** 开源支持者可能一直担心微软会像混入羊群中的狼一样,但事实是,开源软件的本质保护了它无法被完全控制。*[00:20:30]* 没有一家公司能够拥有 Linux 并以某种特定的方式控制它。Greg Kroah-Hartman 是 Linux 基金会的一名成员。
|
||||
|
||||
**Greg Kroah-Hartman:** 每个公司和个人都以自私的方式为 Linux 做出贡献。他们之所以这样做是因为他们想要解决他们所面临的问题,可能是硬件无法工作,或者是他们想要添加一个新功能来做其他事情,又或者想在他们的产品中使用它。这很棒,因为他们会把代码贡献回去,此后每个人都会从中受益,这样每个人都可以用到这份代码。正是因为这种自私,所有的公司,所有的人都能从中受益。
|
||||
|
||||
**Saron Yitbarek:** *[00:21:00]* 微软已经意识到,在即将到来的云战争中,与 Linux 作战就像与空气作战一样。Linux 和开源不是敌人,它们是空气。如今,微软以白金会员的身份加入了 Linux 基金会。他们成为 GitHub 开源项目的头号贡献者。*[00:21:30]* 2017 年 9 月,他们甚至加入了<ruby>开源促进联盟<rt>Open Source Initiative</rt></ruby>。现在,微软在开源许可证下发布了很多代码。微软的 John Gossman 描述了他们开源 .net 时所发生的事情。起初,他们并不认为自己能得到什么回报。
|
||||
|
||||
**John Gossman:** 我们本没有指望来自社区的贡献,然而,三年后,超过 50% 的对 .net 框架库的贡献来自于微软之外。这包括大量的代码。*[00:22:00]* 三星为 .net 提供了 ARM 支持。Intel 和 ARM 以及其他一些芯片厂商已经为 .net 框架贡献了特定于他们处理器的代码生成,以及数量惊人的修复、性能改进等等 —— 既有单个贡献者也有社区。
|
||||
|
||||
**Saron Yitbarek:** 直到几年前,今天的这个微软,这个开放的微软,还是不可想象的。
|
||||
|
||||
*[00:22:30]* 我是 Saron Yitbarek,这里是代码英雄。好吧,我们已经看到了为了赢得数百万桌面用户的爱而战的激烈场面。我们已经看到开源软件在专有软件巨头的背后悄然崛起,并攫取了巨大的市场份额。*[00:23:00]* 我们已经看到了一批批的代码英雄将编程领域变成了我你今天看到的这个样子。如今,大企业正在吸收开源软件,通过这一切,每个人都从他人那里受益。
|
||||
|
||||
在技术的西部荒野,一贯如此。苹果受到施乐的启发,微软受到苹果的启发,Linux 受到 UNIX 的启发。进化、借鉴、不断成长。如果比喻成大卫和歌利亚(LCTT 译注:西方经典的以弱胜强战争中的两个主角)的话,开源软件不再是大卫,但是,你知道吗?它也不是歌利亚。*[00:23:30]* 开源已经超越了传统。它已经成为其他人战斗的战场。随着开源道路变得不可避免,新的战争,那些在云计算中进行的战争,那些在开源战场上进行的战争正在加剧。
|
||||
|
||||
这是 Steven Levy,他是一名作者。
|
||||
|
||||
**Steven Levy:** 基本上,到目前为止,包括微软在内,有四到五家公司,正以各种方式努力把自己打造成为全方位的平台,比如人工智能领域。你能看到智能助手之间的战争,你猜怎么着?*[00:24:00]* 苹果有一个智能助手,叫 Siri。微软有一个,叫 Cortana。谷歌有谷歌助手。三星也有一个智能助手。亚马逊也有一个,叫 Alexa。我们看到这些战斗遍布各地。也许,你可以说,最热门的人工智能平台将控制我们生活中所有的东西,而这五家公司就是在为此而争斗。
|
||||
|
||||
**Saron Yitbarek:** *[00:24:30]* 如果你正在寻找另一个反叛者,它们就像 Linux 奇袭微软那样,偷偷躲在 Facebook、谷歌或亚马逊身后,你也许要等很久,因为正如作家 James Allworth 所指出的,成为一个真正的反叛者只会变得越来越难。
|
||||
|
||||
**James Allworth:** 规模一直以来都是一种优势,但规模优势本质上……怎么说呢,我认为以前它们在本质上是线性的,现在它们在本质上是指数型的了,所以,一旦你开始以某种方法走在前面,另一个新玩家要想赶上来就变得越来越难了。*[00:25:00]* 我认为在互联网时代这大体来说来说是正确的,无论是因为规模,还是数据赋予组织的竞争力的重要性和优势。一旦你走在前面,你就会吸引更多的客户,这就给了你更多的数据,让你能做得更好,这之后,客户还有什么理由选择排名第二的公司呢,难道是因为因为他们落后了这么远么?*[00:25:30]* 我认为在云的时代这个逻辑也不会有什么不同。
|
||||
|
||||
**Saron Yitbarek:** 这个故事始于史蒂夫·乔布斯和比尔·盖茨这样的非凡的英雄,但科技的进步已经呈现出一种众包、有机的感觉。我认为据说我们的开源英雄林纳斯·托瓦兹在第一次发明 Linux 内核时甚至没有一个真正的计划。他无疑是一位才华横溢的年轻开发者,但他也像潮汐前的一滴水一样。*[00:26:00]* 变革是不可避免的。据估计,对于一家专有软件公司来说,用他们老式的、专有的方式创建一个 Linux 发行版将花费他们超过 100 亿美元。这说明了开源的力量。
|
||||
|
||||
最后,这并不是一个专有模型所能与之竞争的东西。成功的公司必须保持开放。这是最大、最终极的教训。*[00:26:30]* 还有一点要记住:当我们连接在一起的时候,我们在已有基础上成长和建设的能力是无限的。不管这些公司有多大,我们都不必坐等他们给我们更好的东西。想想那些为了纯粹的创造乐趣而学习编码的新开发者,那些自己动手丰衣足食的人。
|
||||
|
||||
未来的优秀程序员无管来自何方,只要能够访问代码,他们就能构建下一个大项目。
|
||||
|
||||
*[00:27:00]* 以上就是我们关于操作系统战争的两个故事。这场战争塑造了我们的数字生活。争夺主导地位的斗争从桌面转移到了服务器机房,最终进入了云计算领域。过去的敌人难以置信地变成了盟友,众包的未来让一切都变得开放。*[00:27:30]* 听着,我知道,在这段历史之旅中,还有很多英雄我们没有提到,所以给我们写信吧。分享你的故事。[Redhat.com/commandlineheroes](https://www.redhat.com/commandlineheroes) 。我恭候佳音。
|
||||
|
||||
在本季剩下的时间里,我们将学习今天的英雄们在创造什么,以及他们要经历什么样的战斗才能将他们的创造变为现实。让我们从壮丽的编程一线回来看看更多的传奇故事吧。我们每两周放一集新的博客。几周后,我们将为你带来第三集:敏捷革命。
|
||||
|
||||
*[00:28:00]* 代码英雄是一款红帽公司原创的播客。要想免费自动获得新一集的代码英雄,请订阅我们的节目。只要在苹果播客、Spotify、谷歌 Play,或其他应用中搜索“Command Line Heroes”。然后点击“订阅”。这样你就会第一个知道什么时候有新剧集了。
|
||||
|
||||
我是 Saron Yitbarek。感谢收听。继续编码。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.redhat.com/en/command-line-heroes/season-1/os-wars-part-2-rise-of-linux
|
||||
|
||||
作者:[redhat][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lujun9972](https://github.com/lujun9972)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.redhat.com
|
||||
[b]: https://github.com/lujun9972
|
480
published/20180330 Go on very small hardware Part 1.md
Normal file
480
published/20180330 Go on very small hardware Part 1.md
Normal file
@ -0,0 +1,480 @@
|
||||
Go 语言在极小硬件上的运用(一)
|
||||
=========
|
||||
|
||||
Go 语言,能在多低下的配置上运行并发挥作用呢?
|
||||
|
||||
我最近购买了一个特别便宜的开发板:
|
||||
|
||||
![STM32F030F4P6](https://ziutek.github.io/images/mcu/f030-demo-board/board.jpg)
|
||||
|
||||
我购买它的理由有三个。首先,我(作为程序员)从未接触过 STM320 系列的开发板。其次,STM32F10x 系列使用也有点少了。STM320 系列的 MCU 很便宜,有更新一些的外设,对系列产品进行了改进,问题修复也做得更好了。最后,为了这篇文章,我选用了这一系列中最低配置的开发板,整件事情就变得有趣起来了。
|
||||
|
||||
### 硬件部分
|
||||
|
||||
[STM32F030F4P6][3] 给人留下了很深的印象:
|
||||
|
||||
* CPU: [Cortex M0][1] 48 MHz(最低配置,只有 12000 个逻辑门电路)
|
||||
* RAM: 4 KB,
|
||||
* Flash: 16 KB,
|
||||
* ADC、SPI、I2C、USART 和几个定时器
|
||||
|
||||
以上这些采用了 TSSOP20 封装。正如你所见,这是一个很小的 32 位系统。
|
||||
|
||||
### 软件部分
|
||||
|
||||
如果你想知道如何在这块开发板上使用 [Go][4] 编程,你需要反复阅读硬件规范手册。你必须面对这样的真实情况:在 Go 编译器中给 Cortex-M0 提供支持的可能性很小。而且,这还仅仅只是第一个要解决的问题。
|
||||
|
||||
我会使用 [Emgo][5],但别担心,之后你会看到,它如何让 Go 在如此小的系统上尽可能发挥作用。
|
||||
|
||||
在我拿到这块开发板之前,对 [stm32/hal][6] 系列下的 F0 MCU 没有任何支持。在简单研究[参考手册][7]后,我发现 STM32F0 系列是 STM32F3 削减版,这让在新端口上开发的工作变得容易了一些。
|
||||
|
||||
如果你想接着本文的步骤做下去,需要先安装 Emgo
|
||||
|
||||
```
|
||||
cd $HOME
|
||||
git clone https://github.com/ziutek/emgo/
|
||||
cd emgo/egc
|
||||
go install
|
||||
```
|
||||
|
||||
然后设置一下环境变量
|
||||
|
||||
```
|
||||
export EGCC=path_to_arm_gcc # eg. /usr/local/arm/bin/arm-none-eabi-gcc
|
||||
export EGLD=path_to_arm_linker # eg. /usr/local/arm/bin/arm-none-eabi-ld
|
||||
export EGAR=path_to_arm_archiver # eg. /usr/local/arm/bin/arm-none-eabi-ar
|
||||
|
||||
export EGROOT=$HOME/emgo/egroot
|
||||
export EGPATH=$HOME/emgo/egpath
|
||||
|
||||
export EGARCH=cortexm0
|
||||
export EGOS=noos
|
||||
export EGTARGET=f030x6
|
||||
```
|
||||
|
||||
更详细的说明可以在 [Emgo][8] 官网上找到。
|
||||
|
||||
要确保 `egc` 在你的 `PATH` 中。 你可以使用 `go build` 来代替 `go install`,然后把 `egc` 复制到你的 `$HOME/bin` 或 `/usr/local/bin` 中。
|
||||
|
||||
现在,为你的第一个 Emgo 程序创建一个新文件夹,随后把示例中链接器脚本复制过来:
|
||||
|
||||
```
|
||||
mkdir $HOME/firstemgo
|
||||
cd $HOME/firstemgo
|
||||
cp $EGPATH/src/stm32/examples/f030-demo-board/blinky/script.ld .
|
||||
```
|
||||
|
||||
### 最基本程序
|
||||
|
||||
在 `main.go` 文件中创建一个最基本的程序:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
func main() {
|
||||
}
|
||||
```
|
||||
|
||||
文件编译没有出现任何问题:
|
||||
|
||||
```
|
||||
$ egc
|
||||
$ arm-none-eabi-size cortexm0.elf
|
||||
text data bss dec hex filename
|
||||
7452 172 104 7728 1e30 cortexm0.elf
|
||||
```
|
||||
|
||||
第一次编译可能会花点时间。编译后产生的二进制占用了 7624 个字节的 Flash 空间(文本 + 数据)。对于一个什么都没做的程序来说,占用的空间有些大。还剩下 8760 字节,可以用来做些有用的事。
|
||||
|
||||
不妨试试传统的 “Hello, World!” 程序:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, World!")
|
||||
}
|
||||
```
|
||||
|
||||
不幸的是,这次结果有些糟糕:
|
||||
|
||||
```
|
||||
$ egc
|
||||
/usr/local/arm/bin/arm-none-eabi-ld: /home/michal/P/go/src/github.com/ziutek/emgo/egpath/src/stm32/examples/f030-demo-board/blog/cortexm0.elf section `.text' will not fit in region `Flash'
|
||||
/usr/local/arm/bin/arm-none-eabi-ld: region `Flash' overflowed by 10880 bytes
|
||||
exit status 1
|
||||
```
|
||||
|
||||
“Hello, World!” 需要 STM32F030x6 上至少 32KB 的 Flash 空间。
|
||||
|
||||
`fmt` 包强制包含整个 `strconv` 和 `reflect` 包。这三个包,即使在精简版本中的 Emgo 中,占用空间也很大。我们不能使用这个例子了。有很多的应用不需要好看的文本输出。通常,一个或多个 LED,或者七段数码管显示就足够了。不过,在第二部分,我会尝试使用 `strconv` 包来格式化,并在 UART 上显示一些数字和文本。
|
||||
|
||||
### 闪烁
|
||||
|
||||
我们的开发板上有一个与 PA4 引脚和 VCC 相连的 LED。这次我们的代码稍稍长了一些:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"delay"
|
||||
|
||||
"stm32/hal/gpio"
|
||||
"stm32/hal/system"
|
||||
"stm32/hal/system/timer/systick"
|
||||
)
|
||||
|
||||
var led gpio.Pin
|
||||
|
||||
func init() {
|
||||
system.SetupPLL(8, 1, 48/8)
|
||||
systick.Setup(2e6)
|
||||
|
||||
gpio.A.EnableClock(false)
|
||||
led = gpio.A.Pin(4)
|
||||
|
||||
cfg := &gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain}
|
||||
led.Setup(cfg)
|
||||
}
|
||||
|
||||
func main() {
|
||||
for {
|
||||
led.Clear()
|
||||
delay.Millisec(100)
|
||||
led.Set()
|
||||
delay.Millisec(900)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
按照惯例,`init` 函数用来初始化和配置外设。
|
||||
|
||||
`system.SetupPLL(8, 1, 48/8)` 用来配置 RCC,将外部的 8 MHz 振荡器的 PLL 作为系统时钟源。PLL 分频器设置为 1,倍频数设置为 48/8 =6,这样系统时钟频率为 48MHz。
|
||||
|
||||
`systick.Setup(2e6)` 将 Cortex-M SYSTICK 时钟作为系统时钟,每隔 2e6 次纳秒运行一次(每秒钟 500 次)。
|
||||
|
||||
`gpio.A.EnableClock(false)` 开启了 GPIO A 口的时钟。`False` 意味着这一时钟在低功耗模式下会被禁用,但在 STM32F0 系列中并未实现这一功能。
|
||||
|
||||
`led.Setup(cfg)` 设置 PA4 引脚为开漏输出。
|
||||
|
||||
`led.Clear()` 将 PA4 引脚设为低,在开漏设置中,打开 LED。
|
||||
|
||||
`led.Set()` 将 PA4 设为高电平状态,关掉LED。
|
||||
|
||||
编译这个代码:
|
||||
|
||||
```
|
||||
$ egc
|
||||
$ arm-none-eabi-size cortexm0.elf
|
||||
text data bss dec hex filename
|
||||
9772 172 168 10112 2780 cortexm0.elf
|
||||
```
|
||||
|
||||
正如你所看到的,这个闪烁程序占用了 2320 字节,比最基本程序占用空间要大。还有 6440 字节的剩余空间。
|
||||
|
||||
看看代码是否能运行:
|
||||
|
||||
```
|
||||
$ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; reset run; exit'
|
||||
Open On-Chip Debugger 0.10.0+dev-00319-g8f1f912a (2018-03-07-19:20)
|
||||
Licensed under GNU GPL v2
|
||||
For bug reports, read
|
||||
http://openocd.org/doc/doxygen/bugs.html
|
||||
debug_level: 0
|
||||
adapter speed: 1000 kHz
|
||||
adapter_nsrst_delay: 100
|
||||
none separate
|
||||
adapter speed: 950 kHz
|
||||
target halted due to debug-request, current mode: Thread
|
||||
xPSR: 0xc1000000 pc: 0x0800119c msp: 0x20000da0
|
||||
adapter speed: 4000 kHz
|
||||
** Programming Started **
|
||||
auto erase enabled
|
||||
target halted due to breakpoint, current mode: Thread
|
||||
xPSR: 0x61000000 pc: 0x2000003a msp: 0x20000da0
|
||||
wrote 10240 bytes from file cortexm0.elf in 0.817425s (12.234 KiB/s)
|
||||
** Programming Finished **
|
||||
adapter speed: 950 kHz
|
||||
```
|
||||
|
||||
在这篇文章中,这是我第一次,将一个短视频转换成[动画 PNG][9]。我对此印象很深,再见了 YouTube。 对于 IE 用户,我很抱歉,更多信息请看 [apngasm][10]。我本应该学习 HTML5,但现在,APNG 是我最喜欢的,用来播放循环短视频的方法了。
|
||||
|
||||
![STM32F030F4P6](https://ziutek.github.io/images/mcu/f030-demo-board/blinky.png)
|
||||
|
||||
### 更多的 Go 语言编程
|
||||
|
||||
如果你不是一个 Go 程序员,但你已经听说过一些关于 Go 语言的事情,你可能会说:“Go 语法很好,但跟 C 比起来,并没有明显的提升。让我看看 Go 语言的通道和协程!”
|
||||
|
||||
接下来我会一一展示:
|
||||
|
||||
```
|
||||
import (
|
||||
"delay"
|
||||
|
||||
"stm32/hal/gpio"
|
||||
"stm32/hal/system"
|
||||
"stm32/hal/system/timer/systick"
|
||||
)
|
||||
|
||||
var led1, led2 gpio.Pin
|
||||
|
||||
func init() {
|
||||
system.SetupPLL(8, 1, 48/8)
|
||||
systick.Setup(2e6)
|
||||
|
||||
gpio.A.EnableClock(false)
|
||||
led1 = gpio.A.Pin(4)
|
||||
led2 = gpio.A.Pin(5)
|
||||
|
||||
cfg := &gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain}
|
||||
led1.Setup(cfg)
|
||||
led2.Setup(cfg)
|
||||
}
|
||||
|
||||
func blinky(led gpio.Pin, period int) {
|
||||
for {
|
||||
led.Clear()
|
||||
delay.Millisec(100)
|
||||
led.Set()
|
||||
delay.Millisec(period - 100)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
go blinky(led1, 500)
|
||||
blinky(led2, 1000)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
代码改动很小: 添加了第二个 LED,上一个例子中的 `main` 函数被重命名为 `blinky` 并且需要提供两个参数。 `main` 在新的协程中先调用 `blinky`,所以两个 LED 灯在并行使用。值得一提的是,`gpio.Pin` 可以同时访问同一 GPIO 口的不同引脚。
|
||||
|
||||
Emgo 还有很多不足。其中之一就是你需要提前规定 `goroutines(tasks)` 的最大执行数量。是时候修改 `script.ld` 了:
|
||||
|
||||
```
|
||||
ISRStack = 1024;
|
||||
MainStack = 1024;
|
||||
TaskStack = 1024;
|
||||
MaxTasks = 2;
|
||||
|
||||
INCLUDE stm32/f030x4
|
||||
INCLUDE stm32/loadflash
|
||||
INCLUDE noos-cortexm
|
||||
```
|
||||
|
||||
栈的大小需要靠猜,现在还不用关心这一点。
|
||||
|
||||
```
|
||||
$ egc
|
||||
$ arm-none-eabi-size cortexm0.elf
|
||||
text data bss dec hex filename
|
||||
10020 172 172 10364 287c cortexm0.elf
|
||||
```
|
||||
|
||||
另一个 LED 和协程一共占用了 248 字节的 Flash 空间。
|
||||
|
||||
![STM32F030F4P6](https://ziutek.github.io/images/mcu/f030-demo-board/goroutines.png)
|
||||
|
||||
### 通道
|
||||
|
||||
通道是 Go 语言中协程之间相互通信的一种[推荐方式][11]。Emgo 甚至能允许通过*中断处理*来使用缓冲通道。下一个例子就展示了这种情况。
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"delay"
|
||||
"rtos"
|
||||
|
||||
"stm32/hal/gpio"
|
||||
"stm32/hal/irq"
|
||||
"stm32/hal/system"
|
||||
"stm32/hal/system/timer/systick"
|
||||
"stm32/hal/tim"
|
||||
)
|
||||
|
||||
var (
|
||||
leds [3]gpio.Pin
|
||||
timer *tim.Periph
|
||||
ch = make(chan int, 1)
|
||||
)
|
||||
|
||||
func init() {
|
||||
system.SetupPLL(8, 1, 48/8)
|
||||
systick.Setup(2e6)
|
||||
|
||||
gpio.A.EnableClock(false)
|
||||
leds[0] = gpio.A.Pin(4)
|
||||
leds[1] = gpio.A.Pin(5)
|
||||
leds[2] = gpio.A.Pin(9)
|
||||
|
||||
cfg := &gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain}
|
||||
for _, led := range leds {
|
||||
led.Set()
|
||||
led.Setup(cfg)
|
||||
}
|
||||
|
||||
timer = tim.TIM3
|
||||
pclk := timer.Bus().Clock()
|
||||
if pclk < system.AHB.Clock() {
|
||||
pclk *= 2
|
||||
}
|
||||
freq := uint(1e3) // Hz
|
||||
timer.EnableClock(true)
|
||||
timer.PSC.Store(tim.PSC(pclk/freq - 1))
|
||||
timer.ARR.Store(700) // ms
|
||||
timer.DIER.Store(tim.UIE)
|
||||
timer.CR1.Store(tim.CEN)
|
||||
|
||||
rtos.IRQ(irq.TIM3).Enable()
|
||||
}
|
||||
|
||||
func blinky(led gpio.Pin, period int) {
|
||||
for range ch {
|
||||
led.Clear()
|
||||
delay.Millisec(100)
|
||||
led.Set()
|
||||
delay.Millisec(period - 100)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
go blinky(leds[1], 500)
|
||||
blinky(leds[2], 500)
|
||||
}
|
||||
|
||||
func timerISR() {
|
||||
timer.SR.Store(0)
|
||||
leds[0].Set()
|
||||
select {
|
||||
case ch <- 0:
|
||||
// Success
|
||||
default:
|
||||
leds[0].Clear()
|
||||
}
|
||||
}
|
||||
|
||||
//c:__attribute__((section(".ISRs")))
|
||||
var ISRs = [...]func(){
|
||||
irq.TIM3: timerISR,
|
||||
}
|
||||
```
|
||||
|
||||
与之前例子相比较下的不同:
|
||||
|
||||
1. 添加了第三个 LED,并连接到 PA9 引脚(UART 头的 TXD 引脚)。
|
||||
2. 时钟(`TIM3`)作为中断源。
|
||||
3. 新函数 `timerISR` 用来处理 `irq.TIM3` 的中断。
|
||||
4. 新增容量为 1 的缓冲通道是为了 `timerISR` 和 `blinky` 协程之间的通信。
|
||||
5. `ISRs` 数组作为*中断向量表*,是更大的*异常向量表*的一部分。
|
||||
6. `blinky` 中的 `for` 语句被替换成 `range` 语句。
|
||||
|
||||
为了方便起见,所有的 LED,或者说它们的引脚,都被放在 `leds` 这个数组里。另外,所有引脚在被配置为输出之前,都设置为一种已知的初始状态(高电平状态)。
|
||||
|
||||
在这个例子里,我们想让时钟以 1 kHz 的频率运行。为了配置 TIM3 预分频器,我们需要知道它的输入时钟频率。通过参考手册我们知道,输入时钟频率在 `APBCLK = AHBCLK` 时,与 `APBCLK` 相同,反之等于 2 倍的 `APBCLK`。
|
||||
|
||||
如果 CNT 寄存器增加 1 kHz,那么 ARR 寄存器的值等于*更新事件*(重载事件)在毫秒中的计数周期。 为了让更新事件产生中断,必须要设置 DIER 寄存器中的 UIE 位。CEN 位能启动时钟。
|
||||
|
||||
时钟外设在低功耗模式下必须启用,为了自身能在 CPU 处于休眠时保持运行: `timer.EnableClock(true)`。这在 STM32F0 中无关紧要,但对代码可移植性却十分重要。
|
||||
|
||||
`timerISR` 函数处理 `irq.TIM3` 的中断请求。`timer.SR.Store(0)` 会清除 SR 寄存器里的所有事件标志,无效化向 [NVIC][12] 发出的所有中断请求。凭借经验,由于中断请求无效的延时性,需要在程序一开始马上清除所有的中断标志。这避免了无意间再次调用处理。为了确保万无一失,需要先清除标志,再读取,但是在我们的例子中,清除标志就已经足够了。
|
||||
|
||||
下面的这几行代码:
|
||||
|
||||
```
|
||||
select {
|
||||
case ch <- 0:
|
||||
// Success
|
||||
default:
|
||||
leds[0].Clear()
|
||||
}
|
||||
```
|
||||
|
||||
是 Go 语言中,如何在通道上非阻塞地发送消息的方法。中断处理程序无法一直等待通道中的空余空间。如果通道已满,则执行 `default`,开发板上的LED就会开启,直到下一次中断。
|
||||
|
||||
`ISRs` 数组包含了中断向量表。`//c:__attribute__((section(".ISRs")))` 会导致链接器将数组插入到 `.ISRs` 节中。
|
||||
|
||||
`blinky` 的 `for` 循环的新写法:
|
||||
|
||||
```
|
||||
for range ch {
|
||||
led.Clear()
|
||||
delay.Millisec(100)
|
||||
led.Set()
|
||||
delay.Millisec(period - 100)
|
||||
}
|
||||
```
|
||||
|
||||
等价于:
|
||||
|
||||
```
|
||||
for {
|
||||
_, ok := <-ch
|
||||
if !ok {
|
||||
break // Channel closed.
|
||||
}
|
||||
led.Clear()
|
||||
delay.Millisec(100)
|
||||
led.Set()
|
||||
delay.Millisec(period - 100)
|
||||
}
|
||||
```
|
||||
|
||||
注意,在这个例子中,我们不在意通道中收到的值,我们只对其接受到的消息感兴趣。我们可以在声明时,将通道元素类型中的 `int` 用空结构体 `struct{}` 来代替,发送消息时,用 `struct{}{}` 结构体的值代替 0,但这部分对新手来说可能会有些陌生。
|
||||
|
||||
让我们来编译一下代码:
|
||||
|
||||
```
|
||||
$ egc
|
||||
$ arm-none-eabi-size cortexm0.elf
|
||||
text data bss dec hex filename
|
||||
11096 228 188 11512 2cf8 cortexm0.elf
|
||||
```
|
||||
|
||||
新的例子占用了 11324 字节的 Flash 空间,比上一个例子多占用了 1132 字节。
|
||||
|
||||
采用现在的时序,两个闪烁协程从通道中获取数据的速度,比 `timerISR` 发送数据的速度要快。所以它们在同时等待新数据,你还能观察到 `select` 的随机性,这也是 [Go 规范][13]所要求的。
|
||||
|
||||
![STM32F030F4P6](https://ziutek.github.io/images/mcu/f030-demo-board/channels1.png)
|
||||
|
||||
开发板上的 LED 一直没有亮起,说明通道从未出现过溢出。
|
||||
|
||||
我们可以加快消息发送的速度,将 `timer.ARR.Store(700)` 改为 `timer.ARR.Store(200)`。 现在 `timerISR` 每秒钟发送 5 条消息,但是两个接收者加起来,每秒也只能接受 4 条消息。
|
||||
|
||||
![STM32F030F4P6](https://ziutek.github.io/images/mcu/f030-demo-board/channels2.png)
|
||||
|
||||
正如你所看到的,`timerISR` 开启黄色 LED 灯,意味着通道上已经没有剩余空间了。
|
||||
|
||||
第一部分到这里就结束了。你应该知道,这一部分并未展示 Go 中最重要的部分,接口。
|
||||
|
||||
协程和通道只是一些方便好用的语法。你可以用自己的代码来替换它们,这并不容易,但也可以实现。接口是Go 语言的基础。这是文章中 [第二部分][14]所要提到的.
|
||||
|
||||
在 Flash 上我们还有些剩余空间。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://ziutek.github.io/2018/03/30/go_on_very_small_hardware.html
|
||||
|
||||
作者:[Michał Derkacz][a]
|
||||
译者:[wenwensnow](https://github.com/wenwensnow)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://ziutek.github.io/
|
||||
[1]:https://en.wikipedia.org/wiki/ARM_Cortex-M#Cortex-M0
|
||||
[2]:https://ziutek.github.io/2018/03/30/go_on_very_small_hardware.html
|
||||
[3]:http://www.st.com/content/st_com/en/products/microcontrollers/stm32-32-bit-arm-cortex-mcus/stm32-mainstream-mcus/stm32f0-series/stm32f0x0-value-line/stm32f030f4.html
|
||||
[4]:https://golang.org/
|
||||
[5]:https://github.com/ziutek/emgo
|
||||
[6]:https://github.com/ziutek/emgo/tree/master/egpath/src/stm32/hal
|
||||
[7]:http://www.st.com/resource/en/reference_manual/dm00091010.pdf
|
||||
[8]:https://github.com/ziutek/emgo
|
||||
[9]:https://en.wikipedia.org/wiki/APNG
|
||||
[10]:http://apngasm.sourceforge.net/
|
||||
[11]:https://blog.golang.org/share-memory-by-communicating
|
||||
[12]:http://infocenter.arm.com/help/topic/com.arm.doc.ddi0432c/Cihbecee.html
|
||||
[13]:https://golang.org/ref/spec#Select_statements
|
||||
[14]:https://ziutek.github.io/2018/04/14/go_on_very_small_hardware2.html
|
115
published/20180704 BASHing data- Truncated data items.md
Normal file
115
published/20180704 BASHing data- Truncated data items.md
Normal file
@ -0,0 +1,115 @@
|
||||
如何发现截断的数据项
|
||||
======
|
||||
|
||||
**截断**(形容词):缩写、删节、缩减、剪切、剪裁、裁剪、修剪……
|
||||
|
||||
数据项被截断的一种情况是将其输入到数据库字段中,该字段的字符限制比数据项的长度要短。例如,字符串:
|
||||
|
||||
```
|
||||
Yarrow Ravine Rattlesnake Habitat Area, 2 mi ENE of Yermo CA
|
||||
```
|
||||
|
||||
是 60 个字符长。如果你将其输入到具有 50 个字符限制的“位置”字段,则可以获得:
|
||||
|
||||
```
|
||||
Yarrow Ravine Rattlesnake Habitat Area, 2 mi ENE #末尾带有一个空格
|
||||
```
|
||||
|
||||
截断也可能导致数据错误,比如你打算输入:
|
||||
|
||||
```
|
||||
Sally Ann Hunter (aka Sally Cleveland)
|
||||
```
|
||||
|
||||
但是你忘记了闭合的括号:
|
||||
|
||||
```
|
||||
Sally Ann Hunter (aka Sally Cleveland
|
||||
```
|
||||
|
||||
这会让使用数据的用户觉得 Sally 是否有被修剪掉了数据项的其它的别名。
|
||||
|
||||
截断的数据项很难检测。在审核数据时,我使用三种不同的方法来查找可能的截断,但我仍然可能会错过一些。
|
||||
|
||||
**数据项的长度分布。**第一种方法是捕获我在各个字段中找到的大多数截断的数据。我将字段传递给 `awk` 命令,该命令按字段宽度计算数据项,然后我使用 `sort` 以宽度的逆序打印计数。例如,要检查以 `tab` 分隔的文件 `midges` 中的第 33 个字段:
|
||||
|
||||
```
|
||||
awk -F"\t" 'NR>1 {a[length($33)]++} \
|
||||
END {for (i in a) print i FS a[i]}' midges | sort -nr
|
||||
```
|
||||
|
||||
![distro1][1]
|
||||
|
||||
最长的条目恰好有 50 个字符,这是可疑的,并且在该宽度处存在数据项的“凸起”,这更加可疑。检查这些 50 个字符的项目会发现截断:
|
||||
|
||||
![distro2][2]
|
||||
|
||||
我用这种方式检查的其他数据表有 100、200 和 255 个字符的“凸起”。在每种情况下,这种“凸起”都包含明显的截断。
|
||||
|
||||
**未匹配的括号。**第二种方法查找类似 `...(Sally Cleveland` 的数据项。一个很好的起点是数据表中所有标点符号的统计。这里我检查文件 `mag2`:
|
||||
|
||||
```
|
||||
grep -o "[[:punct:]]" file | sort | uniqc
|
||||
```
|
||||
|
||||
![punct][3]
|
||||
|
||||
请注意,`mag2` 中的开括号和闭括号的数量不相等。要查看发生了什么,我使用 `unmatched` 函数,它接受三个参数并检查数据表中的所有字段。第一个参数是文件名,第二个和第三个是开括号和闭括号,用引号括起来。
|
||||
|
||||
```
|
||||
unmatched()
|
||||
{
|
||||
awk -F"\t" -v start="$2" -v end="$3" \
|
||||
'{for (i=1;i<=NF;i++) \
|
||||
if (split($i,a,start) != split($i,b,end)) \
|
||||
print "line "NR", field "i":\n"$i}' "$1"
|
||||
}
|
||||
```
|
||||
|
||||
如果在字段中找到开括号和闭括号之间不匹配,则 `unmatched` 会报告行号和字段号。这依赖于 `awk` 的 `split` 函数,它返回由分隔符分隔的元素数(包括空格)。这个数字总是比分隔符的数量多一个:
|
||||
|
||||
![split][4]
|
||||
|
||||
这里 `ummatched` 检查 `mag2` 中的圆括号并找到一些可能的截断:
|
||||
|
||||
![unmatched][5]
|
||||
|
||||
我使用 `unmatched` 来找到不匹配的圆括号 `()`、方括号 `[]`、花括号 `{}` 和尖括号 `<>`,但该函数可用于任何配对的标点字符。
|
||||
|
||||
**意外的结尾。**第三种方法查找以尾随空格或非终止标点符号结尾的数据项,如逗号或连字符。这可以在单个字段上用 `cut` 用管道输入到 `grep` 完成,或者用 `awk` 一步完成。在这里,我正在检查以制表符分隔的表 `herp5` 的字段 47,并提取可疑数据项及其行号:
|
||||
|
||||
```
|
||||
cut -f47 herp5 | grep -n "[ ,;:-]$"
|
||||
或
|
||||
awk -F"\t" '$47 ~ /[ ,;:-]$/ {print NR": "$47}' herp5
|
||||
```
|
||||
|
||||
![herps5][6]
|
||||
|
||||
用于制表符分隔文件的 awk 命令的全字段版本是:
|
||||
|
||||
```
|
||||
awk -F"\t" '{for (i=1;i<=NF;i++) if ($i ~ /[ ,;:-]$/) \
|
||||
print "line "NR", field "i":\n"$i}' file
|
||||
```
|
||||
|
||||
**谨慎的想法。**在我对字段进行的验证测试期间也会出现截断。例如,我可能会在“年”的字段中检查合理的 4 位数条目,并且有个 `198` 可能是 198n?还是 1898 年?带有丢失字符的截断数据项是个谜。 作为数据审计员,我只能报告(可能的)字符损失,并建议数据编制者或管理者恢复(可能)丢失的字符。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.polydesmida.info/BASHing/2018-07-04.html
|
||||
|
||||
作者:[polydesmida][a]
|
||||
选题:[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/) 荣誉推出
|
||||
|
||||
[a]:https://www.polydesmida.info/
|
||||
[1]:https://www.polydesmida.info/BASHing/img1/2018-07-04_1.png
|
||||
[2]:https://www.polydesmida.info/BASHing/img1/2018-07-04_2.png
|
||||
[3]:https://www.polydesmida.info/BASHing/img1/2018-07-04_3.png
|
||||
[4]:https://www.polydesmida.info/BASHing/img1/2018-07-04_4.png
|
||||
[5]:https://www.polydesmida.info/BASHing/img1/2018-07-04_5.png
|
||||
[6]:https://www.polydesmida.info/BASHing/img1/2018-07-04_6.png
|
@ -0,0 +1,141 @@
|
||||
Linux 上 5 个最好 CAD 软件
|
||||
======
|
||||
|
||||
[计算机辅助设计 (CAD)][1] 是很多工程流程的必不可少的部分。CAD 用于建筑、汽车零部件设计、航天飞机研究、航空、桥梁施工、室内设计,甚至服装和珠宝设计等专业领域。
|
||||
|
||||
在 Linux 上并不原生支持一些专业级 CAD 软件,如 SolidWorks 和 Autodesk AutoCAD。因此,今天,我们将看看排名靠前的 Linux 上可用的 CAD 软件。预知详情,请看下文。
|
||||
|
||||
### Linux 可用的最好的 CAD 软件
|
||||
|
||||
![CAD Software for Linux][2]
|
||||
|
||||
在我们查看这份 Linux 的 CAD 软件列表前,你应该记住一件事,在这里不是所有的应用程序都是开源软件。我们也将包含一些非自由和开源软件的 CAD 软件来帮助普通的 Linux 用户。
|
||||
|
||||
我们为基于 Ubuntu 的 Linux 发行版提供了安装操作指南。对于其它发行版,你可以检查相应的网站来了解安装程序步骤。
|
||||
|
||||
该列表没有任何特殊顺序。在第一顺位的 CAD 应用程序不能认为比在第三顺位的好,以此类推。
|
||||
|
||||
#### 1、FreeCAD
|
||||
|
||||
对于 3D 建模,FreeCAD 是一个极好的选择,它是自由 (免费和自由) 和开源软件。FreeCAD 坚持以构建机械工程和产品设计为目标。FreeCAD 是多平台的,可用于 Windows、Mac OS X+ 以及 Linux。
|
||||
|
||||
![freecad][3]
|
||||
|
||||
尽管 FreeCAD 已经是很多 Linux 用户的选择,应该注意到,FreeCAD 仍然是 0.17 版本,因此,不适用于重要的部署。但是最近开发加速了。
|
||||
|
||||
- [FreeCAD][4]
|
||||
|
||||
FreeCAD 并不专注于 direct-2D 绘图和真实形状的动画,但是它对机械工程相关的设计极好。FreeCAD 的 0.15 版本在 Ubuntu 存储库中可用。你可以通过运行下面的命令安装。
|
||||
|
||||
```
|
||||
sudo apt install freecad
|
||||
```
|
||||
|
||||
为获取新的每日构建(目前 0.17),打开一个终端(`ctrl+alt+t`),并逐个运行下面的命令。
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:freecad-maintainers/freecad-daily
|
||||
sudo apt update
|
||||
sudo apt install freecad-daily
|
||||
```
|
||||
|
||||
#### 2、LibreCAD
|
||||
|
||||
LibreCAD 是一个自由开源的、2D CAD 解决方案。一般来说,CAD 是一个资源密集型任务,如果你有一个相当普通的硬件,那么我建议你使用 LibreCAD ,因为它在资源使用方面真的轻量化。LibreCAD 是几何图形结构方面的一个极好的候选者。
|
||||
|
||||
![librecad][5]
|
||||
|
||||
作为一个 2D 工具,LibreCAD 是好的,但是它不能在 3D 模型和渲染上工作。它有时可能不稳定,但是,它有一个可靠的自动保存,它不会让你的工作浪费。
|
||||
|
||||
- [LibreCAD][6]
|
||||
|
||||
你可以通过运行下面的命令安装 LibreCAD。
|
||||
|
||||
```
|
||||
sudo apt install librecad
|
||||
```
|
||||
|
||||
#### 3、OpenSCAD
|
||||
|
||||
OpenSCAD 是一个自由的 3D CAD 软件。OpenSCAD 非常轻量和灵活。OpenSCAD 不是交互式的。你需要‘编程’模型,OpenSCAD 来解释这些代码来渲染一个可视化模型。在某种意义上说,它是一个编译器。你不能直接绘制模型,而是描述模型。
|
||||
|
||||
![openscad][7]
|
||||
|
||||
OpenSCAD 是这个列表上最复杂的工具,但是,一旦你了解它,它将提供一个令人愉快的工作经历。
|
||||
|
||||
- [OpenSCAD][8]
|
||||
|
||||
你可以使用下面的命令来安装 OpenSCAD。
|
||||
|
||||
```
|
||||
sudo apt-get install openscad
|
||||
```
|
||||
|
||||
#### 4、BRL-CAD
|
||||
|
||||
BRL-CAD 是最老的 CAD 工具之一。它也深受 Linux/UNIX 用户喜爱,因为它与模块化和自由的 *nix 哲学相一致。
|
||||
|
||||
![BRL-CAD rendering by Sean][9]
|
||||
|
||||
BRL-CAD 始于 1979 年,并且,它仍然在积极开发。现在,BRL-CAD 不是 AutoCAD,但是对于像热穿透和弹道穿透等等的运输研究仍然是一个极好的选择。BRL-CAD 构成 CSG 的基础,而不是边界表示。在选择 BRL-CAD 时,你可能需要记住这一点。你可以从它的官方网站下载 BRL-CAD 。
|
||||
|
||||
- [BRL-CAD][10]
|
||||
|
||||
#### 5、DraftSight (非开源)
|
||||
|
||||
如果你习惯在 AutoCAD 上作业。那么,DraftSight 将是完美的替代。
|
||||
|
||||
DraftSight 是一个在 Linux 上可用的极好的 CAD 工具。它有相当类似于 AutoCAD 的工作流,这使得迁移更容易。它甚至提供一种类似的外观和感觉。DrafSight 也兼容 AutoCAD 的 .dwg 文件格式。 但是,DrafSight 是一个 2D CAD 软件。截至当前,它不支持 3D CAD 。
|
||||
|
||||
![draftsight][11]
|
||||
|
||||
尽管 DrafSight 是一款起价 149 美元的商业软件。在 [DraftSight 网站][12]上可获得一个免费版本。你可以下载 .deb 软件包,并在基于 Ubuntu 的发行版上安装它。为了开始使用 DraftSight ,你需要使用你的电子邮件 ID 来注册你的免费版本。
|
||||
|
||||
- [DraftSight][12]
|
||||
|
||||
#### 荣誉提名
|
||||
|
||||
* 随着云计算技术的巨大发展,像 [OnShape][13] 的云 CAD 解决方案已经变得日渐流行。
|
||||
* [SolveSpace][14] 是另一个值得一提的开源软件项目。它支持 3D 模型。
|
||||
* 西门子 NX 是一个在 Windows、Mac OS 及 Linux 上可用的工业级 CAD 解决方案,但是它贵得离谱,所以,在这个列表中被忽略。
|
||||
* 接下来,你有 [LeoCAD][15],它是一个 CAD 软件,在软件中你使用乐高积木来构建东西。你使用这些信息做些什么取决于你。
|
||||
|
||||
### 我对 Linux 上的 CAD 的看法
|
||||
|
||||
尽管在 Linux 上游戏变得流行,我总是告诉我的铁杆游戏朋友坚持使用 Windows。类似地,如果你是一名在你是课程中使用 CAD 的工科学生,我建议你使用学校规定的软件 (AutoCAD、SolidEdge、Catia),这些软件通常只在 Windows 上运行。
|
||||
|
||||
对于高级专业人士来说,当我们讨论行业标准时,这些工具根本达不到标准。
|
||||
|
||||
对于想在 WINE 中运行 AutoCAD 的那些人来说,尽管一些较旧版本的 AutoCAD 可以安装在 WINE 上,它们根本不执行工作,小故障和崩溃严重损害这些体验。
|
||||
|
||||
话虽如此,我高度尊重上述列表中软件的开发者的工作。他们丰富了 FOSS 世界。很高兴看到像 FreeCAD 一样的软件在近些年中加速开发速度。
|
||||
|
||||
好了,今天到此为止。使用下面的评论区与我们分享你的想法,不用忘记分享这篇文章。谢谢。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/cad-software-linux/
|
||||
|
||||
作者:[Aquil Roshan][a]
|
||||
选题:[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/) 荣誉推出
|
||||
|
||||
[a]:https://itsfoss.com/author/aquil/
|
||||
[1]:https://en.wikipedia.org/wiki/Computer-aided_design
|
||||
[2]:https://i1.wp.com/itsfoss.com/wp-content/uploads/2018/08/cad-software-linux.jpeg
|
||||
[3]:https://i1.wp.com/itsfoss.com/wp-content/uploads/2018/07/freecad.jpg
|
||||
[4]:https://www.freecadweb.org/
|
||||
[5]:https://i1.wp.com/itsfoss.com/wp-content/uploads/2018/07/librecad.jpg
|
||||
[6]:https://librecad.org/
|
||||
[7]:https://i1.wp.com/itsfoss.com/wp-content/uploads/2018/07/openscad.jpg
|
||||
[8]:http://www.openscad.org/
|
||||
[9]:https://i1.wp.com/itsfoss.com/wp-content/uploads/2018/07/brlcad.jpg
|
||||
[10]:https://brlcad.org/
|
||||
[11]:https://i1.wp.com/itsfoss.com/wp-content/uploads/2018/07/draftsight.jpg
|
||||
[12]:https://www.draftsight2018.com/
|
||||
[13]:https://www.onshape.com/
|
||||
[14]:http://solvespace.com/index.pl
|
||||
[15]:https://www.leocad.org/
|
@ -0,0 +1,89 @@
|
||||
区块链能如何补充开源
|
||||
======
|
||||
|
||||
> 了解区块链如何成为去中心化的开源补贴模型。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201909/16/111521od1yn9r1nr1eii9o.jpg)
|
||||
|
||||
《<ruby>[大教堂与集市][1]<rt>The Cathedral and The Bazaar</rt></ruby>》是 20 年前由<ruby>埃里克·史蒂文·雷蒙德<rt>Eric Steven Raymond<rt></ruby>(ESR)撰写的经典开源故事。在这个故事中,ESR 描述了一种新的革命性的软件开发模型,其中复杂的软件项目是在没有(或者很少的)集中管理的情况下构建的。这个新模型就是<ruby>开源<rt>open source</rt></ruby>。
|
||||
|
||||
ESR 的故事比较了两种模式:
|
||||
|
||||
* 经典模型(由“大教堂”所代表),其中软件由一小群人在封闭和受控的环境中通过缓慢而稳定的发布制作而成。
|
||||
* 以及新模式(由“集市”所代表),其中软件是在开放的环境中制作的,个人可以自由参与,但仍然可以产生一个稳定和连贯的系统。
|
||||
|
||||
开源如此成功的一些原因可以追溯到 ESR 所描述的创始原则。尽早发布、经常发布,并接受许多头脑必然比一个更好的事实,让开源项目进入全世界的人才库(很少有公司能够使用闭源模式与之匹敌)。
|
||||
|
||||
在 ESR 对黑客社区的反思分析 20 年后,我们看到开源成为占据主导地位的的模式。它不再仅仅是为了满足开发人员的个人喜好,而是创新发生的地方。甚至是全球[最大][2]软件公司也正在转向这种模式,以便继续占据主导地位。
|
||||
|
||||
### 易货系统
|
||||
|
||||
如果我们仔细研究开源模型在实践中的运作方式,我们就会意识到它是一个封闭系统,只对开源开发者和技术人员开放。影响项目方向的唯一方法是加入开源社区,了解成文和不成文的规则,学习如何贡献、编码标准等,并自己亲力完成。
|
||||
|
||||
这就是集市的运作方式,也是这个易货系统类比的来源。易货系统是一种交换服务和货物以换取其他服务和货物的方法。在市场中(即软件的构建地)这意味着为了获取某些东西,你必须自己也是一个生产者并回馈一些东西——那就是通过交换你的时间和知识来完成任务。集市是开源开发者与其他开源开发者交互并以开源方式生成开源软件的地方。
|
||||
|
||||
易货系统向前迈出了一大步,从自给自足的状态演变而来,而在自给自足的状态下,每个人都必须成为所有行业的杰出人选。使用易货系统的集市(开源模式)允许具有共同兴趣和不同技能的人们收集、协作和创造个人无法自行创造的东西。易货系统简单,没有现代货币系统那么复杂,但也有一些局限性,例如:
|
||||
|
||||
* 缺乏可分性:在没有共同的交换媒介的情况下,不能将较大的不可分割的商品/价值兑换成较小的商品/价值。例如,如果你想在开源项目中进行一些哪怕是小的更改,有时你可能仍需要经历一个高进入门槛。
|
||||
* 存储价值:如果一个项目对贵公司很重要,你可能需要投入大量投资/承诺。但由于它是开源开发者之间的易货系统,因此拥有强大发言权的唯一方法是雇佣许多开源贡献者,但这并非总是可行的。
|
||||
* 转移价值:如果你投资了一个项目(受过培训的员工、雇用开源开发者)并希望将重点转移到另一个项目,却不可能快速转移(你在上一个项目中拥有的)专业知识、声誉和影响力。
|
||||
* 时间脱钩:易货系统没有为延期或提前承诺提供良好的机制。在开源世界中,这意味着用户无法提前或在未来期间以可衡量的方式表达对项目的承诺或兴趣。
|
||||
|
||||
下面,我们将探讨如何使用集市的后门解决这些限制。
|
||||
|
||||
### 货币系统
|
||||
|
||||
人们因为不同的原因勾连于集市上:有些人在那里学习,有些是出于满足开发者个人的喜好,有些人为大型软件工厂工作。因为在集市中拥有发言权的唯一方法是成为开源社区的一份子并加入这个易货系统,为了在开源世界获得信誉,许多大型软件公司雇用这些开发者并以货币方式支付薪酬。这代表可以使用货币系统来影响集市,开源不再只是为了满足开发者个人的喜好,它也占据全球整体软件生产的重要部分,并且有许多人想要施加影响。
|
||||
|
||||
开源设定了开发人员交互的指导原则,并以分布式方式构建一致的系统。它决定了项目的治理方式、软件的构建方式以及其成果如何分发给用户。它是分散的实体共同构建高质量软件的开放共识模型。但是开源模型并没有包括如何补贴开源的部分,无论是直接还是间接地,通过内在或外在动机的赞助,都与集市无关。
|
||||
|
||||
![](https://opensource.com/sites/default/files/uploads/tokenomics_-_page_4.png)
|
||||
|
||||
目前,没有相当于以补贴为目的的去中心化式开源开发模型。大多数开源补贴都是集中式的,通常一家公司通过雇用该项目的主要开源开发者来主导该项目。说实话,这是目前最好的状况,因为它保证了开发人员将长期获得报酬,项目也将继续蓬勃发展。
|
||||
|
||||
项目垄断情景也有例外情况:例如,一些云原生计算基金会(CNCF)项目是由大量的竞争公司开发的。此外,Apache 软件基金会(ASF)旨在通过鼓励不同的贡献者来使他们管理的项目不被单一供应商所主导,但实际上大多数受欢迎的项目仍然是单一供应商项目。
|
||||
|
||||
我们缺少的是一个开放的、去中心化的模式,就像一个没有集中协调和所有权的集市一样,消费者(开源用户)和生产者(开源开发者)在市场力量和开源价值的驱动下相互作用。为了补充开源,这样的模型也必须是开放和去中心化的,这就是为什么我认为区块链技术[最适合][3]的原因。
|
||||
|
||||
旨在补贴开源开发的大多数现有区块链(和非区块链)平台主要针对的是漏洞赏金、小型和零碎的任务。少数人还专注于资助新的开源项目。但并没有多少平台旨在提供维持开源项目持续开发的机制 —— 基本上,这个系统可以模仿开源服务提供商公司或开放核心、基于开源的 SaaS 产品公司的行为:确保开发人员可以获得持续和可预测的激励,并根据激励者(即用户)的优先事项指导项目开发。这种模型将解决上面列出的易货系统的局限性:
|
||||
|
||||
* 允许可分性:如果你想要一些小的修复,你可以支付少量费用,而不是成为项目的开源开发者的全部费用。
|
||||
* 存储价值:你可以在项目中投入大量资金,并确保其持续发展和你的发言权。
|
||||
* 转移价值:在任何时候,你都可以停止投资项目并将资金转移到其他项目中。
|
||||
* 时间脱钩:允许定期定期付款和订阅。
|
||||
|
||||
还有其他好处,纯粹是因为这种基于区块链的系统是透明和去中心化的:根据用户的承诺、开放的路线图承诺、去中心化决策等来量化项目的价值/实用性。
|
||||
|
||||
### 总结
|
||||
|
||||
一方面,我们看到大公司雇用开源开发者并收购开源初创公司甚至基础平台(例如微软收购 GitHub)。许多(甚至大多数)能够长期成功运行的开源项目都集中在单个供应商周围。开源的重要性及其集中化是一个事实。
|
||||
|
||||
另一方面,[维持开源软件][4]的挑战正变得越来越明显,许多人正在更深入地研究这个领域及其基本问题。有一些项目具有很高的知名度和大量的贡献者,但还有许多其他也重要的项目缺乏足够的贡献者和维护者。
|
||||
|
||||
有[许多努力][3]试图通过区块链来解决开源的挑战。这些项目应提高透明度、去中心化和补贴,并在开源用户和开发人员之间建立直接联系。这个领域还很年轻,但是进展很快,随着时间的推移,集市将会有一个加密货币系统。
|
||||
|
||||
如果有足够的时间和足够的技术,去中心化就会发生在很多层面:
|
||||
|
||||
* 互联网是一种去中心化的媒介,它释放了全球分享和获取知识的潜力。
|
||||
* 开源是一种去中心化的协作模式,它释放了全球的创新潜力。
|
||||
* 同样,区块链可以补充开源,成为去中心化的开源补贴模式。
|
||||
|
||||
请在[推特][5]上关注我在这个领域的其他帖子。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/9/barter-currency-system
|
||||
|
||||
作者:[Bilgin lbryam][a]
|
||||
选题:[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/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/bibryam
|
||||
[1]: http://catb.org/
|
||||
[2]: http://oss.cash/
|
||||
[3]: https://opensource.com/article/18/8/open-source-tokenomics
|
||||
[4]: https://www.youtube.com/watch?v=VS6IpvTWwkQ
|
||||
[5]: http://twitter.com/bibryam
|
48
published/20181113 Eldoc Goes Global.md
Normal file
48
published/20181113 Eldoc Goes Global.md
Normal file
@ -0,0 +1,48 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lujun9972)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11306-1.html)
|
||||
[#]: subject: (Eldoc Goes Global)
|
||||
[#]: via: (https://emacsredux.com/blog/2018/11/13/eldoc-goes-global/)
|
||||
[#]: author: (Bozhidar Batsov https://emacsredux.com)
|
||||
|
||||
Emacs:Eldoc 全局化了
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201909/05/045702d99v9vv4xy0aybmm.jpg)
|
||||
|
||||
最近我注意到 Emacs 25.1 增加了一个名为 `global-eldoc-mode` 的模式,它是流行的 `eldoc-mode` 的一个全局化的变体。而且与 `eldoc-mode` 不同的是,`global-eldoc-mode` 默认是开启的!
|
||||
|
||||
这意味着你可以删除 Emacs 配置中为主模式开启 `eldoc-mode` 的代码了:
|
||||
|
||||
```
|
||||
;; That code is now redundant
|
||||
(add-hook 'emacs-lisp-mode-hook #'eldoc-mode)
|
||||
(add-hook 'ielm-mode-hook #'eldoc-mode)
|
||||
(add-hook 'cider-mode-hook #'eldoc-mode)
|
||||
(add-hook 'cider-repl-mode-hook #'eldoc-mode)
|
||||
```
|
||||
|
||||
[有人说][1] `global-eldoc-mode` 在某些不支持的模式中会有性能问题。我自己从未遇到过,但若你像禁用它则只需要这样:
|
||||
|
||||
```
|
||||
(global-eldoc-mode -1)
|
||||
```
|
||||
|
||||
现在是时候清理我的配置了!删除代码就是这么爽!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://emacsredux.com/blog/2018/11/13/eldoc-goes-global/
|
||||
|
||||
作者:[Bozhidar Batsov][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lujun9972](https://github.com/lujun9972)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://emacsredux.com
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://emacs.stackexchange.com/questions/31414/how-to-globally-disable-eldoc
|
252
published/20181227 Linux commands for measuring disk activity.md
Normal file
252
published/20181227 Linux commands for measuring disk activity.md
Normal file
@ -0,0 +1,252 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (laingke)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11387-1.html)
|
||||
[#]: subject: (Linux commands for measuring disk activity)
|
||||
[#]: via: (https://www.networkworld.com/article/3330497/linux/linux-commands-for-measuring-disk-activity.html)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
用于测量磁盘活动的 Linux 命令
|
||||
======
|
||||
> Linux 发行版提供了几个度量磁盘活动的有用命令。让我们了解一下其中的几个。
|
||||
|
||||
![](https://images.idgesg.net/images/article/2018/12/tape-measure-100782593-large.jpg)
|
||||
|
||||
Linux 系统提供了一套方便的命令,帮助你查看磁盘有多忙,而不仅仅是磁盘有多满。在本文中,我们将研究五个非常有用的命令,用于查看磁盘活动。其中两个命令(`iostat` 和 `ioping`)可能必须添加到你的系统中,这两个命令一样要求你使用 sudo 特权,所有这五个命令都提供了查看磁盘活动的有用方法。
|
||||
|
||||
这些命令中最简单、最直观的一个可能是 `dstat` 了。
|
||||
|
||||
### dtstat
|
||||
|
||||
尽管 `dstat` 命令以字母 “d” 开头,但它提供的统计信息远远不止磁盘活动。如果你只想查看磁盘活动,可以使用 `-d` 选项。如下所示,你将得到一个磁盘读/写测量值的连续列表,直到使用 `CTRL-c` 停止显示为止。注意,在第一个报告信息之后,显示中的每个后续行将在接下来的时间间隔内报告磁盘活动,缺省值仅为一秒。
|
||||
|
||||
```
|
||||
$ dstat -d
|
||||
-dsk/total-
|
||||
read writ
|
||||
949B 73k
|
||||
65k 0 <== first second
|
||||
0 24k <== second second
|
||||
0 16k
|
||||
0 0 ^C
|
||||
```
|
||||
|
||||
在 `-d` 选项后面包含一个数字将把间隔设置为该秒数。
|
||||
|
||||
```
|
||||
$ dstat -d 10
|
||||
-dsk/total-
|
||||
read writ
|
||||
949B 73k
|
||||
65k 81M <== first five seconds
|
||||
0 21k <== second five second
|
||||
0 9011B ^C
|
||||
```
|
||||
|
||||
请注意,报告的数据可能以许多不同的单位显示——例如,M(Mb)、K(Kb)和 B(字节)。
|
||||
|
||||
如果没有选项,`dstat` 命令还将显示许多其他信息——指示 CPU 如何使用时间、显示网络和分页活动、报告中断和上下文切换。
|
||||
|
||||
```
|
||||
$ dstat
|
||||
You did not select any stats, using -cdngy by default.
|
||||
--total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system--
|
||||
usr sys idl wai stl| read writ| recv send| in out | int csw
|
||||
0 0 100 0 0| 949B 73k| 0 0 | 0 3B| 38 65
|
||||
0 0 100 0 0| 0 0 | 218B 932B| 0 0 | 53 68
|
||||
0 1 99 0 0| 0 16k| 64B 468B| 0 0 | 64 81 ^C
|
||||
```
|
||||
|
||||
`dstat` 命令提供了关于整个 Linux 系统性能的有价值的见解,几乎可以用它灵活而功能强大的命令来代替 `vmstat`、`netstat`、`iostat` 和 `ifstat` 等较旧的工具集合,该命令结合了这些旧工具的功能。要深入了解 `dstat` 命令可以提供的其它信息,请参阅这篇关于 [dstat][1] 命令的文章。
|
||||
|
||||
### iostat
|
||||
|
||||
`iostat` 命令通过观察设备活动的时间与其平均传输速率之间的关系,帮助监视系统输入/输出设备的加载情况。它有时用于评估磁盘之间的活动平衡。
|
||||
|
||||
```
|
||||
$ iostat
|
||||
Linux 4.18.0-041800-generic (butterfly) 12/26/2018 _x86_64_ (2 CPU)
|
||||
|
||||
avg-cpu: %user %nice %system %iowait %steal %idle
|
||||
0.07 0.01 0.03 0.05 0.00 99.85
|
||||
|
||||
Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn
|
||||
loop0 0.00 0.00 0.00 1048 0
|
||||
loop1 0.00 0.00 0.00 365 0
|
||||
loop2 0.00 0.00 0.00 1056 0
|
||||
loop3 0.00 0.01 0.00 16169 0
|
||||
loop4 0.00 0.00 0.00 413 0
|
||||
loop5 0.00 0.00 0.00 1184 0
|
||||
loop6 0.00 0.00 0.00 1062 0
|
||||
loop7 0.00 0.00 0.00 5261 0
|
||||
sda 1.06 0.89 72.66 2837453 232735080
|
||||
sdb 0.00 0.02 0.00 48669 40
|
||||
loop8 0.00 0.00 0.00 1053 0
|
||||
loop9 0.01 0.01 0.00 18949 0
|
||||
loop10 0.00 0.00 0.00 56 0
|
||||
loop11 0.00 0.00 0.00 7090 0
|
||||
loop12 0.00 0.00 0.00 1160 0
|
||||
loop13 0.00 0.00 0.00 108 0
|
||||
loop14 0.00 0.00 0.00 3572 0
|
||||
loop15 0.01 0.01 0.00 20026 0
|
||||
loop16 0.00 0.00 0.00 24 0
|
||||
```
|
||||
|
||||
当然,当你只想关注磁盘时,Linux 回环设备上提供的所有统计信息都会使结果显得杂乱无章。不过,该命令也确实提供了 `-p` 选项,该选项使你可以仅查看磁盘——如以下命令所示。
|
||||
|
||||
```
|
||||
$ iostat -p sda
|
||||
Linux 4.18.0-041800-generic (butterfly) 12/26/2018 _x86_64_ (2 CPU)
|
||||
|
||||
avg-cpu: %user %nice %system %iowait %steal %idle
|
||||
0.07 0.01 0.03 0.05 0.00 99.85
|
||||
|
||||
Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn
|
||||
sda 1.06 0.89 72.54 2843737 232815784
|
||||
sda1 1.04 0.88 72.54 2821733 232815784
|
||||
```
|
||||
|
||||
请注意 `tps` 是指每秒的传输量。
|
||||
|
||||
你还可以让 `iostat` 提供重复的报告。在下面的示例中,我们使用 `-d` 选项每五秒钟进行一次测量。
|
||||
|
||||
```
|
||||
$ iostat -p sda -d 5
|
||||
Linux 4.18.0-041800-generic (butterfly) 12/26/2018 _x86_64_ (2 CPU)
|
||||
|
||||
Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn
|
||||
sda 1.06 0.89 72.51 2843749 232834048
|
||||
sda1 1.04 0.88 72.51 2821745 232834048
|
||||
|
||||
Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn
|
||||
sda 0.80 0.00 11.20 0 56
|
||||
sda1 0.80 0.00 11.20 0 56
|
||||
```
|
||||
|
||||
如果你希望省略第一个(自启动以来的统计信息)报告,请在命令中添加 `-y`。
|
||||
|
||||
```
|
||||
$ iostat -p sda -d 5 -y
|
||||
Linux 4.18.0-041800-generic (butterfly) 12/26/2018 _x86_64_ (2 CPU)
|
||||
|
||||
Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn
|
||||
sda 0.80 0.00 11.20 0 56
|
||||
sda1 0.80 0.00 11.20 0 56
|
||||
```
|
||||
|
||||
接下来,我们看第二个磁盘驱动器。
|
||||
|
||||
```
|
||||
$ iostat -p sdb
|
||||
Linux 4.18.0-041800-generic (butterfly) 12/26/2018 _x86_64_ (2 CPU)
|
||||
|
||||
avg-cpu: %user %nice %system %iowait %steal %idle
|
||||
0.07 0.01 0.03 0.05 0.00 99.85
|
||||
|
||||
Device tps kB_read/s kB_wrtn/s kB_read kB_wrtn
|
||||
sdb 0.00 0.02 0.00 48669 40
|
||||
sdb2 0.00 0.00 0.00 4861 40
|
||||
sdb1 0.00 0.01 0.00 35344 0
|
||||
```
|
||||
|
||||
### iotop
|
||||
|
||||
`iotop` 命令是类似 `top` 的实用程序,用于查看磁盘 I/O。它收集 Linux 内核提供的 I/O 使用信息,以便你了解哪些进程在磁盘 I/O 方面的要求最高。在下面的示例中,循环时间被设置为 5 秒。显示将自动更新,覆盖前面的输出。
|
||||
|
||||
```
|
||||
$ sudo iotop -d 5
|
||||
Total DISK READ: 0.00 B/s | Total DISK WRITE: 1585.31 B/s
|
||||
Current DISK READ: 0.00 B/s | Current DISK WRITE: 12.39 K/s
|
||||
TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
|
||||
32492 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.12 % [kworker/u8:1-ev~_power_efficient]
|
||||
208 be/3 root 0.00 B/s 1585.31 B/s 0.00 % 0.11 % [jbd2/sda1-8]
|
||||
1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % init splash
|
||||
2 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kthreadd]
|
||||
3 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_gp]
|
||||
4 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [rcu_par_gp]
|
||||
8 be/0 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [mm_percpu_wq]
|
||||
```
|
||||
|
||||
### ioping
|
||||
|
||||
`ioping` 命令是一种完全不同的工具,但是它可以报告磁盘延迟——也就是磁盘响应请求需要多长时间,而这有助于诊断磁盘问题。
|
||||
|
||||
```
|
||||
$ sudo ioping /dev/sda1
|
||||
4 KiB <<< /dev/sda1 (block device 111.8 GiB): request=1 time=960.2 us (warmup)
|
||||
4 KiB <<< /dev/sda1 (block device 111.8 GiB): request=2 time=841.5 us
|
||||
4 KiB <<< /dev/sda1 (block device 111.8 GiB): request=3 time=831.0 us
|
||||
4 KiB <<< /dev/sda1 (block device 111.8 GiB): request=4 time=1.17 ms
|
||||
^C
|
||||
--- /dev/sda1 (block device 111.8 GiB) ioping statistics ---
|
||||
3 requests completed in 2.84 ms, 12 KiB read, 1.05 k iops, 4.12 MiB/s
|
||||
generated 4 requests in 3.37 s, 16 KiB, 1 iops, 4.75 KiB/s
|
||||
min/avg/max/mdev = 831.0 us / 947.9 us / 1.17 ms / 158.0 us
|
||||
```
|
||||
|
||||
### atop
|
||||
|
||||
`atop` 命令,像 `top` 一样提供了大量有关系统性能的信息,包括有关磁盘活动的一些统计信息。
|
||||
|
||||
```
|
||||
ATOP - butterfly 2018/12/26 17:24:19 37d3h13m------ 10ed
|
||||
PRC | sys 0.03s | user 0.01s | #proc 179 | #zombie 0 | #exit 6 |
|
||||
CPU | sys 1% | user 0% | irq 0% | idle 199% | wait 0% |
|
||||
cpu | sys 1% | user 0% | irq 0% | idle 99% | cpu000 w 0% |
|
||||
CPL | avg1 0.00 | avg5 0.00 | avg15 0.00 | csw 677 | intr 470 |
|
||||
MEM | tot 5.8G | free 223.4M | cache 4.6G | buff 253.2M | slab 394.4M |
|
||||
SWP | tot 2.0G | free 2.0G | | vmcom 1.9G | vmlim 4.9G |
|
||||
DSK | sda | busy 0% | read 0 | write 7 | avio 1.14 ms |
|
||||
NET | transport | tcpi 4 | tcpo stall 8 | udpi 1 | udpo 0swout 2255 |
|
||||
NET | network | ipi 10 | ipo 7 | ipfrw 0 | deliv 60.67 ms |
|
||||
NET | enp0s25 0% | pcki 10 | pcko 8 | si 1 Kbps | so 3 Kbp0.73 ms |
|
||||
|
||||
PID SYSCPU USRCPU VGROW RGROW ST EXC THR S CPUNR CPU CMD 1/1673e4 |
|
||||
3357 0.01s 0.00s 672K 824K -- - 1 R 0 0% atop
|
||||
3359 0.01s 0.00s 0K 0K NE 0 0 E - 0% <ps>
|
||||
3361 0.00s 0.01s 0K 0K NE 0 0 E - 0% <ps>
|
||||
3363 0.01s 0.00s 0K 0K NE 0 0 E - 0% <ps>
|
||||
31357 0.00s 0.00s 0K 0K -- - 1 S 1 0% bash
|
||||
3364 0.00s 0.00s 8032K 756K N- - 1 S 1 0% sleep
|
||||
2931 0.00s 0.00s 0K 0K -- - 1 I 1 0% kworker/u8:2-e
|
||||
3356 0.00s 0.00s 0K 0K -E 0 0 E - 0% <sleep>
|
||||
3360 0.00s 0.00s 0K 0K NE 0 0 E - 0% <sleep>
|
||||
3362 0.00s 0.00s 0K 0K NE 0 0 E - 0% <sleep>
|
||||
```
|
||||
|
||||
如果你*只*想查看磁盘统计信息,则可以使用以下命令轻松进行管理:
|
||||
|
||||
```
|
||||
$ atop | grep DSK
|
||||
DSK | sda | busy 0% | read 122901 | write 3318e3 | avio 0.67 ms |
|
||||
DSK | sdb | busy 0% | read 1168 | write 103 | avio 0.73 ms |
|
||||
DSK | sda | busy 2% | read 0 | write 92 | avio 2.39 ms |
|
||||
DSK | sda | busy 2% | read 0 | write 94 | avio 2.47 ms |
|
||||
DSK | sda | busy 2% | read 0 | write 99 | avio 2.26 ms |
|
||||
DSK | sda | busy 2% | read 0 | write 94 | avio 2.43 ms |
|
||||
DSK | sda | busy 2% | read 0 | write 94 | avio 2.43 ms |
|
||||
DSK | sda | busy 2% | read 0 | write 92 | avio 2.43 ms |
|
||||
^C
|
||||
```
|
||||
|
||||
### 了解磁盘 I/O
|
||||
|
||||
Linux 提供了足够的命令,可以让你很好地了解磁盘的工作强度,并帮助你关注潜在的问题或减缓。希望这些命令中的一个可以告诉你何时需要质疑磁盘性能。偶尔使用这些命令将有助于确保当你需要检查磁盘,特别是忙碌或缓慢的磁盘时可以显而易见地发现它们。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3330497/linux/linux-commands-for-measuring-disk-activity.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[laingke](https://github.com/laingke)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.networkworld.com/article/3291616/linux/examining-linux-system-performance-with-dstat.html
|
||||
[2]: https://www.facebook.com/NetworkWorld/
|
||||
[3]: https://www.linkedin.com/company/network-world
|
@ -0,0 +1,227 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (laingke)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11373-1.html)
|
||||
[#]: subject: (Create an online store with this Java-based framework)
|
||||
[#]: via: (https://opensource.com/article/19/1/scipio-erp)
|
||||
[#]: author: (Paul Piper https://opensource.com/users/madppiper)
|
||||
|
||||
使用 Java 框架 Scipio ERP 创建一个在线商店
|
||||
======
|
||||
|
||||
> Scipio ERP 具有包罗万象的应用程序和功能。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201909/22/133258hqvwax5w1zvq5ffa.jpg)
|
||||
|
||||
如果,你想在网上销售产品或服务,但要么找不到合适的软件,要么觉得定制成本太高?那么,[Scipio ERP][1] 也许正是你想要的。
|
||||
|
||||
Scipio ERP 是一个基于 Java 的开源的电子商务框架,具有包罗万象的应用程序和功能。这个项目于 2014 年从 [Apache OFBiz][2] 分叉而来,侧重于更好的定制和更现代的吸引力。这个电子商务组件非常丰富,可以在多商店环境中工作,同时支持国际化,具有琳琅满目的产品配置,而且它还兼容现代 HTML 框架。该软件还为许多其他业务场景提供标准应用程序,例如会计、仓库管理或销售团队自动化。它都是高度标准化的,因此易于定制,如果你想要的不仅仅是一个虚拟购物车,这是非常棒的。
|
||||
|
||||
该系统也使得跟上现代 Web 标准变得非常容易。所有界面都是使用系统的“[模板工具包][3]”构建的,这是一个易于学习的宏集,可以将 HTML 与所有应用程序分开。正因为如此,每个应用程序都已经标准化到核心。听起来令人困惑?它真的不是 HTML——它看起来很像 HTML,但你写的内容少了很多。
|
||||
|
||||
### 初始安装
|
||||
|
||||
在你开始之前,请确保你已经安装了 Java 1.8(或更高版本)的 SDK 以及一个 Git 客户端。完成了?太棒了!接下来,切换到 Github 上的主分支:
|
||||
|
||||
```
|
||||
git clone https://github.com/ilscipio/scipio-erp.git
|
||||
cd scipio-erp
|
||||
git checkout master
|
||||
```
|
||||
|
||||
要安装该系统,只需要运行 `./install.sh` 并从命令行中选择任一选项。在开发过程中,最好一直使用 “installation for development”(选项 1),它还将安装一系列演示数据。对于专业安装,你可以修改初始配置数据(“种子数据”),以便自动为你设置公司和目录数据。默认情况下,系统将使用内部数据库运行,但是它[也可以配置][4]使用各种关系数据库,比如 PostgreSQL 和 MariaDB 等。
|
||||
|
||||
![安装向导][6]
|
||||
|
||||
*按照安装向导完成初始配置*
|
||||
|
||||
通过命令 `./start.sh` 启动系统然后打开链接 <https://localhost:8443/setup/> 完成配置。如果你安装了演示数据, 你可以使用用户名 `admin` 和密码 `scipio` 进行登录。在安装向导中,你可以设置公司简介、会计、仓库、产品目录、在线商店和额外的用户配置信息。暂时在产品商店配置界面上跳过网站实体的配置。系统允许你使用不同的底层代码运行多个在线商店;除非你想这样做,一直选择默认值是最简单的。
|
||||
|
||||
祝贺你,你刚刚安装了 Scipio ERP!在界面上操作一两分钟,感受一下它的功能。
|
||||
|
||||
### 捷径
|
||||
|
||||
在你进入自定义之前,这里有一些方便的命令可以帮助你:
|
||||
|
||||
* 创建一个 shop-override:`./ant create-component-shop-override`
|
||||
* 创建一个新组件:`./ant create-component`
|
||||
* 创建一个新主题组件:`./ant create-theme`
|
||||
* 创建管理员用户:`./ant create-admin-user-login`
|
||||
* 各种其他实用功能:`./ant -p`
|
||||
* 用于安装和更新插件的实用程序:`./git-addons help`
|
||||
|
||||
另外,请记下以下位置:
|
||||
|
||||
* 将 Scipio 作为服务运行的脚本:`/tools/scripts/`
|
||||
* 日志输出目录:`/runtime/logs`
|
||||
* 管理应用程序:`<https://localhost:8443/admin/>`
|
||||
* 电子商务应用程序:`<https://localhost:8443/shop/>`
|
||||
|
||||
最后,Scipio ERP 在以下五个主要目录中构建了所有代码:
|
||||
|
||||
* `framework`: 框架相关的源,应用程序服务器,通用界面和配置
|
||||
* `applications`: 核心应用程序
|
||||
* `addons`: 第三方扩展
|
||||
* `themes`: 修改界面外观
|
||||
* `hot-deploy`: 你自己的组件
|
||||
|
||||
除了一些配置,你将在 `hot-deploy` 和 `themes` 目录中进行开发。
|
||||
|
||||
### 在线商店定制
|
||||
|
||||
要真正使系统成为你自己的系统,请开始考虑使用[组件][7]。组件是一种模块化方法,可以覆盖、扩展和添加到系统中。你可以将组件视为独立 Web 模块,可以捕获有关数据库([实体][8])、功能([服务][9])、界面([视图][10])、[事件和操作][11]和 Web 应用程序等的信息。由于组件功能,你可以添加自己的代码,同时保持与原始源兼容。
|
||||
|
||||
运行命令 `./ant create-component-shop-override` 并按照步骤创建你的在线商店组件。该操作将会在 `hot-deploy` 目录内创建一个新目录,该目录将扩展并覆盖原始的电子商务应用程序。
|
||||
|
||||
![组件目录结构][13]
|
||||
|
||||
*一个典型的组件目录结构。*
|
||||
|
||||
你的组件将具有以下目录结构:
|
||||
|
||||
* `config`: 配置
|
||||
* `data`: 种子数据
|
||||
* `entitydef`: 数据库表定义
|
||||
* `script`: Groovy 脚本的位置
|
||||
* `servicedef`: 服务定义
|
||||
* `src`: Java 类
|
||||
* `webapp`: 你的 web 应用程序
|
||||
* `widget`: 界面定义
|
||||
|
||||
此外,`ivy.xml` 文件允许你将 Maven 库添加到构建过程中,`ofbiz-component.xml` 文件定义整个组件和 Web 应用程序结构。除了一些在当前目录所能够看到的,你还可以在 Web 应用程序的 `WEB-INF` 目录中找到 `controller.xml` 文件。这允许你定义请求实体并将它们连接到事件和界面。仅对于界面来说,你还可以使用内置的 CMS 功能,但优先要坚持使用核心机制。在引入更改之前,请熟悉 `/applications/shop/`。
|
||||
|
||||
#### 添加自定义界面
|
||||
|
||||
还记得[模板工具包][3]吗?你会发现它在每个界面都有使用到。你可以将其视为一组易于学习的宏,它用来构建所有内容。下面是一个例子:
|
||||
|
||||
```
|
||||
<@section title="Title">
|
||||
<@heading id="slider">Slider</@heading>
|
||||
<@row>
|
||||
<@cell columns=6>
|
||||
<@slider id="" class="" controls=true indicator=true>
|
||||
<@slide link="#" image="https://placehold.it/800x300">Just some content…</@slide>
|
||||
<@slide title="This is a title" link="#" image="https://placehold.it/800x300"></@slide>
|
||||
</@slider>
|
||||
</@cell>
|
||||
<@cell columns=6>Second column</@cell>
|
||||
</@row>
|
||||
</@section>
|
||||
```
|
||||
|
||||
不是很难,对吧?同时,主题包含 HTML 定义和样式。这将权力交给你的前端开发人员,他们可以定义每个宏的输出,并坚持使用自己的构建工具进行开发。
|
||||
|
||||
我们快点试试吧。首先,在你自己的在线商店上定义一个请求。你将修改此代码。一个内置的 CMS 系统也可以通过 <https://localhost:8443/cms/> 进行访问,它允许你以更有效的方式创建新模板和界面。它与模板工具包完全兼容,并附带可根据你的喜好采用的示例模板。但是既然我们试图在这里理解系统,那么首先让我们采用更复杂的方法。
|
||||
|
||||
打开你商店 `webapp` 目录中的 [controller.xml][14] 文件。控制器会跟踪请求事件并相应地执行操作。下面的操作将会在 `/shop/test` 下创建一个新的请求:
|
||||
|
||||
```
|
||||
<!-- Request Mappings -->
|
||||
<request-map uri="test">
|
||||
<security https="true" auth="false"/>
|
||||
<response name="success" type="view" value="test"/>
|
||||
</request-map>
|
||||
```
|
||||
|
||||
你可以定义多个响应,如果需要,可以在请求中使用事件或服务调用来确定你可能要使用的响应。我选择了“视图”类型的响应。视图是渲染的响应;其他类型是请求重定向、转发等。系统附带各种渲染器,可让你稍后确定输出;为此,请添加以下内容:
|
||||
|
||||
```
|
||||
<!-- View Mappings -->
|
||||
<view-map name="test" type="screen" page="component://mycomponent/widget/CommonScreens.xml#test"/>
|
||||
```
|
||||
|
||||
用你自己的组件名称替换 `my-component`。然后,你可以通过在 `widget/CommonScreens.xml` 文件的标签内添加以下内容来定义你的第一个界面:
|
||||
|
||||
```
|
||||
<screen name="test">
|
||||
<section>
|
||||
<actions>
|
||||
</actions>
|
||||
<widgets>
|
||||
<decorator-screen name="CommonShopAppDecorator" location="component://shop/widget/CommonScreens.xml">
|
||||
<decorator-section name="body">
|
||||
<platform-specific><html><html-template location="component://mycomponent/webapp/mycomponent/test/test.ftl"/></html></platform-specific>
|
||||
</decorator-section>
|
||||
</decorator-screen>
|
||||
</widgets>
|
||||
</section>
|
||||
</screen>
|
||||
```
|
||||
|
||||
商店界面实际上非常模块化,由多个元素组成([小部件、动作和装饰器][15])。为简单起见,请暂时保留原样,并通过添加第一个模板工具包文件来完成新网页。为此,创建一个新的 `webapp/mycomponent/test/test.ftl` 文件并添加以下内容:
|
||||
|
||||
```
|
||||
<@alert type="info">Success!</@alert>
|
||||
```
|
||||
|
||||
![自定义的界面][17]
|
||||
|
||||
*一个自定义的界面。*
|
||||
|
||||
打开 <https://localhost:8443/shop/control/test/> 并惊叹于你自己的成就。
|
||||
|
||||
#### 自定义主题
|
||||
|
||||
通过创建自己的主题来修改商店的界面外观。所有主题都可以作为组件在 `themes` 文件夹中找到。运行命令 `./ant create-theme` 来创建你自己的主题。
|
||||
|
||||
![主题组件布局][19]
|
||||
|
||||
*一个典型的主题组件布局。*
|
||||
|
||||
以下是最重要的目录和文件列表:
|
||||
|
||||
* 主题配置:`data/*ThemeData.xml`
|
||||
* 特定主题封装的 HTML:`includes/*.ftl`
|
||||
* 模板工具包 HTML 定义:`includes/themeTemplate.ftl`
|
||||
* CSS 类定义:`includes/themeStyles.ftl`
|
||||
* CSS 框架: `webapp/theme-title/`
|
||||
|
||||
快速浏览工具包中的 Metro 主题;它使用 Foundation CSS 框架并且充分利用了这个框架。然后,然后,在新构建的 `webapp/theme-title` 目录中设置自己的主题并开始开发。Foundation-shop 主题是一个非常简单的特定于商店的主题实现,你可以将其用作你自己工作的基础。
|
||||
|
||||
瞧!你已经建立了自己的在线商店,准备个性化定制吧!
|
||||
|
||||
![搭建完成的 Scipio ERP 在线商店][21]
|
||||
|
||||
*一个搭建完成的基于 Scipio ERP的在线商店。*
|
||||
|
||||
### 接下来是什么?
|
||||
|
||||
Scipio ERP 是一个功能强大的框架,可简化复杂的电子商务应用程序的开发。为了更完整的理解,请查看项目[文档][7],尝试[在线演示][22],或者[加入社区][23].
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/1/scipio-erp
|
||||
|
||||
作者:[Paul Piper][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[laingke](https://github.com/laingke)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/madppiper
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.scipioerp.com
|
||||
[2]: https://ofbiz.apache.org/
|
||||
[3]: https://www.scipioerp.com/community/developer/freemarker-macros/
|
||||
[4]: https://www.scipioerp.com/community/developer/installation-configuration/configuration/#database-configuration
|
||||
[5]: /file/419711
|
||||
[6]: https://opensource.com/sites/default/files/uploads/setup_step5_sm.jpg (Setup wizard)
|
||||
[7]: https://www.scipioerp.com/community/developer/architecture/components/
|
||||
[8]: https://www.scipioerp.com/community/developer/entities/
|
||||
[9]: https://www.scipioerp.com/community/developer/services/
|
||||
[10]: https://www.scipioerp.com/community/developer/views-requests/
|
||||
[11]: https://www.scipioerp.com/community/developer/events-actions/
|
||||
[12]: /file/419716
|
||||
[13]: https://opensource.com/sites/default/files/uploads/component_structure.jpg (component directory structure)
|
||||
[14]: https://www.scipioerp.com/community/developer/views-requests/request-controller/
|
||||
[15]: https://www.scipioerp.com/community/developer/views-requests/screen-widgets-decorators/
|
||||
[16]: /file/419721
|
||||
[17]: https://opensource.com/sites/default/files/uploads/success_screen_sm.jpg (Custom screen)
|
||||
[18]: /file/419726
|
||||
[19]: https://opensource.com/sites/default/files/uploads/theme_structure.jpg (theme component layout)
|
||||
[20]: /file/419731
|
||||
[21]: https://opensource.com/sites/default/files/uploads/finished_shop_1_sm.jpg (Finished Scipio ERP shop)
|
||||
[22]: https://www.scipioerp.com/demo/
|
||||
[23]: https://forum.scipioerp.com/
|
223
published/20190401 Build and host a website with Git.md
Normal file
223
published/20190401 Build and host a website with Git.md
Normal file
@ -0,0 +1,223 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11303-1.html)
|
||||
[#]: subject: (Build and host a website with Git)
|
||||
[#]: via: (https://opensource.com/article/19/4/building-hosting-website-git)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
用 Git 建立和托管网站
|
||||
======
|
||||
|
||||
> 你可以让 Git 帮助你轻松发布你的网站。在我们《鲜为人知的 Git 用法》系列的第一篇文章中学习如何做到。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201909/04/134312l912496eegpoqaqe.png)
|
||||
|
||||
[Git][2] 是一个少有的能将如此多的现代计算封装到一个程序之中的应用程序,它可以用作许多其他应用程序的计算引擎。虽然它以跟踪软件开发中的源代码更改而闻名,但它还有许多其他用途,可以让你的生活更轻松、更有条理。在这个 Git 系列中,我们将分享七种鲜为人知的使用 Git 的方法。
|
||||
|
||||
创建一个网站曾经是极其简单的,而同时它又是一种黑魔法。回到 Web 1.0 的旧时代(不是每个人都会这样称呼它),你可以打开任何网站,查看其源代码,并对 HTML 及其内联样式和基于表格的布局进行反向工程,在这样的一两个下午之后,你就会感觉自己像一个程序员一样。不过要让你创建的页面放到互联网上,仍然有一些问题,因为这意味着你需要处理服务器、FTP 以及 webroot 目录和文件权限。虽然从那时起,现代网站变得愈加复杂,但如果你让 Git 帮助你,自出版可以同样容易(或更容易!)。
|
||||
|
||||
### 用 Hugo 创建一个网站
|
||||
|
||||
[Hugo][3] 是一个开源的静态站点生成器。静态网站是过去的 Web 的基础(如果你回溯到很久以前,那就是 Web 的*全部*了)。静态站点有几个优点:它们相对容易编写,因为你不必编写代码;它们相对安全,因为页面上没有执行代码;并且它们可以非常快,因为除了在页面上传输的任何内容之外没有任何处理。
|
||||
|
||||
Hugo 并不是唯一一个静态站点生成器。[Grav][4]、[Pico][5]、[Jekyll][6]、[Podwrite][7] 以及许多其他的同类软件都提供了一种创建一个功能最少的、只需要很少维护的网站的简单方法。Hugo 恰好是内置集成了 GitLab 集成的一个静态站点生成器,这意味着你可以使用免费的 GitLab 帐户生成和托管你的网站。
|
||||
|
||||
Hugo 也有一些非常大的用户。例如,如果你曾经去过 [Let's Encrypt](https://letsencrypt.org/) 网站,那么你已经用过了一个用 Hugo 构建的网站。
|
||||
|
||||
![Let's Encrypt website][8]
|
||||
|
||||
#### 安装 Hugo
|
||||
|
||||
Hugo 是跨平台的,你可以在 [Hugo 的入门资源][9]中找到适用于 MacOS、Windows、Linux、OpenBSD 和 FreeBSD 的安装说明。
|
||||
|
||||
如果你使用的是 Linux 或 BSD,最简单的方法是从软件存储库或 ports 树安装 Hugo。确切的命令取决于你的发行版,但在 Fedora 上,你应该输入:
|
||||
|
||||
```
|
||||
$ sudo dnf install hugo
|
||||
```
|
||||
|
||||
通过打开终端并键入以下内容确认你已正确安装:
|
||||
|
||||
```
|
||||
$ hugo help
|
||||
```
|
||||
|
||||
这将打印 `hugo` 命令的所有可用选项。如果你没有看到,你可能没有正确安装 Hugo 或需要[将该命令添加到你的路径][10]。
|
||||
|
||||
#### 创建你的站点
|
||||
|
||||
要构建 Hugo 站点,你必须有个特定的目录结构,通过输入以下命令 Hugo 将为你生成它:
|
||||
|
||||
```
|
||||
$ hugo new site mysite
|
||||
```
|
||||
|
||||
你现在有了一个名为 `mysite` 的目录,它包含构建 Hugo 网站所需的默认目录。
|
||||
|
||||
Git 是你将网站放到互联网上的接口,因此切换到你新的 `mysite` 文件夹,并将其初始化为 Git 存储库:
|
||||
|
||||
```
|
||||
$ cd mysite
|
||||
$ git init .
|
||||
```
|
||||
|
||||
Hugo 与 Git 配合的很好,所以你甚至可以使用 Git 为你的网站安装主题。除非你计划开发你正在安装的主题,否则可以使用 `--depth` 选项克隆该主题的源的最新状态:
|
||||
|
||||
```
|
||||
$ git clone --depth 1 https://github.com/darshanbaral/mero.git themes/mero
|
||||
```
|
||||
|
||||
现在为你的网站创建一些内容:
|
||||
|
||||
```
|
||||
$ hugo new posts/hello.md
|
||||
```
|
||||
|
||||
使用你喜欢的文本编辑器编辑 `content/posts` 目录中的 `hello.md` 文件。Hugo 接受 Markdown 文件,并会在发布时将它们转换为经过主题化的 HTML 文件,因此你的内容必须采用 [Markdown 格式][11]。
|
||||
|
||||
如果要在帖子中包含图像,请在 `static` 目录中创建一个名为 `images` 的文件夹。将图像放入此文件夹,并使用以 `/images` 开头的绝对路径在标记中引用它们。例如:
|
||||
|
||||
```
|
||||
![A picture of a thing](/images/thing.jpeg)
|
||||
```
|
||||
|
||||
#### 选择主题
|
||||
|
||||
你可以在 [themes.gohugo.io][12] 找到更多主题,但最好在测试时保持一个基本主题。标准的 Hugo 测试主题是 [Ananke][13]。某些主题具有复杂的依赖关系,而另外一些主题如果没有复杂的配置的话,也许不会以你预期的方式呈现页面。本例中使用的 Mero 主题捆绑了一个详细的 `config.toml` 配置文件,但是(为了简单起见)我将在这里只提供基本的配置。在文本编辑器中打开名为 `config.toml` 的文件,并添加三个配置参数:
|
||||
|
||||
```
|
||||
languageCode = "en-us"
|
||||
title = "My website on the web"
|
||||
theme = "mero"
|
||||
|
||||
[params]
|
||||
author = "Seth Kenlon"
|
||||
description = "My hugo demo"
|
||||
```
|
||||
|
||||
#### 预览
|
||||
|
||||
在你准备发布之前不必(预先)在互联网上放置任何内容。在你开发网站时,你可以通过启动 Hugo 附带的仅限本地访问的 Web 服务器来预览你的站点。
|
||||
|
||||
```
|
||||
$ hugo server --buildDrafts --disableFastRender
|
||||
```
|
||||
|
||||
打开 Web 浏览器并导航到 <http://localhost:1313> 以查看正在进行的工作。
|
||||
|
||||
### 用 Git 发布到 GitLab
|
||||
|
||||
要在 GitLab 上发布和托管你的站点,请为你的站点内容创建一个存储库。
|
||||
|
||||
要在 GitLab 中创建存储库,请单击 GitLab 的 “Projects” 页面中的 “New Project” 按钮。创建一个名为 `yourGitLabUsername.gitlab.io` 的空存储库,用你的 GitLab 用户名或组名替换 `yourGitLabUsername`。你必须使用此命名方式作为该项目的名称。你也可以稍后为其添加自定义域。
|
||||
|
||||
不要在 GitLab 上包含许可证或 README 文件(因为你已经在本地启动了一个项目,现在添加这些文件会使将你的数据推向 GitLab 时更加复杂,以后你可以随时添加它们)。
|
||||
|
||||
在 GitLab 上创建空存储库后,将其添加为 Hugo 站点的本地副本的远程位置,该站点已经是一个 Git 存储库:
|
||||
|
||||
```
|
||||
$ git remote add origin git@gitlab.com:skenlon/mysite.git
|
||||
```
|
||||
|
||||
创建名为 `.gitlab-ci.yml` 的 GitLab 站点配置文件并输入以下选项:
|
||||
|
||||
```
|
||||
image: monachus/hugo
|
||||
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
|
||||
pages:
|
||||
script:
|
||||
- hugo
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
only:
|
||||
- master
|
||||
```
|
||||
|
||||
`image` 参数定义了一个为你的站点提供服务的容器化图像。其他参数是告诉 GitLab 服务器在将新代码推送到远程存储库时要执行的操作的说明。有关 GitLab 的 CI/CD(持续集成和交付)选项的更多信息,请参阅 [GitLab 文档的 CI/CD 部分][14]。
|
||||
|
||||
#### 设置排除的内容
|
||||
|
||||
你的 Git 存储库已配置好,在 GitLab 服务器上构建站点的命令也已设置,你的站点已准备好发布了。对于你的第一个 Git 提交,你必须采取一些额外的预防措施,以便你不会对你不打算进行版本控制的文件进行版本控制。
|
||||
|
||||
首先,将构建你的站点时 Hugo 创建的 `/public` 目录添加到 `.gitignore` 文件。你无需在 Git 中管理已完成发布的站点;你需要跟踪的是你的 Hugo 源文件。
|
||||
|
||||
```
|
||||
$ echo "/public" >> .gitignore
|
||||
```
|
||||
|
||||
如果不创建 Git 子模块,则无法在 Git 存储库中维护另一个 Git 存储库。为了简单起见,请移除嵌入的存储库的 `.git` 目录,以使主题(存储库)只是一个主题(目录)。
|
||||
|
||||
请注意,你**必须**将你的主题文件添加到你的 Git 存储库,以便 GitLab 可以访问该主题。如果不提交主题文件,你的网站将无法成功构建。
|
||||
|
||||
```
|
||||
$ mv themes/mero/.git ~/.local/share/Trash/files/
|
||||
```
|
||||
|
||||
你也可以像使用[回收站][15]一样使用 `trash`:
|
||||
|
||||
```
|
||||
$ trash themes/mero/.git
|
||||
```
|
||||
|
||||
现在,你可以将本地项目目录的所有内容添加到 Git 并将其推送到 GitLab:
|
||||
|
||||
```
|
||||
$ git add .
|
||||
$ git commit -m 'hugo init'
|
||||
$ git push -u origin HEAD
|
||||
```
|
||||
|
||||
### 用 GitLab 上线
|
||||
|
||||
将代码推送到 GitLab 后,请查看你的项目页面。有个图标表示 GitLab 正在处理你的构建。第一次推送代码可能需要几分钟,所以请耐心等待。但是,请不要**一直**等待,因为该图标并不总是可靠地更新。
|
||||
|
||||
![GitLab processing your build][16]
|
||||
|
||||
当你在等待 GitLab 组装你的站点时,请转到你的项目设置并找到 “Pages” 面板。你的网站准备就绪后,它的 URL 就可以用了。该 URL 是 `yourGitLabUsername.gitlab.io/yourProjectName`。导航到该地址以查看你的劳动成果。
|
||||
|
||||
![Previewing Hugo site][17]
|
||||
|
||||
如果你的站点无法正确组装,GitLab 提供了可以深入了解 CI/CD 管道的日志。查看错误消息以找出发生了什么问题。
|
||||
|
||||
### Git 和 Web
|
||||
|
||||
Hugo(或 Jekyll 等类似工具)只是利用 Git 作为 Web 发布工具的一种方式。使用服务器端 Git 挂钩,你可以使用最少的脚本设计你自己的 Git-to-web 工作流。使用 GitLab 的社区版,你可以自行托管你自己的 GitLab 实例;或者你可以使用 [Gitolite][18] 或 [Gitea][19] 等替代方案,并使用本文作为自定义解决方案的灵感来源。祝你玩得开心!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/building-hosting-website-git
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/web_browser_desktop_devlopment_design_system_computer.jpg?itok=pfqRrJgh (web development and design, desktop and browser)
|
||||
[2]: https://git-scm.com/
|
||||
[3]: http://gohugo.io
|
||||
[4]: http://getgrav.org
|
||||
[5]: http://picocms.org/
|
||||
[6]: https://jekyllrb.com
|
||||
[7]: http://slackermedia.info/podwrite/
|
||||
[8]: https://opensource.com/sites/default/files/uploads/letsencrypt-site.jpg (Let's Encrypt website)
|
||||
[9]: https://gohugo.io/getting-started/installing
|
||||
[10]: https://opensource.com/article/17/6/set-path-linux
|
||||
[11]: https://commonmark.org/help/
|
||||
[12]: https://themes.gohugo.io/
|
||||
[13]: https://themes.gohugo.io/gohugo-theme-ananke/
|
||||
[14]: https://docs.gitlab.com/ee/ci/#overview
|
||||
[15]: http://slackermedia.info/trashy
|
||||
[16]: https://opensource.com/sites/default/files/uploads/hugo-gitlab-cicd.jpg (GitLab processing your build)
|
||||
[17]: https://opensource.com/sites/default/files/uploads/hugo-demo-site.jpg (Previewing Hugo site)
|
||||
[18]: http://gitolite.com
|
||||
[19]: http://gitea.io
|
244
published/20190402 Manage your daily schedule with Git.md
Normal file
244
published/20190402 Manage your daily schedule with Git.md
Normal file
@ -0,0 +1,244 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11320-1.html)
|
||||
[#]: subject: (Manage your daily schedule with Git)
|
||||
[#]: via: (https://opensource.com/article/19/4/calendar-git)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
用 Git 管理你的每日行程
|
||||
======
|
||||
|
||||
> 像源代码一样对待时间并在 Git 的帮助下维护你的日历。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201909/09/061835la7ne9edtlr7kn18.png)
|
||||
|
||||
[Git][2] 是一个少有的能将如此多的现代计算封装到一个程序之中的应用程序,它可以用作许多其他应用程序的计算引擎。虽然它以跟踪软件开发中的源代码更改而闻名,但它还有许多其他用途,可以让你的生活更轻松、更有条理。在这个 Git 系列中,我们将分享七种鲜为人知的使用 Git 的方法。
|
||||
|
||||
今天,我们将使用 Git 来跟踪你的日历。
|
||||
|
||||
### 使用 Git 跟踪你的日程安排
|
||||
|
||||
如果时间本身只是可以管理和版本控制的源代码呢?虽然证明或反驳这种理论可能超出了本文的范围,但在 Git 的帮助下,你可以将时间视为源代码并管理你的日程安排。
|
||||
|
||||
日历的卫冕冠军是 [CalDAV][3] 协议,它支撑了如 [NextCloud][4] 这样的流行的开源及闭源的日历应用程序。CalDAV 没什么问题(评论者,请注意),但它并不适合所有人,除此之外,它还有一种不同于单一文化的鼓舞人心的东西。
|
||||
|
||||
因为我对大量使用 GUI 的 CalDAV 客户端没有兴趣(如果你正在寻找一个好的终端 CalDAV 查看器,请参阅 [khal][5]),我开始研究基于文本的替代方案。基于文本的日历具有在[明文][6]中工作的所有常见好处。它很轻巧,非常便携,只要它结构化,就很容易解析和美化(无论*美丽*对你意味着什么)。
|
||||
|
||||
最重要的是,它正是 Git 旨在管理的内容。
|
||||
|
||||
### Org 模式不是一种可怕的方式
|
||||
|
||||
如果你没有对你的明文添加结构,它很快就会陷入一种天马行空般的混乱,变成恶魔才能懂的符号。幸运的是,有一种用于日历的标记语法,它包含在令人尊敬的生产力 Emacs 模式 —— [Org 模式][7] 中(承认吧,你其实一直想开始使用它)。
|
||||
|
||||
许多人没有意识到 Org 模式的惊人之处在于[你不需要知道甚至不需要使用 Emacs][8]来利用 Org 模式建立的约定。如果你使用 Emacs,你会得到许多很棒的功能,但是如果 Emacs 对你来说太难了,那么你可以实现一个基于 Git 的 Org 模式的日历系统,而不需要安装 Emacs。
|
||||
|
||||
关于 Org 模式你唯一需要知道的部分是它的语法。Org 模式的语法维护成本低、直观。使用 Org 模式而不是 GUI 日历应用程序进行日历记录的最大区别在于工作流程:你可以创建一个任务列表,然后每天分配一个任务,而不是转到日历并查找要安排任务的日期。
|
||||
|
||||
组织模式中的列表使用星号(`*`)作为项目符号。这是我的游戏任务列表:
|
||||
|
||||
```
|
||||
* Gaming
|
||||
** Build Stardrifter character
|
||||
** Read Stardrifter rules
|
||||
** Stardrifter playtest
|
||||
|
||||
** Blue Planet @ Mike's
|
||||
|
||||
** Run Rappan Athuk
|
||||
*** Purchase hard copy
|
||||
*** Skim Rappan Athuk
|
||||
*** Build Rappan Athuk maps in maptool
|
||||
*** Sort Rappan Athuk tokens
|
||||
```
|
||||
|
||||
如果你熟悉 [CommonMark][9] 或 Markdown,你会注意到,Org 模式不是使用空格来创建子任务,而是更明确地使用了其它项目符号。无论你的使用背景和列表是什么,这都是一种构建列表的直观且简单的方法,它显然与 Emacs 没有内在联系(尽管使用 Emacs 为你提供了快捷方式,因此你可以快速地重新排列列表)。
|
||||
|
||||
要将列表转换为日历中的计划任务或事件,请返回并添加关键字 `SCHEDULED` 和(可选)`:CATEGORY:`。
|
||||
|
||||
```
|
||||
* Gaming
|
||||
:CATEGORY: Game
|
||||
** Build Stardrifter character
|
||||
SCHEDULED: <2019-03-22 18:00-19:00>
|
||||
** Read Stardrifter rules
|
||||
SCHEDULED: <2019-03-22 19:00-21:00>
|
||||
** Stardrifter playtest
|
||||
SCHEDULED: <2019-03-25 0900-1300>
|
||||
** Blue Planet @ Mike's
|
||||
SCHEDULED: <2019-03-18 18:00-23:00 +1w>
|
||||
|
||||
and so on...
|
||||
```
|
||||
|
||||
`SCHEDULED` 关键字将该条目标记为你希望收到通知的事件,并且可选的 `:CATEGORY:` 关键字是一个可供你自己使用的任意标记系统(在 Emacs 中,你可以根据类别对条目使用颜色代码)。
|
||||
|
||||
对于重复事件,你可以使用符号(如`+1w`)创建每周事件或 `+2w` 以进行每两周一次的事件,依此类推。
|
||||
|
||||
所有可用于 Org 模式的花哨标记都[记录于文档][10],所以不要犹豫,找到更多技巧来让它满足你的需求。
|
||||
|
||||
### 放进 Git
|
||||
|
||||
如果没有 Git,你的 Org 模式的日程安排只不过是本地计算机上的文件。这是 21 世纪,所以你至少需要可以在手机上使用你的日历,即便不是在你所有的个人电脑上。你可以使用 Git 为自己和他人发布日历。
|
||||
|
||||
首先,为 `.org` 文件创建一个目录。我将我的存储在 `~/cal` 中。
|
||||
|
||||
```
|
||||
$ mkdir ~/cal
|
||||
```
|
||||
|
||||
转到你的目录并使其成为 Git 存储库:
|
||||
|
||||
```
|
||||
$ cd cal
|
||||
$ git init
|
||||
```
|
||||
|
||||
将 `.org` 文件移动到你本地的 Git 存储库。在实践中,我为每个类别维护一个 `.org` 文件。
|
||||
|
||||
```
|
||||
$ mv ~/*.org ~/cal
|
||||
$ ls
|
||||
Game.org Meal.org Seth.org Work.org
|
||||
```
|
||||
|
||||
暂存并提交你的文件:
|
||||
|
||||
```
|
||||
$ git add *.org
|
||||
$ git commit -m 'cal init'
|
||||
```
|
||||
|
||||
### 创建一个 Git 远程源
|
||||
|
||||
要在任何地方提供日历,你必须在互联网上拥有 Git 存储库。你的日历是纯文本,因此任何 Git 存储库都可以。你可以将日历放在 [GitLab][11] 或任何其他公共 Git 托管服务(甚至是专有服务)上,只要你的主机允许,你甚至可以将该存储库标记为私有库。如果你不想将日历发布到你无法控制的服务器,则可以自行托管 Git 存储库,或者为单个用户使用裸存储库,或者使用 [Gitolite][12] 或 [Gitea][13] 等前端服务。
|
||||
|
||||
为了简单起见,我将假设一个自托管的 Git 裸存储库。你可以使用 Git 命令在任何具有 SSH 访问权限的服务器上创建一个远程裸存储库:
|
||||
|
||||
```
|
||||
$ ssh -p 22122 [seth@example.com][14]
|
||||
[remote]$ mkdir cal.git
|
||||
[remote]$ cd cal.git
|
||||
[remote]$ git init --bare
|
||||
[remote]$ exit
|
||||
```
|
||||
|
||||
这个裸存储库可以作为你日历在互联网上的家。
|
||||
|
||||
将其设置为本地 Git 存储库(在你的计算机上,而不是你的服务器上)的远程源:
|
||||
|
||||
```
|
||||
$ git remote add origin seth@example.com:/home/seth/cal.git
|
||||
```
|
||||
|
||||
然后推送你的日历到该服务器:
|
||||
|
||||
```
|
||||
$ git push -u origin HEAD
|
||||
```
|
||||
|
||||
将你的日历放在 Git 存储库中,就可以在任何运行 Git 的设备上使用它。这意味着你可以对计划进行更新和更改,并将更改推送到上游,以便在任何地方进行更新。
|
||||
|
||||
我使用这种方法使我的日历在我的工作笔记本电脑和家庭工作站之间保持同步。由于我每天大部分时间都在使用 Emacs,因此能够在 Emacs 中查看和编辑我的日历是一个很大的便利。对于大多数使用移动设备的人来说也是如此,因此下一步是在移动设备上设置 Org 模式的日历系统。
|
||||
|
||||
### 移动设备上的 Git
|
||||
|
||||
由于你的日历数据是纯文本的,严格来说,你可以在任何可以读取文本文件的设备上“使用”它。这是这个系统之美的一部分;你永远不会缺少原始数据。但是,要按照你希望的现代日历的工作方式将日历集成到移动设备上,你需要两个组件:移动设备上的 Git 客户端和 Org 模式查看器。
|
||||
|
||||
#### 移动设备上的 Git 客户端
|
||||
|
||||
[MGit][15] 是 Android 上的优秀 Git 客户端。同样,iOS 也有 Git 客户端。
|
||||
|
||||
一旦安装了 MGit(或类似的 Git 客户端),你必须克隆日历存储库,以便在你的手机上有副本。要从移动设备访问服务器,必须设置 SSH 密钥进行身份验证。MGit 可以为你生成和存储密钥,你必须将其添加到服务器的 `~/.ssh/authorized_keys` 文件或托管的 Git 的帐户设置中的 SSH 密钥中。
|
||||
|
||||
你必须手动执行此操作。MGit 没有登录你的服务器或托管的 Git 帐户的界面。如果你不这样做,你的移动设备将无法访问你的服务器以访问你的日历数据。
|
||||
|
||||
我是通过将我在 MGit 中生成的密钥文件通过 [KDE Connect][16] 复制到我的笔记本电脑来实现的(但你可以通过蓝牙、SD 卡读卡器或 USB 电缆进行相同操作,具体取决于你访问手机上的数据的首选方法)。 我用这个命令将密钥(一个名为 `calkey` 的文件)复制到我的服务器:
|
||||
|
||||
```
|
||||
$ cat calkey | ssh seth@example.com "cat >> /home/seth/.ssh/authorized_keys"
|
||||
```
|
||||
|
||||
你可能有不同的方法,但如果你曾经将服务器设置为无密码登录,这是完全相同的过程。如果你使用的是 GitLab 等托管的 Git 服务,则必须将密钥文件的内容复制并粘贴到用户帐户的 SSH 密钥面板中。
|
||||
|
||||
![Adding key file data to GitLab][17]
|
||||
|
||||
完成后,你的移动设备可以向你的服务器授权,但仍需要知道在哪里查找你的日历数据。不同的应用程序可能使用不同的表示法,但 MGit 使用普通的旧式 Git-over-SSH。这意味着如果你使用的是非标准 SSH 端口,则必须指定要使用的 SSH 端口:
|
||||
|
||||
```
|
||||
$ git clone ssh://seth@example.com:22122//home/seth/git/cal.git
|
||||
```
|
||||
|
||||
![Specifying SSH port in MGit][18]
|
||||
|
||||
如果你使用其他应用程序,它可能会使用不同的语法,允许你在特殊字段中提供端口,或删除 `ssh://` 前缀。如果遇到问题,请参阅应用程序文档。
|
||||
|
||||
将存储库克隆到手机。
|
||||
|
||||
![Cloned repositories][19]
|
||||
|
||||
很少有 Git 应用程序设置为自动更新存储库。有一些应用程序可以用来自动拉取,或者你可以设置 Git 钩子来推送服务器的更新 —— 但我不会在这里讨论这些。目前,在对日历进行更新后,请务必在 MGit 中手动提取新更改(或者如果在手机上更改了事件,请将更改推送到服务器)。
|
||||
|
||||
![MGit push/pull settings][20]
|
||||
|
||||
#### 移动设备上的日历
|
||||
|
||||
有一些应用程序可以为移动设备上的 Org 模式提供前端。[Orgzly][21] 是一个很棒的开源 Android 应用程序,它为 Org 模式的从 Agenda 模式到 TODO 列表的大多数功能提供了一个界面。安装并启动它。
|
||||
|
||||
从主菜单中,选择“设置同步存储库”,然后选择包含日历文件的目录(即,从服务器克隆的 Git 存储库)。
|
||||
|
||||
给 Orgzly 一点时间来导入数据,然后使用 Orgzly 的[汉堡包][22]菜单选择日程视图。
|
||||
|
||||
![Orgzly's agenda view][23]
|
||||
|
||||
在 Orgzly 的“设置提醒”菜单中,你可以选择在手机上触发通知的事件类型。你可以获得 `SCHEDULED` 任务,`DEADLINE` 任务或任何分配了事件时间的任何通知。如果你将手机用作任务管理器,那么你将永远不会错过 Org 模式和 Orgzly 的活动。
|
||||
|
||||
![Orgzly notification][24]
|
||||
|
||||
Orgzly 不仅仅是一个解析器。你可以编辑和更新事件,甚至标记事件为 `DONE`。
|
||||
|
||||
![Orgzly to-do list][25]
|
||||
|
||||
### 专为你而设计
|
||||
|
||||
关于使用 Org 模式和 Git 的重要一点是,这两个应用程序都非常灵活,并且你可以自定义它们的工作方式和内容,以便它们能够适应你的需求。如果本文中的内容是对你如何组织生活或管理每周时间表的冒犯,但你喜欢此提案提供的其他部分,那么请丢弃你不喜欢的部分。如果需要,你可以在 Emacs 中使用 Org 模式,或者你可以将其用作日历标记。你可以将手机设置为在一天结束时从计算机上拉取 Git 数据,而不是从互联网上的服务器上,或者你可以将计算机配置为在手机插入时同步日历,或者你可以每天管理它,就像你把你工作日所需的所有东西都装到你的手机上一样。这取决于你,而这是关于 Git、Org 模式和开源的最重要的事情。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/calendar-git
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/web-design-monitor-website.png?itok=yUK7_qR0 (website design image)
|
||||
[2]: https://git-scm.com/
|
||||
[3]: https://tools.ietf.org/html/rfc4791
|
||||
[4]: http://nextcloud.com
|
||||
[5]: https://github.com/pimutils/khal
|
||||
[6]: https://plaintextproject.online/
|
||||
[7]: https://orgmode.org
|
||||
[8]: https://opensource.com/article/19/1/productivity-tool-org-mode
|
||||
[9]: https://commonmark.org/
|
||||
[10]: https://orgmode.org/manual/
|
||||
[11]: http://gitlab.com
|
||||
[12]: http://gitolite.com/gitolite/index.html
|
||||
[13]: https://gitea.io/en-us/
|
||||
[14]: mailto:seth@example.com
|
||||
[15]: https://f-droid.org/en/packages/com.manichord.mgit
|
||||
[16]: https://community.kde.org/KDEConnect
|
||||
[17]: https://opensource.com/sites/default/files/uploads/gitlab-add-key.jpg (Adding key file data to GitLab)
|
||||
[18]: https://opensource.com/sites/default/files/uploads/mgit-0.jpg (Specifying SSH port in MGit)
|
||||
[19]: https://opensource.com/sites/default/files/uploads/mgit-1.jpg (Cloned repositories)
|
||||
[20]: https://opensource.com/sites/default/files/uploads/mgit-2.jpg (MGit push/pull settings)
|
||||
[21]: https://f-droid.org/en/packages/com.orgzly/
|
||||
[22]: https://en.wikipedia.org/wiki/Hamburger_button
|
||||
[23]: https://opensource.com/sites/default/files/uploads/orgzly-agenda.jpg (Orgzly's agenda view)
|
||||
[24]: https://opensource.com/sites/default/files/uploads/orgzly-cal-notify.jpg (Orgzly notification)
|
||||
[25]: https://opensource.com/sites/default/files/uploads/orgzly-cal-todo.jpg (Orgzly to-do list)
|
144
published/20190403 Use Git as the backend for chat.md
Normal file
144
published/20190403 Use Git as the backend for chat.md
Normal file
@ -0,0 +1,144 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11342-1.html)
|
||||
[#]: subject: (Use Git as the backend for chat)
|
||||
[#]: via: (https://opensource.com/article/19/4/git-based-chat)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
用 Git 作为聊天应用的后端
|
||||
======
|
||||
|
||||
> GIC 是一个聊天应用程序的原型,展示了一种使用 Git 的新方法。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201909/15/100905euzi3l5xgslsgx7i.png)
|
||||
|
||||
[Git][2] 是一个少有的能将如此多的现代计算封装到一个程序之中的应用程序,它可以用作许多其他应用程序的计算引擎。虽然它以跟踪软件开发中的源代码更改而闻名,但它还有许多其他用途,可以让你的生活更轻松、更有条理。在这个 Git 系列中,我们将分享七种鲜为人知的使用 Git 的方法。
|
||||
|
||||
今天我们来看看 GIC,它是一个基于 Git 的聊天应用。
|
||||
|
||||
### 初识 GIC
|
||||
|
||||
虽然 Git 的作者们可能期望会为 Git 创建前端,但毫无疑问他们从未预料到 Git 会成为某种后端,如聊天客户端的后端。然而,这正是开发人员 Ephi Gabay 用他的实验性的概念验证应用 [GIC][3] 所做的事情:用 [Node.js][4] 编写的聊天客户端,使用 Git 作为其后端数据库。
|
||||
|
||||
GIC 并没有打算用于生产用途。这纯粹是一种编程练习,但它证明了开源技术的灵活性。令人惊讶的是,除了 Node 库和 Git 本身,该客户端只包含 300 行代码。这是这个聊天客户端和开源所反映出来的最好的地方之一:建立在现有工作基础上的能力。眼见为实,你应该自己亲自来了解一下 GIC。
|
||||
|
||||
### 架设起来
|
||||
|
||||
GIC 使用 Git 作为引擎,因此你需要一个空的 Git 存储库为聊天室和记录器提供服务。存储库可以托管在任何地方,只要你和需要访问聊天服务的人可以访问该存储库就行。例如,你可以在 GitLab 等免费 Git 托管服务上设置 Git 存储库,并授予聊天用户对该 Git 存储库的贡献者访问权限。(他们必须能够提交到存储库,因为每个聊天消息都是一个文本的提交。)
|
||||
|
||||
如果你自己托管,请创建一个中心化的裸存储库。聊天中的每个用户必须在裸存储库所在的服务器上拥有一个帐户。你可以使用如 [Gitolite][5] 或 [Gitea][6] 这样的 Git 托管软件创建特定于 Git 的帐户,或者你可以在服务器上为他们提供个人用户帐户,可以使用 `git-shell` 来限制他们只能访问 Git。
|
||||
|
||||
自托管实例的性能最好。无论你是自己托管还是使用托管服务,你创建的 Git 存储库都必须具有一个活跃分支,否则 GIC 将无法在用户聊天时进行提交,因为没有 Git HEAD。确保分支初始化和活跃的最简单方法是在创建存储库时提交 `README` 或许可证文件。如果你没有这样做,你可以在事后创建并提交一个:
|
||||
|
||||
```
|
||||
$ echo "chat logs" > README
|
||||
$ git add README
|
||||
$ git commit -m 'just creating a HEAD ref'
|
||||
$ git push -u origin HEAD
|
||||
```
|
||||
|
||||
### 安装 GIC
|
||||
|
||||
由于 GIC 基于 Git 并使用 Node.js 编写,因此必须首先安装 Git、Node.js 和 Node 包管理器npm(它应该与 Node 捆绑在一起)。安装它们的命令因 Linux 或 BSD 发行版而异,这是 Fedora 上的一个示例命令:
|
||||
|
||||
```
|
||||
$ sudo dnf install git nodejs
|
||||
```
|
||||
|
||||
如果你没有运行 Linux 或 BSD,请按照 [git-scm.com][7] 和 [nodejs.org][8] 上的安装说明进行操作。
|
||||
|
||||
因此,GIC 没有安装过程。每个用户(在此示例中为 Alice 和 Bob)必须将存储库克隆到其硬盘驱动器:
|
||||
|
||||
```
|
||||
$ git clone https://github.com/ephigabay/GIC GIC
|
||||
```
|
||||
|
||||
将目录更改为 GIC 目录并使用 `npm` 安装 Node.js 依赖项:
|
||||
|
||||
```
|
||||
$ cd GIC
|
||||
$ npm install
|
||||
```
|
||||
|
||||
等待 Node 模块下载并安装。
|
||||
|
||||
### 配置 GIC
|
||||
|
||||
GIC 唯一需要的配置是 Git 聊天存储库的位置。编辑 `config.js` 文件:
|
||||
|
||||
```
|
||||
module.exports = {
|
||||
gitRepo: 'seth@example.com:/home/gitchat/chatdemo.git',
|
||||
messageCheckInterval: 500,
|
||||
branchesCheckInterval: 5000
|
||||
};
|
||||
```
|
||||
|
||||
在尝试 GIC 之前测试你与 Git 存储库的连接,以确保你的配置是正确的:
|
||||
|
||||
```
|
||||
$ git clone --quiet seth@example.com:/home/gitchat/chatdemo.git > /dev/null
|
||||
```
|
||||
|
||||
假设你没有收到任何错误,就可以开始聊天了。
|
||||
|
||||
### 用 Git 聊天
|
||||
|
||||
在 GIC 目录中启动聊天客户端:
|
||||
|
||||
```
|
||||
$ npm start
|
||||
```
|
||||
|
||||
客户端首次启动时,必须克隆聊天存储库。由于它几乎是一个空的存储库,因此不会花费很长时间。输入你的消息,然后按回车键发送消息。
|
||||
|
||||
![GIC][10]
|
||||
|
||||
*基于 Git 的聊天客户端。 他们接下来会怎么想?*
|
||||
|
||||
正如问候消息所说,Git 中的分支在 GIC 中就是聊天室或频道。无法在 GIC 的 UI 中创建新分支,但如果你在另一个终端会话或 Web UI 中创建一个分支,它将立即显示在 GIC 中。将一些 IRC 式的命令加到 GIC 中并不需要太多工作。
|
||||
|
||||
聊了一会儿之后,可以看看你的 Git 存储库。由于聊天发生在 Git 中,因此存储库本身也是聊天日志:
|
||||
|
||||
```
|
||||
$ git log --pretty=format:"%p %cn %s"
|
||||
4387984 Seth Kenlon Hey Chani, did you submit a talk for All Things Open this year?
|
||||
36369bb Chani No I didn't get a chance. Did you?
|
||||
[...]
|
||||
```
|
||||
|
||||
### 退出 GIC
|
||||
|
||||
Vim 以来,还没有一个应用程序像 GIC 那么难以退出。你看,没有办法停止 GIC。它会一直运行,直到它被杀死。当你准备停止 GIC 时,打开另一个终端选项卡或窗口并发出以下命令:
|
||||
|
||||
```
|
||||
$ kill `pgrep npm`
|
||||
```
|
||||
|
||||
GIC 是一个新奇的事物。这是一个很好的例子,说明开源生态系统如何鼓励和促进创造力和探索,并挑战我们从不同角度审视应用程序。尝试下 GIC,也许它会给你一些思路。至少,它可以让你与 Git 度过一个下午。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/git-based-chat
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/talk_chat_team_mobile_desktop.png?itok=d7sRtKfQ (Team communication, chat)
|
||||
[2]: https://git-scm.com/
|
||||
[3]: https://github.com/ephigabay/GIC
|
||||
[4]: https://nodejs.org/en/
|
||||
[5]: http://gitolite.com
|
||||
[6]: http://gitea.io
|
||||
[7]: http://git-scm.com
|
||||
[8]: http://nodejs.org
|
||||
[9]: mailto:seth@example.com
|
||||
[10]: https://opensource.com/sites/default/files/uploads/gic.jpg (GIC)
|
@ -0,0 +1,345 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (LuuMing)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11307-1.html)
|
||||
[#]: subject: (A beginner's guide to building DevOps pipelines with open source tools)
|
||||
[#]: via: (https://opensource.com/article/19/4/devops-pipeline)
|
||||
[#]: author: (Bryant Son https://opensource.com/users/brson/users/milindsingh/users/milindsingh/users/dscripter)
|
||||
|
||||
使用开源工具构建 DevOps 流水线的初学者指南
|
||||
======
|
||||
|
||||
> 如果你是 DevOps 新人,请查看这 5 个步骤来构建你的第一个 DevOps 流水线。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201909/05/060323yizmqwn43zwy13za.jpg)
|
||||
|
||||
DevOps 已经成为解决软件开发过程中出现的缓慢、孤立或者其他故障的默认方式。但是当你刚接触 DevOps 并且不确定从哪开始时,就意义不大了。本文探索了什么是 DevOps 流水线并且提供了创建它的 5 个步骤。尽管这个教程并不全面,但可以给你以后上手和扩展打下基础。首先,插入一个小故事。
|
||||
|
||||
### 我的 DevOps 之旅
|
||||
|
||||
我曾经在花旗集团的云小组工作,开发<ruby><rt>Infrastructure as a Service</rt>基础设施即服务</ruby>网页应用来管理花旗的云基础设施,但我经常对研究如何让开发流水线更加高效以及如何带给团队积极的文化感兴趣。我在 Greg Lavender 推荐的书中找到了答案。Greg Lavender 是花旗的云架构和基础设施工程(即 [Phoenix 项目][2])的 CTO。这本书尽管解释的是 DevOps 原理,但它读起来像一本小说。
|
||||
|
||||
书后面的一张表展示了不同公司部署在发布环境上的频率:
|
||||
|
||||
公司 | 部署频率
|
||||
---|---
|
||||
Amazon | 23,000 次/天
|
||||
Google | 5,500 次/天
|
||||
Netflix | 500 次/天
|
||||
Facebook | 1 次/天
|
||||
Twitter | 3 次/周
|
||||
典型企业 | 1 次/9 个月
|
||||
|
||||
Amazon、Google、Netflix 怎么能做到如此之频繁?那是因为这些公司弄清楚了如何去实现一个近乎完美的 DevOps 流水线。
|
||||
|
||||
但在花旗实施 DevOps 之前,情况并非如此。那时候,我的团队拥有不同<ruby>构建阶段<rt>stage</rt></ruby>的环境,但是在开发服务器上的部署非常手工。所有的开发人员都只能访问一个基于 IBM WebSphere Application 社区版的开发环境服务器。问题是当多个用户同时尝试部署时,服务器就会宕机,因此开发人员在部署时就得互相通知,这一点相当痛苦。此外,还存在代码测试覆盖率低、手动部署过程繁琐以及无法根据定义的任务或用户需求跟踪代码部署的问题。
|
||||
|
||||
我意识到必须做些事情,同时也找到了一个有同样感受的同事。我们决定合作去构建一个初始的 DevOps 流水线 —— 他设置了一个虚拟机和一个 Tomcat 服务器,而我则架设了 Jenkins,集成了 Atlassian Jira、BitBucket 和代码覆盖率测试。这个业余项目非常成功:我们近乎全自动化了开发流水线,并在开发服务器上实现了几乎 100% 的正常运行,我们可以追踪并改进代码覆盖率测试,并且 Git 分支能够与部署任务和 jira 任务关联在一起。此外,大多数用来构建 DevOps 所使用的工具都是开源的。
|
||||
|
||||
现在我意识到了我们的 DevOps 流水线是多么的原始,因为我们没有利用像 Jenkins 文件或 Ansible 这样的高级设置。然而,这个简单的过程运作良好,这也许是因为 [Pareto][3] 原则(也被称作 80/20 法则)。
|
||||
|
||||
### DevOps 和 CI/CD 流水线的简要介绍
|
||||
|
||||
如果你问一些人,“什么是 DevOps?”,你或许会得到一些不同的回答。DevOps,就像敏捷,已经发展到涵盖着诸多不同的学科,但大多数人至少会同意这些:DevOps 是一个软件开发实践或一个<ruby>软件开发生命周期<rt>software development lifecycle</rt></ruby>(SDLC),并且它的核心原则是一种文化上的变革 —— 开发人员与非开发人员呼吸着同一片天空的气息,之前手工的事情变得自动化;每个人做着自己擅长的事;同一时间的部署变得更加频繁;吞吐量提升;灵活度增加。
|
||||
|
||||
虽然拥有正确的软件工具并非实现 DevOps 环境所需的唯一东西,但一些工具却是必要的。最关键的一个便是持续集成和持续部署(CI/CD)。在流水线环境中,拥有不同的构建阶段(例如:DEV、INT、TST、QA、UAT、STG、PROD),手动的工作能实现自动化,开发人员可以实现高质量的代码,灵活而且大量部署。
|
||||
|
||||
这篇文章描述了一个构建 DevOps 流水线的五步方法,就像下图所展示的那样,使用开源的工具实现。
|
||||
|
||||
![Complete DevOps pipeline][4]
|
||||
|
||||
闲话少说,让我们开始吧。
|
||||
|
||||
### 第一步:CI/CD 框架
|
||||
|
||||
首先你需要的是一个 CI/CD 工具,Jenkins,是一个基于 Java 的 MIT 许可下的开源 CI/CD 工具,它是推广 DevOps 运动的工具,并已成为了<ruby>事实标准<rt>de facto standard</rt><ruby>。
|
||||
|
||||
所以,什么是 Jenkins?想象它是一种神奇的万能遥控,能够和许多不同的服务器和工具打交道,并且能够将它们统一安排起来。就本身而言,像 Jenkins 这样的 CI/CD 工具本身是没有用的,但随着接入不同的工具与服务器时会变得非常强大。
|
||||
|
||||
Jenkins 仅是众多构建 DevOps 流水线的开源 CI/CD 工具之一。
|
||||
|
||||
名称 | 许可证
|
||||
---|---
|
||||
[Jenkins][5] | Creative Commons 和 MIT
|
||||
[Travis CI][6] | MIT
|
||||
[CruiseControl][7] | BSD
|
||||
[Buildbot][8] | GPL
|
||||
[Apache Gump][9] | Apache 2.0
|
||||
[Cabie][10] | GNU
|
||||
|
||||
下面就是使用 CI/CD 工具时 DevOps 看起来的样子。
|
||||
|
||||
![CI/CD tool][11]
|
||||
|
||||
你的 CI/CD 工具在本地主机上运行,但目前你还不能够做些别的。让我们紧随 DevOps 之旅的脚步。
|
||||
|
||||
### 第二步:源代码控制管理
|
||||
|
||||
验证 CI/CD 工具可以执行某些魔术的最佳(也可能是最简单)方法是与源代码控制管理(SCM)工具集成。为什么需要源代码控制?假设你在开发一个应用。无论你什么时候构建应用,无论你使用的是 Java、Python、C++、Go、Ruby、JavaScript 或任意一种语言,你都在编程。你所编写的程序代码称为源代码。在一开始,特别是只有你一个人工作时,将所有的东西放进本地文件夹里或许都是可以的。但是当项目变得庞大并且邀请其他人协作后,你就需要一种方式来避免共享代码修改时的合并冲突。你也需要一种方式来恢复一个之前的版本——备份、复制并粘贴的方式已经过时了。你(和你的团队)想要更好的解决方式。
|
||||
|
||||
这就是 SCM 变得不可或缺的原因。SCM 工具通过在仓库中保存代码来帮助进行版本控制与多人协作。
|
||||
|
||||
尽管这里有许多 SCM 工具,但 Git 是最标准恰当的。我极力推荐使用 Git,但如果你喜欢这里仍有其他的开源工具。
|
||||
|
||||
名称 | 许可证
|
||||
---|---
|
||||
[Git][12] | GPLv2 & LGPL v2.1
|
||||
[Subversion][13] | Apache 2.0
|
||||
[Concurrent Versions System][14] (CVS) | GNU
|
||||
[Vesta][15] | LGPL
|
||||
[Mercurial][16] | GNU GPL v2+
|
||||
|
||||
拥有 SCM 之后,DevOps 流水线看起来就像这样。
|
||||
|
||||
![Source control management][17]
|
||||
|
||||
CI/CD 工具能够自动化进行源代码检入检出以及完成成员之间的协作。还不错吧?但是,如何才能把它变成可工作的应用程序,使得数十亿人来使用并欣赏它呢?
|
||||
|
||||
### 第三步:自动化构建工具
|
||||
|
||||
真棒!现在你可以检出代码并将修改提交到源代码控制,并且可以邀请你的朋友就源代码控制进行协作。但是到目前为止你还没有构建出应用。要想让它成为一个网页应用,必须将其编译并打包成可部署的包或可执行程序(注意,像 JavaScript 或 PHP 这样的解释型编程语言不需要进行编译)。
|
||||
|
||||
于是就引出了自动化构建工具。无论你决定使用哪一款构建工具,它们都有一个共同的目标:将源代码构建成某种想要的格式,并且将清理、编译、测试、部署到某个位置这些任务自动化。构建工具会根据你的编程语言而有不同,但这里有一些通常使用的开源工具值得考虑。
|
||||
|
||||
名称 | 许可证 | 编程语言
|
||||
---|---|---
|
||||
[Maven][18] | Apache 2.0 | Java
|
||||
[Ant][19] | Apache 2.0 | Java
|
||||
[Gradle][20] | Apache 2.0 | Java
|
||||
[Bazel][21] | Apache 2.0 | Java
|
||||
[Make][22] | GNU | N/A
|
||||
[Grunt][23] | MIT | JavaScript
|
||||
[Gulp][24] | MIT | JavaScript
|
||||
[Buildr][25] | Apache | Ruby
|
||||
[Rake][26] | MIT | Ruby
|
||||
[A-A-P][27] | GNU | Python
|
||||
[SCons][28] | MIT | Python
|
||||
[BitBake][29] | GPLv2 | Python
|
||||
[Cake][30] | MIT | C#
|
||||
[ASDF][31] | Expat (MIT) | LISP
|
||||
[Cabal][32] | BSD | Haskell
|
||||
|
||||
太棒了!现在你可以将自动化构建工具的配置文件放进源代码控制管理系统中,并让你的 CI/CD 工具构建它。
|
||||
|
||||
![Build automation tool][33]
|
||||
|
||||
一切都如此美好,对吧?但是在哪里部署它呢?
|
||||
|
||||
### 第四步:网页应用服务器
|
||||
|
||||
到目前为止,你有了一个可执行或可部署的打包文件。对任何真正有用的应用程序来说,它必须提供某种服务或者接口,所以你需要一个容器来发布你的应用。
|
||||
|
||||
对于网页应用,网页应用服务器就是容器。应用程序服务器提供了环境,让可部署包中的编程逻辑能够被检测到、呈现界面,并通过打开套接字为外部世界提供网页服务。在其他环境下你也需要一个 HTTP 服务器(比如虚拟机)来安装服务应用。现在,我假设你将会自己学习这些东西(尽管我会在下面讨论容器)。
|
||||
|
||||
这里有许多开源的网页应用服务器。
|
||||
|
||||
名称 | 协议 | 编程语言
|
||||
---|---|---
|
||||
[Tomcat][34] | Apache 2.0 | Java
|
||||
[Jetty][35] | Apache 2.0 | Java
|
||||
[WildFly][36] | GNU Lesser Public | Java
|
||||
[GlassFish][37] | CDDL & GNU Less Public | Java
|
||||
[Django][38] | 3-Clause BSD | Python
|
||||
[Tornado][39] | Apache 2.0 | Python
|
||||
[Gunicorn][40] | MIT | Python
|
||||
[Python Paste][41] | MIT | Python
|
||||
[Rails][42] | MIT | Ruby
|
||||
[Node.js][43] | MIT | Javascript
|
||||
|
||||
现在 DevOps 流水线差不多能用了,干得好!
|
||||
|
||||
![Web application server][44]
|
||||
|
||||
尽管你可以在这里停下来并进行进一步的集成,但是代码质量对于应用开发者来说是一件非常重要的事情。
|
||||
|
||||
### 第五步:代码覆盖测试
|
||||
|
||||
实现代码测试件可能是另一个麻烦的需求,但是开发者需要尽早地捕捉程序中的所有错误并提升代码质量来保证最终用户满意度。幸运的是,这里有许多开源工具来测试你的代码并提出改善质量的建议。甚至更好的,大部分 CI/CD 工具能够集成这些工具并将测试过程自动化进行。
|
||||
|
||||
代码测试分为两个部分:“代码测试框架”帮助进行编写与运行测试,“代码质量改进工具”帮助提升代码的质量。
|
||||
|
||||
#### 代码测试框架
|
||||
|
||||
名称 | 许可证 | 编程语言
|
||||
---|---|---
|
||||
[JUnit][45] | Eclipse Public License | Java
|
||||
[EasyMock][46] | Apache | Java
|
||||
[Mockito][47] | MIT | Java
|
||||
[PowerMock][48] | Apache 2.0 | Java
|
||||
[Pytest][49] | MIT | Python
|
||||
[Hypothesis][50] | Mozilla | Python
|
||||
[Tox][51] | MIT | Python
|
||||
|
||||
#### 代码质量改进工具
|
||||
|
||||
名称 | 许可证 | 编程语言
|
||||
---|---|---
|
||||
[Cobertura][52] | GNU | Java
|
||||
[CodeCover][53] | Eclipse Public (EPL) | Java
|
||||
[Coverage.py][54] | Apache 2.0 | Python
|
||||
[Emma][55] | Common Public License | Java
|
||||
[JaCoCo][56] | Eclipse Public License | Java
|
||||
[Hypothesis][50] | Mozilla | Python
|
||||
[Tox][51] | MIT | Python
|
||||
[Jasmine][57] | MIT | JavaScript
|
||||
[Karma][58] | MIT | JavaScript
|
||||
[Mocha][59] | MIT | JavaScript
|
||||
[Jest][60] | MIT | JavaScript
|
||||
|
||||
注意,之前提到的大多数工具和框架都是为 Java、Python、JavaScript 写的,因为 C++ 和 C# 是专有编程语言(尽管 GCC 是开源的)。
|
||||
|
||||
现在你已经运用了代码覆盖测试工具,你的 DevOps 流水线应该就像教程开始那幅图中展示的那样了。
|
||||
|
||||
### 可选步骤
|
||||
|
||||
#### 容器
|
||||
|
||||
正如我之前所说,你可以在虚拟机(VM)或服务器上发布你的应用,但是容器是一个更好的解决方法。
|
||||
|
||||
[什么是容器][61]?简要的介绍就是 VM 需要占用操作系统大量的资源,它提升了应用程序的大小,而容器仅仅需要一些库和配置来运行应用程序。显然,VM 仍有重要的用途,但容器对于发布应用(包括应用程序服务器)来说是一个更为轻量的解决方式。
|
||||
|
||||
尽管对于容器来说也有其他的选择,但是 Docker 和 Kubernetes 更为广泛。
|
||||
|
||||
名称 | 许可证
|
||||
---|---
|
||||
[Docker][62] | Apache 2.0
|
||||
[Kubernetes][63] | Apache 2.0
|
||||
|
||||
了解更多信息,请查看 [Opensource.com][64] 上关于 Docker 和 Kubernetes 的其它文章:
|
||||
|
||||
* [什么是 Docker?][65]
|
||||
* [Docker 简介][66]
|
||||
* [什么是 Kubernetes?][67]
|
||||
* [从零开始的 Kubernetes 实践][68]
|
||||
|
||||
#### 中间件自动化工具
|
||||
|
||||
我们的 DevOps 流水线大部分集中在协作构建与部署应用上,但你也可以用 DevOps 工具完成许多其他的事情。其中之一便是利用它实现<ruby>基础设施管理<rt>Infrastructure as Code</rt></ruby>(IaC)工具,这也是熟知的中间件自动化工具。这些工具帮助完成中间件的自动化安装、管理和其他任务。例如,自动化工具可以用正确的配置下拉应用程序,例如网页服务器、数据库和监控工具,并且部署它们到应用服务器上。
|
||||
|
||||
这里有几个开源的中间件自动化工具值得考虑:
|
||||
|
||||
名称 | 许可证
|
||||
---|---
|
||||
[Ansible][69] | GNU Public
|
||||
[SaltStack][70] | Apache 2.0
|
||||
[Chef][71] | Apache 2.0
|
||||
[Puppet][72] | Apache or GPL
|
||||
|
||||
获取更多中间件自动化工具,查看 [Opensource.com][64] 上的其它文章:
|
||||
|
||||
* [Ansible 快速入门指南][73]
|
||||
* [Ansible 自动化部署策略][74]
|
||||
* [配置管理工具 Top 5][75]
|
||||
|
||||
### 之后的发展
|
||||
|
||||
这只是一个完整 DevOps 流水线的冰山一角。从 CI/CD 工具开始并且探索其他可以自动化的东西来使你的团队更加轻松的工作。并且,寻找[开源通讯工具][76]可以帮助你的团队一起工作的更好。
|
||||
|
||||
发现更多见解,这里有一些非常棒的文章来介绍 DevOps :
|
||||
|
||||
* [什么是 DevOps][77]
|
||||
* [掌握 5 件事成为 DevOps 工程师][78]
|
||||
* [所有人的 DevOps][79]
|
||||
* [在 DevOps 中开始使用预测分析][80]
|
||||
|
||||
使用开源 agile 工具来集成 DevOps 也是一个很好的主意:
|
||||
|
||||
* [什么是 agile ?][81]
|
||||
* [4 步成为一个了不起的 agile 开发者][82]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/devops-pipeline
|
||||
|
||||
作者:[Bryant Son][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[LuMing](https://github.com/LuuMing)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/brson/users/milindsingh/users/milindsingh/users/dscripter
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/network_team_career_hand.png?itok=_ztl2lk_ (Shaking hands, networking)
|
||||
[2]: https://www.amazon.com/dp/B078Y98RG8/
|
||||
[3]: https://en.wikipedia.org/wiki/Pareto_principle
|
||||
[4]: https://opensource.com/sites/default/files/uploads/1_finaldevopspipeline.jpg (Complete DevOps pipeline)
|
||||
[5]: https://github.com/jenkinsci/jenkins
|
||||
[6]: https://github.com/travis-ci/travis-ci
|
||||
[7]: http://cruisecontrol.sourceforge.net
|
||||
[8]: https://github.com/buildbot/buildbot
|
||||
[9]: https://gump.apache.org
|
||||
[10]: http://cabie.tigris.org
|
||||
[11]: https://opensource.com/sites/default/files/uploads/2_runningjenkins.jpg (CI/CD tool)
|
||||
[12]: https://git-scm.com
|
||||
[13]: https://subversion.apache.org
|
||||
[14]: http://savannah.nongnu.org/projects/cvs
|
||||
[15]: http://www.vestasys.org
|
||||
[16]: https://www.mercurial-scm.org
|
||||
[17]: https://opensource.com/sites/default/files/uploads/3_sourcecontrolmanagement.jpg (Source control management)
|
||||
[18]: https://maven.apache.org
|
||||
[19]: https://ant.apache.org
|
||||
[20]: https://gradle.org/
|
||||
[21]: https://bazel.build
|
||||
[22]: https://www.gnu.org/software/make
|
||||
[23]: https://gruntjs.com
|
||||
[24]: https://gulpjs.com
|
||||
[25]: http://buildr.apache.org
|
||||
[26]: https://github.com/ruby/rake
|
||||
[27]: http://www.a-a-p.org
|
||||
[28]: https://www.scons.org
|
||||
[29]: https://www.yoctoproject.org/software-item/bitbake
|
||||
[30]: https://github.com/cake-build/cake
|
||||
[31]: https://common-lisp.net/project/asdf
|
||||
[32]: https://www.haskell.org/cabal
|
||||
[33]: https://opensource.com/sites/default/files/uploads/4_buildtools.jpg (Build automation tool)
|
||||
[34]: https://tomcat.apache.org
|
||||
[35]: https://www.eclipse.org/jetty/
|
||||
[36]: http://wildfly.org
|
||||
[37]: https://javaee.github.io/glassfish
|
||||
[38]: https://www.djangoproject.com/
|
||||
[39]: http://www.tornadoweb.org/en/stable
|
||||
[40]: https://gunicorn.org
|
||||
[41]: https://github.com/cdent/paste
|
||||
[42]: https://rubyonrails.org
|
||||
[43]: https://nodejs.org/en
|
||||
[44]: https://opensource.com/sites/default/files/uploads/5_applicationserver.jpg (Web application server)
|
||||
[45]: https://junit.org/junit5
|
||||
[46]: http://easymock.org
|
||||
[47]: https://site.mockito.org
|
||||
[48]: https://github.com/powermock/powermock
|
||||
[49]: https://docs.pytest.org
|
||||
[50]: https://hypothesis.works
|
||||
[51]: https://github.com/tox-dev/tox
|
||||
[52]: http://cobertura.github.io/cobertura
|
||||
[53]: http://codecover.org/
|
||||
[54]: https://github.com/nedbat/coveragepy
|
||||
[55]: http://emma.sourceforge.net
|
||||
[56]: https://github.com/jacoco/jacoco
|
||||
[57]: https://jasmine.github.io
|
||||
[58]: https://github.com/karma-runner/karma
|
||||
[59]: https://github.com/mochajs/mocha
|
||||
[60]: https://jestjs.io
|
||||
[61]: /resources/what-are-linux-containers
|
||||
[62]: https://www.docker.com
|
||||
[63]: https://kubernetes.io
|
||||
[64]: http://Opensource.com
|
||||
[65]: https://opensource.com/resources/what-docker
|
||||
[66]: https://opensource.com/business/15/1/introduction-docker
|
||||
[67]: https://opensource.com/resources/what-is-kubernetes
|
||||
[68]: https://opensource.com/article/17/11/kubernetes-lightning-talk
|
||||
[69]: https://www.ansible.com
|
||||
[70]: https://www.saltstack.com
|
||||
[71]: https://www.chef.io
|
||||
[72]: https://puppet.com
|
||||
[73]: https://opensource.com/article/19/2/quickstart-guide-ansible
|
||||
[74]: https://opensource.com/article/19/1/automating-deployment-strategies-ansible
|
||||
[75]: https://opensource.com/article/18/12/configuration-management-tools
|
||||
[76]: https://opensource.com/alternatives/slack
|
||||
[77]: https://opensource.com/resources/devops
|
||||
[78]: https://opensource.com/article/19/2/master-devops-engineer
|
||||
[79]: https://opensource.com/article/18/11/how-non-engineer-got-devops
|
||||
[80]: https://opensource.com/article/19/1/getting-started-predictive-analytics-devops
|
||||
[81]: https://opensource.com/article/18/10/what-agile
|
||||
[82]: https://opensource.com/article/19/2/steps-agile-developer
|
261
published/20190409 Working with variables on Linux.md
Normal file
261
published/20190409 Working with variables on Linux.md
Normal file
@ -0,0 +1,261 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (MjSeven)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11344-1.html)
|
||||
[#]: subject: (Working with variables on Linux)
|
||||
[#]: via: (https://www.networkworld.com/article/3387154/working-with-variables-on-linux.html#tk.rss_all)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
在 Linux 中使用变量
|
||||
======
|
||||
|
||||
> 变量通常看起来像 `$var` 这样,但它们也有 `$1`、`$*`、`$?` 和 `$$` 这种形式。让我们来看看所有这些 `$` 值可以告诉你什么。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201909/15/105140faf2jzyybubu1d0c.jpg)
|
||||
|
||||
有许多重要的值都存储在 Linux 系统中,我们称为“变量”,但实际上变量有几种类型,并且一些有趣的命令可以帮助你使用它们。在上一篇文章中,我们研究了[环境变量][2]以及它们定义在何处。在本文中,我们来看一看在命令行和脚本中使用的变量。
|
||||
|
||||
### 用户变量
|
||||
|
||||
虽然在命令行中设置变量非常容易,但是有一些有趣的技巧。要设置变量,你只需这样做:
|
||||
|
||||
```
|
||||
$ myvar=11
|
||||
$ myvar2="eleven"
|
||||
```
|
||||
|
||||
要显示这些值,只需这样做:
|
||||
|
||||
```
|
||||
$ echo $myvar
|
||||
11
|
||||
$ echo $myvar2
|
||||
eleven
|
||||
```
|
||||
|
||||
你也可以使用这些变量。例如,要递增一个数字变量,使用以下任意一个命令:
|
||||
|
||||
```
|
||||
$ myvar=$((myvar+1))
|
||||
$ echo $myvar
|
||||
12
|
||||
$ ((myvar=myvar+1))
|
||||
$ echo $myvar
|
||||
13
|
||||
$ ((myvar+=1))
|
||||
$ echo $myvar
|
||||
14
|
||||
$ ((myvar++))
|
||||
$ echo $myvar
|
||||
15
|
||||
$ let "myvar=myvar+1"
|
||||
$ echo $myvar
|
||||
16
|
||||
$ let "myvar+=1"
|
||||
$ echo $myvar
|
||||
17
|
||||
$ let "myvar++"
|
||||
$ echo $myvar
|
||||
18
|
||||
```
|
||||
|
||||
使用其中的一些,你可以增加一个变量的值。例如:
|
||||
|
||||
```
|
||||
$ myvar0=0
|
||||
$ ((myvar0++))
|
||||
$ echo $myvar0
|
||||
1
|
||||
$ ((myvar0+=10))
|
||||
$ echo $myvar0
|
||||
11
|
||||
```
|
||||
|
||||
通过这些选项,你可能会发现它们是容易记忆、使用方便的。
|
||||
|
||||
你也可以*删除*一个变量 -- 这意味着没有定义它。
|
||||
|
||||
```
|
||||
$ unset myvar
|
||||
$ echo $myvar
|
||||
```
|
||||
|
||||
另一个有趣的选项是,你可以设置一个变量并将其设为**只读**。换句话说,变量一旦设置为只读,它的值就不能改变(除非一些非常复杂的命令行魔法才可以)。这意味着你也不能删除它。
|
||||
|
||||
```
|
||||
$ readonly myvar3=1
|
||||
$ echo $myvar3
|
||||
1
|
||||
$ ((myvar3++))
|
||||
-bash: myvar3: readonly variable
|
||||
$ unset myvar3
|
||||
-bash: unset: myvar3: cannot unset: readonly variable
|
||||
```
|
||||
|
||||
你可以使用这些设置和递增选项中来赋值和操作脚本中的变量,但也有一些非常有用的*内部变量*可以用于在脚本中。注意,你无法重新赋值或增加它们的值。
|
||||
|
||||
### 内部变量
|
||||
|
||||
在脚本中可以使用很多变量来计算参数并显示有关脚本本身的信息。
|
||||
|
||||
* `$1`、`$2`、`$3` 等表示脚本的第一个、第二个、第三个等参数。
|
||||
* `$#` 表示参数的数量。
|
||||
* `$*` 表示所有参数。
|
||||
* `$0` 表示脚本的名称。
|
||||
* `$?` 表示先前运行的命令的返回码(0 代表成功)。
|
||||
* `$$` 显示脚本的进程 ID。
|
||||
* `$PPID` 显示 shell 的进程 ID(脚本的父进程)。
|
||||
|
||||
其中一些变量也适用于命令行,但显示相关信息:
|
||||
|
||||
* `$0` 显示你正在使用的 shell 的名称(例如,-bash)。
|
||||
* `$$` 显示 shell 的进程 ID。
|
||||
* `$PPID` 显示 shell 的父进程的进程 ID(对我来说,是 sshd)。
|
||||
|
||||
为了查看它们的结果,如果我们将所有这些变量都放入一个脚本中,比如:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
echo $0
|
||||
echo $1
|
||||
echo $2
|
||||
echo $#
|
||||
echo $*
|
||||
echo $?
|
||||
echo $$
|
||||
echo $PPID
|
||||
```
|
||||
|
||||
当我们调用这个脚本时,我们会看到如下内容:
|
||||
|
||||
```
|
||||
$ tryme one two three
|
||||
/home/shs/bin/tryme <== 脚本名称
|
||||
one <== 第一个参数
|
||||
two <== 第二个参数
|
||||
3 <== 参数的个数
|
||||
one two three <== 所有的参数
|
||||
0 <== 上一条 echo 命令的返回码
|
||||
10410 <== 脚本的进程 ID
|
||||
10109 <== 父进程 ID
|
||||
```
|
||||
|
||||
如果我们在脚本运行完毕后检查 shell 的进程 ID,我们可以看到它与脚本中显示的 PPID 相匹配:
|
||||
|
||||
```
|
||||
$ echo $$
|
||||
10109 <== shell 的进程 ID
|
||||
```
|
||||
|
||||
当然,比起简单地显示它们的值,更有用的方式是使用它们。我们来看一看它们可能的用处。
|
||||
|
||||
检查是否已提供参数:
|
||||
|
||||
```
|
||||
if [ $# == 0 ]; then
|
||||
echo "$0 filename"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
检查特定进程是否正在运行:
|
||||
|
||||
```
|
||||
ps -ef | grep apache2 > /dev/null
|
||||
if [ $? != 0 ]; then
|
||||
echo Apache is not running
|
||||
exit
|
||||
fi
|
||||
```
|
||||
|
||||
在尝试访问文件之前验证文件是否存在:
|
||||
|
||||
```
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "Usage: $0 lines filename"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f $2 ]; then
|
||||
echo "Error: File $2 not found"
|
||||
exit 2
|
||||
else
|
||||
head -$1 $2
|
||||
fi
|
||||
```
|
||||
|
||||
在下面的小脚本中,我们检查是否提供了正确数量的参数、第一个参数是否为数字,以及第二个参数代表的文件是否存在。
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "Usage: $0 lines filename"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $1 != [0-9]* ]]; then
|
||||
echo "Error: $1 is not numeric"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ ! -f $2 ]; then
|
||||
echo "Error: File $2 not found"
|
||||
exit 3
|
||||
else
|
||||
echo top of file
|
||||
head -$1 $2
|
||||
fi
|
||||
```
|
||||
|
||||
### 重命名变量
|
||||
|
||||
在编写复杂的脚本时,为脚本的参数指定名称通常很有用,而不是继续将它们称为 `$1`、`$2` 等。等到第 35 行,阅读你脚本的人可能已经忘了 `$2` 表示什么。如果你将一个重要参数的值赋给 `$filename` 或 `$numlines`,那么他就不容易忘记。
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "Usage: $0 lines filename"
|
||||
exit 1
|
||||
else
|
||||
numlines=$1
|
||||
filename=$2
|
||||
fi
|
||||
|
||||
if [[ $numlines != [0-9]* ]]; then
|
||||
echo "Error: $numlines is not numeric"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ ! -f $ filename]; then
|
||||
echo "Error: File $filename not found"
|
||||
exit 3
|
||||
else
|
||||
echo top of file
|
||||
head -$numlines $filename
|
||||
fi
|
||||
```
|
||||
|
||||
当然,这个示例脚本只是运行 `head` 命令来显示文件中的前 x 行,但它的目的是显示如何在脚本中使用内部参数来帮助确保脚本运行良好,或在失败时清晰地知道失败原因。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3387154/working-with-variables-on-linux.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2019/04/variable-key-keyboard-100793080-large.jpg
|
||||
[2]: https://linux.cn/article-10916-1.html
|
||||
[3]: https://www.youtube.com/playlist?list=PL7D2RMSmRO9J8OTpjFECi8DJiTQdd4hua
|
||||
[4]: https://www.facebook.com/NetworkWorld/
|
||||
[5]: https://www.linkedin.com/company/network-world
|
@ -0,0 +1,229 @@
|
||||
ddgr:一个从终端搜索 DuckDuckGo 的命令行工具
|
||||
======
|
||||
|
||||
在 Linux 中,Bash 技巧非常棒,它使 Linux 中的一切成为可能。
|
||||
|
||||
对于开发人员或系统管理员来说,它真的很管用,因为他们大部分时间都在使用终端。你知道他们为什么喜欢这种技巧吗?
|
||||
|
||||
因为这些技巧可以提高他们的工作效率,也能使他们工作更快。
|
||||
|
||||
### 什么是 ddgr
|
||||
|
||||
[ddgr][1] 是一个命令行实用程序,用于从终端搜索 DuckDuckGo。如果设置了 `BROWSER` 环境变量,ddgr 可以在几个基于文本的浏览器中开箱即用。
|
||||
|
||||
确保你的系统安装了任何一个基于文本的浏览器。你可能知道 [googler][2],它允许用户从 Linux 命令行进行 Google 搜索。
|
||||
|
||||
它在命令行用户中非常受欢迎,他们期望对隐私敏感的 DuckDuckGo 也有类似的实用程序,这就是 `ddgr` 出现的原因。
|
||||
|
||||
与 Web 界面不同,你可以指定每页要查看的搜索结果数。
|
||||
|
||||
**建议阅读:**
|
||||
|
||||
- [Googler – 从 Linux 命令行搜索 Google][2]
|
||||
- [Buku – Linux 中一个强大的命令行书签管理器][3]
|
||||
- [SoCLI – 从终端搜索和浏览 StackOverflow 的简单方法][4]
|
||||
- [RTV(Reddit 终端查看器)- 一个简单的 Reddit 终端查看器][5]
|
||||
|
||||
### 什么是 DuckDuckGo
|
||||
|
||||
DDG 即 DuckDuckGo。DuckDuckGo(DDG)是一个真正保护用户搜索和隐私的互联网搜索引擎。它没有过滤用户的个性化搜索结果,对于给定的搜索词,它会向所有用户显示相同的搜索结果。
|
||||
|
||||
大多数用户更喜欢谷歌搜索引擎,但是如果你真的担心隐私,那么你可以放心地使用 DuckDuckGo。
|
||||
|
||||
### ddgr 特性
|
||||
|
||||
* 快速且干净(没有广告、多余的 URL 或杂物参数),自定义颜色
|
||||
* 旨在以最小的空间提供最高的可读性
|
||||
* 指定每页显示的搜索结果数
|
||||
* 可以在 omniprompt 中导航结果,在浏览器中打开 URL
|
||||
* 用于 Bash、Zsh 和 Fish 的搜索和选项补完脚本
|
||||
* 支持 DuckDuckGo Bang(带有自动补完)
|
||||
* 直接在浏览器中打开第一个结果(如同 “I’m Feeling Ducky”)
|
||||
* 不间断搜索:无需退出即可在 omniprompt 中触发新搜索
|
||||
* 关键字支持(例如:filetype:mime、site:somesite.com)
|
||||
* 按时间、指定区域搜索,禁用安全搜索
|
||||
* 支持 HTTPS 代理,支持 Do Not Track,可选择禁用用户代理字符串
|
||||
* 支持自定义 URL 处理程序脚本或命令行实用程序
|
||||
* 全面的文档,man 页面有方便的使用示例
|
||||
* 最小的依赖关系
|
||||
|
||||
### 需要条件
|
||||
|
||||
`ddgr` 需要 Python 3.4 或更高版本。因此,确保你的系统应具有 Python 3.4 或更高版本。
|
||||
|
||||
```
|
||||
$ python3 --version
|
||||
Python 3.6.3
|
||||
```
|
||||
|
||||
### 如何在 Linux 中安装 ddgr
|
||||
|
||||
我们可以根据发行版使用以下命令轻松安装 `ddgr`。
|
||||
|
||||
对于 Fedora ,使用 [DNF 命令][6]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
# dnf install ddgr
|
||||
```
|
||||
|
||||
或者我们可以使用 [SNAP 命令][7]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
# snap install ddgr
|
||||
```
|
||||
|
||||
对于 LinuxMint/Ubuntu,使用 [APT-GET 命令][8] 或 [APT 命令][9]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
$ sudo add-apt-repository ppa:twodopeshaggy/jarun
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install ddgr
|
||||
```
|
||||
|
||||
对于基于 Arch Linux 的系统,使用 [Yaourt 命令][10]或 [Packer 命令][11]从 AUR 仓库安装 `ddgr`。
|
||||
|
||||
```
|
||||
$ yaourt -S ddgr
|
||||
或
|
||||
$ packer -S ddgr
|
||||
```
|
||||
|
||||
对于 Debian,使用 [DPKG 命令][12] 安装 `ddgr`。
|
||||
|
||||
```
|
||||
# wget https://github.com/jarun/ddgr/releases/download/v1.2/ddgr_1.2-1_debian9.amd64.deb
|
||||
# dpkg -i ddgr_1.2-1_debian9.amd64.deb
|
||||
```
|
||||
|
||||
对于 CentOS 7,使用 [YUM 命令][13]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
# yum install https://github.com/jarun/ddgr/releases/download/v1.2/ddgr-1.2-1.el7.3.centos.x86_64.rpm
|
||||
```
|
||||
|
||||
对于 opensuse,使用 [zypper 命令][14]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
# zypper install https://github.com/jarun/ddgr/releases/download/v1.2/ddgr-1.2-1.opensuse42.3.x86_64.rpm
|
||||
```
|
||||
|
||||
### 如何启动 ddgr
|
||||
|
||||
在终端上输入 `ddgr` 命令,不带任何选项来进行 DuckDuckGo 搜索。你将获得类似于下面的输出。
|
||||
|
||||
```
|
||||
$ ddgr
|
||||
```
|
||||
|
||||
![][16]
|
||||
|
||||
### 如何使用 ddgr 进行搜索
|
||||
|
||||
我们可以通过两种方式启动搜索。从 omniprompt 或者直接从终端开始。你可以搜索任何你想要的短语。
|
||||
|
||||
直接从终端:
|
||||
|
||||
```
|
||||
$ ddgr 2daygeek
|
||||
```
|
||||
|
||||
![][17]
|
||||
|
||||
从 omniprompt:
|
||||
|
||||
![][18]
|
||||
|
||||
### Omniprompt 快捷方式
|
||||
|
||||
输入 `?` 以获得 omniprompt,它将显示关键字列表和进一步使用 `ddgr` 的快捷方式。
|
||||
|
||||
![][19]
|
||||
|
||||
### 如何移动下一页、上一页和第一页
|
||||
|
||||
它允许用户移动下一页、上一页或第一页。
|
||||
|
||||
* `n`: 移动到下一组搜索结果
|
||||
* `p`: 移动到上一组搜索结果
|
||||
* `f`: 跳转到第一页
|
||||
|
||||
![][20]
|
||||
|
||||
### 如何启动新搜索
|
||||
|
||||
`d` 选项允许用户从 omniprompt 发起新的搜索。例如,我搜索了 “2daygeek website”,现在我将搜索 “Magesh Maruthamuthu” 这个新短语。
|
||||
|
||||
从 omniprompt:
|
||||
|
||||
```
|
||||
ddgr (? for help) d magesh maruthmuthu
|
||||
```
|
||||
|
||||
![][21]
|
||||
|
||||
### 在搜索结果中显示完整的 URL
|
||||
|
||||
默认情况下,它仅显示文章标题,在搜索中添加 `x` 选项以在搜索结果中显示完整的文章网址。
|
||||
|
||||
```
|
||||
$ ddgr -n 5 -x 2daygeek
|
||||
```
|
||||
|
||||
![][22]
|
||||
|
||||
### 限制搜索结果
|
||||
|
||||
默认情况下,搜索结果每页显示 10 个结果。如果你想为方便起见限制页面结果,可以使用 `ddgr` 带有 `--num` 或 ` -n` 参数。
|
||||
|
||||
```
|
||||
$ ddgr -n 5 2daygeek
|
||||
```
|
||||
|
||||
![][23]
|
||||
|
||||
### 网站特定搜索
|
||||
|
||||
要搜索特定网站的特定页面,使用以下格式。这将从网站获取给定关键字的结果。例如,我们在 2daygeek 网站下搜索 “Package Manager”,查看结果。
|
||||
|
||||
```
|
||||
$ ddgr -n 5 --site 2daygeek "package manager"
|
||||
```
|
||||
|
||||
![][24]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/ddgr-duckduckgo-search-from-the-command-line-in-linux/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.2daygeek.com/author/magesh/
|
||||
[1]:https://github.com/jarun/ddgr
|
||||
[2]:https://www.2daygeek.com/googler-google-search-from-the-command-line-on-linux/
|
||||
[3]:https://www.2daygeek.com/buku-command-line-bookmark-manager-linux/
|
||||
[4]:https://www.2daygeek.com/socli-search-and-browse-stack-overflow-from-linux-terminal/
|
||||
[5]:https://www.2daygeek.com/rtv-reddit-terminal-viewer-a-simple-terminal-viewer-for-reddit/
|
||||
[6]:https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[7]:https://www.2daygeek.com/snap-command-examples/
|
||||
[8]:https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[9]:https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[10]:https://www.2daygeek.com/install-yaourt-aur-helper-on-arch-linux/
|
||||
[11]:https://www.2daygeek.com/install-packer-aur-helper-on-arch-linux/
|
||||
[12]:https://www.2daygeek.com/dpkg-command-to-manage-packages-on-debian-ubuntu-linux-mint-systems/
|
||||
[13]:https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/
|
||||
[14]:https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/
|
||||
[15]:data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[16]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux1.png
|
||||
[17]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-3.png
|
||||
[18]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-2.png
|
||||
[19]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-4.png
|
||||
[20]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-5a.png
|
||||
[21]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-6a.png
|
||||
[22]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-7a.png
|
||||
[23]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-8.png
|
||||
[24]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-9a.png
|
155
published/201905/20180429 The Easiest PDO Tutorial (Basics).md
Normal file
155
published/201905/20180429 The Easiest PDO Tutorial (Basics).md
Normal file
@ -0,0 +1,155 @@
|
||||
PHP PDO 简单教程
|
||||
======
|
||||
|
||||
![](http://www.theitstuff.com/wp-content/uploads/2018/04/php-language.jpg)
|
||||
|
||||
大约 80% 的 Web 应用程序由 PHP 提供支持。类似地,SQL 也是如此。PHP 5.5 版本之前,我们有用于访问 MySQL 数据库的 mysql_ 命令,但由于安全性不足,它们最终被弃用。
|
||||
|
||||
弃用这件事是发生在 2013 年的 PHP 5.5 上,我写这篇文章的时间是 2018 年,PHP 版本为 7.2。mysql_ 的弃用带来了访问数据库的两种主要方法:mysqli 和 PDO 库。
|
||||
|
||||
虽然 mysqli 库是官方指定的,但由于 mysqli 只能支持 mysql 数据库,而 PDO 可以支持 12 种不同类型的数据库驱动程序,因此 PDO 获得了更多的赞誉。此外,PDO 还有其它一些特性,使其成为大多数开发人员的更好选择。你可以在下表中看到一些特性比较:
|
||||
|
||||
| | PDO | MySQLi
|
||||
---|---|---
|
||||
| 数据库支持 | 12 种驱动 | 只有 MySQL
|
||||
| 范例 | OOP | 过程 + OOP
|
||||
| 预处理语句(客户端侧) | Yes | No
|
||||
| 1命名参数 | Yes | No
|
||||
|
||||
现在我想对于大多数开发人员来说,PDO 是首选的原因已经很清楚了。所以让我们深入研究它,并希望在本文中尽量涵盖关于 PDO 你需要的了解的。
|
||||
|
||||
### 连接
|
||||
|
||||
第一步是连接到数据库,由于 PDO 是完全面向对象的,所以我们将使用 PDO 类的实例。
|
||||
|
||||
我们要做的第一件事是定义主机、数据库名称、用户名、密码和数据库字符集。
|
||||
|
||||
```
|
||||
$host = 'localhost';
|
||||
$db = 'theitstuff';
|
||||
$user = 'root';
|
||||
$pass = 'root';
|
||||
$charset = 'utf8mb4';
|
||||
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
|
||||
$conn = new PDO($dsn, $user, $pass);
|
||||
```
|
||||
|
||||
之后,正如你在上面的代码中看到的,我们创建了 DSN 变量,DSN 变量只是一个保存数据库信息的变量。对于一些在外部服务器上运行 MySQL 的人,你还可以通过提供一个 `port=$port_number` 来调整端口号。
|
||||
|
||||
最后,你可以创建一个 PDO 类的实例,我使用了 `$conn` 变量,并提供了 `$dsn`、`$user`、`$pass` 参数。如果你遵循这些步骤,你现在应该有一个名为 `$conn` 的对象,它是 PDO 连接类的一个实例。现在是时候进入数据库并运行一些查询。
|
||||
|
||||
### 一个简单的 SQL 查询
|
||||
|
||||
现在让我们运行一个简单的 SQL 查询。
|
||||
|
||||
```
|
||||
$tis = $conn->query('SELECT name, age FROM students');
|
||||
while ($row = $tis->fetch())
|
||||
{
|
||||
echo $row['name']."\t";
|
||||
echo $row['age'];
|
||||
echo "<br>";
|
||||
}
|
||||
```
|
||||
|
||||
这是使用 PDO 运行查询的最简单形式。我们首先创建了一个名为 `tis`(TheITStuff 的缩写 )的变量,然后你可以看到我们使用了创建的 `$conn` 对象中的查询函数。
|
||||
|
||||
然后我们运行一个 `while` 循环并创建了一个 `$row` 变量来从 `$tis` 对象中获取内容,最后通过调用列名来显示每一行。
|
||||
|
||||
很简单,不是吗?现在让我们来看看预处理语句。
|
||||
|
||||
### 预处理语句
|
||||
|
||||
预处理语句是人们开始使用 PDO 的主要原因之一,因为它提供了可以阻止 SQL 注入的语句。
|
||||
|
||||
有两种基本方法可供使用,你可以使用位置参数或命名参数。
|
||||
|
||||
#### 位置参数
|
||||
|
||||
让我们看一个使用位置参数的查询示例。
|
||||
|
||||
```
|
||||
$tis = $conn->prepare("INSERT INTO STUDENTS(name, age) values(?, ?)");
|
||||
$tis->bindValue(1,'mike');
|
||||
$tis->bindValue(2,22);
|
||||
$tis->execute();
|
||||
```
|
||||
|
||||
在上面的例子中,我们放置了两个问号,然后使用 `bindValue()` 函数将值映射到查询中。这些值绑定到语句问号中的位置。
|
||||
|
||||
我还可以使用变量而不是直接提供值,通过使用 `bindParam()` 函数相同例子如下:
|
||||
|
||||
```
|
||||
$name='Rishabh'; $age=20;
|
||||
$tis = $conn->prepare("INSERT INTO STUDENTS(name, age) values(?, ?)");
|
||||
$tis->bindParam(1,$name);
|
||||
$tis->bindParam(2,$age);
|
||||
$tis->execute();
|
||||
```
|
||||
|
||||
### 命名参数
|
||||
|
||||
命名参数也是预处理语句,它将值/变量映射到查询中的命名位置。由于没有位置绑定,因此在多次使用相同变量的查询中非常有效。
|
||||
|
||||
```
|
||||
$name='Rishabh'; $age=20;
|
||||
$tis = $conn->prepare("INSERT INTO STUDENTS(name, age) values(:name, :age)");
|
||||
$tis->bindParam(':name', $name);
|
||||
$tis->bindParam(':age', $age);
|
||||
$tis->execute();
|
||||
```
|
||||
|
||||
你可以注意到,唯一的变化是我使用 `:name` 和 `:age` 作为占位符,然后将变量映射到它们。冒号在参数之前使用,让 PDO 知道该位置是一个变量,这非常重要。
|
||||
|
||||
你也可以类似地使用 `bindValue()` 来使用命名参数直接映射值。
|
||||
|
||||
### 获取数据
|
||||
|
||||
PDO 在获取数据时非常丰富,它实际上提供了许多格式来从数据库中获取数据。
|
||||
|
||||
你可以使用 `PDO::FETCH_ASSOC` 来获取关联数组,`PDO::FETCH_NUM` 来获取数字数组,使用 `PDO::FETCH_OBJ` 来获取对象数组。
|
||||
|
||||
```
|
||||
$tis = $conn->prepare("SELECT * FROM STUDENTS");
|
||||
$tis->execute();
|
||||
$result = $tis->fetchAll(PDO::FETCH_ASSOC);
|
||||
```
|
||||
|
||||
你可以看到我使用了 `fetchAll`,因为我想要所有匹配的记录。如果只需要一行,你可以简单地使用 `fetch`。
|
||||
|
||||
现在我们已经获取了数据,现在是时候循环它了,这非常简单。
|
||||
|
||||
```
|
||||
foreach ($result as $lnu){
|
||||
echo $lnu['name'];
|
||||
echo $lnu['age']."<br>";
|
||||
}
|
||||
```
|
||||
|
||||
你可以看到,因为我请求了关联数组,所以我正在按名称访问各个成员。
|
||||
|
||||
虽然在定义希望如何传输递数据方面没有要求,但在定义 `$conn` 变量本身时,实际上可以将其设置为默认值。
|
||||
|
||||
你需要做的就是创建一个 `$options` 数组,你可以在其中放入所有默认配置,只需在 `$conn` 变量中传递数组即可。
|
||||
|
||||
```
|
||||
$options = [
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
];
|
||||
$conn = new PDO($dsn, $user, $pass, $options);
|
||||
```
|
||||
|
||||
这是一个非常简短和快速的 PDO 介绍,我们很快就会制作一个高级教程。如果你在理解本教程的任何部分时遇到任何困难,请在评论部分告诉我,我会在那你为你解答。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: http://www.theitstuff.com/easiest-pdo-tutorial-basics
|
||||
|
||||
作者:[Rishabh Kandari][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:http://www.theitstuff.com/author/reevkandari
|
@ -1,21 +1,22 @@
|
||||
没有恶棍,英雄又将如何?如何向你的 Python 游戏中添加一个敌人
|
||||
如何向你的 Python 游戏中添加一个敌人
|
||||
======
|
||||
|
||||
> 在本系列的第五部分,学习如何增加一个坏蛋与你的好人战斗。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/game-dogs-chess-play-lead.png?itok=NAuhav4Z)
|
||||
|
||||
在本系列的前几篇文章中(参见 [第一部分][1]、[第二部分][2]、[第三部分][3] 以及 [第四部分][4]),你已经学习了如何使用 Pygame 和 Python 在一个空白的视频游戏世界中生成一个可玩的角色。但没有恶棍,英雄又将如何?
|
||||
|
||||
如果你没有敌人,那将会是一个非常无聊的游戏。所以在此篇文章中,你将为你的游戏添加一个敌人并构建一个用于创建关卡的框架。
|
||||
|
||||
在对玩家妖精实现全部功能仍有许多事情可做之前,跳向敌人似乎就很奇怪。但你已经学到了很多东西,创造恶棍与与创造玩家妖精非常相似。所以放轻松,使用你已经掌握的知识,看看能挑起怎样一些麻烦。
|
||||
在对玩家妖精实现全部功能之前,就来实现一个敌人似乎就很奇怪。但你已经学到了很多东西,创造恶棍与与创造玩家妖精非常相似。所以放轻松,使用你已经掌握的知识,看看能挑起怎样一些麻烦。
|
||||
|
||||
针对本次训练,你能够从 [Open Game Art][5] 下载一些预创建的素材。此处是我使用的一些素材:
|
||||
|
||||
|
||||
+ 印加花砖(译注:游戏中使用的花砖贴图)
|
||||
+ 印加花砖(LCTT 译注:游戏中使用的花砖贴图)
|
||||
+ 一些侵略者
|
||||
+ 妖精、角色、物体以及特效
|
||||
|
||||
|
||||
### 创造敌方妖精
|
||||
|
||||
是的,不管你意识到与否,你其实已经知道如何去实现敌人。这个过程与创造一个玩家妖精非常相似:
|
||||
@ -24,40 +25,27 @@
|
||||
2. 创建 `update` 方法使得敌人能够检测碰撞
|
||||
3. 创建 `move` 方法使得敌人能够四处游荡
|
||||
|
||||
|
||||
|
||||
从类入手。从概念上看,它与你的 Player 类大体相同。你设置一张或者一组图片,然后设置妖精的初始位置。
|
||||
从类入手。从概念上看,它与你的 `Player` 类大体相同。你设置一张或者一组图片,然后设置妖精的初始位置。
|
||||
|
||||
在继续下一步之前,确保你有一张你的敌人的图像,即使只是一张临时图像。将图像放在你的游戏项目的 `images` 目录(你放置你的玩家图像的相同目录)。
|
||||
|
||||
如果所有的活物都拥有动画,那么游戏看起来会好得多。为敌方妖精设置动画与为玩家妖精设置动画具有相同的方式。但现在,为了保持简单,我们使用一个没有动画的妖精。
|
||||
|
||||
在你代码 `objects` 节的顶部,使用以下代码创建一个叫做 `Enemy` 的类:
|
||||
|
||||
```
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
|
||||
'''
|
||||
|
||||
生成一个敌人
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self,x,y,img):
|
||||
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
|
||||
self.image = pygame.image.load(os.path.join('images',img))
|
||||
|
||||
self.image.convert_alpha()
|
||||
|
||||
self.image.set_colorkey(ALPHA)
|
||||
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
self.rect.x = x
|
||||
|
||||
self.rect.y = y
|
||||
|
||||
```
|
||||
|
||||
如果你想让你的敌人动起来,使用让你的玩家拥有动画的 [相同方式][4]。
|
||||
@ -67,25 +55,21 @@ class Enemy(pygame.sprite.Sprite):
|
||||
你能够通过告诉类,妖精应使用哪张图像,应出现在世界上的什么地方,来生成不只一个敌人。这意味着,你能够使用相同的敌人类,在游戏世界的任意地方生成任意数量的敌方妖精。你需要做的仅仅是调用这个类,并告诉它应使用哪张图像,以及你期望生成点的 X 和 Y 坐标。
|
||||
|
||||
再次,这从原则上与生成一个玩家精灵相似。在你脚本的 `setup` 节添加如下代码:
|
||||
|
||||
```
|
||||
enemy = Enemy(20,200,'yeti.png') # 生成敌人
|
||||
|
||||
enemy_list = pygame.sprite.Group() # 创建敌人组
|
||||
|
||||
enemy_list.add(enemy) # 将敌人加入敌人组
|
||||
|
||||
```
|
||||
|
||||
在示例代码中,X 坐标为 20,Y 坐标为 200。你可能需要根据你的敌方妖精的大小,来调整这些数字,但尽量生成在一个地方,使得你的玩家妖精能够到它。`Yeti.png` 是用于敌人的图像。
|
||||
在示例代码中,X 坐标为 20,Y 坐标为 200。你可能需要根据你的敌方妖精的大小,来调整这些数字,但尽量生成在一个范围内,使得你的玩家妖精能够碰到它。`Yeti.png` 是用于敌人的图像。
|
||||
|
||||
接下来,将敌人组的所有敌人绘制在屏幕上。现在,你只有一个敌人,如果你想要更多你可以稍后添加。一但你将一个敌人加入敌人组,它就会在主循环中被绘制在屏幕上。中间这一行是你需要添加的新行:
|
||||
|
||||
```
|
||||
player_list.draw(world)
|
||||
|
||||
enemy_list.draw(world) # 刷新敌人
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
```
|
||||
|
||||
启动你的游戏,你的敌人会出现在游戏世界中你选择的 X 和 Y 坐标处。
|
||||
@ -96,42 +80,31 @@ enemy_list.add(enemy) # 将敌人加入敌人组
|
||||
|
||||
思考一下“关卡”是什么。你如何知道你是在游戏中的一个特定关卡中呢?
|
||||
|
||||
你可以把关卡想成一系列项目的集合。就像你刚刚创建的这个平台中,一个关卡,包含了平台、敌人放置、赃物等的一个特定排列。你可以创建一个类,用来在你的玩家附近创建关卡。最终,当你创建了超过一个关卡,你就可以在你的玩家达到特定目标时,使用这个类生成下一个关卡。
|
||||
你可以把关卡想成一系列项目的集合。就像你刚刚创建的这个平台中,一个关卡,包含了平台、敌人放置、战利品等的一个特定排列。你可以创建一个类,用来在你的玩家附近创建关卡。最终,当你创建了一个以上的关卡,你就可以在你的玩家达到特定目标时,使用这个类生成下一个关卡。
|
||||
|
||||
将你写的用于生成敌人及其群组的代码,移动到一个每次生成新关卡时都会被调用的新函数中。你需要做一些修改,使得每次你创建新关卡时,你都能够创建一些敌人。
|
||||
|
||||
```
|
||||
class Level():
|
||||
|
||||
def bad(lvl,eloc):
|
||||
|
||||
if lvl == 1:
|
||||
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # 生成敌人
|
||||
|
||||
enemy_list = pygame.sprite.Group() # 生成敌人组
|
||||
|
||||
enemy_list.add(enemy) # 将敌人加入敌人组
|
||||
|
||||
if lvl == 2:
|
||||
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
|
||||
|
||||
return enemy_list
|
||||
|
||||
```
|
||||
|
||||
`return` 语句确保了当你调用 `Level.bad` 方法时,你将会得到一个 `enemy_list` 变量包含了所有你定义的敌人。
|
||||
|
||||
因为你现在将创造敌人作为每个关卡的一部分,你的 `setup` 部分也需要做些更改。不同于创造一个敌人,取而代之的是你必须去定义敌人在那里生成,以及敌人属于哪个关卡。
|
||||
|
||||
```
|
||||
eloc = []
|
||||
|
||||
eloc = [200,20]
|
||||
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
|
||||
```
|
||||
|
||||
再次运行游戏来确认你的关卡生成正确。与往常一样,你应该会看到你的玩家,并且能看到你在本章节中添加的敌人。
|
||||
@ -140,31 +113,27 @@ enemy_list = Level.bad( 1, eloc )
|
||||
|
||||
一个敌人如果对玩家没有效果,那么它不太算得上是一个敌人。当玩家与敌人发生碰撞时,他们通常会对玩家造成伤害。
|
||||
|
||||
因为你可能想要去跟踪玩家的生命值,因此碰撞检测发生在 Player 类,而不是 Enemy 类中。当然如果你想,你也可以跟踪敌人的生命值。它们之间的逻辑与代码大体相似,现在,我们只需要跟踪玩家的生命值。
|
||||
因为你可能想要去跟踪玩家的生命值,因此碰撞检测发生在 `Player` 类,而不是 `Enemy` 类中。当然如果你想,你也可以跟踪敌人的生命值。它们之间的逻辑与代码大体相似,现在,我们只需要跟踪玩家的生命值。
|
||||
|
||||
为了跟踪玩家的生命值,你必须为它确定一个变量。代码示例中的第一行是上下文提示,那么将第二行代码添加到你的 Player 类中:
|
||||
|
||||
```
|
||||
self.frame = 0
|
||||
|
||||
self.health = 10
|
||||
|
||||
```
|
||||
|
||||
在你 Player 类的 `update` 方法中,添加如下代码块:
|
||||
在你 `Player` 类的 `update` 方法中,添加如下代码块:
|
||||
|
||||
```
|
||||
hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
|
||||
for enemy in hit_list:
|
||||
|
||||
self.health -= 1
|
||||
|
||||
print(self.health)
|
||||
|
||||
```
|
||||
|
||||
这段代码使用 Pygame 的 `sprite.spritecollide` 方法,建立了一个碰撞检测器,称作 `enemy_hit`。每当它的父类妖精(生成检测器的玩家妖精)的碰撞区触碰到 `enemy_list` 中的任一妖精的碰撞区时,碰撞检测器都会发出一个信号。当这个信号被接收,`for` 循环就会被触发,同时扣除一点玩家生命值。
|
||||
|
||||
一旦这段代码出现在你 Player 类的 `update` 方法,并且 `update` 方法在你的主循环中被调用,Pygame 会在每个时钟 tick 检测一次碰撞。
|
||||
一旦这段代码出现在你 `Player` 类的 `update` 方法,并且 `update` 方法在你的主循环中被调用,Pygame 会在每个时钟滴答中检测一次碰撞。
|
||||
|
||||
### 移动敌人
|
||||
|
||||
@ -176,60 +145,41 @@ enemy_list = Level.bad( 1, eloc )
|
||||
|
||||
举个例子,你告诉你的敌方妖精向右移动 10 步,向左移动 10 步。但敌方妖精不会计数,因此你需要创建一个变量来跟踪你的敌人已经移动了多少步,并根据计数变量的值来向左或向右移动你的敌人。
|
||||
|
||||
首先,在你的 Enemy 类中创建计数变量。添加以下代码示例中的最后一行代码:
|
||||
首先,在你的 `Enemy` 类中创建计数变量。添加以下代码示例中的最后一行代码:
|
||||
|
||||
```
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
self.rect.x = x
|
||||
|
||||
self.rect.y = y
|
||||
|
||||
self.counter = 0 # 计数变量
|
||||
|
||||
```
|
||||
|
||||
然后,在你的 Enemy 类中创建一个 `move` 方法。使用 if-else 循环来创建一个所谓的死循环:
|
||||
然后,在你的 `Enemy` 类中创建一个 `move` 方法。使用 if-else 循环来创建一个所谓的死循环:
|
||||
|
||||
* 如果计数在 0 到 100 之间,向右移动;
|
||||
* 如果计数在 100 到 200 之间,向左移动;
|
||||
* 如果计数大于 200,则将计数重置为 0。
|
||||
|
||||
|
||||
|
||||
死循环没有终点,因为循环判断条件永远为真,所以它将永远循环下去。在此情况下,计数器总是介于 0 到 100 或 100 到 200 之间,因此敌人会永远地从左向右再从右向左移动。
|
||||
|
||||
你用于敌人在每个方向上移动距离的具体值,取决于你的屏幕尺寸,更确切地说,取决于你的敌人移动的平台大小。从较小的值开始,依据习惯逐步提高数值。首先进行如下尝试:
|
||||
|
||||
```
|
||||
def move(self):
|
||||
|
||||
'''
|
||||
|
||||
敌人移动
|
||||
|
||||
'''
|
||||
|
||||
distance = 80
|
||||
|
||||
speed = 8
|
||||
|
||||
|
||||
|
||||
if self.counter >= 0 and self.counter <= distance:
|
||||
|
||||
self.rect.x += speed
|
||||
|
||||
elif self.counter >= distance and self.counter <= distance*2:
|
||||
|
||||
self.rect.x -= speed
|
||||
|
||||
else:
|
||||
|
||||
self.counter = 0
|
||||
|
||||
|
||||
|
||||
self.counter += 1
|
||||
|
||||
```
|
||||
|
||||
你可以根据需要调整距离和速度。
|
||||
@ -237,13 +187,11 @@ enemy_list = Level.bad( 1, eloc )
|
||||
当你现在启动游戏,这段代码有效果吗?
|
||||
|
||||
当然不,你应该也知道原因。你必须在主循环中调用 `move` 方法。如下示例代码中的第一行是上下文提示,那么添加最后两行代码:
|
||||
|
||||
```
|
||||
enemy_list.draw(world) #refresh enemy
|
||||
|
||||
for e in enemy_list:
|
||||
|
||||
e.move()
|
||||
|
||||
```
|
||||
|
||||
启动你的游戏看看当你打击敌人时发生了什么。你可能需要调整妖精的生成地点,使得你的玩家和敌人能够碰撞。当他们发生碰撞时,查看 [IDLE][6] 或 [Ninja-IDE][7] 的控制台,你可以看到生命值正在被扣除。
|
||||
@ -261,15 +209,15 @@ via: https://opensource.com/article/18/5/pygame-enemy
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[cycoe](https://github.com/cycoe)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[1]:https://opensource.com/article/17/10/python-101
|
||||
[2]:https://opensource.com/article/17/12/game-framework-python
|
||||
[3]:https://opensource.com/article/17/12/game-python-add-a-player
|
||||
[4]:https://opensource.com/article/17/12/game-python-moving-player
|
||||
[1]:https://linux.cn/article-9071-1.html
|
||||
[2]:https://linux.cn/article-10850-1.html
|
||||
[3]:https://linux.cn/article-10858-1.html
|
||||
[4]:https://linux.cn/article-10874-1.html
|
||||
[5]:https://opengameart.org
|
||||
[6]:https://docs.python.org/3/library/idle.html
|
||||
[7]:http://ninja-ide.org/
|
@ -0,0 +1,86 @@
|
||||
Adobe Lightroom 的三个开源替代品
|
||||
=======
|
||||
|
||||
> 摄影师们:在没有 Lightroom 套件的情况下,可以看看这些 RAW 图像处理器。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/camera-photography-film.jpg?itok=oe2ixyu6)
|
||||
|
||||
如今智能手机的摄像功能已经完备到多数人认为可以代替传统摄影了。虽然这在傻瓜相机的市场中是个事实,但是对于许多摄影爱好者和专业摄影师看来,一个高端单反相机所能带来的照片景深、清晰度以及真实质感是口袋中的智能手机无法与之相比的。
|
||||
|
||||
所有的这些功能在便利性上要付出一些很小的代价;就像传统的胶片相机中的反色负片,单反照相得到的 RAW 格式文件必须预先处理才能印刷或编辑;因此对于单反相机,照片的后期处理是无可替代的,并且 首选应用就是 Adobe Lightroom。但是由于 Adobe Lightroom 的昂贵价格、基于订阅的定价模式以及专有许可证都使更多人开始关注其开源替代品。
|
||||
|
||||
Lightroom 有两大主要功能:处理 RAW 格式的图片文件,以及数字资产管理系统(DAM) —— 通过标签、评星以及其他元数据信息来简单清晰地整理照片。
|
||||
|
||||
在这篇文章中,我们将介绍三个开源的图片处理软件:Darktable、LightZone 以及 RawTherapee。所有的软件都有 DAM 系统,但没有任何一个具有 Lightroom 基于机器学习的图像分类和标签功能。如果你想要知道更多关于开源的 DAM 系统的软件,可以看 Terry Hacock 的文章:“[开源项目的 DAM 管理][2]”,他分享了他在自己的 [Lunatics!][3] 电影项目研究过的开源多媒体软件。
|
||||
|
||||
### Darktable
|
||||
|
||||
![Darktable][4]
|
||||
|
||||
类似其他两个软件,Darktable 可以处理 RAW 格式的图像并将它们转换成可用的文件格式 —— JPEG、PNG、TIFF、PPM、PFM 和 EXR,它同时支持 Google 和 Facebook 的在线相册,上传至 Flikr,通过邮件附件发送以及创建在线相册。
|
||||
|
||||
它有 61 个图像处理模块,可以调整图像的对比度、色调、明暗、色彩、噪点;添加水印;切割以及旋转;等等。如同另外两个软件一样,不论你做出多少次修改,这些修改都是“无损的” —— 你的初始 RAW 图像文件始终会被保存。
|
||||
|
||||
Darktable 可以从 400 多种相机型号中直接导入照片,以及有 JPEG、CR2、DNG、OpenEXR 和 PFM 等格式的支持。图像在一个数据库中显示,因此你可以轻易地过滤并查询这些元数据,包括了文字标签、评星以及颜色标签。软件同时支持 21 种语言,支持 Linux、MacOS、BSD、Solaris 11/GNOME 以及 Windows(Windows 版本是最新发布的,Darktable 声明它比起其他版本可能还有一些不完备之处,有一些未实现的功能)。
|
||||
|
||||
Darktable 在开源许可证 [GPLv3][7] 下发布,你可以了解更多它的 [特性][8],查阅它的 [用户手册][9],或者直接去 Github 上看[源代码][10] 。
|
||||
|
||||
### LightZone
|
||||
|
||||
![LightZone's tool stack][11]
|
||||
|
||||
[LightZone][12] 和其他两个软件类似同样是无损的 RAW 格式图像处理工具:它是跨平台的,有 Windows、MacOS 和 Linux 版本,除 RAW 格式之外,它还支持 JPG 和 TIFF 格式的图像处理。接下来说说 LightZone 其他独特特性。
|
||||
|
||||
这个软件最初在 2005 年时,是以专有许可证发布的图像处理软件,后来在 BSD 证书下开源。此外,在你下载这个软件之前,你必须注册一个免费账号,以便 LightZone的 开发团队可以跟踪软件的下载数量以及建立相关社区。(许可很快,而且是自动的,因此这不是一个很大的使用障碍。)
|
||||
|
||||
除此之外的一个特性是这个软件的图像处理通常是通过很多可组合的工具实现的,而不是叠加滤镜(就像大多数图像处理软件),这些工具组可以被重新编排以及移除,以及被保存并且复制用到另一些图像上。如果想要编辑图片的部分区域,你还可以通过矢量工具或者根据色彩和亮度来选择像素。
|
||||
|
||||
想要了解更多,见 LightZone 的[论坛][13] 或者查看 Github上的 [源代码][14]。
|
||||
|
||||
### RawTherapee
|
||||
|
||||
![RawTherapee][15]
|
||||
|
||||
[RawTherapee][16] 是另一个值得关注的开源([GPL][17])的 RAW 图像处理器。就像 Darktable 和 LightZone,它是跨平台的(支持 Windows、MacOS 和 Linux),一切修改都在无损条件下进行,因此不论你叠加多少滤镜做出多少改变,你都可以回到你最初的 RAW 文件。
|
||||
|
||||
RawTherapee 采用的是一个面板式的界面,包括一个历史记录面板来跟踪你做出的修改,以方便随时回到先前的图像;一个快照面板可以让你同时处理一张照片的不同版本;一个可滚动的工具面板可以方便准确地选择工具。这些工具包括了一系列的调整曝光、色彩、细节、图像变换以及去马赛克功能。
|
||||
|
||||
这个软件可以从多数相机直接导入 RAW 文件,并且支持超过 25 种语言,得到了广泛使用。批量处理以及 [SSE][18] 优化这类功能也进一步提高了图像处理的速度以及对 CPU 性能的利用。
|
||||
|
||||
RawTherapee 还提供了很多其他 [功能][19];可以查看它的 [官方文档][20] 以及 [源代码][21] 了解更多细节。
|
||||
|
||||
你是否在摄影中使用另外的开源 RAW 图像处理工具?有任何建议和推荐都可以在评论中分享。
|
||||
|
||||
------
|
||||
|
||||
via: https://opensource.com/alternatives/adobe-lightroom
|
||||
|
||||
作者:[Opensource.com][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[scoutydren](https://github.com/scoutydren)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com
|
||||
[1]: https://en.wikipedia.org/wiki/Raw_image_format
|
||||
[2]: https://opensource.com/article/18/3/movie-open-source-software
|
||||
[3]: http://lunatics.tv/
|
||||
[4]: https://opensource.com/sites/default/files/styles/panopoly_image_original/public/uploads/raw-image-processors_darkroom1.jpg?itok=0fjk37tC "Darktable"
|
||||
[5]: http://www.darktable.org/
|
||||
[6]: https://www.darktable.org/about/faq/#faq-windows
|
||||
[7]: https://github.com/darktable-org/darktable/blob/master/LICENSE
|
||||
[8]: https://www.darktable.org/about/features/
|
||||
[9]: https://www.darktable.org/resources/
|
||||
[10]: https://github.com/darktable-org/darktable
|
||||
[11]: https://opensource.com/sites/default/files/styles/panopoly_image_original/public/uploads/raw-image-processors_lightzone1tookstack.jpg?itok=1e3s85CZ
|
||||
[12]: http://www.lightzoneproject.org/
|
||||
[13]: http://www.lightzoneproject.org/Forum
|
||||
[14]: https://github.com/ktgw0316/LightZone
|
||||
[15]: https://opensource.com/sites/default/files/styles/panopoly_image_original/public/uploads/raw-image-processors_rawtherapee.jpg?itok=meiuLxPw "RawTherapee"
|
||||
[16]: http://rawtherapee.com/
|
||||
[17]: https://github.com/Beep6581/RawTherapee/blob/dev/LICENSE.txt
|
||||
[18]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
|
||||
[19]: http://rawpedia.rawtherapee.com/Features
|
||||
[20]: http://rawpedia.rawtherapee.com/Main_Page
|
||||
[21]: https://github.com/Beep6581/RawTherapee
|
@ -0,0 +1,593 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10902-1.html)
|
||||
[#]: subject: (Put platforms in a Python game with Pygame)
|
||||
[#]: via: (https://opensource.com/article/18/7/put-platforms-python-game)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
在 Pygame 游戏中放置平台
|
||||
======
|
||||
|
||||
> 在这个从零构建一个 Python 游戏系列的第六部分中,为你的角色创建一些平台来旅行。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/header.png?itok=iq8HFoEJ)
|
||||
|
||||
这是仍在进行中的关于使用 Pygame 模块来在 Python 3 中创建电脑游戏的系列文章的第六部分。先前的文章是:
|
||||
|
||||
+ [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程][24]
|
||||
+ [使用 Python 和 Pygame 模块构建一个游戏框架][25]
|
||||
+ [如何在你的 Python 游戏中添加一个玩家][26]
|
||||
+ [用 Pygame 使你的游戏角色移动起来][27]
|
||||
+ [如何向你的 Python 游戏中添加一个敌人][28]
|
||||
|
||||
一个平台类游戏需要平台。
|
||||
|
||||
在 [Pygame][1] 中,平台本身也是个妖精,正像你那个可玩的妖精。这一点是重要的,因为有个是对象的平台,可以使你的玩家妖精更容易与之互动。
|
||||
|
||||
创建平台有两个主要步骤。首先,你必须给该对象编写代码,然后,你必须映射出你希望该对象出现的位置。
|
||||
|
||||
### 编码平台对象
|
||||
|
||||
要构建一个平台对象,你要创建一个名为 `Platform` 的类。它是一个妖精,正像你的 `Player` [妖精][2] 一样,带有很多相同的属性。
|
||||
|
||||
你的 `Platform` 类需要知道很多平台类型的信息,它应该出现在游戏世界的哪里、它应该包含的什么图片等等。这其中很多信息可能还尚不存在,这要看你为你的游戏计划了多少,但是没有关系。正如直到[移动你的游戏角色][3]那篇文章结束时,你都没有告诉你的玩家妖精移动速度有多快,你不必事先告诉 `Platform` 每一件事。
|
||||
|
||||
在这系列中你所写的脚本的开头附近,创建一个新的类。在这个代码示例中前三行是用于说明上下文,因此在注释的下面添加代码:
|
||||
|
||||
```
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
## 新代码如下:
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x location, y location, img width, img height, img file
|
||||
def __init__(self,xloc,yloc,imgw,imgh,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img)).convert()
|
||||
self.image.convert_alpha()
|
||||
self.image.set_colorkey(ALPHA)
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.y = yloc
|
||||
self.rect.x = xloc
|
||||
```
|
||||
|
||||
当被调用时,这个类在某个 X 和 Y 位置上创建一个屏上对象,具有某种宽度和高度,并使用某种图像作为纹理。这与如何在屏上绘制出玩家或敌人非常类似。
|
||||
|
||||
### 平台的类型
|
||||
|
||||
下一步是绘制出你的平台需要出现的地方。
|
||||
|
||||
#### 瓷砖方式
|
||||
|
||||
实现平台类游戏世界有几种不同的方法。在最初的横向滚轴游戏中,例如,马里奥超级兄弟和刺猬索尼克,这个技巧是使用“瓷砖”方式,也就是说有几个代表地面和各种平台的块,并且这些块被重复使用来制作一个关卡。你只能有 8 或 12 种不同的块,你可以将它们排列在屏幕上来创建地面、浮动的平台,以及你游戏中需要的一切其它的事物。有人发现这是制作游戏最容易的方法了,因为你只需要制作(或下载)一小组关卡素材就能创建很多不同的关卡。然而,这里的代码需要一点数学知识。
|
||||
|
||||
![Supertux, a tile-based video game][5]
|
||||
|
||||
*[SuperTux][6] ,一个基于瓷砖的电脑游戏。*
|
||||
|
||||
#### 手工绘制方式
|
||||
|
||||
另一种方法是将每个素材作为一个整体图像。如果你喜欢为游戏世界创建素材,那你会在用图形应用程序构建游戏世界的每个部分上花费很多时间。这种方法不需要太多的数学知识,因为所有的平台都是整体的、完整的对象,你只需要告诉 [Python][7] 将它们放在屏幕上的什么位置。
|
||||
|
||||
每种方法都有优势和劣势,并且根据于你选择使用的方式,代码稍有不同。我将覆盖这两方面,所以你可以在你的工程中使用一种或另一种,甚至两者的混合。
|
||||
|
||||
### 关卡绘制
|
||||
|
||||
总的来说,绘制你的游戏世界是关卡设计和游戏编程中的一个重要的部分。这需要数学知识,但是没有什么太难的,而且 Python 擅长数学,它会有所帮助。
|
||||
|
||||
你也许发现先在纸张上设计是有用的。拿一张表格纸,并绘制一个方框来代表你的游戏窗体。在方框中绘制平台,并标记其每一个平台的 X 和 Y 坐标,以及它的宽度和高度。在方框中的实际位置没有必要是精确的,你只要保持数字合理即可。譬如,假设你的屏幕是 720 像素宽,那么你不能在一个屏幕上放 8 块 100 像素的平台。
|
||||
|
||||
当然,不是你游戏中的所有平台都必须容纳在一个屏幕大小的方框里,因为你的游戏将随着你的玩家行走而滚动。所以,可以继续绘制你的游戏世界到第一屏幕的右侧,直到关卡结束。
|
||||
|
||||
如果你更喜欢精确一点,你可以使用方格纸。当设计一个瓷砖类的游戏时,这是特别有用的,因为每个方格可以代表一个瓷砖。
|
||||
|
||||
![Example of a level map][9]
|
||||
|
||||
*一个关卡地图示例。*
|
||||
|
||||
#### 坐标系
|
||||
|
||||
你可能已经在学校中学习过[笛卡尔坐标系][10]。你学习的东西也适用于 Pygame,除了在 Pygame 中你的游戏世界的坐标系的原点 `0,0` 是放置在你的屏幕的左上角而不是在中间,是你在地理课上用过的坐标是在中间的。
|
||||
|
||||
![Example of coordinates in Pygame][12]
|
||||
|
||||
*在 Pygame 中的坐标示例。*
|
||||
|
||||
X 轴起始于最左边的 0,向右无限增加。Y 轴起始于屏幕顶部的 0,向下延伸。
|
||||
|
||||
#### 图片大小
|
||||
|
||||
如果你不知道你的玩家、敌人、平台是多大的,绘制出一个游戏世界是毫无意义的。你可以在图形程序中找到你的平台或瓷砖的尺寸。例如在 [Krita][13] 中,单击“图像”菜单,并选择“属性”。你可以在“属性”窗口的最顶部处找到它的尺寸。
|
||||
|
||||
另外,你也可以创建一个简单的 Python 脚本来告诉你的一个图像的尺寸。打开一个新的文本文件,并输入这些代码到其中:
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from PIL import Image
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
print(sys.argv[1])
|
||||
else:
|
||||
sys.exit('Syntax: identify.py [filename]')
|
||||
|
||||
pic = sys.argv[1]
|
||||
dim = Image.open(pic)
|
||||
X = dim.size[0]
|
||||
Y = dim.size[1]
|
||||
|
||||
print(X,Y)
|
||||
```
|
||||
|
||||
保存该文本文件为 `identify.py`。
|
||||
|
||||
要使用这个脚本,你必须安装一些额外的 Python 模块,它们包含了这个脚本中新使用的关键字:
|
||||
|
||||
```
|
||||
$ pip3 install Pillow --user
|
||||
```
|
||||
|
||||
一旦安装好,在你游戏工程目录中运行这个脚本:
|
||||
|
||||
```
|
||||
$ python3 ./identify.py images/ground.png
|
||||
(1080, 97)
|
||||
```
|
||||
|
||||
在这个示例中,地面平台的图形的大小是 1080 像素宽和 97 像素高。
|
||||
|
||||
### 平台块
|
||||
|
||||
如果你选择单独地绘制每个素材,你必须创建想要插入到你的游戏世界中的几个平台和其它元素,每个素材都放在它自己的文件中。换句话说,你应该让每个素材都有一个文件,像这样:
|
||||
|
||||
![One image file per object][15]
|
||||
|
||||
*每个对象一个图形文件。*
|
||||
|
||||
你可以按照你希望的次数重复使用每个平台,只要确保每个文件仅包含一个平台。你不能使用一个文件包含全部素材,像这样:
|
||||
|
||||
![Your level cannot be one image file][17]
|
||||
|
||||
*你的关卡不能是一个图形文件。*
|
||||
|
||||
当你完成时,你可能希望你的游戏看起来像这样,但是如果你在一个大文件中创建你的关卡,你就没有方法从背景中区分出一个平台,因此,要么把对象绘制在它们自己的文件中,要么从一个更大的文件中裁剪出它们,并保存为单独的副本。
|
||||
|
||||
**注意:** 如同你的其它素材,你可以使用 [GIMP][18]、Krita、[MyPaint][19],或 [Inkscape][20] 来创建你的游戏素材。
|
||||
|
||||
平台出现在每个关卡开始的屏幕上,因此你必须在你的 `Level` 类中添加一个 `platform` 函数。在这里特例是地面平台,它重要到应该拥有它自己的一个组。通过把地面看作一组特殊类型的平台,你可以选择它是否滚动,或它上面是否可以站立,而其它平台可以漂浮在它上面。这取决于你。
|
||||
|
||||
添加这两个函数到你的 `Level` 类:
|
||||
|
||||
```
|
||||
def ground(lvl,x,y,w,h):
|
||||
ground_list = pygame.sprite.Group()
|
||||
if lvl == 1:
|
||||
ground = Platform(x,y,w,h,'block-ground.png')
|
||||
ground_list.add(ground)
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
|
||||
def platform( lvl ):
|
||||
plat_list = pygame.sprite.Group()
|
||||
if lvl == 1:
|
||||
plat = Platform(200, worldy-97-128, 285,67,'block-big.png')
|
||||
plat_list.add(plat)
|
||||
plat = Platform(500, worldy-97-320, 197,54,'block-small.png')
|
||||
plat_list.add(plat)
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return plat_list
|
||||
```
|
||||
|
||||
`ground` 函数需要一个 X 和 Y 位置,以便 Pygame 知道在哪里放置地面平台。它也需要知道平台的宽度和高度,这样 Pygame 知道地面延伸到每个方向有多远。该函数使用你的 `Platform` 类来生成一个屏上对象,然后将这个对象添加到 `ground_list` 组。
|
||||
|
||||
`platform` 函数本质上是相同的,除了其有更多的平台。在这个示例中,仅有两个平台,但是你可以想有多少就有多少。在进入一个平台后,在列出另一个前你必须添加它到 `plat_list` 中。如果你不添加平台到组中,那么它将不出现在你的游戏中。
|
||||
|
||||
> **提示:** 很难想象你的游戏世界的 0 是在顶部,因为在真实世界中发生的情况是相反的;当估计你有多高时,你不会从上往下测量你自己,而是从脚到头顶来测量。
|
||||
>
|
||||
> 如果对你来说从“地面”上来构建你的游戏世界更容易,将 Y 轴值表示为负数可能有帮助。例如,你知道你的游戏世界的底部是 `worldy` 的值。因此 `worldy` 减去地面的高度(在这个示例中是 97)是你的玩家正常站立的位置。如果你的角色是 64 像素高,那么地面减去 128 正好是你的玩家的两倍高。事实上,一个放置在 128 像素处平台大约是相对于你的玩家的两层楼高度。一个平台在 -320 处比三层楼更高。等等。
|
||||
|
||||
正像你现在可能所知的,如果你不使用它们,你的类和函数是没有价值的。添加这些代码到你的设置部分(第一行只是上下文,所以添加最后两行):
|
||||
|
||||
```
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
ground_list = Level.ground( 1,0,worldy-97,1080,97 )
|
||||
plat_list = Level.platform( 1 )
|
||||
```
|
||||
|
||||
并把这些行加到你的主循环(再一次,第一行仅用于上下文):
|
||||
|
||||
```
|
||||
enemy_list.draw(world) # 刷新敌人
|
||||
ground_list.draw(world) # 刷新地面
|
||||
plat_list.draw(world) # 刷新平台
|
||||
```
|
||||
|
||||
### 瓷砖平台
|
||||
|
||||
瓷砖类游戏世界更容易制作,因为你只需要在前面绘制一些块,就能在游戏中一再使用它们创建每个平台。在像 [OpenGameArt.org][21] 这样的网站上甚至有一套瓷砖供你来使用。
|
||||
|
||||
`Platform` 类与在前面部分中的类是相同的。
|
||||
|
||||
`ground` 和 `platform` 在 `Level` 类中,然而,必须使用循环来计算使用多少块来创建每个平台。
|
||||
|
||||
如果你打算在你的游戏世界中有一个坚固的地面,这种地面是很简单的。你只需要从整个窗口的一边到另一边“克隆”你的地面瓷砖。例如,你可以创建一个 X 和 Y 值的列表来规定每个瓷砖应该放置的位置,然后使用一个循环来获取每个值并绘制每一个瓷砖。这仅是一个示例,所以不要添加这到你的代码:
|
||||
|
||||
```
|
||||
# Do not add this to your code
|
||||
gloc = [0,656,64,656,128,656,192,656,256,656,320,656,384,656]
|
||||
```
|
||||
|
||||
不过,如果你仔细看,你可以看到所有的 Y 值是相同的,X 值以 64 的增量不断地增加 —— 这就是瓷砖的大小。这种重复是精确地,是计算机擅长的,因此你可以使用一点数学逻辑来让计算机为你做所有的计算:
|
||||
|
||||
添加这些到你的脚本的设置部分:
|
||||
|
||||
```
|
||||
gloc = []
|
||||
tx = 64
|
||||
ty = 64
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
gloc.append(i*tx)
|
||||
i=i+1
|
||||
|
||||
ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
```
|
||||
|
||||
现在,不管你的窗口的大小,Python 会通过瓷砖的宽度分割游戏世界的宽度,并创建一个数组列表列出每个 X 值。这里不计算 Y 值,因为在平的地面上这个从不会变化。
|
||||
|
||||
为了在一个函数中使用数组,使用一个 `while` 循环,查看每个条目并在适当的位置添加一个地面瓷砖:
|
||||
|
||||
```
|
||||
def ground(lvl,gloc,tx,ty):
|
||||
ground_list = pygame.sprite.Group()
|
||||
i=0
|
||||
if lvl == 1:
|
||||
while i < len(gloc):
|
||||
ground = Platform(gloc[i],worldy-ty,tx,ty,'tile-ground.png')
|
||||
ground_list.add(ground)
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
```
|
||||
|
||||
除了 `while` 循环,这几乎与在上面一部分中提供的瓷砖类平台的 `ground` 函数的代码相同。
|
||||
|
||||
对于移动的平台,原理是相似的,但是这里有一些技巧可以使它简单。
|
||||
|
||||
你可以通过它的起始像素(它的 X 值)、距地面的高度(它的 Y 值)、绘制多少瓷砖来定义一个平台,而不是通过像素绘制每个平台。这样,你不必操心每个平台的宽度和高度。
|
||||
|
||||
这个技巧的逻辑有一点复杂,因此请仔细复制这些代码。有一个 `while` 循环嵌套在另一个 `while` 循环的内部,因为这个函数必须考虑每个数组项的三个值来成功地建造一个完整的平台。在这个示例中,这里仅有三个平台以 `ploc.append` 语句定义,但是你的游戏可能需要更多,因此你需要多少就定义多少。当然,有一些不会出现,因为它们远在屏幕外,但是一旦当你进行滚动时,它们将呈现在眼前。
|
||||
|
||||
```
|
||||
def platform(lvl,tx,ty):
|
||||
plat_list = pygame.sprite.Group()
|
||||
ploc = []
|
||||
i=0
|
||||
if lvl == 1:
|
||||
ploc.append((200,worldy-ty-128,3))
|
||||
ploc.append((300,worldy-ty-256,3))
|
||||
ploc.append((500,worldy-ty-128,4))
|
||||
while i < len(ploc):
|
||||
j=0
|
||||
while j <= ploc[i][2]:
|
||||
plat = Platform((ploc[i][0]+(j*tx)),ploc[i][1],tx,ty,'tile.png')
|
||||
plat_list.add(plat)
|
||||
j=j+1
|
||||
print('run' + str(i) + str(ploc[i]))
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return plat_list
|
||||
```
|
||||
|
||||
要让这些平台出现在你的游戏世界,它们必须出现在你的主循环中。如果你还没有这样做,添加这些行到你的主循环(再一次,第一行仅被用于上下文)中:
|
||||
|
||||
```
|
||||
enemy_list.draw(world) # 刷新敌人
|
||||
ground_list.draw(world) # 刷新地面
|
||||
plat_list.draw(world) # 刷新平台
|
||||
```
|
||||
|
||||
启动你的游戏,根据需要调整你的平台的放置位置。如果你看不见屏幕外产生的平台,不要担心;你不久后就可以修复它。
|
||||
|
||||
到目前为止,这是游戏的图片和代码:
|
||||
|
||||
![Pygame game][23]
|
||||
|
||||
*到目前为止,我们的 Pygame 平台。*
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# draw a world
|
||||
# add a player and player control
|
||||
# add player movement
|
||||
# add enemy and basic collision
|
||||
# add platform
|
||||
|
||||
# GNU All-Permissive License
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without any warranty.
|
||||
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
|
||||
'''
|
||||
Objects
|
||||
'''
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x location, y location, img width, img height, img file
|
||||
def __init__(self,xloc,yloc,imgw,imgh,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img)).convert()
|
||||
self.image.convert_alpha()
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.y = yloc
|
||||
self.rect.x = xloc
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn a player
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.movex = 0
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.health = 10
|
||||
self.score = 1
|
||||
self.images = []
|
||||
for i in range(1,9):
|
||||
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
|
||||
img.convert_alpha()
|
||||
img.set_colorkey(ALPHA)
|
||||
self.images.append(img)
|
||||
self.image = self.images[0]
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
def control(self,x,y):
|
||||
'''
|
||||
control player movement
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
Update sprite position
|
||||
'''
|
||||
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
|
||||
# moving left
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# moving right
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
|
||||
# collisions
|
||||
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
for enemy in enemy_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn an enemy
|
||||
'''
|
||||
def __init__(self,x,y,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img))
|
||||
#self.image.convert_alpha()
|
||||
#self.image.set_colorkey(ALPHA)
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.x = x
|
||||
self.rect.y = y
|
||||
self.counter = 0
|
||||
|
||||
def move(self):
|
||||
'''
|
||||
enemy movement
|
||||
'''
|
||||
distance = 80
|
||||
speed = 8
|
||||
|
||||
if self.counter >= 0 and self.counter <= distance:
|
||||
self.rect.x += speed
|
||||
elif self.counter >= distance and self.counter <= distance*2:
|
||||
self.rect.x -= speed
|
||||
else:
|
||||
self.counter = 0
|
||||
|
||||
self.counter += 1
|
||||
|
||||
class Level():
|
||||
def bad(lvl,eloc):
|
||||
if lvl == 1:
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
|
||||
enemy_list = pygame.sprite.Group() # create enemy group
|
||||
enemy_list.add(enemy) # add enemy to group
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return enemy_list
|
||||
|
||||
def loot(lvl,lloc):
|
||||
print(lvl)
|
||||
|
||||
def ground(lvl,gloc,tx,ty):
|
||||
ground_list = pygame.sprite.Group()
|
||||
i=0
|
||||
if lvl == 1:
|
||||
while i < len(gloc):
|
||||
print("blockgen:" + str(i))
|
||||
ground = Platform(gloc[i],worldy-ty,tx,ty,'ground.png')
|
||||
ground_list.add(ground)
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
|
||||
'''
|
||||
Setup
|
||||
'''
|
||||
worldx = 960
|
||||
worldy = 720
|
||||
|
||||
fps = 40 # frame rate
|
||||
ani = 4 # animation cycles
|
||||
clock = pygame.time.Clock()
|
||||
pygame.init()
|
||||
main = True
|
||||
|
||||
BLUE = (25,25,200)
|
||||
BLACK = (23,23,23 )
|
||||
WHITE = (254,254,254)
|
||||
ALPHA = (0,255,0)
|
||||
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = world.get_rect()
|
||||
player = Player() # spawn player
|
||||
player.rect.x = 0
|
||||
player.rect.y = 0
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
steps = 10 # how fast to move
|
||||
|
||||
eloc = []
|
||||
eloc = [200,20]
|
||||
gloc = []
|
||||
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
|
||||
tx = 64 #tile size
|
||||
ty = 64 #tile size
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
gloc.append(i*tx)
|
||||
i=i+1
|
||||
print("block: " + str(i))
|
||||
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
|
||||
'''
|
||||
Main loop
|
||||
'''
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit(); sys.exit()
|
||||
main = False
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(-steps,0)
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
|
||||
# world.fill(BLACK)
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.update()
|
||||
player_list.draw(world) #refresh player position
|
||||
enemy_list.draw(world) # refresh enemies
|
||||
ground_list.draw(world) # refresh enemies
|
||||
for e in enemy_list:
|
||||
e.move()
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
(LCTT 译注:到本文翻译完为止,该系列已经近一年没有继续更新了~)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/7/put-platforms-python-game
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.pygame.org/news
|
||||
[2]: https://opensource.com/article/17/12/game-python-add-a-player
|
||||
[3]: https://opensource.com/article/17/12/game-python-moving-player
|
||||
[4]: /file/403841
|
||||
[5]: https://opensource.com/sites/default/files/uploads/supertux.png (Supertux, a tile-based video game)
|
||||
[6]: https://www.supertux.org/
|
||||
[7]: https://www.python.org/
|
||||
[8]: /file/403861
|
||||
[9]: https://opensource.com/sites/default/files/uploads/layout.png (Example of a level map)
|
||||
[10]: https://en.wikipedia.org/wiki/Cartesian_coordinate_system
|
||||
[11]: /file/403871
|
||||
[12]: https://opensource.com/sites/default/files/uploads/pygame_coordinates.png (Example of coordinates in Pygame)
|
||||
[13]: https://krita.org/en/
|
||||
[14]: /file/403876
|
||||
[15]: https://opensource.com/sites/default/files/uploads/pygame_floating.png (One image file per object)
|
||||
[16]: /file/403881
|
||||
[17]: https://opensource.com/sites/default/files/uploads/pygame_flattened.png (Your level cannot be one image file)
|
||||
[18]: https://www.gimp.org/
|
||||
[19]: http://mypaint.org/about/
|
||||
[20]: https://inkscape.org/en/
|
||||
[21]: https://opengameart.org/content/simplified-platformer-pack
|
||||
[22]: /file/403886
|
||||
[23]: https://opensource.com/sites/default/files/uploads/pygame_platforms.jpg (Pygame game)
|
||||
[24]: https://linux.cn/article-9071-1.html
|
||||
[25]: https://linux.cn/article-10850-1.html
|
||||
[26]: https://linux.cn/article-10858-1.html
|
||||
[27]: https://linux.cn/article-10874-1.html
|
||||
[28]: https://linux.cn/article-10883-1.html
|
||||
|
@ -1,15 +1,16 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (cycoe)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10874-1.html)
|
||||
[#]: subject: (Using Pygame to move your game character around)
|
||||
[#]: via: (https://opensource.com/article/17/12/game-python-moving-player)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
用 Pygame 使你的游戏角色移动起来
|
||||
======
|
||||
在本系列的第四部分,学习如何编写移动游戏角色的控制代码。
|
||||
> 在本系列的第四部分,学习如何编写移动游戏角色的控制代码。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/python4-game.png?itok=tXFHaLdt)
|
||||
|
||||
在这个系列的第一篇文章中,我解释了如何使用 Python 创建一个简单的[基于文本的骰子游戏][1]。在第二部分中,我向你们展示了如何从头开始构建游戏,即从 [创建游戏的环境][2] 开始。然后在第三部分,我们[创建了一个玩家妖精][3],并且使它在你的(而不是空的)游戏世界内生成。你可能已经注意到,如果你不能移动你的角色,那么游戏不是那么有趣。在本篇文章中,我们将使用 Pygame 来添加键盘控制,如此一来你就可以控制你的角色的移动。
|
||||
@ -18,7 +19,7 @@
|
||||
|
||||
在本系列的第二篇文章中,你已经为退出游戏创建了一个按键,移动角色的(按键)原则也是相同的。但是,使你的角色移动起来要稍微复杂一点。
|
||||
|
||||
让我们从简单的部分入手:设置控制器按键
|
||||
让我们从简单的部分入手:设置控制器按键。
|
||||
|
||||
### 为控制你的玩家妖精设置按键
|
||||
|
||||
@ -56,11 +57,11 @@ while main == True:
|
||||
main = False
|
||||
```
|
||||
|
||||
一些人偏好使用键盘字母 W、A、S 和 D 来控制玩家角色,而另一些偏好使用方向键。因此确保你包含了两种选项。
|
||||
一些人偏好使用键盘字母 `W`、`A`、`S` 和 `D` 来控制玩家角色,而另一些偏好使用方向键。因此确保你包含了两种选项。
|
||||
|
||||
**注意:**当你在编程时,同时考虑所有用户是非常重要的。如果你写代码只是为了自己运行,那么很可能你会成为你写的程序的唯一用户。更重要的是,如果你想找一个通过写代码赚钱的工作,你写的代码就应该让所有人都能运行。给你的用户选择权,比如提供使用方向键或 WASD 的选项,是一个优秀程序员的标志。
|
||||
注意:当你在编程时,同时考虑所有用户是非常重要的。如果你写代码只是为了自己运行,那么很可能你会成为你写的程序的唯一用户。更重要的是,如果你想找一个通过写代码赚钱的工作,你写的代码就应该让所有人都能运行。给你的用户选择权,比如提供使用方向键或 WASD 的选项,是一个优秀程序员的标志。
|
||||
|
||||
使用 Python 启动你的游戏,并在你按下“上下左右”方向键或 A、D 和 W 键的时候查看控制台窗口的输出。
|
||||
使用 Python 启动你的游戏,并在你按下“上下左右”方向键或 `A`、`D` 和 `W` 键的时候查看控制台窗口的输出。
|
||||
|
||||
```
|
||||
$ python ./your-name_game.py
|
||||
@ -77,9 +78,9 @@ $ python ./your-name_game.py
|
||||
|
||||
为了使你的妖精移动起来,你必须为你的妖精创建一个属性代表移动。当你的妖精没有在移动时,这个变量被设为 `0`。
|
||||
|
||||
如果你正在为你的妖精设置动画,或者你决定在将来为他设置动画,你还必须跟踪帧来使走路循环保持在轨迹上。
|
||||
如果你正在为你的妖精设置动画,或者你决定在将来为它设置动画,你还必须跟踪帧来使走路循环保持在轨迹上。
|
||||
|
||||
在 Player 类中创建如下变量。开头两行作为上下文对照(如果你一直跟着做,你的代码中就已经有这两行),因此只需要添加最后三行:
|
||||
在 `Player` 类中创建如下变量。开头两行作为上下文对照(如果你一直跟着做,你的代码中就已经有这两行),因此只需要添加最后三行:
|
||||
|
||||
```
|
||||
def __init__(self):
|
||||
@ -91,9 +92,9 @@ $ python ./your-name_game.py
|
||||
|
||||
设置好了这些变量,是时候去为妖精移动编写代码了。
|
||||
|
||||
玩家妖精不需要时刻响应控制,优势它并没有在移动。控制妖精的代码,仅仅只是玩家妖精所有能做的事情中的一小部分。在 Python 中,当你想要使一个对象做某件事并独立于剩余其他代码时,你可以将你的新代码放入一个函数。Python 的函数以关键词 `def` 开头,(该关键词)代表了定义函数。
|
||||
玩家妖精不需要时刻响应控制,有时它并没有在移动。控制妖精的代码,仅仅只是玩家妖精所有能做的事情中的一小部分。在 Python 中,当你想要使一个对象做某件事并独立于剩余其他代码时,你可以将你的新代码放入一个函数。Python 的函数以关键词 `def` 开头,(该关键词)代表了定义函数。
|
||||
|
||||
在你的 Player 类中创建如下函数,来为你的妖精在屏幕上的位置增加几个像素。现在先不要担心你增加几个像素,这将在后续的代码中确定。
|
||||
在你的 `Player` 类中创建如下函数,来为你的妖精在屏幕上的位置增加几个像素。现在先不要担心你增加几个像素,这将在后续的代码中确定。
|
||||
|
||||
```
|
||||
def control(self,x,y):
|
||||
@ -154,7 +155,7 @@ steps = 10 # 移动多少个像素
|
||||
|
||||
现在你已经有了适当的函数和变量,使用你的按键来触发函数并将变量传递给你的妖精。
|
||||
|
||||
为此,将主循环中的 `print` 语句替换为玩家妖精的名字(player)、函数(.control)以及你希望玩家妖精在每个循环中沿 X 轴和 Y 轴移动的步数。
|
||||
为此,将主循环中的 `print` 语句替换为玩家妖精的名字(`player`)、函数(`.control`)以及你希望玩家妖精在每个循环中沿 X 轴和 Y 轴移动的步数。
|
||||
|
||||
```
|
||||
if event.type == pygame.KEYDOWN:
|
||||
@ -176,7 +177,7 @@ steps = 10 # 移动多少个像素
|
||||
main = False
|
||||
```
|
||||
|
||||
记住,`steps` 变量代表了当一个按键被按下时,你的妖精会移动多少个像素。如果当你按下 D 或右方向键时,你的妖精的位置增加了 10 个像素。那么当你停止按下这个键时,你必须(将 `step`)减 10(`-steps`)来使你的妖精的动量回到 0。
|
||||
记住,`steps` 变量代表了当一个按键被按下时,你的妖精会移动多少个像素。如果当你按下 `D` 或右方向键时,你的妖精的位置增加了 10 个像素。那么当你停止按下这个键时,你必须(将 `step`)减 10(`-steps`)来使你的妖精的动量回到 0。
|
||||
|
||||
现在尝试你的游戏。注意:它不会像你预想的那样运行。
|
||||
|
||||
@ -340,14 +341,14 @@ via: https://opensource.com/article/17/12/game-python-moving-player
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[cycoe](https://github.com/cycoe)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/article/17/10/python-101
|
||||
[2]: https://opensource.com/article/17/12/program-game-python-part-2-creating-game-world
|
||||
[3]: https://opensource.com/article/17/12/program-game-python-part-3-spawning-player
|
||||
[1]: https://linux.cn/article-9071-1.html
|
||||
[2]: https://linux.cn/article-10850-1.html
|
||||
[3]: https://linux.cn/article-10858-1.html
|
||||
[4]: http://pygame.org/docs/ref/joystick.html
|
||||
[5]: http://pygame.org/docs/ref/mouse.html#module-pygame.mouse
|
172
published/201905/20190107 Aliases- To Protect and Serve.md
Normal file
172
published/201905/20190107 Aliases- To Protect and Serve.md
Normal file
@ -0,0 +1,172 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10918-1.html)
|
||||
[#]: subject: (Aliases: To Protect and Serve)
|
||||
[#]: via: (https://www.linux.com/blog/learn/2019/1/aliases-protect-and-serve)
|
||||
[#]: author: (Paul Brown https://www.linux.com/users/bro66)
|
||||
|
||||
命令别名:保护和服务
|
||||
======
|
||||
|
||||
> Linux shell 允许你将命令彼此链接在一起,一次触发执行复杂的操作,并且可以对此创建别名作为快捷方式。
|
||||
|
||||
![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/prairie-path_1920.jpg?itok=wRARsM7p)
|
||||
|
||||
让我们将继续我们的别名系列。到目前为止,你可能已经阅读了我们的[关于别名的第一篇文章][1],并且应该非常清楚它们是如何为你省去很多麻烦的最简单方法。例如,你已经看到它们帮助我们减少了输入,让我们看看别名派上用场的其他几个案例。
|
||||
|
||||
### 别名即快捷方式
|
||||
|
||||
Linux shell 最美妙的事情之一是可以使用数以万计的选项和把命令连接在一起执行真正复杂的操作。好吧,也许这种美丽是在旁观者的眼中的,但是我们觉得这个功能很实用。
|
||||
|
||||
不利的一面是,你经常需要记得难以记忆或难以打字出来的命令组合。比如说硬盘上的空间非常宝贵,而你想要做一些清洁工作。你的第一步可能是寻找隐藏在你的家目录里的东西。你可以用来判断的一个标准是查找不再使用的内容。`ls` 可以帮助你:
|
||||
|
||||
```
|
||||
ls -lct
|
||||
```
|
||||
|
||||
上面的命令显示了每个文件和目录的详细信息(`-l`),并显示了每一项上次访问的时间(`-c`),然后它按从最近访问到最少访问的顺序排序这个列表(`-t`)。
|
||||
|
||||
这难以记住吗?你可能不会每天都使用 `-c` 和 `-t` 选项,所以也许是吧。无论如何,定义一个别名,如:
|
||||
|
||||
```
|
||||
alias lt='ls -lct'
|
||||
```
|
||||
|
||||
会更容易一些。
|
||||
|
||||
然后,你也可能希望列表首先显示最旧的文件:
|
||||
|
||||
```
|
||||
alias lo='lt -F | tac'
|
||||
```
|
||||
|
||||
![aliases][3]
|
||||
|
||||
*图 1:使用 lt 和 lo 别名。*
|
||||
|
||||
这里有一些有趣的事情。首先,我们使用别名(`lt`)来创建另一个别名 —— 这是完全可以的。其次,我们将一个新参数传递给 `lt`(后者又通过 `lt` 别名的定义传递给了 `ls`)。
|
||||
|
||||
`-F` 选项会将特殊符号附加到项目的名称后,以便更好地区分常规文件(没有符号)和可执行文件(附加了 `*`)、目录文件(以 `/` 结尾),以及所有链接文件、符号链接文件(以 `@` 符号结尾)等等。`-F` 选项是当你回归到单色终端的日子里,没有其他方法可以轻松看到列表项之间的差异时用的。在这里使用它是因为当你将输出从 `lt` 传递到 `tac` 时,你会丢失 `ls` 的颜色。
|
||||
|
||||
第三件我们需要注意的事情是我们使用了管道。管道用于你将一个命令的输出传递给另外一个命令时。第二个命令可以使用这些输出作为它的输入。在包括 Bash 在内的许多 shell 里,你可以使用管道符(`|`) 来做传递。
|
||||
|
||||
在这里,你将来自 `lt -F` 的输出导给 `tac`。`tac` 这个命令有点玩笑的意思,你或许听说过 `cat` 命令,它名义上用于将文件彼此连接(con`cat`),而在实践中,它被用于将一个文件的内容打印到终端。`tac` 做的事情一样,但是它是以逆序将接收到的内容输出出来。明白了吗?`cat` 和 `tac`,技术人有时候也挺有趣的。
|
||||
|
||||
`cat` 和 `tac` 都能输出通过管道传递过来的内容,在这里,也就是一个按时间顺序排序的文件列表。
|
||||
|
||||
那么,在有些离题之后,最终我们得到的就是这个列表将当前目录中的文件和目录以新鲜度的逆序列出(即老的在前)。
|
||||
|
||||
最后你需要注意的是,当在当前目录或任何目录运行 `lt` 时:
|
||||
|
||||
```
|
||||
# 这可以工作:
|
||||
lt
|
||||
# 这也可以:
|
||||
lt /some/other/directory
|
||||
```
|
||||
|
||||
……而 `lo` 只能在当前目录奏效:
|
||||
|
||||
```
|
||||
# 这可工作:
|
||||
lo
|
||||
# 而这不行:
|
||||
lo /some/other/directory
|
||||
```
|
||||
|
||||
这是因为 Bash 会展开别名的组分。当你键入:
|
||||
|
||||
```
|
||||
lt /some/other/directory
|
||||
```
|
||||
|
||||
Bash 实际上运行的是:
|
||||
|
||||
```
|
||||
ls -lct /some/other/directory
|
||||
```
|
||||
|
||||
这是一个有效的 Bash 命令。
|
||||
|
||||
而当你键入:
|
||||
|
||||
```
|
||||
lo /some/other/directory
|
||||
```
|
||||
|
||||
Bash 试图运行:
|
||||
|
||||
```
|
||||
ls -lct -F | tac /some/other/directory
|
||||
```
|
||||
|
||||
这不是一个有效的命令,主要是因为 `/some/other/directory` 是个目录,而 `cat` 和 `tac` 不能用于目录。
|
||||
|
||||
### 更多的别名快捷方式
|
||||
|
||||
* `alias lll='ls -R'` 会打印出目录的内容,并深入到子目录里面打印子目录的内容,以及子目录的子目录,等等。这是一个查看一个目录下所有内容的方式。
|
||||
* `mkdir='mkdir -pv'` 可以让你一次性创建目录下的目录。按照 `mkdir` 的基本形式,要创建一个包含子目录的目录,你必须这样:
|
||||
|
||||
```
|
||||
mkdir newdir
|
||||
mkdir newdir/subdir
|
||||
```
|
||||
|
||||
或这样:
|
||||
|
||||
```
|
||||
mkdir -p newdir/subdir
|
||||
```
|
||||
|
||||
而用这个别名你将只需要这样就行:
|
||||
|
||||
```
|
||||
mkdir newdir/subdir
|
||||
```
|
||||
|
||||
你的新 `mkdir` 也会告诉你创建子目录时都做了什么。
|
||||
|
||||
### 别名也是一种保护
|
||||
|
||||
别名的另一个好处是它可以作为防止你意外地删除或覆写已有的文件的保护措施。你可能听说过这个 Linux 新用户的传言,当他们以 root 身份运行:
|
||||
|
||||
```
|
||||
rm -rf /
|
||||
```
|
||||
|
||||
整个系统就爆了。而决定输入如下命令的用户:
|
||||
|
||||
```
|
||||
rm -rf /some/directory/ *
|
||||
```
|
||||
|
||||
就很好地干掉了他们的家目录的全部内容。这里不小心键入的目录和 `*` 之间的那个空格有时候很容易就会被忽视掉。
|
||||
|
||||
这两种情况我们都可以通过 `alias rm='rm -i'` 别名来避免。`-i` 选项会使 `rm` 询问用户是否真的要做这个操作,在你对你的文件系统做出不可弥补的损失之前给你第二次机会。
|
||||
|
||||
对于 `cp` 也是一样,它能够覆盖一个文件而不会给你任何提示。创建一个类似 `alias cp='cp -i'` 来保持安全吧。
|
||||
|
||||
### 下一次
|
||||
|
||||
我们越来越深入到了脚本领域,下一次,我们将沿着这个方向,看看如何在命令行组合命令以给你真正的乐趣,并可靠地解决系统管理员每天面临的问题。
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/blog/learn/2019/1/aliases-protect-and-serve
|
||||
|
||||
作者:[Paul Brown][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/users/bro66
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://linux.cn/article-10377-1.html
|
||||
[2]: https://www.linux.com/files/images/fig01png-0
|
||||
[3]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/fig01_0.png?itok=crqTm_va (aliases)
|
||||
[4]: https://www.linux.com/licenses/category/used-permission
|
@ -1,20 +1,20 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10884-1.html)
|
||||
[#]: subject: (Virtual filesystems in Linux: Why we need them and how they work)
|
||||
[#]: via: (https://opensource.com/article/19/3/virtual-filesystems-linux)
|
||||
[#]: author: (Alison Chariken )
|
||||
|
||||
Linux 中的虚拟文件系统
|
||||
详解 Linux 中的虚拟文件系统
|
||||
======
|
||||
|
||||
> 虚拟文件系统是一种神奇的抽象,它使得 “一切皆文件” 哲学在 Linux 中成为了可能。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/documents_papers_file_storage_work.png?itok=YlXpAqAJ)
|
||||
|
||||
什么是文件系统?根据早期的 Linux 贡献者和作家 [Robert Love][1] 所说,“文件系统是一个遵循特定结构的数据的分层存储。” 不过,这种描述也同样适用于 VFAT(虚拟文件分配表)、Git 和[Cassandra][2](一种 [NoSQL 数据库][3])。那么如何区别文件系统呢?
|
||||
什么是文件系统?根据早期的 Linux 贡献者和作家 [Robert Love][1] 所说,“文件系统是一个遵循特定结构的数据的分层存储。” 不过,这种描述也同样适用于 VFAT(<ruby>虚拟文件分配表<rt>Virtual File Allocation Table</rt></ruby>)、Git 和[Cassandra][2](一种 [NoSQL 数据库][3])。那么如何区别文件系统呢?
|
||||
|
||||
### 文件系统基础概念
|
||||
|
||||
@ -22,72 +22,73 @@ Linux 内核要求文件系统必须是实体,它还必须在持久对象上
|
||||
|
||||
![][5]
|
||||
|
||||
如果我们能够 `open()`、`read()` 和 `write()`,它就是一个文件,如这个主控台会话所示。
|
||||
*如果我们能够 `open()`、`read()` 和 `write()`,它就是一个文件,如这个主控台会话所示。*
|
||||
|
||||
VFS 是著名的类 Unix 系统中 “一切皆文件” 的基础。让我们看一下它有多奇怪,上面的小演示体现了字符设备 `/dev/console` 实际的工作。该图显示了一个在虚拟电传打字(tty)上的交互式 Bash 会话。将一个字符串发送到虚拟控制台设备会使其显示在虚拟屏幕上。而 VFS 甚至还有其它更奇怪的属性。例如,它[可以在其中寻址][6]。
|
||||
VFS 是著名的类 Unix 系统中 “一切皆文件” 概念的基础。让我们看一下它有多奇怪,上面的小小演示体现了字符设备 `/dev/console` 实际的工作。该图显示了一个在虚拟电传打字控制台(tty)上的交互式 Bash 会话。将一个字符串发送到虚拟控制台设备会使其显示在虚拟屏幕上。而 VFS 甚至还有其它更奇怪的属性。例如,它[可以在其中寻址][6]。
|
||||
|
||||
熟悉的文件系统如 ext4、NFS 和 /proc 在名为 [file_operations] [7] 的 C 语言数据结构中都提供了三大函数的定义。此外,特定的文件系统会以熟悉的面向对象的方式扩展和覆盖了 VFS 功能。正如 Robert Love 指出的那样,VFS 的抽象使 Linux 用户可以轻松地将文件复制到(复制自)外部操作系统或抽象实体(如管道),而无需担心其内部数据格式。在用户空间,通过系统调用,进程可以使用一个文件系统的 `read()`方法从文件复制到内核的数据结构中,然后使用另一种文件系统的 `write()` 方法输出数据。
|
||||
我们熟悉的文件系统如 ext4、NFS 和 /proc 都在名为 [file_operations] [7] 的 C 语言数据结构中提供了三大函数的定义。此外,个别的文件系统会以熟悉的面向对象的方式扩展和覆盖了 VFS 功能。正如 Robert Love 指出的那样,VFS 的抽象使 Linux 用户可以轻松地将文件复制到(或复制自)外部操作系统或抽象实体(如管道),而无需担心其内部数据格式。在用户空间这一侧,通过系统调用,进程可以使用文件系统方法之一 `read()` 从文件复制到内核的数据结构中,然后使用另一种文件系统的方法 `write()` 输出数据。
|
||||
|
||||
属于 VFS 基本类型的函数定义本身可以在内核源代码的 [fs/*.c 文件][8] 中找到,而 `fs/` 的子目录中包含了特定的文件系统。内核还包含了类似文件系统的实体,例如 cgroup、`/dev` 和 tmpfs,它们在引导过程的早期需要,因此定义在内核的 `init/` 子目录中。请注意,cgroup、`/dev` 和 tmpfs 不会调用 `file_operations` 的三大函数,而是直接读取和写入内存。
|
||||
属于 VFS 基本类型的函数定义本身可以在内核源代码的 [fs/*.c 文件][8] 中找到,而 `fs/` 的子目录中包含了特定的文件系统。内核还包含了类似文件系统的实体,例如 cgroup、`/dev` 和 tmpfs,在引导过程的早期需要它们,因此定义在内核的 `init/` 子目录中。请注意,cgroup、`/dev` 和 tmpfs 不会调用 `file_operations` 的三大函数,而是直接读取和写入内存。
|
||||
|
||||
下图大致说明了用户空间如何访问通常挂载在 Linux 系统上的各种类型的文件系统。未显示的是像管道、dmesg 和 POSIX 时钟这样的结构,它们也实现了 `struct file_operations`,并且因此其访问要通过 VFS 层。
|
||||
下图大致说明了用户空间如何访问通常挂载在 Linux 系统上的各种类型文件系统。像管道、dmesg 和 POSIX 时钟这样的结构在此图中未显示,它们也实现了 `struct file_operations`,而且其访问也要通过 VFS 层。
|
||||
|
||||
![How userspace accesses various types of filesystems][9]
|
||||
|
||||
VFS 是系统调用和特定 `file_operations` 的实现(如 ext4 和 procfs)之间的“垫片层”。然后,`file_operations` 函数可以与特定于设备的驱动程序或内存访问器进行通信。tmpfs、devtmpfs 和 cgroup 不使用 `file_operations` 而是直接访问内存。
|
||||
VFS 是个“垫片层”,位于系统调用和特定 `file_operations` 的实现(如 ext4 和 procfs)之间。然后,`file_operations` 函数可以与特定于设备的驱动程序或内存访问器进行通信。tmpfs、devtmpfs 和 cgroup 不使用 `file_operations` 而是直接访问内存。
|
||||
|
||||
VFS 的存在促进了代码重用,因为与文件系统相关的基本方法不需要由每种文件系统类型重新实现。代码重用是一种被广泛接受的软件工程最佳实践!唉,如果重用的代码[引入了严重的错误][10],那么继承常用方法的所有实现都会受到影响。
|
||||
VFS 的存在促进了代码重用,因为与文件系统相关的基本方法不需要由每种文件系统类型重新实现。代码重用是一种被广泛接受的软件工程最佳实践!唉,但是如果重用的代码[引入了严重的错误][10],那么继承常用方法的所有实现都会受到影响。
|
||||
|
||||
### /tmp:一个小提示
|
||||
|
||||
找出系统中存在的 VFS 的简单方法是键入 `mount | grep -v sd | grep -v :/`,在大多数计算机上,它将列出所有未驻留在磁盘上也不是 NFS 的已挂载文件系统。其中一个列出的 VFS 挂载肯定是 `/ tmp`,对吧?
|
||||
找出系统中存在的 VFS 的简单方法是键入 `mount | grep -v sd | grep -v :/`,在大多数计算机上,它将列出所有未驻留在磁盘上,同时也不是 NFS 的已挂载文件系统。其中一个列出的 VFS 挂载肯定是 `/tmp`,对吧?
|
||||
|
||||
![Man with shocked expression][11]
|
||||
|
||||
*每个人都知道把 /tmp 放在物理存储设备上简直是疯了!图片:<https://tinyurl.com/ybomxyfo>*
|
||||
*谁都知道把 /tmp 放在物理存储设备上简直是疯了!图片:<https://tinyurl.com/ybomxyfo>*
|
||||
|
||||
为什么把 `/tmp` 留在存储设备上是不可取的?因为 `/tmp` 中的文件是临时的(!),并且存储设备比内存慢,所以创建了 tmpfs 这种文件系统。此外,比起内存,物理设备频繁写入更容易磨损。最后,`/tmp` 中的文件可能包含敏感信息,因此在每次重新启动时让它们消失是一项功能。
|
||||
|
||||
不幸的是,默认情况下,某些 Linux 发行版的安装脚本仍会在存储设备上创建 /tmp。如果你的系统出现这种情况,请不要绝望。按照一直优秀的 [Arch Wiki][12] 上的简单说明来解决问题就行,记住分配给 tmpfs 的内存不能用于其他目的。换句话说,带有巨大 tmpfs 并且其中包含大文件的系统可能会耗尽内存并崩溃。另一个提示:编辑 `/etc/fstab` 文件时,请务必以换行符结束,否则系统将无法启动。(猜猜我怎么知道。)
|
||||
不幸的是,默认情况下,某些 Linux 发行版的安装脚本仍会在存储设备上创建 /tmp。如果你的系统出现这种情况,请不要绝望。按照一直优秀的 [Arch Wiki][12] 上的简单说明来解决问题就行,记住分配给 tmpfs 的内存就不能用于其他目的了。换句话说,包含了大文件的庞大的 tmpfs 可能会让系统耗尽内存并崩溃。
|
||||
|
||||
另一个提示:编辑 `/etc/fstab` 文件时,请务必以换行符结束,否则系统将无法启动。(猜猜我怎么知道。)
|
||||
|
||||
### /proc 和 /sys
|
||||
|
||||
除了 `/tmp` 之外,大多数 Linux 用户最熟悉的 VFS 是 `/proc` 和 `/sys`。(`/dev` 依赖于共享内存,没有 `file_operations`)。为什么有两种?让我们来看看更多细节。
|
||||
除了 `/tmp` 之外,大多数 Linux 用户最熟悉的 VFS 是 `/proc` 和 `/sys`。(`/dev` 依赖于共享内存,而没有 `file_operations` 结构)。为什么有两种呢?让我们来看看更多细节。
|
||||
|
||||
procfs 提供了内核的瞬时状态及其为用户空间控制的进程的快照。在 `/proc` 中,内核发布有关其提供的工具的信息,如中断、虚拟内存和调度程序。此外,`/proc/sys` 是存放可以通过 [sysctl 命令][13]配置的设置的地方,可供用户空间访问。单个进程的状态和统计信息在 `/proc/<PID>` 目录中报告。
|
||||
procfs 为用户空间提供了内核及其控制的进程的瞬时状态的快照。在 `/proc` 中,内核发布有关其提供的设施的信息,如中断、虚拟内存和调度程序。此外,`/proc/sys` 是存放可以通过 [sysctl 命令][13]配置的设置的地方,可供用户空间访问。单个进程的状态和统计信息在 `/proc/<PID>` 目录中报告。
|
||||
|
||||
![Console][14]
|
||||
|
||||
*/proc/meminfo 是一个空文件,但仍包含有价值的信息。*
|
||||
|
||||
`/proc` 文件的行为说明了 VFS 可以与磁盘上的文件系统不同。一方面,`/proc/meminfo` 包含命令 `free` 提供的信息。另一方面,它还是空的!怎么会这样?这种情况让人联想起康奈尔大学物理学家 N. David Mermin 在 1985 年写的一篇名为“[没有人看见月亮的情况吗?][15]现实和量子理论。”事实是当进程从 `/proc` 请求内存时内核再收集有关内存的统计信息,并且当没有人在查看时,`/proc` 中的文件实际上没有任何内容。正如 [Mermin 所说][16],“这是一个基本的量子学说,一般来说,测量不会揭示被测属性的预先存在的价值。”(关于月球的问题的答案留作练习。)
|
||||
`/proc` 文件的行为说明了 VFS 可以与磁盘上的文件系统不同。一方面,`/proc/meminfo` 包含了可由命令 `free` 展现出来的信息。另一方面,它还是空的!怎么会这样?这种情况让人联想起康奈尔大学物理学家 N. David Mermin 在 1985 年写的一篇名为《[没有人看见月亮的情况吗?][15]现实和量子理论》。事实是当进程从 `/proc` 请求数据时内核再收集有关内存的统计信息,而且当没有人查看它时,`/proc` 中的文件实际上没有任何内容。正如 [Mermin 所说][16],“这是一个基本的量子学说,一般来说,测量不会揭示被测属性的预先存在的价值。”(关于月球的问题的答案留作练习。)
|
||||
|
||||
![Full moon][17]
|
||||
|
||||
*当没有进程访问它们时,/proc 中的文件为空。([来源][18])*
|
||||
|
||||
procfs 的空文件是有道理的,因为那里可用的信息是动态的。sysfs 的情况不同。让我们比较一下 `/proc` 与 `/sys` 中不为空的文件数量。
|
||||
procfs 的空文件是有道理的,因为那里可用的信息是动态的。sysfs 的情况则不同。让我们比较一下 `/proc` 与 `/sys` 中不为空的文件数量。
|
||||
|
||||
![](https://opensource.com/sites/default/files/uploads/virtualfilesystems_6-filesize.png)
|
||||
|
||||
procfs 只有一个,即导出的内核配置,这是一个例外,因为每次启动只需要生成一次。另一方面,`/sys` 有许多较大的文件,其中大多数包含一页内存。通常,sysfs 文件只包含一个数字或字符串,与通过读取 `/proc/meminfo` 等文件生成的信息表格形成鲜明对比。
|
||||
procfs 只有一个不为空的文件,即导出的内核配置,这是一个例外,因为每次启动只需要生成一次。另一方面,`/sys` 有许多更大一些的文件,其中大多数由一页内存组成。通常,sysfs 文件只包含一个数字或字符串,与通过读取 `/proc/meminfo` 等文件生成的信息表格形成鲜明对比。
|
||||
|
||||
sysfs 的目的是将内核称为“kobjects”的可读写属性公开给用户空间。kobjects 的唯一目的是引用计数:当删除对 kobject 的最后一个引用时,系统将回收与之关联的资源。然而,`/sys` 构成了内核著名的“[到用户空间的稳定 ABI][19]”,它的大部分内容[在任何情况下都没有人会“破坏”][20]。这并不意味着 sysfs 中的文件是静态,这与易失性对象的引用计数相反。
|
||||
sysfs 的目的是将内核称为 “kobject” 的可读写属性公开给用户空间。kobject 的唯一目的是引用计数:当删除对 kobject 的最后一个引用时,系统将回收与之关联的资源。然而,`/sys` 构成了内核著名的“[到用户空间的稳定 ABI][19]”,它的大部分内容[在任何情况下都没有人能“破坏”][20]。但这并不意味着 sysfs 中的文件是静态,这与易失性对象的引用计数相反。
|
||||
|
||||
内核的稳定 ABI 反而限制了 `/sys` 中可能出现的内容,而不是任何给定时刻实际存在的内容。列出 sysfs 中文件的权限可以了解如何设置或读取设备、模块、文件系统等的可配置、可调参数。Logic 强调 procfs 也是内核稳定 ABI 的一部分的结论,尽管内核的[文档][19]没有明确说明。
|
||||
内核的稳定 ABI 限制了 `/sys` 中可能出现的内容,而不是任何给定时刻实际存在的内容。列出 sysfs 中文件的权限可以了解如何设置或读取设备、模块、文件系统等的可配置、可调参数。逻辑上强调 procfs 也是内核稳定 ABI 的一部分的结论,尽管内核的[文档][19]没有明确说明。
|
||||
|
||||
![Console][21]
|
||||
|
||||
*sysfs 中的文件恰好描述了实体的每个属性,并且可以是可读的、可写的或两者兼而有之。文件中的“0”表示 SSD 不可移动的存储设备。*
|
||||
*sysfs 中的文件确切地描述了实体的每个属性,并且可以是可读的、可写的,或两者兼而有之。文件中的“0”表示 SSD 不可移动的存储设备。*
|
||||
|
||||
### 用 eBPF 和 bcc 工具一窥 VFS 内部
|
||||
|
||||
了解内核如何管理 sysfs 文件的最简单方法是观察它的运行情况,在 ARM64 或 x86_64 上观看的最简单方法是使用 eBPF。eBPF(<ruby>扩展的伯克利数据包过滤器<rt>extended Berkeley Packet Filter</rt></ruby>)由[在内核中运行的虚拟机][22]组成,特权用户可以从命令行进行查询。内核源代码告诉读者内核可以做什么;在一个启动的系统上运行 eBPF 工具会显示内核实际上做了什么。
|
||||
了解内核如何管理 sysfs 文件的最简单方法是观察它的运行情况,在 ARM64 或 x86_64 上观看的最简单方法是使用 eBPF。eBPF(<ruby>扩展的伯克利数据包过滤器<rt>extended Berkeley Packet Filter</rt></ruby>)由[在内核中运行的虚拟机][22]组成,特权用户可以从命令行进行查询。内核源代码告诉读者内核可以做什么;而在一个启动的系统上运行 eBPF 工具会显示内核实际上做了什么。
|
||||
|
||||
令人高兴的是,通过 [bcc][23] 工具入门使用 eBPF 非常容易,这些工具在[主要 Linux 发行版的软件包][24] 中都有,并且已经由 Brendan Gregg [充分地给出了文档说明][25]。bcc 工具是带有小段嵌入式 C 语言片段的 Python 脚本,这意味着任何对这两种语言熟悉的人都可以轻松修改它们。当前统计,[bcc/tools 中有 80 个 Python 脚本][26],使系统管理员或开发人员很有可能能够找到与她/他的需求相关的现有脚本。
|
||||
|
||||
要了解 VFS 在正在运行的系统上的工作情况,请尝试使用简单的 [vfscount][27] 或 [vfsstat][28],这表明每秒都会发生数十次对 `vfs_open()` 及其相关的调用
|
||||
令人高兴的是,通过 [bcc][23] 工具入门使用 eBPF 非常容易,这些工具在[主要 Linux 发行版的软件包][24] 中都有,并且已经由 Brendan Gregg [给出了充分的文档说明][25]。bcc 工具是带有小段嵌入式 C 语言片段的 Python 脚本,这意味着任何对这两种语言熟悉的人都可以轻松修改它们。据当前统计,[bcc/tools 中有 80 个 Python 脚本][26],使得系统管理员或开发人员很有可能能够找到与她/他的需求相关的已有脚本。
|
||||
|
||||
要了解 VFS 在正在运行中的系统上的工作情况,请尝试使用简单的 [vfscount][27] 或 [vfsstat][28] 脚本,这可以看到每秒都会发生数十次对 `vfs_open()` 及其相关的调用。
|
||||
|
||||
![Console - vfsstat.py][29]
|
||||
|
||||
@ -95,37 +96,35 @@ sysfs 的目的是将内核称为“kobjects”的可读写属性公开给用户
|
||||
|
||||
作为一个不太重要的例子,让我们看一下在运行的系统上插入 USB 记忆棒时 sysfs 中会发生什么。
|
||||
|
||||
|
||||
![Console when USB is inserted][30]
|
||||
|
||||
*用 eBPF 观察插入 USB 记忆棒时 /sys 中会发生什么,简单的和复杂的例子。*
|
||||
|
||||
在上面的第一个简单示例中,只要 `sysfs_create_files()` 命令运行,[trace.py][31] bcc 工具脚本就会打印出一条消息。我们看到 `sysfs_create_files()` 由一个 kworker 线程启动,以响应 USB 棒插入事件,但是它创建了什么文件?第二个例子说明了 eBPF 的强大能力。这里,`trace.py` 正在打印内核回溯(`-K` 选项)以及 `sysfs_create_files()` 创建的文件的名称。单引号内的代码段是一些 C 源代码,包括一个易于识别的格式字符串,提供的 Python 脚本[引入 LLVM 即时编译器(JIT)][32] 在内核虚拟机内编译和执行它。必须在第二个命令中重现完整的 `sysfs_create_files()` 函数签名,以便格式字符串可以引用其中一个参数。在此 C 片段中出错会导致可识别的 C 编译器错误。例如,如果省略 `-I` 参数,则结果为“无法编译 BPF 文本”。熟悉 C 或 Python 的开发人员会发现 bcc 工具易于扩展和修改。
|
||||
在上面的第一个简单示例中,只要 `sysfs_create_files()` 命令运行,[trace.py][31] bcc 工具脚本就会打印出一条消息。我们看到 `sysfs_create_files()` 由一个 kworker 线程启动,以响应 USB 棒的插入事件,但是它创建了什么文件?第二个例子说明了 eBPF 的强大能力。这里,`trace.py` 正在打印内核回溯(`-K` 选项)以及 `sysfs_create_files()` 创建的文件的名称。单引号内的代码段是一些 C 源代码,包括一个易于识别的格式字符串,所提供的 Python 脚本[引入 LLVM 即时编译器(JIT)][32] 来在内核虚拟机内编译和执行它。必须在第二个命令中重现完整的 `sysfs_create_files()` 函数签名,以便格式字符串可以引用其中一个参数。在此 C 片段中出错会导致可识别的 C 编译器错误。例如,如果省略 `-I` 参数,则结果为“无法编译 BPF 文本”。熟悉 C 或 Python 的开发人员会发现 bcc 工具易于扩展和修改。
|
||||
|
||||
插入 USB 记忆棒后,内核回溯显示 PID 7711 是一个 kworker 线程,它在 sysfs 中创建了一个名为 `events` 的文件。使用 `sysfs_remove_files()` 进行相应的调用表明,删除 USB 记忆棒会导致删除该 `events` 文件,这与引用计数的想法保持一致。在 USB 棒插入期间(未显示)在 eBPF 中观察 `sysfs_create_link()` 表明创建了不少于 48 个符号链接。
|
||||
|
||||
无论如何,`events` 文件的目的是什么?使用 [cscope][33] 查找函数 [`__device_add_disk()`][34] 显示它调用 `disk_add_events()`,并且可以将 “media_change” 或 “eject_request” 写入到该文件。这里,内核的块层通知用户空间 “磁盘” 的出现和消失。考虑一下这种调查 USB 棒插入工作原理的方法与试图仅从源头中找出该过程的速度有多快。
|
||||
无论如何,`events` 文件的目的是什么?使用 [cscope][33] 查找函数 [`__device_add_disk()`][34] 显示它调用 `disk_add_events()`,并且可以将 “media_change” 或 “eject_request” 写入到该文件。这里,内核的块层通知用户空间该 “磁盘” 的出现和消失。考虑一下这种检查 USB 棒的插入的工作原理的方法与试图仅从源头中找出该过程的速度有多快。
|
||||
|
||||
### 只读根文件系统使得嵌入式设备成为可能
|
||||
|
||||
确实,没有人通过拔出电源插头来关闭服务器或桌面系统。为什么?因为物理存储设备上挂载的文件系统可能有挂起的(未完成的)写入,并且记录其状态的数据结构可能与写入存储器的内容不同步。当发生这种情况时,系统所有者将不得不在下次启动时等待 [fsck 文件系统恢复工具][35] 运行完成,在最坏的情况下,实际上会丢失数据。
|
||||
|
||||
然而,狂热爱好者会听说许多物联网和嵌入式设备,如路由器、恒温器和汽车现在都运行 Linux。许多这些设备几乎完全没有用户界面,并且没有办法干净地“解除启动”它们。想一想使用启动电池耗尽的汽车,其中[运行 Linux 的主机设备][36] 的电源会不断加电断电。当引擎最终开始运行时,系统如何在没有长时间 fsck 的情况下启动呢?答案是嵌入式设备依赖于[只读根文件系统][37](简称 ro-rootfs)。
|
||||
|
||||
然而,狂热爱好者会听说许多物联网和嵌入式设备,如路由器、恒温器和汽车现在都运行着 Linux。许多这些设备几乎完全没有用户界面,并且没有办法干净地让它们“解除启动”。想一想启动电池耗尽的汽车,其中[运行 Linux 的主机设备][36] 的电源会不断加电断电。当引擎最终开始运行时,系统如何在没有长时间 fsck 的情况下启动呢?答案是嵌入式设备依赖于[只读根文件系统][37](简称 ro-rootfs)。
|
||||
|
||||
![Photograph of a console][38]
|
||||
|
||||
*ro-rootfs 是嵌入式系统不经常需要 fsck 的原因。 来源:<https://tinyurl.com/yxoauoub>*
|
||||
|
||||
ro-rootfs 提供了许多优点,虽然这些优点不如耐用性那么显然。一个是,如果没有 Linux 进程可以写入,那么恶意软件无法写入 `/usr` 或 `/lib`。另一个是,基本上不可变的文件系统对于远程设备的现场支持至关重要,因为支持人员拥有名义上与现场相同的本地系统。也许最重要(但也是最微妙)的优势是 ro-rootfs 迫使开发人员在项目的设计阶段就决定哪些系统对象是不可变的。处理 ro-rootfs 可能经常是不方便甚至是痛苦的,[编程语言中的常量变量][39]经常就是这样,但带来的好处很容易偿还额外的开销。
|
||||
ro-rootfs 提供了许多优点,虽然这些优点不如耐用性那么显然。一个是,如果 Linux 进程不可以写入,那么恶意软件也无法写入 `/usr` 或 `/lib`。另一个是,基本上不可变的文件系统对于远程设备的现场支持至关重要,因为支持人员拥有理论上与现场相同的本地系统。也许最重要(但也是最微妙)的优势是 ro-rootfs 迫使开发人员在项目的设计阶段就决定好哪些系统对象是不可变的。处理 ro-rootfs 可能经常是不方便甚至是痛苦的,[编程语言中的常量变量][39]经常就是这样,但带来的好处很容易偿还这种额外的开销。
|
||||
|
||||
对于嵌入式开发人员,创建只读根文件系统确实需要做一些额外的工作,而这正是 VFS 的用武之地。Linux 需要 `/var` 中的文件可写,此外,嵌入式系统运行的许多流行应用程序将尝试在 `$HOME` 中创建配置点文件。放在家目录中的配置文件的一种解决方案通常是预生成它们并将它们构建到 rootfs 中。对于 `/var`,一种方法是将其挂载在单独的可写分区上,而 `/` 本身以只读方式挂载。使用绑定或叠加挂载是另一种流行的替代方案。
|
||||
对于嵌入式开发人员,创建只读根文件系统确实需要做一些额外的工作,而这正是 VFS 的用武之地。Linux 需要 `/var` 中的文件可写,此外,嵌入式系统运行的许多流行应用程序会尝试在 `$HOME` 中创建配置的点文件。放在家目录中的配置文件的一种解决方案通常是预生成它们并将它们构建到 rootfs 中。对于 `/var`,一种方法是将其挂载在单独的可写分区上,而 `/` 本身以只读方式挂载。使用绑定或叠加挂载是另一种流行的替代方案。
|
||||
|
||||
### 绑定和叠加挂载以及在容器中的使用
|
||||
|
||||
运行 [man mount][40] 是了解<ruby>绑定挂载<rt>bind mount</rt></ruby>和<ruby>叠加挂载<rt>overlay mount</rt></ruby>的最好办法,这使嵌入式开发人员和系统管理员能够在一个路径位置创建文件系统,然后在另外一个路径将其提供给应用程序。对于嵌入式系统,这代表着可以将文件存储在 `/var` 中的不可写闪存设备上,但是在启动时将 tmpfs 中的路径叠加挂载或绑定挂载到 `/var` 路径上,这样应用程序就可以在那里随意写它们的内容了。下次加电时,`/var` 中的变化将会消失。叠加挂载提供了 tmpfs 和底层文件系统之间的联合,允许对 ro-rootfs 中的现有文件进行直接修改,而绑定挂载可以使新的空 tmpfs 目录在 ro-rootfs 路径中显示为可写。虽然叠加文件系统是一种适当的文件系统类型,但绑定挂载由 [VFS 命名空间工具][41] 实现的。
|
||||
运行 [man mount][40] 是了解<ruby>绑定挂载<rt>bind mount</rt></ruby>和<ruby>叠加挂载<rt>overlay mount</rt></ruby>的最好办法,这种方法使得嵌入式开发人员和系统管理员能够在一个路径位置创建文件系统,然后以另外一个路径将其提供给应用程序。对于嵌入式系统,这代表着可以将文件存储在 `/var` 中的不可写闪存设备上,但是在启动时将 tmpfs 中的路径叠加挂载或绑定挂载到 `/var` 路径上,这样应用程序就可以在那里随意写它们的内容了。下次加电时,`/var` 中的变化将会消失。叠加挂载为 tmpfs 和底层文件系统提供了联合,允许对 ro-rootfs 中的现有文件进行直接修改,而绑定挂载可以使新的空 tmpfs 目录在 ro-rootfs 路径中显示为可写。虽然叠加文件系统是一种适当的文件系统类型,而绑定挂载由 [VFS 命名空间工具][41] 实现的。
|
||||
|
||||
根据叠加挂载和绑定挂载的描述,没有人会对 [Linux容器][42] 大量使用它们感到惊讶。让我们通过运行 bcc 的 `mountsnoop` 工具监视当使用 [systemd-nspawn][43] 启动容器时会发生什么:
|
||||
根据叠加挂载和绑定挂载的描述,没有人会对 [Linux 容器][42] 中大量使用它们感到惊讶。让我们通过运行 bcc 的 `mountsnoop` 工具监视当使用 [systemd-nspawn][43] 启动容器时会发生什么:
|
||||
|
||||
![Console - system-nspawn invocation][44]
|
||||
|
||||
@ -135,13 +134,13 @@ ro-rootfs 提供了许多优点,虽然这些优点不如耐用性那么显然
|
||||
|
||||
![Console - Running mountsnoop][45]
|
||||
|
||||
在容器 “启动” 期间运行 `mountsnoop` 可以看到容器运行时很大程度上依赖于绑定挂载。(仅显示冗长输出的开头)
|
||||
*在容器 “启动” 期间运行 `mountsnoop` 可以看到容器运行时很大程度上依赖于绑定挂载。(仅显示冗长输出的开头)*
|
||||
|
||||
这里,`systemd-nspawn` 将主机的 procfs 和 sysfs 中的选定文件其 rootfs 中的路径提供给容器按。除了设置绑定挂载时的 `MS_BIND` 标志之外,`mount` 系统调用的一些其他标志用于确定主机命名空间和容器中的更改之间的关系。例如,绑定挂载可以将 `/proc` 和 `/sys` 中的更改传播到容器,也可以隐藏它们,具体取决于调用。
|
||||
这里,`systemd-nspawn` 将主机的 procfs 和 sysfs 中的选定文件按其 rootfs 中的路径提供给容器。除了设置绑定挂载时的 `MS_BIND` 标志之外,`mount` 系统调用的一些其它标志用于确定主机命名空间和容器中的更改之间的关系。例如,绑定挂载可以将 `/proc` 和 `/sys` 中的更改传播到容器,也可以隐藏它们,具体取决于调用。
|
||||
|
||||
### 总结
|
||||
|
||||
理解 Linux 内部结构似乎是一项不可能完成的任务,因为除了 Linux 用户空间应用程序和 glibc 这样的 C 库中的系统调用接口,内核本身也包含大量代码。取得进展的一种方法是阅读一个内核子系统的源代码,重点是理解面向用户空间的系统调用和头文件以及主要的内核内部接口,这里以 `file_operations` 表为例的。`file_operations` 使得“一切都是文件”可以实际工作,因此掌握它们收获特别大。顶级 `fs/` 目录中的内核 C 源文件构成了虚拟文件系统的实现,虚拟文件系统是支持流行的文件系统和存储设备的广泛且相对简单的互操作性的垫片层。通过 Linux 命名空间进行绑定挂载和覆盖挂载是 VFS 魔术,它使容器和只读根文件系统成为可能。结合对源代码的研究,eBPF 内核工具及其 bcc 接口使得探测内核比以往任何时候都更简单。
|
||||
理解 Linux 内部结构看似是一项不可能完成的任务,因为除了 Linux 用户空间应用程序和 glibc 这样的 C 库中的系统调用接口,内核本身也包含大量代码。取得进展的一种方法是阅读一个内核子系统的源代码,重点是理解面向用户空间的系统调用和头文件以及主要的内核内部接口,这里以 `file_operations` 表为例。`file_operations` 使得“一切都是文件”得以可以实际工作,因此掌握它们收获特别大。顶级 `fs/` 目录中的内核 C 源文件构成了虚拟文件系统的实现,虚拟文件系统是支持流行的文件系统和存储设备的广泛且相对简单的互操作性的垫片层。通过 Linux 命名空间进行绑定挂载和覆盖挂载是 VFS 魔术,它使容器和只读根文件系统成为可能。结合对源代码的研究,eBPF 内核工具及其 bcc 接口使得探测内核比以往任何时候都更简单。
|
||||
|
||||
非常感谢 [Akkana Peck][46] 和 [Michael Eager][47] 的评论和指正。
|
||||
|
||||
@ -154,7 +153,7 @@ via: https://opensource.com/article/19/3/virtual-filesystems-linux
|
||||
作者:[Alison Chariken][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,56 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10914-1.html)
|
||||
[#]: subject: (Blockchain 2.0: Blockchain In Real Estate [Part 4])
|
||||
[#]: via: (https://www.ostechnix.com/blockchain-2-0-blockchain-in-real-estate/)
|
||||
[#]: author: (ostechnix https://www.ostechnix.com/author/editor/)
|
||||
|
||||
区块链 2.0:房地产区块链(四)
|
||||
======
|
||||
|
||||
![](https://www.ostechnix.com/wp-content/uploads/2019/03/Blockchain-In-Real-Estate-720x340.png)
|
||||
|
||||
### 区块链 2.0:“更”智能的房地产
|
||||
|
||||
在本系列的[上一篇文章][1]中我们探讨了区块链的特征,这些区块链将使机构能够将**传统银行**和**融资系统**转换和交织在一起。这部分将探讨**房地产区块链**。房地产业正在走向革命。它是人类已知的交易最活跃、最重要的资产类别之一。然而,由于充满了监管障碍和欺诈、欺骗的无数可能性,它也是最难参与交易的之一。利用适当的共识算法的区块链的分布式分类账本功能被吹捧为这个行业的前进方向,而这个行业传统上被认为其面对变革是保守的。
|
||||
|
||||
就其无数的业务而言,房地产一直是一个非常保守的行业。这似乎也是理所当然的。2008 年金融危机或 20 世纪上半叶的大萧条等重大经济危机成功摧毁了该行业及其参与者。然而,与大多数具有经济价值的产品一样,房地产行业具有弹性,而这种弹性则源于其保守性。
|
||||
|
||||
全球房地产市场由价值 228 万亿 [^1] 美元的资产类别组成,出入不大。其他投资资产,如股票、债券和股票合计价值仅为 170 万亿美元。显然,在这样一个行业中实施的交易在很大程度上都是精心策划和执行的。很多时候,房地产也因许多欺诈事件而臭名昭著,并且随之而来的是毁灭性的损失。由于其运营非常保守,该行业也难以驾驭。它受到了法律的严格监管,创造了一个交织在一起的细微差别网络,这对于普通人来说太难以完全理解,使得大多数人无法进入和参与。如果你曾参与过这样的交易,那么你就会知道纸质文件的重要性和长期性。
|
||||
|
||||
从一个微不足道的开始,虽然是一个重要的例子,以显示当前的记录管理实践在房地产行业有多糟糕,考虑一下[产权保险业务][2] [^3]。产权保险用于对冲土地所有权和所有权记录不可接受且从而无法执行的可能性。诸如此类的保险产品也称为赔偿保险。在许多情况下,法律要求财产拥有产权保险,特别是在处理多年来多次易手的财产时。抵押贷款公司在支持房地产交易时也可能坚持同样的要求。事实上,这种产品自 19 世纪 50 年代就已存在,并且仅在美国每年至少有 1.5 万亿美元的商业价值这一事实证明了一开始的说法。在这种情况下,这些记录的维护方式必须进行改革,区块链提供了一个可持续解决方案。根据[美国土地产权协会][4],平均每个案例的欺诈平均约为 10 万美元,并且涉及交易的所有产权中有 25% 的文件存在问题。区块链允许设置一个不可变的永久数据库,该数据库将跟踪资产本身,记录已经进入的每个交易或投资。这样的分类帐本系统将使包括一次性购房者在内的房地产行业的每个人的生活更加轻松,并使诸如产权保险等金融产品基本上无关紧要。将诸如房地产之类的实物资产转换为这样的数字资产是非常规的,并且目前仅在理论上存在。然而,这种变化迫在眉睫,而不是迟到 [^5]。
|
||||
|
||||
区块链在房地产中影响最大的领域如上所述,在维护透明和安全的产权管理系统方面。基于区块链的财产记录可以包含有关财产、其所在地、所有权历史以及相关的公共记录的[信息][6]。这将允许房地产交易快速完成,并且无需第三方监控和监督。房地产评估和税收计算等任务成为有形的、客观的参数问题,而不是主观测量和猜测,因为可靠的历史数据是可公开验证的。[UBITQUITY][7] 就是这样一个平台,为企业客户提供定制的基于区块链的解决方案。该平台允许客户跟踪所有房产细节、付款记录、抵押记录,甚至允许运行智能合约,自动处理税收和租赁。
|
||||
|
||||
这为我们带来了房地产区块链的第二大机遇和用例。由于该行业受到众多第三方的高度监管,除了参与交易的交易对手外,尽职调查和财务评估可能非常耗时。这些流程主要通过离线渠道进行,文书工作需要在最终评估报告出来之前进行数天。对于公司房地产交易尤其如此,这构成了顾问所收取的总计费时间的大部分。如果交易由抵押背书,则这些过程的重复是不可避免的。一旦与所涉及的人员和机构的数字身份相结合,就可以完全避免当前的低效率,并且可以在几秒钟内完成交易。租户、投资者、相关机构、顾问等可以单独验证数据并达成一致的共识,从而验证永久性的财产记录 [^8]。这提高了验证流程的准确性。房地产巨头 RE/MAX 最近宣布与服务提供商 XYO Network Partners 合作,[建立墨西哥房上市地产国家数据库][9]。他们希望有朝一日能够创建世界上最大的(截至目前)去中心化房地产登记中心之一。
|
||||
|
||||
然而,区块链可以带来的另一个重要且可以说是非常民主的变化是投资房地产。与其他投资资产类别不同,即使是小型家庭投资者也可能参与其中,房地产通常需要大量的手工付款才能参与。诸如 ATLANT 和 BitOfProperty 之类的公司将房产的账面价值代币化,并将其转换为加密货币的等价物。这些代币随后在交易所出售,类似于股票和股票的交易方式。[房地产后续产生的任何现金流都会根据其在财产中的“份额”记入贷方或借记给代币所有者][4]。
|
||||
|
||||
然而,尽管如此,区块链技术仍处于房地产领域的早期采用阶段,目前的法规还没有明确定义它。诸如分布式应用程序、分布式匿名组织(DAO)、智能合约等概念在许多国家的法律领域是闻所未闻的。一旦所有利益相关者充分接受了区块链复杂性的良好教育,就会彻底改革现有的法规和指导方针,这是最务实的前进方式。 同样,这将是一个缓慢而渐进的变化,但是它是一个急需的变化。本系列的下一篇文章将介绍 “智能合约”,例如由 UBITQUITY 和 XYO 等公司实施的那些是如何在区块链中创建和执行的。
|
||||
|
||||
[^1]: HSBC, “Global Real Estate,” no. April, 2008
|
||||
[^3]: D. B. Burke, Law of title insurance. Aspen Law & Business, 2000.
|
||||
[^5]: M. Swan, O’Reilly – Blockchain. Blueprint for a New Economy – 2015.
|
||||
[^8]: Deloite, “Blockchain in commercial real estate The future is here ! Table of contents.”
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/blockchain-2-0-blockchain-in-real-estate/
|
||||
|
||||
作者:[ostechnix][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/editor/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://linux.cn/article-10689-1.html
|
||||
[2]: https://www.forbes.com/sites/jordanlulich/2018/06/21/what-is-title-insurance-and-why-its-important/#1472022b12bb
|
||||
[4]: https://www.cbinsights.com/research/blockchain-real-estate-disruption/#financing
|
||||
[6]: https://www2.deloitte.com/us/en/pages/financial-services/articles/blockchain-in-commercial-real-estate.html
|
||||
[7]: https://www.ubitquity.io/
|
||||
[9]: https://www.businesswire.com/news/home/20181012005068/en/XYO-Network-Partners-REMAX-M%C3%A9xico-Bring-Blockchain
|
@ -0,0 +1,173 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10916-1.html)
|
||||
[#]: subject: (How to manage your Linux environment)
|
||||
[#]: via: (https://www.networkworld.com/article/3385516/how-to-manage-your-linux-environment.html)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
如何管理你的 Linux 环境变量
|
||||
======
|
||||
|
||||
> Linux 用户环境变量可以帮助你找到你需要的命令,无须了解系统如何配置的细节而完成大量工作。而这些设置来自哪里和如何被修改它们是另一个话题。
|
||||
|
||||
![IIP Photo Archive \(CC BY 2.0\)][1]
|
||||
|
||||
在 Linux 系统上的用户账户配置以多种方法简化了系统的使用。你可以运行命令,而不需要知道它们的位置。你可以重新使用先前运行的命令,而不用发愁系统是如何追踪到它们的。你可以查看你的电子邮件,查看手册页,并容易地回到你的家目录,而不用管你在文件系统中身在何方。并且,当需要的时候,你可以调整你的账户设置,以便其更符合你喜欢的方式。
|
||||
|
||||
Linux 环境设置来自一系列的文件:一些是系统范围(意味着它们影响所有用户账户),一些是处于你的家目录中的配置文件里。系统范围的设置在你登录时生效,而本地设置在其后生效,所以,你在你账户中作出的更改将覆盖系统范围设置。对于 bash 用户,这些文件包含这些系统文件:
|
||||
|
||||
```
|
||||
/etc/environment
|
||||
/etc/bash.bashrc
|
||||
/etc/profile
|
||||
```
|
||||
|
||||
以及一些本地文件:
|
||||
|
||||
```
|
||||
~/.bashrc
|
||||
~/.profile # 如果有 ~/.bash_profile 或 ~/.bash_login 就不会读此文件
|
||||
~/.bash_profile
|
||||
~/.bash_login
|
||||
```
|
||||
|
||||
你可以修改本地存在的四个文件的任何一个,因为它们处于你的家目录,并且它们是属于你的。
|
||||
|
||||
### 查看你的 Linux 环境设置
|
||||
|
||||
为查看你的环境设置,使用 `env` 命令。你的输出将可能与这相似:
|
||||
|
||||
```
|
||||
$ env
|
||||
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;
|
||||
01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:
|
||||
*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:
|
||||
*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:
|
||||
*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;
|
||||
31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:
|
||||
*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:
|
||||
*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:
|
||||
*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:
|
||||
*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:
|
||||
*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:
|
||||
*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:
|
||||
*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:
|
||||
*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:
|
||||
*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:
|
||||
*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:
|
||||
*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:
|
||||
*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.spf=00;36:
|
||||
SSH_CONNECTION=192.168.0.21 34975 192.168.0.11 22
|
||||
LESSCLOSE=/usr/bin/lesspipe %s %s
|
||||
LANG=en_US.UTF-8
|
||||
OLDPWD=/home/shs
|
||||
XDG_SESSION_ID=2253
|
||||
USER=shs
|
||||
PWD=/home/shs
|
||||
HOME=/home/shs
|
||||
SSH_CLIENT=192.168.0.21 34975 22
|
||||
XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
|
||||
SSH_TTY=/dev/pts/0
|
||||
MAIL=/var/mail/shs
|
||||
TERM=xterm
|
||||
SHELL=/bin/bash
|
||||
SHLVL=1
|
||||
LOGNAME=shs
|
||||
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
|
||||
XDG_RUNTIME_DIR=/run/user/1000
|
||||
PATH=/home/shs/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
||||
LESSOPEN=| /usr/bin/lesspipe %s
|
||||
_=/usr/bin/env
|
||||
```
|
||||
|
||||
虽然你可能会看到大量的输出,上面显示的第一大部分用于在命令行上使用颜色标识各种文件类型。当你看到类似 `*.tar=01;31:` 这样的东西,这告诉你 `tar` 文件将以红色显示在文件列表中,然而 `*.jpg=01;35:` 告诉你 jpg 文件将以紫色显现出来。这些颜色旨在使它易于从一个文件列表中分辨出某些文件。你可以在《[在 Linux 命令行中自定义你的颜色][3]》处学习更多关于这些颜色的定义,和如何自定义它们。
|
||||
|
||||
当你更喜欢一种不加装饰的显示时,一种关闭颜色显示的简单方法是使用如下命令:
|
||||
|
||||
```
|
||||
$ ls -l --color=never
|
||||
```
|
||||
|
||||
这个命令可以简单地转换到一个别名:
|
||||
|
||||
```
|
||||
$ alias ll2='ls -l --color=never'
|
||||
```
|
||||
|
||||
你也可以使用 `echo` 命令来单独地显现某个设置。在这个命令中,我们显示在历史缓存区中将被记忆命令的数量:
|
||||
|
||||
```
|
||||
$ echo $HISTSIZE
|
||||
1000
|
||||
```
|
||||
|
||||
如果你已经移动到某个位置,你在文件系统中的最后位置会被记在这里:
|
||||
|
||||
```
|
||||
PWD=/home/shs
|
||||
OLDPWD=/tmp
|
||||
```
|
||||
|
||||
### 作出更改
|
||||
|
||||
你可以使用一个像这样的命令更改环境设置,但是,如果你希望保持这个设置,在你的 `~/.bashrc` 文件中添加一行代码,例如 `HISTSIZE=1234`。
|
||||
|
||||
```
|
||||
$ export HISTSIZE=1234
|
||||
```
|
||||
|
||||
### “export” 一个变量的本意是什么
|
||||
|
||||
导出一个环境变量可使设置用于你的 shell 和可能的子 shell。默认情况下,用户定义的变量是本地的,并不被导出到新的进程,例如,子 shell 和脚本。`export` 命令使得环境变量可用在子进程中发挥功用。
|
||||
|
||||
### 添加和移除变量
|
||||
|
||||
你可以很容易地在命令行和子 shell 上创建新的变量,并使它们可用。然而,当你登出并再次回来时这些变量将消失,除非你也将它们添加到 `~/.bashrc` 或一个类似的文件中。
|
||||
|
||||
```
|
||||
$ export MSG="Hello, World!"
|
||||
```
|
||||
|
||||
如果你需要,你可以使用 `unset` 命令来消除一个变量:
|
||||
|
||||
```
|
||||
$ unset MSG
|
||||
```
|
||||
|
||||
如果变量是局部定义的,你可以通过加载你的启动文件来简单地将其设置回来。例如:
|
||||
|
||||
```
|
||||
$ echo $MSG
|
||||
Hello, World!
|
||||
$ unset $MSG
|
||||
$ echo $MSG
|
||||
|
||||
$ . ~/.bashrc
|
||||
$ echo $MSG
|
||||
Hello, World!
|
||||
```
|
||||
|
||||
### 小结
|
||||
|
||||
用户账户是用一组恰当的启动文件设立的,创建了一个有用的用户环境,而个人用户和系统管理员都可以通过编辑他们的个人设置文件(对于用户)或很多来自设置起源的文件(对于系统管理员)来更改默认设置。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3385516/how-to-manage-your-linux-environment.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2019/03/environment-rocks-leaves-100792229-large.jpg
|
||||
[2]: https://www.youtube.com/playlist?list=PL7D2RMSmRO9J8OTpjFECi8DJiTQdd4hua
|
||||
[3]: https://www.networkworld.com/article/3269587/customizing-your-text-colors-on-the-linux-command-line.html
|
||||
[4]: https://www.facebook.com/NetworkWorld/
|
||||
[5]: https://www.linkedin.com/company/network-world
|
209
published/201905/20190415 Kubernetes on Fedora IoT with k3s.md
Normal file
209
published/201905/20190415 Kubernetes on Fedora IoT with k3s.md
Normal file
@ -0,0 +1,209 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (StdioA)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10908-1.html)
|
||||
[#]: subject: (Kubernetes on Fedora IoT with k3s)
|
||||
[#]: via: (https://fedoramagazine.org/kubernetes-on-fedora-iot-with-k3s/)
|
||||
[#]: author: (Lennart Jern https://fedoramagazine.org/author/lennartj/)
|
||||
|
||||
使用 k3s 在 Fedora IoT 上运行 K8S
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/28/094048yrzlik9oek5rbs5s.jpg)
|
||||
|
||||
Fedora IoT 是一个即将发布的、面向物联网的 Fedora 版本。去年 Fedora Magazine 的《[如何使用 Fedora IoT 点亮 LED 灯][2]》一文第一次介绍了它。从那以后,它与 Fedora Silverblue 一起不断改进,以提供针对面向容器的工作流的不可变基础操作系统。
|
||||
|
||||
Kubernetes 是一个颇受欢迎的容器编排系统。它可能最常用在那些能够处理巨大负载的强劲硬件上。不过,它也能在像树莓派 3 这样轻量级的设备上运行。让我们继续阅读,来了解如何运行它。
|
||||
|
||||
### 为什么用 Kubernetes?
|
||||
|
||||
虽然 Kubernetes 在云计算领域风靡一时,但让它在小型单板机上运行可能并不是常见的。不过,我们有非常明确的理由来做这件事。首先,这是一个不需要昂贵硬件就可以学习并熟悉 Kubernetes 的好方法;其次,由于它的流行性,市面上有[大量应用][3]进行了预先打包,以用于在 Kubernetes 集群中运行。更不用说,当你遇到问题时,会有大规模的社区用户为你提供帮助。
|
||||
|
||||
最后但同样重要的是,即使是在家庭实验室这样的小规模环境中,容器编排也确实能够使事情变得更加简单。虽然在学习曲线方面,这一点并不明显,但这些技能在你将来与任何集群打交道的时候都会有帮助。不管你面对的是一个单节点树莓派集群,还是一个大规模的机器学习场,它们的操作方式都是类似的。
|
||||
|
||||
#### K3s - 轻量级的 Kubernetes
|
||||
|
||||
一个“正常”安装的 Kubernetes(如果有这么一说的话)对于物联网来说有点沉重。K8s 的推荐内存配置,是每台机器 2GB!不过,我们也有一些替代品,其中一个新人是 [k3s][4] —— 一个轻量级的 Kubernetes 发行版。
|
||||
|
||||
K3s 非常特殊,因为它将 etcd 替换成了 SQLite 以满足键值存储需求。还有一点,在于整个 k3s 将使用一个二进制文件分发,而不是每个组件一个。这减少了内存占用并简化了安装过程。基于上述原因,我们只需要 512MB 内存即可运行 k3s,极度适合小型单板电脑!
|
||||
|
||||
### 你需要的东西
|
||||
|
||||
1. Fedora IoT 运行在虚拟机或实体设备中运行的。在[这里][5]可以看到优秀的入门指南。一台机器就足够了,不过两台可以用来测试向集群添加更多节点。
|
||||
2. [配置防火墙][6],允许 6443 和 8372 端口的通信。或者,你也可以简单地运行 `systemctl stop firewalld` 来为这次实验关闭防火墙。
|
||||
|
||||
### 安装 k3s
|
||||
|
||||
安装 k3s 非常简单。直接运行安装脚本:
|
||||
|
||||
```
|
||||
curl -sfL https://get.k3s.io | sh -
|
||||
```
|
||||
|
||||
它会下载、安装并启动 k3s。安装完成后,运行以下命令来从服务器获取节点列表:
|
||||
|
||||
```
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
需要注意的是,有几个选项可以通过环境变量传递给安装脚本。这些选项可以在[文档][7]中找到。当然,你也完全可以直接下载二进制文件来手动安装 k3s。
|
||||
|
||||
对于实验和学习来说,这样已经很棒了,不过单节点的集群也不能算一个集群。幸运的是,添加另一个节点并不比设置第一个节点要难。只需要向安装脚本传递两个环境变量,它就可以找到第一个节点,而不用运行 k3s 的服务器部分。
|
||||
|
||||
```
|
||||
curl -sfL https://get.k3s.io | K3S_URL=https://example-url:6443 \
|
||||
K3S_TOKEN=XXX sh -
|
||||
```
|
||||
|
||||
上面的 `example-url` 应被替换为第一个节点的 IP 地址,或一个完全限定域名。在该节点中,(用 XXX 表示的)令牌可以在 `/var/lib/rancher/k3s/server/node-token` 文件中找到。
|
||||
|
||||
### 部署一些容器
|
||||
|
||||
现在我们有了一个 Kubernetes 集群,我们可以真正做些什么呢?让我们从部署一个简单的 Web 服务器开始吧。
|
||||
|
||||
```
|
||||
kubectl create deployment my-server --image nginx
|
||||
```
|
||||
|
||||
这会从名为 `nginx` 的容器镜像中创建出一个名叫 `my-server` 的 [部署][8](默认使用 docker hub 注册中心,以及 `latest` 标签)。
|
||||
|
||||
```
|
||||
kubectl get pods
|
||||
```
|
||||
|
||||
为了访问到 pod 中运行的 nginx 服务器,首先通过一个 [服务][9] 来暴露该部署。以下命令将创建一个与该部署同名的服务。
|
||||
|
||||
```
|
||||
kubectl expose deployment my-server --port 80
|
||||
```
|
||||
|
||||
服务将作为一种负载均衡器和 Pod 的 DNS 记录来工作。比如,当运行第二个 Pod 时,我们只需指定 `my-server`(服务名称)就可以通过 `curl` 访问 nginx 服务器。有关如何操作,可以看下面的实例。
|
||||
|
||||
```
|
||||
# 启动一个 pod,在里面以交互方式运行 bash
|
||||
kubectl run debug --generator=run-pod/v1 --image=fedora -it -- bash
|
||||
# 等待 bash 提示符出现
|
||||
curl my-server
|
||||
# 你可以看到“Welcome to nginx!”的输出页面
|
||||
```
|
||||
|
||||
### Ingress 控制器及外部 IP
|
||||
|
||||
默认状态下,一个服务只能获得一个 ClusterIP(只能从集群内部访问),但你也可以通过把它的类型设置为 [LoadBalancer][10] 为该服务申请一个外部 IP。不过,并非所有应用都需要自己的 IP 地址。相反,通常可以通过基于 Host 请求头部或请求路径进行路由,从而使多个服务共享一个 IP 地址。你可以在 Kubernetes 使用 [Ingress][11] 完成此操作,而这也是我们要做的。Ingress 也提供了额外的功能,比如无需配置应用即可对流量进行 TLS 加密。
|
||||
|
||||
Kubernetes 需要 Ingress 控制器来使 Ingress 资源工作,k3s 包含 [Traefik][12] 正是出于此目的。它还包含了一个简单的服务负载均衡器,可以为集群中的服务提供外部 IP。这篇[文档][13]描述了这种服务:
|
||||
|
||||
> k3s 包含一个使用可用主机端口的基础服务负载均衡器。比如,如果你尝试创建一个监听 80 端口的负载均衡器,它会尝试在集群中寻找一个 80 端口空闲的节点。如果没有可用端口,那么负载均衡器将保持在 Pending 状态。
|
||||
>
|
||||
> k3s README
|
||||
|
||||
Ingress 控制器已经通过这个负载均衡器暴露在外。你可以使用以下命令找到它正在使用的 IP 地址。
|
||||
|
||||
```
|
||||
$ kubectl get svc --all-namespaces
|
||||
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
default kubernetes ClusterIP 10.43.0.1 443/TCP 33d
|
||||
default my-server ClusterIP 10.43.174.38 80/TCP 30m
|
||||
kube-system kube-dns ClusterIP 10.43.0.10 53/UDP,53/TCP,9153/TCP 33d
|
||||
kube-system traefik LoadBalancer 10.43.145.104 10.0.0.8 80:31596/TCP,443:31539/TCP 33d
|
||||
```
|
||||
|
||||
找到名为 `traefik` 的服务。在上面的例子中,我们感兴趣的 IP 是 10.0.0.8。
|
||||
|
||||
### 路由传入的请求
|
||||
|
||||
让我们创建一个 Ingress,使它通过基于 Host 头部的路由规则将请求路由至我们的服务器。这个例子中我们使用 [xip.io][14] 来避免必要的 DNS 记录配置工作。它的工作原理是将 IP 地址作为子域包含,以使用 `10.0.0.8.xip.io` 的任何子域来达到 IP `10.0.0.8`。换句话说,`my-server.10.0.0.8.xip.io` 被用于访问集群中的 Ingress 控制器。你现在就可以尝试(使用你自己的 IP,而不是 10.0.0.8)。如果没有 Ingress,你应该会访问到“默认后端”,只是一个写着“404 page not found”的页面。
|
||||
|
||||
我们可以使用以下 Ingress 让 Ingress 控制器将请求路由到我们的 Web 服务器的服务。
|
||||
|
||||
```
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: my-server
|
||||
spec:
|
||||
rules:
|
||||
- host: my-server.10.0.0.8.xip.io
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: my-server
|
||||
servicePort: 80
|
||||
```
|
||||
|
||||
将以上片段保存到 `my-ingress.yaml` 文件中,然后运行以下命令将其加入集群:
|
||||
|
||||
```
|
||||
kubectl apply -f my-ingress.yaml
|
||||
```
|
||||
|
||||
你现在应该能够在你选择的完全限定域名中访问到 nginx 的默认欢迎页面了。在我的例子中,它是 `my-server.10.0.0.8.xip.io`。Ingress 控制器会通过 Ingress 中包含的信息来路由请求。对 `my-server.10.0.0.8.xip.io` 的请求将被路由到 Ingress 中定义为 `backend` 的服务和端口(在本例中为 `my-server` 和 `80`)。
|
||||
|
||||
### 那么,物联网呢?
|
||||
|
||||
想象如下场景:你的家或农场周围有很多的设备。它是一个具有各种硬件功能、传感器和执行器的物联网设备的异构集合。也许某些设备拥有摄像头、天气或光线传感器。其它设备可能会被连接起来,用来控制通风、灯光、百叶窗或闪烁的 LED。
|
||||
|
||||
这种情况下,你想从所有传感器中收集数据,在最终使用它来制定决策和控制执行器之前,也可能会对其进行处理和分析。除此之外,你可能还想配置一个仪表盘来可视化那些正在发生的事情。那么 Kubernetes 如何帮助我们来管理这样的事情呢?我们怎么保证 Pod 在合适的设备上运行?
|
||||
|
||||
简单的答案就是“标签”。你可以根据功能来标记节点,如下所示:
|
||||
|
||||
```
|
||||
kubectl label nodes <node-name> <label-key>=<label-value>
|
||||
# 举例
|
||||
kubectl label nodes node2 camera=available
|
||||
```
|
||||
|
||||
一旦它们被打上标签,我们就可以轻松地使用 [nodeSelector][15] 为你的工作负载选择合适的节点。拼图的最后一块:如果你想在*所有*合适的节点上运行 Pod,那应该使用 [DaemonSet][16] 而不是部署。换句话说,应为每个使用唯一传感器的数据收集应用程序创建一个 DaemonSet,并使用 nodeSelector 确保它们仅在具有适当硬件的节点上运行。
|
||||
|
||||
服务发现功能允许 Pod 通过服务名称来寻找彼此,这项功能使得这类分布式系统的管理工作变得易如反掌。你不需要为应用配置 IP 地址或自定义端口,也不需要知道它们。相反,它们可以通过集群中的命名服务轻松找到彼此。
|
||||
|
||||
#### 充分利用空闲资源
|
||||
|
||||
随着集群的启动并运行,收集数据并控制灯光和气候,可能使你觉得你已经把它完成了。不过,集群中还有大量的计算资源可以用于其它项目。这才是 Kubernetes 真正出彩的地方。
|
||||
|
||||
你不必担心这些资源的确切位置,或者去计算是否有足够的内存来容纳额外的应用程序。这正是编排系统所解决的问题!你可以轻松地在集群中部署更多的应用,让 Kubernetes 来找出适合运行它们的位置(或是否适合运行它们)。
|
||||
|
||||
为什么不运行一个你自己的 [NextCloud][17] 实例呢?或者运行 [gitea][18]?你还可以为你所有的物联网容器设置一套 CI/CD 流水线。毕竟,如果你可以在集群中进行本地构建,为什么还要在主计算机上构建并交叉编译它们呢?
|
||||
|
||||
这里的要点是,Kubernetes 可以更容易地利用那些你可能浪费掉的“隐藏”资源。Kubernetes 根据可用资源和容错处理规则来调度 Pod,因此你也无需手动完成这些工作。但是,为了帮助 Kubernetes 做出合理的决定,你绝对应该为你的工作负载添加[资源请求][19]配置。
|
||||
|
||||
### 总结
|
||||
|
||||
尽管 Kuberenetes 或一般的容器编排平台通常不会与物联网相关联,但在管理分布式系统时,使用一个编排系统肯定是有意义的。你不仅可以使用统一的方式来处理多样化和异构的设备,还可以简化它们的通信方式。此外,Kubernetes 还可以更好地对闲置资源加以利用。
|
||||
|
||||
容器技术使构建“随处运行”应用的想法成为可能。现在,Kubernetes 可以更轻松地来负责“随处”的部分。作为构建一切的不可变基础,我们使用 Fedora IoT。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/kubernetes-on-fedora-iot-with-k3s/
|
||||
|
||||
作者:[Lennart Jern][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[StdioA](https://github.com/StdioA)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/lennartj/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/04/k3s-1-816x345.png
|
||||
[2]: https://linux.cn/article-10380-1.html
|
||||
[3]: https://hub.helm.sh/
|
||||
[4]: https://k3s.io
|
||||
[5]: https://docs.fedoraproject.org/en-US/iot/getting-started/
|
||||
[6]: https://github.com/rancher/k3s#open-ports--network-security
|
||||
[7]: https://github.com/rancher/k3s#systemd
|
||||
[8]: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
|
||||
[9]: https://kubernetes.io/docs/concepts/services-networking/service/
|
||||
[10]: https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer
|
||||
[11]: https://kubernetes.io/docs/concepts/services-networking/ingress/
|
||||
[12]: https://traefik.io/
|
||||
[13]: https://github.com/rancher/k3s/blob/master/README.md#service-load-balancer
|
||||
[14]: http://xip.io/
|
||||
[15]: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
|
||||
[16]: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/
|
||||
[17]: https://nextcloud.com/
|
||||
[18]: https://gitea.io/en-us/
|
||||
[19]: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
|
@ -1,85 +1,86 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10891-1.html)
|
||||
[#]: subject: (Detecting malaria with deep learning)
|
||||
[#]: via: (https://opensource.com/article/19/4/detecting-malaria-deep-learning)
|
||||
[#]: author: (Dipanjan Sarkar https://opensource.com/users/djsarkar)
|
||||
[#]: author: (Dipanjan Sarkar https://opensource.com/users/djsarkar)
|
||||
|
||||
使用深度学习检测疟疾
|
||||
==================
|
||||
人工智能结合开源硬件工具能够提升严重传染病疟疾的诊断。
|
||||
|
||||
> 人工智能结合开源硬件工具能够提升严重传染病疟疾的诊断。
|
||||
|
||||
![][1]
|
||||
|
||||
人工智能(AI)和开源工具,技术,和框架是促进社会进步的强有力的结合。_“健康就是财富”_可能有点陈词滥调,但它却是非常准确的!在本篇文章,我们将测试 AI 是如何与低花费,有效,精确的开源深度学习方法一起被利用来检测致死的传染病疟疾。
|
||||
人工智能(AI)和开源工具、技术和框架是促进社会进步的强有力的结合。“健康就是财富”可能有点陈词滥调,但它却是非常准确的!在本篇文章,我们将测试 AI 是如何与低成本、有效、精确的开源深度学习方法结合起来一起用来检测致死的传染病疟疾。
|
||||
|
||||
我既不是一个医生,也不是一个医疗保健研究者,我也绝不像他们那样合格,我只是对将 AI 应用到医疗保健研究感兴趣。在这片文章中我的想法是展示 AI 和开源解决方案如何帮助疟疾检测和减少人工劳动的方法。
|
||||
|
||||
![Python and TensorFlow][2]
|
||||
|
||||
Python and TensorFlow: 一个构建开源深度学习方法的很棒的结合
|
||||
*Python 和 TensorFlow: 一个构建开源深度学习方法的很棒的结合*
|
||||
|
||||
感谢 Python 的强大 和像 TensorFlow 这样的深度学习框架,我们能够构建鲁棒的,大规模的,有效的深度学习方法。因为这些工具是自由和开源的,我们能够构建低成本的能够轻易被任何人采纳和使用的解决方案。让我们开始吧!
|
||||
感谢 Python 的强大和像 TensorFlow 这样的深度学习框架,我们能够构建健壮的、大规模的、有效的深度学习方法。因为这些工具是自由和开源的,我们能够构建非常经济且易于被任何人采纳和使用的解决方案。让我们开始吧!
|
||||
|
||||
### 项目动机
|
||||
|
||||
疟疾是由_疟原虫_造成的致死的,有传染性的,蚊子传播的疾病,主要通过受感染的雌性按蚊叮咬传播。共有五种寄生虫能够造成疟疾,但是样例中的大多数是这两种类型- _恶性疟原虫_ 和 _间日疟原虫_ 造成的。
|
||||
疟疾是由*疟原虫*造成的致死的、有传染性的、蚊子传播的疾病,主要通过受感染的雌性按蚊叮咬传播。共有五种寄生虫能够引起疟疾,但是大多数病例是这两种类型造成的:恶性疟原虫和间日疟原虫。
|
||||
|
||||
![疟疾热图][3]
|
||||
|
||||
这个地图显示了疟疾在全球传播分布形势,尤其在热带地区,但疾病的性质和致命性是该项目的主要动机。
|
||||
|
||||
如果一个雌性蚊子咬了你,蚊子携带的寄生虫进入你的血液并且开始破坏携带氧气的红细胞(RBC)。通常,疟疾的最初症状类似于流感病毒,在蚊子叮咬后,他们通常在几天或几周内发作。然而,这些致死的寄生虫可以在你的身体里生存长达一年并且不会造成任何症状,延迟治疗可能造成并发症甚至死亡。因此,早期的检查能够挽救生命。
|
||||
如果一只受感染雌性蚊子叮咬了你,蚊子携带的寄生虫进入你的血液,并且开始破坏携带氧气的红细胞(RBC)。通常,疟疾的最初症状类似于流感病毒,在蚊子叮咬后,他们通常在几天或几周内发作。然而,这些致死的寄生虫可以在你的身体里生存长达一年并且不会造成任何症状,延迟治疗可能造成并发症甚至死亡。因此,早期的检查能够挽救生命。
|
||||
|
||||
世界健康组织(WHO)的[疟疾事件][4]暗示世界近乎一半的人口面临疟疾的风险,有超过 2 亿 的疟疾病例,每年由于疟疾造成的死亡近乎 40 万。这是使疟疾检测和诊断快速,简单和有效的一个动机。
|
||||
世界健康组织(WHO)的[疟疾实情][4]表明,世界近乎一半的人口面临疟疾的风险,有超过 2 亿的疟疾病例,每年由于疟疾造成的死亡将近 40 万。这是使疟疾检测和诊断快速、简单和有效的一个动机。
|
||||
|
||||
### 检测疟疾的方法
|
||||
|
||||
有几种方法能够用来检测和诊断疟疾。该文中的项目就是基于 Rajaraman,et al. 的论文:“[预先训练的卷积神经网络作为特征提取器,用于改善薄血涂片图像中的疟疾寄生虫检测][5]”,介绍了一些方法,包含聚合酶链反应(PCR)和快速诊断测试(RDT)。这两种测试通常在高质量的显微镜下使用,但这样的设备不是轻易能够获得的。
|
||||
有几种方法能够用来检测和诊断疟疾。该文中的项目就是基于 Rajaraman, et al. 的论文:“[预先训练的卷积神经网络作为特征提取器,用于改善薄血涂片图像中的疟疾寄生虫检测][5]”介绍的一些方法,包含聚合酶链反应(PCR)和快速诊断测试(RDT)。这两种测试通常用于无法提供高质量显微镜服务的地方。
|
||||
|
||||
标准的疟疾诊断通常使基于血液涂片工作流的,根据 Carlos Ariza 的文章“[Malaria Hero: 一个更快诊断疟原虫的网络应用][6]”,我从中了解到 Adrian Rosebrock 的“[使用 Keras 的深度学习和医学图像分析][7]”。我感激这些优秀的资源的作者,让我在疟原虫预防,诊断和治疗方面有了更多的想法。
|
||||
标准的疟疾诊断通常是基于血液涂片工作流程的,根据 Carlos Ariza 的文章“[Malaria Hero:一个更快诊断疟原虫的网络应用][6]”,我从中了解到 Adrian Rosebrock 的“[使用 Keras 的深度学习和医学图像分析][7]”。我感激这些优秀的资源的作者,让我在疟原虫预防、诊断和治疗方面有了更多的想法。
|
||||
|
||||
![疟原虫检测的血涂片工作流程][8]
|
||||
|
||||
一个疟原虫检测的血涂片工作流程
|
||||
*一个疟原虫检测的血涂片工作流程*
|
||||
|
||||
根据 WHO 草案,诊断通常包括对放大 100 倍的血涂片的集中检测。训练人们人工计数在 5000 个细胞中有多少红细胞中包含疟原虫。正如上述解释中引用的 Rajaraman, et al. 的论文:
|
||||
根据 WHO 方案,诊断通常包括对放大 100 倍的血涂片的集中检测。受过训练的人们手工计算在 5000 个细胞中有多少红细胞中包含疟原虫。正如上述解释中引用的 Rajaraman, et al. 的论文:
|
||||
|
||||
> 薄血涂片帮助检测疟原虫的存在性并且帮助识别造成传染(疾病控制和抑制中心,2012)的物种。诊断准确性在很大程度上取决于人类的专业知识,并且可能受到观察者间差异和疾病流行/资源受限区域大规模诊断所造成的不利影响(Mitiku, Mengistu, and Gelaw, 2003)。可替代的技术是使用聚合酶链反应(PCR)和快速诊断测试(RDT);然而,PCR 分析受限于它的性能(Hommelsheim, et al., 2014),RDT 在疾病流行的地区成本效益低(Hawkes,Katsuva, and Masumbuko, 2009)。
|
||||
> 厚血涂片有助于检测寄生虫的存在,而薄血涂片有助于识别引起感染的寄生虫种类(疾病控制和预防中心, 2012)。诊断准确性在很大程度上取决于诊断人的专业知识,并且可能受到观察者间差异和疾病流行/资源受限区域大规模诊断所造成的不利影响(Mitiku, Mengistu 和 Gelaw, 2003)。可替代的技术是使用聚合酶链反应(PCR)和快速诊断测试(RDT);然而,PCR 分析受限于它的性能(Hommelsheim, et al., 2014),RDT 在疾病流行的地区成本效益低(Hawkes, Katsuva 和 Masumbuko, 2009)。
|
||||
|
||||
因此,疟疾检测可能受益于使用机器学习的自动化。
|
||||
|
||||
### 疟原虫检测的深度学习
|
||||
### 疟疾检测的深度学习
|
||||
|
||||
人工诊断血涂片是一个加强的人工过程,需要专业知识来分类和计数被寄生虫感染的和未感染的细胞。这个过程可能不能很好的规模化,尤其在那些专业人士不足的地区。在利用最先进的图像处理和分析技术提取人工选取特征和构建基于机器学习的分类模型方面取得了一些进展。然而,这些模型不能大规模推广,因为没有更多的数据用来训练,并且人工选取特征需要花费很长时间。
|
||||
人工诊断血涂片是一个繁重的手工过程,需要专业知识来分类和计数被寄生虫感染的和未感染的细胞。这个过程可能不能很好的规模化,尤其在那些专业人士不足的地区。在利用最先进的图像处理和分析技术提取人工选取特征和构建基于机器学习的分类模型方面取得了一些进展。然而,这些模型不能大规模推广,因为没有更多的数据用来训练,并且人工选取特征需要花费很长时间。
|
||||
|
||||
深度学习模型,或者更具体地讲,卷积神经网络(CNNs),已经被证明在各种计算机视觉任务中非常有效。(如果你想有额外的关于 CNNs 的背景知识,我推荐你阅读[视觉识别的 CS2331n 卷积神经网络][9]。)简单地讲,CNN 模型的关键层包含卷积和池化层,正如下面图像显示。
|
||||
深度学习模型,或者更具体地讲,卷积神经网络(CNN),已经被证明在各种计算机视觉任务中非常有效。(如果你想更多的了解关于 CNN 的背景知识,我推荐你阅读[视觉识别的 CS2331n 卷积神经网络][9]。)简单地讲,CNN 模型的关键层包含卷积和池化层,正如下图所示。
|
||||
|
||||
![A typical CNN architecture][10]
|
||||
|
||||
一个典型的 CNN 架构
|
||||
*一个典型的 CNN 架构*
|
||||
|
||||
卷积层从数据中学习空间层级模式,它是平移不变的,因此它们能够学习不同方面的图像。例如,第一个卷积层将学习小的和本地图案,例如边缘和角落,第二个卷积层学习基于第一层的特征的更大的图案,等等。这允许 CNNs 自动化提取特征并且学习对于新数据点通用的有效的特征。池化层帮助下采样和降维。
|
||||
卷积层从数据中学习空间层级模式,它是平移不变的,因此它们能够学习图像的不同方面。例如,第一个卷积层将学习小的和局部图案,例如边缘和角落,第二个卷积层将基于第一层的特征学习更大的图案,等等。这允许 CNN 自动化提取特征并且学习对于新数据点通用的有效的特征。池化层有助于下采样和减少尺寸。
|
||||
|
||||
因此,CNNs 帮助自动化和规模化的特征工程。同样,在模型末尾加上密集层允许我们执行像图像分类这样的任务。使用像 CNNs 者的深度学习模型自动的疟疾检测可能非常有效,便宜和具有规模性,尤其是迁移学习和预训练模型效果非常好,甚至在少量数据的约束下。
|
||||
因此,CNN 有助于自动化和规模化的特征工程。同样,在模型末尾加上密集层允许我们执行像图像分类这样的任务。使用像 CNN 这样的深度学习模型自动的疟疾检测可能非常有效、便宜和具有规模性,尤其是迁移学习和预训练模型效果非常好,甚至在少量数据的约束下。
|
||||
|
||||
Rajaraman, et al. 的论文在一个数据集上利用六个预训练模型在检测疟疾 vs 无感染样本获取到令人吃惊的 95.9% 的准确率。我们的关注点是从头开始尝试一些简单的 CNN 模型和用一个预训练的训练模型使用迁移学习来查看我们能够从相同的数据集中得到什么。我们将使用开源工具和框架,包括 Python 和 TensorFlow,来构建我们的模型。
|
||||
Rajaraman, et al. 的论文在一个数据集上利用六个预训练模型在检测疟疾对比无感染样本获取到令人吃惊的 95.9% 的准确率。我们的重点是从头开始尝试一些简单的 CNN 模型和用一个预训练的训练模型使用迁移学习来查看我们能够从相同的数据集中得到什么。我们将使用开源工具和框架,包括 Python 和 TensorFlow,来构建我们的模型。
|
||||
|
||||
### 数据集
|
||||
|
||||
我们分析的数据来自 Lister Hill 国家生物医学交流中心(LHNCBC),国家医学图书馆(NLM)的一部分,他们细心收集和标记了健康和受感染的血涂片图像的[公众可获得的数据集][11]。这些研究者已经开发了一个运行在 Android 智能手机的移动[疟疾检测应用][12],连接到一个传统的光学显微镜。它们使用 吉姆萨染液 将 150 个受恶性疟原虫感染的和 50 个健康病人的薄血涂片染色,这些薄血涂片是在孟加拉的吉大港医学院附属医院收集和照相的。使用智能手机的内置相机获取每个显微镜视窗内的图像。这些图片由在泰国曼谷的马希多-牛津热带医学研究所的一个专家使用幻灯片阅读器标记的。
|
||||
我们分析的数据来自 Lister Hill 国家生物医学交流中心(LHNCBC)的研究人员,该中心是国家医学图书馆(NLM)的一部分,他们细心收集和标记了公开可用的健康和受感染的血涂片图像的[数据集][11]。这些研究者已经开发了一个运行在 Android 智能手机的[疟疾检测手机应用][12],连接到一个传统的光学显微镜。它们使用吉姆萨染液将 150 个受恶性疟原虫感染的和 50 个健康病人的薄血涂片染色,这些薄血涂片是在孟加拉的吉大港医学院附属医院收集和照相的。使用智能手机的内置相机获取每个显微镜视窗内的图像。这些图片由在泰国曼谷的马希多-牛津热带医学研究所的一个专家使用幻灯片阅读器标记的。
|
||||
|
||||
让我们简洁的查看数据集的结构。首先,我将安装一些基础的依赖(基于使用的操作系统)。
|
||||
让我们简要地查看一下数据集的结构。首先,我将安装一些基础的依赖(基于使用的操作系统)。
|
||||
|
||||
![Installing dependencies][13]
|
||||
|
||||
我使用的是云上的带有一个 GPU 的基于 Debian 的操作系统,这样我能更快的运行我的模型。为了查看目录结构,我们必须安装 tree 依赖(如果我们没有安装的话)使用 **sudo apt install tree**。
|
||||
我使用的是云上的带有一个 GPU 的基于 Debian 的操作系统,这样我能更快的运行我的模型。为了查看目录结构,我们必须使用 `sudo apt install tree` 安装 `tree` 及其依赖(如果我们没有安装的话)。
|
||||
|
||||
![Installing the tree dependency][14]
|
||||
|
||||
我们有两个文件夹包含血细胞的图像,包括受感染的和健康的。我们可以获取关于图像总数更多的细节通过输入:
|
||||
|
||||
我们有两个文件夹包含血细胞的图像,包括受感染的和健康的。我们通过输入可以获取关于图像总数更多的细节:
|
||||
|
||||
```
|
||||
import os
|
||||
@ -97,7 +98,7 @@ len(infected_files), len(healthy_files)
|
||||
(13779, 13779)
|
||||
```
|
||||
|
||||
看起来我们有一个平衡的 13,779 张疟疾的 和 13,779 张非疟疾的(健康的)血细胞图像。让我们根据这些构建数据帧,我们将用这些数据帧来构建我们的数据集。
|
||||
看起来我们有一个平衡的数据集,包含 13,779 张疟疾的和 13,779 张非疟疾的(健康的)血细胞图像。让我们根据这些构建数据帧,我们将用这些数据帧来构建我们的数据集。
|
||||
|
||||
|
||||
```
|
||||
@ -107,8 +108,8 @@ import pandas as pd
|
||||
np.random.seed(42)
|
||||
|
||||
files_df = pd.DataFrame({
|
||||
'filename': infected_files + healthy_files,
|
||||
'label': ['malaria'] * len(infected_files) + ['healthy'] * len(healthy_files)
|
||||
'filename': infected_files + healthy_files,
|
||||
'label': ['malaria'] * len(infected_files) + ['healthy'] * len(healthy_files)
|
||||
}).sample(frac=1, random_state=42).reset_index(drop=True)
|
||||
|
||||
files_df.head()
|
||||
@ -116,9 +117,9 @@ files_df.head()
|
||||
|
||||
![Datasets][15]
|
||||
|
||||
### 构建和参所图像数据集
|
||||
### 构建和了解图像数据集
|
||||
|
||||
为了构建深度学习模型,我们需要训练数据,但是我们还需要使用不可见的数据测试模型的性能。相应的,我们将使用 60:10:30 的划分用于训练,验证和测试数据集。我们将在训练期间应用训练和验证数据集并用测试数据集来检查模型的性能。
|
||||
为了构建深度学习模型,我们需要训练数据,但是我们还需要使用不可见的数据测试模型的性能。相应的,我们将使用 60:10:30 的比例来划分用于训练、验证和测试的数据集。我们将在训练期间应用训练和验证数据集,并用测试数据集来检查模型的性能。
|
||||
|
||||
|
||||
```
|
||||
@ -126,11 +127,11 @@ from sklearn.model_selection import train_test_split
|
||||
from collections import Counter
|
||||
|
||||
train_files, test_files, train_labels, test_labels = train_test_split(files_df['filename'].values,
|
||||
files_df['label'].values,
|
||||
test_size=0.3, random_state=42)
|
||||
files_df['label'].values,
|
||||
test_size=0.3, random_state=42)
|
||||
train_files, val_files, train_labels, val_labels = train_test_split(train_files,
|
||||
train_labels,
|
||||
test_size=0.1, random_state=42)
|
||||
train_labels,
|
||||
test_size=0.1, random_state=42)
|
||||
|
||||
print(train_files.shape, val_files.shape, test_files.shape)
|
||||
print('Train:', Counter(train_labels), '\nVal:', Counter(val_labels), '\nTest:', Counter(test_labels))
|
||||
@ -142,8 +143,7 @@ Val: Counter({'healthy': 970, 'malaria': 959})
|
||||
Test: Counter({'malaria': 4193, 'healthy': 4075})
|
||||
```
|
||||
|
||||
这些图片维度并不相同,因此血涂片和细胞图像是基于人类,测试方法,图片的朝向。让我们总结我们的训练数据集的统计信息来决定最佳的图像维度(牢记,我们根本不会碰测试数据集)。
|
||||
|
||||
这些图片尺寸并不相同,因为血涂片和细胞图像是基于人、测试方法、图片方向不同而不同的。让我们总结我们的训练数据集的统计信息来决定最佳的图像尺寸(牢记,我们根本不会碰测试数据集)。
|
||||
|
||||
```
|
||||
import cv2
|
||||
@ -151,24 +151,25 @@ from concurrent import futures
|
||||
import threading
|
||||
|
||||
def get_img_shape_parallel(idx, img, total_imgs):
|
||||
if idx % 5000 == 0 or idx == (total_imgs - 1):
|
||||
print('{}: working on img num: {}'.format(threading.current_thread().name,
|
||||
idx))
|
||||
return cv2.imread(img).shape
|
||||
if idx % 5000 == 0 or idx == (total_imgs - 1):
|
||||
print('{}: working on img num: {}'.format(threading.current_thread().name,
|
||||
idx))
|
||||
return cv2.imread(img).shape
|
||||
|
||||
ex = futures.ThreadPoolExecutor(max_workers=None)
|
||||
data_inp = [(idx, img, len(train_files)) for idx, img in enumerate(train_files)]
|
||||
print('Starting Img shape computation:')
|
||||
train_img_dims_map = ex.map(get_img_shape_parallel,
|
||||
[record[0] for record in data_inp],
|
||||
[record[1] for record in data_inp],
|
||||
[record[2] for record in data_inp])
|
||||
[record[0] for record in data_inp],
|
||||
[record[1] for record in data_inp],
|
||||
[record[2] for record in data_inp])
|
||||
train_img_dims = list(train_img_dims_map)
|
||||
print('Min Dimensions:', np.min(train_img_dims, axis=0))
|
||||
print('Avg Dimensions:', np.mean(train_img_dims, axis=0))
|
||||
print('Median Dimensions:', np.median(train_img_dims, axis=0))
|
||||
print('Max Dimensions:', np.max(train_img_dims, axis=0))
|
||||
|
||||
|
||||
# Output
|
||||
Starting Img shape computation:
|
||||
ThreadPoolExecutor-0_0: working on img num: 0
|
||||
@ -176,27 +177,26 @@ ThreadPoolExecutor-0_17: working on img num: 5000
|
||||
ThreadPoolExecutor-0_15: working on img num: 10000
|
||||
ThreadPoolExecutor-0_1: working on img num: 15000
|
||||
ThreadPoolExecutor-0_7: working on img num: 17360
|
||||
Min Dimensions: [46 46 3]
|
||||
Avg Dimensions: [132.77311215 132.45757733 3.]
|
||||
Median Dimensions: [130. 130. 3.]
|
||||
Max Dimensions: [385 394 3]
|
||||
Min Dimensions: [46 46 3]
|
||||
Avg Dimensions: [132.77311215 132.45757733 3.]
|
||||
Median Dimensions: [130. 130. 3.]
|
||||
Max Dimensions: [385 394 3]
|
||||
```
|
||||
|
||||
我们应用并行处理来加速图像读取,并且在总结统计时,我们将重新调整每幅图片到 125x125 像素。让我们载入我们所有的图像并重新调整它们为这些固定的大小。
|
||||
|
||||
我们应用并行处理来加速图像读取,并且基于汇总统计结果,我们将每幅图片的尺寸重新调整到 125x125 像素。让我们载入我们所有的图像并重新调整它们为这些固定尺寸。
|
||||
|
||||
```
|
||||
IMG_DIMS = (125, 125)
|
||||
|
||||
def get_img_data_parallel(idx, img, total_imgs):
|
||||
if idx % 5000 == 0 or idx == (total_imgs - 1):
|
||||
print('{}: working on img num: {}'.format(threading.current_thread().name,
|
||||
idx))
|
||||
img = cv2.imread(img)
|
||||
img = cv2.resize(img, dsize=IMG_DIMS,
|
||||
interpolation=cv2.INTER_CUBIC)
|
||||
img = np.array(img, dtype=np.float32)
|
||||
return img
|
||||
if idx % 5000 == 0 or idx == (total_imgs - 1):
|
||||
print('{}: working on img num: {}'.format(threading.current_thread().name,
|
||||
idx))
|
||||
img = cv2.imread(img)
|
||||
img = cv2.resize(img, dsize=IMG_DIMS,
|
||||
interpolation=cv2.INTER_CUBIC)
|
||||
img = np.array(img, dtype=np.float32)
|
||||
return img
|
||||
|
||||
ex = futures.ThreadPoolExecutor(max_workers=None)
|
||||
train_data_inp = [(idx, img, len(train_files)) for idx, img in enumerate(train_files)]
|
||||
@ -205,27 +205,28 @@ test_data_inp = [(idx, img, len(test_files)) for idx, img in enumerate(test_file
|
||||
|
||||
print('Loading Train Images:')
|
||||
train_data_map = ex.map(get_img_data_parallel,
|
||||
[record[0] for record in train_data_inp],
|
||||
[record[1] for record in train_data_inp],
|
||||
[record[2] for record in train_data_inp])
|
||||
[record[0] for record in train_data_inp],
|
||||
[record[1] for record in train_data_inp],
|
||||
[record[2] for record in train_data_inp])
|
||||
train_data = np.array(list(train_data_map))
|
||||
|
||||
print('\nLoading Validation Images:')
|
||||
val_data_map = ex.map(get_img_data_parallel,
|
||||
[record[0] for record in val_data_inp],
|
||||
[record[1] for record in val_data_inp],
|
||||
[record[2] for record in val_data_inp])
|
||||
[record[0] for record in val_data_inp],
|
||||
[record[1] for record in val_data_inp],
|
||||
[record[2] for record in val_data_inp])
|
||||
val_data = np.array(list(val_data_map))
|
||||
|
||||
print('\nLoading Test Images:')
|
||||
test_data_map = ex.map(get_img_data_parallel,
|
||||
[record[0] for record in test_data_inp],
|
||||
[record[1] for record in test_data_inp],
|
||||
[record[2] for record in test_data_inp])
|
||||
[record[0] for record in test_data_inp],
|
||||
[record[1] for record in test_data_inp],
|
||||
[record[2] for record in test_data_inp])
|
||||
test_data = np.array(list(test_data_map))
|
||||
|
||||
train_data.shape, val_data.shape, test_data.shape
|
||||
|
||||
|
||||
# Output
|
||||
Loading Train Images:
|
||||
ThreadPoolExecutor-1_0: working on img num: 0
|
||||
@ -245,8 +246,7 @@ ThreadPoolExecutor-1_8: working on img num: 8267
|
||||
((17361, 125, 125, 3), (1929, 125, 125, 3), (8268, 125, 125, 3))
|
||||
```
|
||||
|
||||
我们再次应用并行处理来加速有关图像载入和重新调整大小。最终,我们获得了想要的维度的图片张量,正如之前描述的。我们现在查看一些血细胞图像样本来对我们的数据什么样有个印象。
|
||||
|
||||
我们再次应用并行处理来加速有关图像载入和重新调整大小的计算。最终,我们获得了所需尺寸的图片张量,正如前面的输出所示。我们现在查看一些血细胞图像样本,以对我们的数据有个印象。
|
||||
|
||||
```
|
||||
import matplotlib.pyplot as plt
|
||||
@ -255,13 +255,13 @@ import matplotlib.pyplot as plt
|
||||
plt.figure(1 , figsize = (8 , 8))
|
||||
n = 0
|
||||
for i in range(16):
|
||||
n += 1
|
||||
r = np.random.randint(0 , train_data.shape[0] , 1)
|
||||
plt.subplot(4 , 4 , n)
|
||||
plt.subplots_adjust(hspace = 0.5 , wspace = 0.5)
|
||||
plt.imshow(train_data[r[0]]/255.)
|
||||
plt.title('{}'.format(train_labels[r[0]]))
|
||||
plt.xticks([]) , plt.yticks([])
|
||||
n += 1
|
||||
r = np.random.randint(0 , train_data.shape[0] , 1)
|
||||
plt.subplot(4 , 4 , n)
|
||||
plt.subplots_adjust(hspace = 0.5 , wspace = 0.5)
|
||||
plt.imshow(train_data[r[0]]/255.)
|
||||
plt.title('{}'.format(train_labels[r[0]]))
|
||||
plt.xticks([]) , plt.yticks([])
|
||||
```
|
||||
|
||||
![Malaria cell samples][16]
|
||||
@ -270,7 +270,6 @@ plt.xticks([]) , plt.yticks([])
|
||||
|
||||
开始我们的模型训练前,我们必须建立一些基础的配置设置。
|
||||
|
||||
|
||||
```
|
||||
BATCH_SIZE = 64
|
||||
NUM_CLASSES = 2
|
||||
@ -290,12 +289,12 @@ val_labels_enc = le.transform(val_labels)
|
||||
|
||||
print(train_labels[:6], train_labels_enc[:6])
|
||||
|
||||
|
||||
# Output
|
||||
['malaria' 'malaria' 'malaria' 'healthy' 'healthy' 'malaria'] [1 1 1 0 0 1]
|
||||
```
|
||||
|
||||
我们修复我们的图像维度,批大小,和历元并编码我们的分类类标签。TensorFlow 2.0 于 2019 年三月发布,这个练习是非常好的借口来试用它。
|
||||
|
||||
我们修复我们的图像尺寸、批量大小,和纪元,并编码我们的分类的类标签。TensorFlow 2.0 于 2019 年三月发布,这个练习是尝试它的完美理由。
|
||||
|
||||
```
|
||||
import tensorflow as tf
|
||||
@ -312,24 +311,23 @@ tf.__version__
|
||||
|
||||
### 深度学习训练
|
||||
|
||||
在模型训练阶段,我们将构建三个深度训练模型,使用我们的训练集训练,使用验证数据比较它们的性能。我们然后保存这些模型并在之后的模型评估阶段使用它们。
|
||||
在模型训练阶段,我们将构建三个深度训练模型,使用我们的训练集训练,使用验证数据比较它们的性能。然后,我们保存这些模型并在之后的模型评估阶段使用它们。
|
||||
|
||||
#### 模型 1:从头开始的 CNN
|
||||
|
||||
我们的第一个疟疾检测模型将从头开始构建和训练一个基础的 CNN。首先,让我们定义我们的模型架构,
|
||||
|
||||
|
||||
```
|
||||
inp = tf.keras.layers.Input(shape=INPUT_SHAPE)
|
||||
|
||||
conv1 = tf.keras.layers.Conv2D(32, kernel_size=(3, 3),
|
||||
activation='relu', padding='same')(inp)
|
||||
activation='relu', padding='same')(inp)
|
||||
pool1 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(conv1)
|
||||
conv2 = tf.keras.layers.Conv2D(64, kernel_size=(3, 3),
|
||||
activation='relu', padding='same')(pool1)
|
||||
activation='relu', padding='same')(pool1)
|
||||
pool2 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(conv2)
|
||||
conv3 = tf.keras.layers.Conv2D(128, kernel_size=(3, 3),
|
||||
activation='relu', padding='same')(pool2)
|
||||
activation='relu', padding='same')(pool2)
|
||||
pool3 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(conv3)
|
||||
|
||||
flat = tf.keras.layers.Flatten()(pool3)
|
||||
@ -343,31 +341,32 @@ out = tf.keras.layers.Dense(1, activation='sigmoid')(drop2)
|
||||
|
||||
model = tf.keras.Model(inputs=inp, outputs=out)
|
||||
model.compile(optimizer='adam',
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
model.summary()
|
||||
|
||||
|
||||
# Output
|
||||
Model: "model"
|
||||
_________________________________________________________________
|
||||
Layer (type) Output Shape Param #
|
||||
Layer (type) Output Shape Param #
|
||||
=================================================================
|
||||
input_1 (InputLayer) [(None, 125, 125, 3)] 0
|
||||
input_1 (InputLayer) [(None, 125, 125, 3)] 0
|
||||
_________________________________________________________________
|
||||
conv2d (Conv2D) (None, 125, 125, 32) 896
|
||||
conv2d (Conv2D) (None, 125, 125, 32) 896
|
||||
_________________________________________________________________
|
||||
max_pooling2d (MaxPooling2D) (None, 62, 62, 32) 0
|
||||
max_pooling2d (MaxPooling2D) (None, 62, 62, 32) 0
|
||||
_________________________________________________________________
|
||||
conv2d_1 (Conv2D) (None, 62, 62, 64) 18496
|
||||
conv2d_1 (Conv2D) (None, 62, 62, 64) 18496
|
||||
_________________________________________________________________
|
||||
...
|
||||
...
|
||||
_________________________________________________________________
|
||||
dense_1 (Dense) (None, 512) 262656
|
||||
dense_1 (Dense) (None, 512) 262656
|
||||
_________________________________________________________________
|
||||
dropout_1 (Dropout) (None, 512) 0
|
||||
dropout_1 (Dropout) (None, 512) 0
|
||||
_________________________________________________________________
|
||||
dense_2 (Dense) (None, 1) 513
|
||||
dense_2 (Dense) (None, 1) 513
|
||||
=================================================================
|
||||
Total params: 15,102,529
|
||||
Trainable params: 15,102,529
|
||||
@ -375,25 +374,25 @@ Non-trainable params: 0
|
||||
_________________________________________________________________
|
||||
```
|
||||
|
||||
基于这些代码的架构,我们的 CNN 模型有三个卷积和一个池化层,跟随两个致密层,以及用于正则化的丢失。让我们训练我们的模型。
|
||||
基于这些代码的架构,我们的 CNN 模型有三个卷积和一个池化层,其后是两个致密层,以及用于正则化的失活。让我们训练我们的模型。
|
||||
|
||||
|
||||
```
|
||||
import datetime
|
||||
|
||||
logdir = os.path.join('/home/dipanzan_sarkar/projects/tensorboard_logs',
|
||||
datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
|
||||
datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
|
||||
tensorboard_callback = tf.keras.callbacks.TensorBoard(logdir, histogram_freq=1)
|
||||
reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5,
|
||||
patience=2, min_lr=0.000001)
|
||||
patience=2, min_lr=0.000001)
|
||||
callbacks = [reduce_lr, tensorboard_callback]
|
||||
|
||||
history = model.fit(x=train_imgs_scaled, y=train_labels_enc,
|
||||
batch_size=BATCH_SIZE,
|
||||
epochs=EPOCHS,
|
||||
validation_data=(val_imgs_scaled, val_labels_enc),
|
||||
callbacks=callbacks,
|
||||
verbose=1)
|
||||
batch_size=BATCH_SIZE,
|
||||
epochs=EPOCHS,
|
||||
validation_data=(val_imgs_scaled, val_labels_enc),
|
||||
callbacks=callbacks,
|
||||
verbose=1)
|
||||
|
||||
|
||||
# Output
|
||||
@ -439,56 +438,52 @@ l2 = ax2.legend(loc="best")
|
||||
|
||||
![Learning curves for basic CNN][17]
|
||||
|
||||
基础 CNN 学习曲线
|
||||
|
||||
我们可以看在在第五个历元,情况并没有改善很多。让我们保存这个模型用于将来的评估。
|
||||
*基础 CNN 学习曲线*
|
||||
|
||||
我们可以看在在第五个纪元,情况并没有改善很多。让我们保存这个模型用于将来的评估。
|
||||
|
||||
```
|
||||
`model.save('basic_cnn.h5')`
|
||||
model.save('basic_cnn.h5')
|
||||
```
|
||||
|
||||
#### 深度迁移学习
|
||||
|
||||
就像人类有与生俱来的能力在不同任务间传输知识,迁移学习允许我们利用从以前任务学到的知识用到新的任务,相关的任务,甚至在机器学习或深度学习的上下文中。如果想深入探究迁移学习,你应该看我的文章“[一个易于理解与现实应用一起学习深度学习中的迁移学习的指导实践][18]”和我的书[ Python 迁移学习实践][19]。
|
||||
就像人类有与生俱来在不同任务间传输知识的能力一样,迁移学习允许我们利用从以前任务学到的知识用到新的相关的任务,即使在机器学习或深度学习的情况下也是如此。如果想深入探究迁移学习,你应该看我的文章“[一个易于理解与现实应用一起学习深度学习中的迁移学习的指导实践][18]”和我的书《[Python 迁移学习实践][19]》。
|
||||
|
||||
![深度迁移学习的想法][20]
|
||||
|
||||
在这篇实践中我们想要探索的想法是:
|
||||
|
||||
> 在我们的问题上下文中,我们能够利用一个预训练深度学习模型(在大数据集上训练的,像 ImageNet)通过应用和迁移知识来解决疟疾检测的问题吗?
|
||||
> 在我们的问题背景下,我们能够利用一个预训练深度学习模型(在大数据集上训练的,像 ImageNet)通过应用和迁移知识来解决疟疾检测的问题吗?
|
||||
|
||||
我们将应用两个深度迁移学习的最流行的策略。
|
||||
我们将应用两个最流行的深度迁移学习策略。
|
||||
|
||||
* 预训练模型作为特征提取器
|
||||
* 微调的预训练模型
|
||||
|
||||
|
||||
|
||||
我们将使用预训练的 VGG-19 深度训练模型,由剑桥大学的视觉几何组(VGG)开发,作为我们的实验。一个像 VGG-19 的预训练模型在一个大的数据集上使用了很多不同的图像分类训练([Imagenet][21])。因此,这个模型应该已经学习到了鲁棒的特征层级结构,相对于你的 CNN 模型学到的特征,是空间不变的,转动不变的,平移不变的。因此,这个模型,已经从百万幅图片中学习到了一个好的特征显示,对于像疟疾检测这样的计算机视觉问题,可以作为一个好的合适新图像的特征提取器。在我们的问题中释放迁移学习的能力之前,让我们先讨论 VGG-19 模型。
|
||||
我们将使用预训练的 VGG-19 深度训练模型(由剑桥大学的视觉几何组(VGG)开发)进行我们的实验。像 VGG-19 这样的预训练模型是在一个大的数据集([Imagenet][21])上使用了很多不同的图像分类训练的。因此,这个模型应该已经学习到了健壮的特征层级结构,相对于你的 CNN 模型学到的特征,是空间不变的、转动不变的、平移不变的。因此,这个模型,已经从百万幅图片中学习到了一个好的特征显示,对于像疟疾检测这样的计算机视觉问题,可以作为一个好的合适新图像的特征提取器。在我们的问题中发挥迁移学习的能力之前,让我们先讨论 VGG-19 模型。
|
||||
|
||||
##### 理解 VGG-19 模型
|
||||
|
||||
VGG-19 模型是一个构建在 ImageNet 数据库之上的 19 层(卷积和全连接的)的深度学习网络,该数据库为了图像识别和分类的目的而开发。该模型由 Karen Simonyan 和 Andrew Zisserman 构建,在它们的论文”[大规模图像识别的非常深的卷积网络][22]“中描述。VGG-19 的架构模型是:
|
||||
VGG-19 模型是一个构建在 ImageNet 数据库之上的 19 层(卷积和全连接的)的深度学习网络,ImageNet 数据库为了图像识别和分类的目的而开发。该模型是由 Karen Simonyan 和 Andrew Zisserman 构建的,在他们的论文“[大规模图像识别的非常深的卷积网络][22]”中进行了描述。VGG-19 的架构模型是:
|
||||
|
||||
![VGG-19 模型架构][23]
|
||||
|
||||
你可以看到我们总共有 16 个使用 3x3 卷积过滤器的卷积层,与最大的池化层来下采样,和由 4096 个单元组成的两个全连接的隐藏层,每个隐藏层之后跟随一个由 1000 个单元组成的致密层,每个单元代表 ImageNet 数据库中的一个分类。我们不需要最后三层,因为我们将使用我们自己的全连接致密层来预测疟疾。我们更关心前五块,因此我们可以利用 VGG 模型作为一个有效的特征提取器。
|
||||
你可以看到我们总共有 16 个使用 3x3 卷积过滤器的卷积层,与最大的池化层来下采样,和由 4096 个单元组成的两个全连接的隐藏层,每个隐藏层之后跟随一个由 1000 个单元组成的致密层,每个单元代表 ImageNet 数据库中的一个分类。我们不需要最后三层,因为我们将使用我们自己的全连接致密层来预测疟疾。我们更关心前五个块,因此我们可以利用 VGG 模型作为一个有效的特征提取器。
|
||||
|
||||
我们将使用模型之一作为一个简单的特征提取器通过冻结五个卷积块的方式来确保它们的位权在每个时期后不会更新。对于最后一个模型,我们会应用微调到 VGG 模型,我们会解冻最后两个块(第 4 和第 5)因此当我们训练我们的模型时,它们的位权在每个时期(每批数据)被更新。
|
||||
我们将使用模型之一作为一个简单的特征提取器,通过冻结五个卷积块的方式来确保它们的位权在每个纪元后不会更新。对于最后一个模型,我们会对 VGG 模型进行微调,我们会解冻最后两个块(第 4 和第 5)因此当我们训练我们的模型时,它们的位权在每个时期(每批数据)被更新。
|
||||
|
||||
#### 模型 2:预训练的模型作为一个特征提取器
|
||||
|
||||
为了构建这个模型,我们将利用 TensorFlow 载入 VGG-19 模型并且冻结卷积块因此我们用够将他们用作特征提取器。我们插入我们自己的致密层在末尾来执行分类任务。
|
||||
|
||||
为了构建这个模型,我们将利用 TensorFlow 载入 VGG-19 模型并冻结卷积块,因此我们能够将它们用作特征提取器。我们在末尾插入我们自己的致密层来执行分类任务。
|
||||
|
||||
```
|
||||
vgg = tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet',
|
||||
input_shape=INPUT_SHAPE)
|
||||
input_shape=INPUT_SHAPE)
|
||||
vgg.trainable = False
|
||||
# Freeze the layers
|
||||
for layer in vgg.layers:
|
||||
layer.trainable = False
|
||||
layer.trainable = False
|
||||
|
||||
base_vgg = vgg
|
||||
base_out = base_vgg.output
|
||||
@ -502,37 +497,38 @@ out = tf.keras.layers.Dense(1, activation='sigmoid')(drop2)
|
||||
|
||||
model = tf.keras.Model(inputs=base_vgg.input, outputs=out)
|
||||
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=1e-4),
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
model.summary()
|
||||
|
||||
|
||||
# Output
|
||||
Model: "model_1"
|
||||
_________________________________________________________________
|
||||
Layer (type) Output Shape Param #
|
||||
Layer (type) Output Shape Param #
|
||||
=================================================================
|
||||
input_2 (InputLayer) [(None, 125, 125, 3)] 0
|
||||
input_2 (InputLayer) [(None, 125, 125, 3)] 0
|
||||
_________________________________________________________________
|
||||
block1_conv1 (Conv2D) (None, 125, 125, 64) 1792
|
||||
block1_conv1 (Conv2D) (None, 125, 125, 64) 1792
|
||||
_________________________________________________________________
|
||||
block1_conv2 (Conv2D) (None, 125, 125, 64) 36928
|
||||
block1_conv2 (Conv2D) (None, 125, 125, 64) 36928
|
||||
_________________________________________________________________
|
||||
...
|
||||
...
|
||||
_________________________________________________________________
|
||||
block5_pool (MaxPooling2D) (None, 3, 3, 512) 0
|
||||
block5_pool (MaxPooling2D) (None, 3, 3, 512) 0
|
||||
_________________________________________________________________
|
||||
flatten_1 (Flatten) (None, 4608) 0
|
||||
flatten_1 (Flatten) (None, 4608) 0
|
||||
_________________________________________________________________
|
||||
dense_3 (Dense) (None, 512) 2359808
|
||||
dense_3 (Dense) (None, 512) 2359808
|
||||
_________________________________________________________________
|
||||
dropout_2 (Dropout) (None, 512) 0
|
||||
dropout_2 (Dropout) (None, 512) 0
|
||||
_________________________________________________________________
|
||||
dense_4 (Dense) (None, 512) 262656
|
||||
dense_4 (Dense) (None, 512) 262656
|
||||
_________________________________________________________________
|
||||
dropout_3 (Dropout) (None, 512) 0
|
||||
dropout_3 (Dropout) (None, 512) 0
|
||||
_________________________________________________________________
|
||||
dense_5 (Dense) (None, 1) 513
|
||||
dense_5 (Dense) (None, 1) 513
|
||||
=================================================================
|
||||
Total params: 22,647,361
|
||||
Trainable params: 2,622,977
|
||||
@ -540,45 +536,42 @@ Non-trainable params: 20,024,384
|
||||
_________________________________________________________________
|
||||
```
|
||||
|
||||
输出是很明白的,在我们的模型中我们有了很多层,我们将只利用 VGG-19 模型的冻结层作为特征提取器。你可以使用下列代码来验证我们的模型有多少层是实际训练的,我们的网络中总共存在多少层。
|
||||
|
||||
从整个输出可以明显看出,在我们的模型中我们有了很多层,我们将只利用 VGG-19 模型的冻结层作为特征提取器。你可以使用下列代码来验证我们的模型有多少层是实际可训练的,以及我们的网络中总共存在多少层。
|
||||
|
||||
```
|
||||
print("Total Layers:", len(model.layers))
|
||||
print("Total trainable layers:",
|
||||
sum([1 for l in model.layers if l.trainable]))
|
||||
sum([1 for l in model.layers if l.trainable]))
|
||||
|
||||
# Output
|
||||
Total Layers: 28
|
||||
Total trainable layers: 6
|
||||
```
|
||||
|
||||
我们将使用和我们之前的模型相似的配置和回调来训练我们的模型。参考 [我的 GitHub 仓库][24] 获取训练模型的完整代码。我们观察下列显示模型精确度和损失曲线。
|
||||
我们将使用和我们之前的模型相似的配置和回调来训练我们的模型。参考[我的 GitHub 仓库][24]以获取训练模型的完整代码。我们观察下列图表,以显示模型精确度和损失曲线。
|
||||
|
||||
![Learning curves for frozen pre-trained CNN][25]
|
||||
|
||||
冻结的预训练的 CNN 的学习曲线
|
||||
|
||||
这显示了我们的模型没有像我们的基础 CNN 模型那样过拟合,但是性能有点不如我们的基础的 CNN 模型。让我们保存这个模型用户将来的评估。
|
||||
*冻结的预训练的 CNN 的学习曲线*
|
||||
|
||||
这表明我们的模型没有像我们的基础 CNN 模型那样过拟合,但是性能有点不如我们的基础的 CNN 模型。让我们保存这个模型,以备将来的评估。
|
||||
|
||||
```
|
||||
`model.save('vgg_frozen.h5')`
|
||||
model.save('vgg_frozen.h5')
|
||||
```
|
||||
|
||||
#### 模型 3:使用图像增强来微调预训练的模型
|
||||
|
||||
在我们的最后一个模型中,我们微调预定义好的 VGG-19 模型的最后两个块中层的位权。我们同样引入图像增强的概念。图像增强背后的想法和名字一样。我们从训练数据集中载入已存在的图像,并且应用转换操作,例如旋转,裁剪,转换,放大缩小,等等,来产生新的,改变的版本。由于这些随机的转换,我们每次获取到的图像不一样。我们将应用一个在 **tf.keras** 的优秀的工具叫做 **ImageDataGenerator** 来帮助构建图像增强器。
|
||||
|
||||
在我们的最后一个模型中,我们将在预定义好的 VGG-19 模型的最后两个块中微调层的位权。我们同样引入了图像增强的概念。图像增强背后的想法和其名字一样。我们从训练数据集中载入现有图像,并且应用转换操作,例如旋转、裁剪、转换、放大缩小等等,来产生新的、改变过的版本。由于这些随机转换,我们每次获取到的图像不一样。我们将应用 tf.keras 中的一个名为 ImageDataGenerator 的优秀工具来帮助构建图像增强器。
|
||||
|
||||
```
|
||||
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255,
|
||||
zoom_range=0.05,
|
||||
rotation_range=25,
|
||||
width_shift_range=0.05,
|
||||
height_shift_range=0.05,
|
||||
shear_range=0.05, horizontal_flip=True,
|
||||
fill_mode='nearest')
|
||||
zoom_range=0.05,
|
||||
rotation_range=25,
|
||||
width_shift_range=0.05,
|
||||
height_shift_range=0.05,
|
||||
shear_range=0.05, horizontal_flip=True,
|
||||
fill_mode='nearest')
|
||||
|
||||
val_datagen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
|
||||
|
||||
@ -587,13 +580,12 @@ train_generator = train_datagen.flow(train_data, train_labels_enc, batch_size=BA
|
||||
val_generator = val_datagen.flow(val_data, val_labels_enc, batch_size=BATCH_SIZE, shuffle=False)
|
||||
```
|
||||
|
||||
我们不会应用任何转换在我们的验证数据集上(除非是调整大小,它是强制性适应的)因为我们将在每个时期来评估我们的模型性能。对于在传输学习上下文中的图像增强的详细解释,请自由查看我们上述引用的[文章][18]。让我们从一批图像增强转换中查看一些样本结果。
|
||||
|
||||
我们不会对我们的验证数据集应用任何转换(除非是调整大小,因为这是必须的),因为我们将使用它评估每个纪元的模型性能。对于在传输学习环境中的图像增强的详细解释,请随时查看我上面引用的[文章][18]。让我们从一批图像增强转换中查看一些样本结果。
|
||||
|
||||
```
|
||||
img_id = 0
|
||||
sample_generator = train_datagen.flow(train_data[img_id:img_id+1], train_labels[img_id:img_id+1],
|
||||
batch_size=1)
|
||||
batch_size=1)
|
||||
sample = [next(sample_generator) for i in range(0,5)]
|
||||
fig, ax = plt.subplots(1,5, figsize=(16, 6))
|
||||
print('Labels:', [item[1][0] for item in sample])
|
||||
@ -602,23 +594,22 @@ l = [ax[i].imshow(sample[i][0][0]) for i in range(0,5)]
|
||||
|
||||
![Sample augmented images][26]
|
||||
|
||||
你可以清晰的看到与之前的输出中我们图像的轻微变化。我们现在构建我们的学习模型,确保 VGG-19 模型的最后两块是可以训练的。
|
||||
|
||||
你可以清晰的看到与之前的输出的我们图像的轻微变化。我们现在构建我们的学习模型,确保 VGG-19 模型的最后两块是可以训练的。
|
||||
|
||||
```
|
||||
vgg = tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet',
|
||||
input_shape=INPUT_SHAPE)
|
||||
input_shape=INPUT_SHAPE)
|
||||
# Freeze the layers
|
||||
vgg.trainable = True
|
||||
|
||||
set_trainable = False
|
||||
for layer in vgg.layers:
|
||||
if layer.name in ['block5_conv1', 'block4_conv1']:
|
||||
set_trainable = True
|
||||
if set_trainable:
|
||||
layer.trainable = True
|
||||
else:
|
||||
layer.trainable = False
|
||||
if layer.name in ['block5_conv1', 'block4_conv1']:
|
||||
set_trainable = True
|
||||
if set_trainable:
|
||||
layer.trainable = True
|
||||
else:
|
||||
layer.trainable = False
|
||||
|
||||
base_vgg = vgg
|
||||
base_out = base_vgg.output
|
||||
@ -632,31 +623,32 @@ out = tf.keras.layers.Dense(1, activation='sigmoid')(drop2)
|
||||
|
||||
model = tf.keras.Model(inputs=base_vgg.input, outputs=out)
|
||||
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=1e-5),
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
print("Total Layers:", len(model.layers))
|
||||
print("Total trainable layers:", sum([1 for l in model.layers if l.trainable]))
|
||||
|
||||
|
||||
# Output
|
||||
Total Layers: 28
|
||||
Total trainable layers: 16
|
||||
```
|
||||
|
||||
在我们的模型中我们降低了学习率,因为我们微调的时候不想在预训练的数据集上做大的位权更新。模型的训练过程可能有轻微的不同,因为我们使用了数据生成器,因此我们应用了 **fit_generator(...)** 函数。
|
||||
|
||||
在我们的模型中我们降低了学习率,因为我们不想在微调的时候对预训练的层做大的位权更新。模型的训练过程可能有轻微的不同,因为我们使用了数据生成器,因此我们将应用 `fit_generator(...)` 函数。
|
||||
|
||||
```
|
||||
tensorboard_callback = tf.keras.callbacks.TensorBoard(logdir, histogram_freq=1)
|
||||
reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5,
|
||||
patience=2, min_lr=0.000001)
|
||||
patience=2, min_lr=0.000001)
|
||||
|
||||
callbacks = [reduce_lr, tensorboard_callback]
|
||||
train_steps_per_epoch = train_generator.n // train_generator.batch_size
|
||||
val_steps_per_epoch = val_generator.n // val_generator.batch_size
|
||||
history = model.fit_generator(train_generator, steps_per_epoch=train_steps_per_epoch, epochs=EPOCHS,
|
||||
validation_data=val_generator, validation_steps=val_steps_per_epoch,
|
||||
verbose=1)
|
||||
validation_data=val_generator, validation_steps=val_steps_per_epoch,
|
||||
verbose=1)
|
||||
|
||||
|
||||
# Output
|
||||
Epoch 1/25
|
||||
@ -675,21 +667,20 @@ Epoch 25/25
|
||||
|
||||
![Learning curves for fine-tuned pre-trained CNN][27]
|
||||
|
||||
微调预训练的 CNN 的学习曲线
|
||||
*微调过的预训练 CNN 的学习曲线*
|
||||
|
||||
让我们保存这个模型,因此我们能够在测试集上使用。
|
||||
|
||||
|
||||
```
|
||||
`model.save('vgg_finetuned.h5')`
|
||||
model.save('vgg_finetuned.h5')
|
||||
```
|
||||
|
||||
这完成了我们的模型训练阶段。我们准备好在测试集上测试我们模型的性能。
|
||||
这就完成了我们的模型训练阶段。现在我们准备好了在测试集上测试我们模型的性能。
|
||||
|
||||
### 深度学习模型性能评估
|
||||
|
||||
我们将评估我们在训练阶段构建的三个模型,通过在我们的测试集上做预测,因为仅仅验证是不够的!我们同样构建了一个检测工具模块叫做 **model_evaluation_utils**,我们可以使用相关分类指标用来评估使用我们深度学习模型的性能。第一步是测量我们的数据集。
|
||||
|
||||
我们将通过在我们的测试集上做预测来评估我们在训练阶段构建的三个模型,因为仅仅验证是不够的!我们同样构建了一个检测工具模块叫做 `model_evaluation_utils`,我们可以使用相关分类指标用来评估使用我们深度学习模型的性能。第一步是扩展我们的数据集。
|
||||
|
||||
```
|
||||
test_imgs_scaled = test_data / 255.
|
||||
@ -701,7 +692,6 @@ test_imgs_scaled.shape, test_labels.shape
|
||||
|
||||
下一步包括载入我们保存的深度学习模型,在测试集上预测。
|
||||
|
||||
|
||||
```
|
||||
# Load Saved Deep Learning Models
|
||||
basic_cnn = tf.keras.models.load_model('./basic_cnn.h5')
|
||||
@ -714,15 +704,14 @@ vgg_frz_preds = vgg_frz.predict(test_imgs_scaled, batch_size=512)
|
||||
vgg_ft_preds = vgg_ft.predict(test_imgs_scaled, batch_size=512)
|
||||
|
||||
basic_cnn_pred_labels = le.inverse_transform([1 if pred > 0.5 else 0
|
||||
for pred in basic_cnn_preds.ravel()])
|
||||
for pred in basic_cnn_preds.ravel()])
|
||||
vgg_frz_pred_labels = le.inverse_transform([1 if pred > 0.5 else 0
|
||||
for pred in vgg_frz_preds.ravel()])
|
||||
for pred in vgg_frz_preds.ravel()])
|
||||
vgg_ft_pred_labels = le.inverse_transform([1 if pred > 0.5 else 0
|
||||
for pred in vgg_ft_preds.ravel()])
|
||||
for pred in vgg_ft_preds.ravel()])
|
||||
```
|
||||
|
||||
下一步是应用我们的 **model_evaluation_utils** 模块根据相应分类指标来检查每个模块的性能。
|
||||
|
||||
下一步是应用我们的 `model_evaluation_utils` 模块根据相应分类指标来检查每个模块的性能。
|
||||
|
||||
```
|
||||
import model_evaluation_utils as meu
|
||||
@ -733,29 +722,29 @@ vgg_frz_metrics = meu.get_metrics(true_labels=test_labels, predicted_labels=vgg_
|
||||
vgg_ft_metrics = meu.get_metrics(true_labels=test_labels, predicted_labels=vgg_ft_pred_labels)
|
||||
|
||||
pd.DataFrame([basic_cnn_metrics, vgg_frz_metrics, vgg_ft_metrics],
|
||||
index=['Basic CNN', 'VGG-19 Frozen', 'VGG-19 Fine-tuned'])
|
||||
index=['Basic CNN', 'VGG-19 Frozen', 'VGG-19 Fine-tuned'])
|
||||
```
|
||||
|
||||
![Model accuracy][28]
|
||||
|
||||
看起来我们的第三个模型在我们的测试集上执行的最好,给出了一个模型精确性为 96% 的 F1得分,比起上述我们早期引用的研究论文和文章中提及的复杂的模型是相当好的。
|
||||
看起来我们的第三个模型在我们的测试集上执行的最好,给出了一个模型精确性为 96% 的 F1 得分,这非常好,与我们之前提到的研究论文和文章中的更复杂的模型相当。
|
||||
|
||||
### 总结
|
||||
|
||||
疟疾检测不是一个简单的程序,全球的合格的人员的可获得性在样例诊断和治疗当中是一个严重的问题。我们看到一个关于疟疾的有趣的真实世界的医学影像案例。易于构建的,开源的技术利用 AI 在检测疟疾方面可以给我们最先进的精确性,因此允许 AI 对社会是有益的。
|
||||
疟疾检测不是一个简单的过程,全球的合格人员的不足在病例诊断和治疗当中是一个严重的问题。我们研究了一个关于疟疾的有趣的真实世界的医学影像案例。利用 AI 的、易于构建的、开源的技术在检测疟疾方面可以为我们提供最先进的精确性,因此使 AI 具有社会效益。
|
||||
|
||||
我鼓励你检查这片文章中提到的文章和研究论文,没有它们,我就不能形成概念并写出来。如果你对运行和采纳这些技术感兴趣,本篇文章所有的代码都可以在[我的 GitHub 仓库][24]获得。记得从[官方网站][11]下载数据。
|
||||
我鼓励你查看这篇文章中提到的文章和研究论文,没有它们,我就不能形成概念并写出来。如果你对运行和采纳这些技术感兴趣,本篇文章所有的代码都可以在[我的 GitHub 仓库][24]获得。记得从[官方网站][11]下载数据。
|
||||
|
||||
让我们希望在健康医疗方面更多的采纳开源的 AI 能力,使它在世界范围内变得便宜些,易用些。
|
||||
让我们希望在健康医疗方面更多的采纳开源的 AI 能力,使它在世界范围内变得更便宜、更易用。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/detecting-malaria-deep-learning
|
||||
|
||||
作者:[Dipanjan (DJ) Sarkar (Red Hat)][a]
|
||||
作者:[Dipanjan (DJ) Sarkar][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,8 +1,8 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10875-1.html)
|
||||
[#]: subject: (Automate backups with restic and systemd)
|
||||
[#]: via: (https://fedoramagazine.org/automate-backups-with-restic-and-systemd/)
|
||||
[#]: author: (Link Dupont https://fedoramagazine.org/author/linkdupont/)
|
||||
@ -12,18 +12,17 @@
|
||||
|
||||
![][1]
|
||||
|
||||
及时备份很重要。即使在 [Fedora Magazine][3] 中,[备份软件][2] 也是一个常见的讨论话题。本文演示了如何仅使用 systemd 以及 **restic** 来自动备份。
|
||||
及时备份很重要。即使在 [Fedora Magazine][3] 中,[备份软件][2] 也是一个常见的讨论话题。本文演示了如何仅使用 systemd 以及 `restic` 来自动备份。
|
||||
|
||||
有关 `restic` 的介绍,请查看我们的文章[在 Fedora 上使用 restic 进行加密备份][4]。然后继续阅读以了解更多详情。
|
||||
|
||||
有关 restic 的介绍,请查看我们的文章[在 Fedora 上使用 restic 进行加密备份][4]。然后继续阅读以了解更多详情。
|
||||
|
||||
为了自动创建快照以及清理数据,需要运行两个 systemd 服务。第一个运行_备份_命令的服务需要以常规频率运行。第二个服务负责数据清理。
|
||||
为了自动创建快照以及清理数据,需要运行两个 systemd 服务。第一个运行*备份*命令的服务需要以常规频率运行。第二个服务负责数据清理。
|
||||
|
||||
如果你根本不熟悉 systemd,那么这是个很好的学习机会。查看 [Magazine 上关于 systemd 的系列文章] [5],从单元文件的这个入门开始:
|
||||
|
||||
> [systemd 单元文件基础][6]
|
||||
- [systemd 单元文件基础][6]
|
||||
|
||||
如果你还没有安装 restic,请注意它在官方的 Fedora 仓库中。要安装它,请[带上 sudo][7] 运行此命令:
|
||||
如果你还没有安装 `restic`,请注意它在官方的 Fedora 仓库中。要安装它,请[带上 sudo][7] 运行此命令:
|
||||
|
||||
```
|
||||
$ sudo dnf install restic
|
||||
@ -31,7 +30,7 @@ $ sudo dnf install restic
|
||||
|
||||
### 备份
|
||||
|
||||
首先,创建 _~/.config/systemd/user/restic-backup.service_。将下面的文本复制并粘贴到文件中以获得最佳效果。
|
||||
首先,创建 `~/.config/systemd/user/restic-backup.service`。将下面的文本复制并粘贴到文件中以获得最佳效果。
|
||||
|
||||
```
|
||||
[Unit]
|
||||
@ -43,7 +42,7 @@ ExecStartPost=restic forget --verbose --tag systemd.timer --group-by "paths,tags
|
||||
EnvironmentFile=%h/.config/restic-backup.conf
|
||||
```
|
||||
|
||||
此服务引用环境文件来加载密钥(例如 _RESTIC_PASSWORD_)。创建 _~/.config/restic-backup.conf_。复制并粘贴以下内容以获得最佳效果。此示例使用 BackBlaze B2 存储。请相应地调整 ID、密钥、仓库和密码值。
|
||||
此服务引用环境文件来加载密钥(例如 `RESTIC_PASSWORD`)。创建 `~/.config/restic-backup.conf`。复制并粘贴以下内容以获得最佳效果。此示例使用 BackBlaze B2 存储。请相应地调整 ID、密钥、仓库和密码值。
|
||||
|
||||
```
|
||||
BACKUP_PATHS="/home/rupert"
|
||||
@ -58,9 +57,9 @@ RESTIC_REPOSITORY=b2:XXXXXXXXXXXXXXXXXX:/
|
||||
RESTIC_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
```
|
||||
|
||||
现在已安装该服务,请重新加载 systemd:_systemctl -user daemon-reload_。尝试手动运行该服务以创建备份:_systemctl -user start restic-backup_。
|
||||
现在已安装该服务,请重新加载 systemd:`systemctl -user daemon-reload`。尝试手动运行该服务以创建备份:`systemctl -user start restic-backup`。
|
||||
|
||||
因为该服务类型是 _oneshot_,它将运行一次并退出。验证服务运行并根据需要创建快照后,设置计时器以定期运行此服务。例如,要每天运行 _restic-backup.service_,请按如下所示创建 _~/.config/systemd/user/restic-backup.timer_。再次复制并粘贴此文本:
|
||||
因为该服务类型是*一次性*,它将运行一次并退出。验证服务运行并根据需要创建快照后,设置计时器以定期运行此服务。例如,要每天运行 `restic-backup.service`,请按如下所示创建 `~/.config/systemd/user/restic-backup.timer`。再次复制并粘贴此文本:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
@ -80,7 +79,7 @@ $ systemctl --user enable --now restic-backup.timer
|
||||
|
||||
### 清理
|
||||
|
||||
虽然主服务运行 _forget_ 命令仅保留保留策略中的快照,但实际上并未从 restic 仓库中删除数据。 _prune_ 命令检查仓库和当前快照,并删除与快照无关的所有数据。由于 _prune_ 可能是一个耗时的过程,因此无需在每次运行备份时运行。这是第二个服务和计时器的场景。首先,通过复制和粘贴此文本来创建文件 _~/.config/systemd/user/restic-prune.service_:
|
||||
虽然主服务运行 `forget` 命令仅保留保留策略中的快照,但实际上并未从 `restic` 仓库中删除数据。 `prune` 命令检查仓库和当前快照,并删除与快照无关的所有数据。由于 `prune` 可能是一个耗时的过程,因此无需在每次运行备份时运行。这是第二个服务和计时器的场景。首先,通过复制和粘贴此文本来创建文件 `~/.config/systemd/user/restic-prune.service`:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
@ -91,7 +90,7 @@ ExecStart=restic prune
|
||||
EnvironmentFile=%h/.config/restic-backup.conf
|
||||
```
|
||||
|
||||
与主 _restic-backup.service_ 服务类似,_restic-prune_ 也是 onehot 服务,并且可以手动运行。设置完服务后,创建 _~/.config/systemd/user/restic-prune.timer_ 并启用相应的计时器:
|
||||
与主 `restic-backup.service` 服务类似,`restic-prune` 也是一次性服务,并且可以手动运行。设置完服务后,创建 `~/.config/systemd/user/restic-prune.timer` 并启用相应的计时器:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
@ -103,11 +102,11 @@ Persistent=true
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
就是这些了!restic 将会每日运行并按月清理数据。
|
||||
就是这些了!`restic` 将会每日运行并按月清理数据。
|
||||
|
||||
* * *
|
||||
|
||||
图片来自 _[Unsplash][9]_ 由 _[ Samuel Zeller][8]_ 拍摄。
|
||||
图片来自 [Unsplash][9] 由 [Samuel Zeller][8] 拍摄。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -116,7 +115,7 @@ via: https://fedoramagazine.org/automate-backups-with-restic-and-systemd/
|
||||
作者:[Link Dupont][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,73 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10903-1.html)
|
||||
[#]: subject: (3 apps to manage personal finances in Fedora)
|
||||
[#]: via: (https://fedoramagazine.org/3-apps-to-manage-personal-finances-in-fedora/)
|
||||
[#]: author: (Paul W. Frields https://fedoramagazine.org/author/pfrields/)
|
||||
|
||||
3 款在 Fedora 中管理个人财务的应用
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
网上有很多可以用来管理你个人财务的服务。虽然它们可能很方便,但这通常也意味着将你最宝贵的个人数据放在你无法监控的公司。也有些人对这些不太在意。
|
||||
|
||||
无论你是否在意,你可能会对你自己系统上的应用感兴趣。这意味着如果你不想,你的数据永远不会离开自己的计算机。这三款之一可能就是你想找的。
|
||||
|
||||
### HomeBank
|
||||
|
||||
HomeBank 是一款可以管理多个账户的全功能软件。它很容易设置并保持更新。它有多种方式画出你的分类和负债,以便你可以看到资金流向何处。它可以通过官方 Fedora 仓库下载。
|
||||
|
||||
![A simple account set up in HomeBank with a few transactions.][2]
|
||||
|
||||
要安装 HomeBank,请打开“软件中心”,搜索 “HomeBank”,然后选择该应用。单击“安装”将其添加到你的系统中。HomeBank 也可以通过 Flatpak 安装。
|
||||
|
||||
### KMyMoney
|
||||
|
||||
KMyMoney 是一个成熟的应用,它已经存在了很长一段时间。它有一系列稳定的功能,可帮助你管理多个帐户,包括资产、负债、税收等。KMyMoney 包含一整套用于管理投资和进行预测的工具。它还提供大量报告,以了解你的资金运作方式。
|
||||
|
||||
![A subset of the many reports available in KMyMoney.][3]
|
||||
|
||||
要安装它,请使用软件中心,或使用命令行:
|
||||
|
||||
```
|
||||
$ sudo dnf install kmymoney
|
||||
```
|
||||
|
||||
### GnuCash
|
||||
|
||||
用于个人财务的最受欢迎的免费 GUI 应用之一是 GnuCash。GnuCash 不仅可以用于个人财务。它还有管理企业收入、资产和负债的功能。这并不意味着你不能用它来管理自己的账户。从查看[在线教程和指南][4]开始了解。
|
||||
|
||||
![Checking account records shown in GnuCash.][5]
|
||||
|
||||
打开“软件中心”,搜索 “GnuCash”,然后选择应用。单击“安装”将其添加到你的系统中。或者如上所述使用 `dnf install` 来安装 “gnucash” 包。
|
||||
|
||||
它现在可以通过 Flathub 安装,这使得安装变得简单。如果你没有安装 Flathub,请查看 [Fedora Magazine 上的这篇文章][6]了解如何使用它。这样你也可以在终端使用 `flatpak install gnucash` 命令。
|
||||
|
||||
* * *
|
||||
|
||||
照片由 [Fabian Blank][7] 拍摄,发布在 [Unsplash][8] 上。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/3-apps-to-manage-personal-finances-in-fedora/
|
||||
|
||||
作者:[Paul W. Frields][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/pfrields/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/04/personal-finance-3-apps-816x345.jpg
|
||||
[2]: https://fedoramagazine.org/wp-content/uploads/2019/04/Screenshot-from-2019-04-28-16-16-16-1024x637.png
|
||||
[3]: https://fedoramagazine.org/wp-content/uploads/2019/04/Screenshot-from-2019-04-28-16-27-10-1-1024x649.png
|
||||
[4]: https://www.gnucash.org/viewdoc.phtml?rev=3&lang=C&doc=guide
|
||||
[5]: https://fedoramagazine.org/wp-content/uploads/2019/04/Screenshot-from-2019-04-28-16-41-27-1024x631.png
|
||||
[6]: https://fedoramagazine.org/install-flathub-apps-fedora/
|
||||
[7]: https://unsplash.com/photos/pElSkGRA2NU?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[8]: https://unsplash.com/search/photos/money?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
@ -1,67 +1,60 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (hopefully2333)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10876-1.html)
|
||||
[#]: subject: (Cisco issues critical security warning for Nexus data-center switches)
|
||||
[#]: via: (https://www.networkworld.com/article/3392858/cisco-issues-critical-security-warning-for-nexus-data-center-switches.html#tk.rss_all)
|
||||
[#]: via: (https://www.networkworld.com/article/3392858/cisco-issues-critical-security-warning-for-nexus-data-center-switches.html)
|
||||
[#]: author: (Michael Cooney https://www.networkworld.com/author/Michael-Cooney/)
|
||||
|
||||
思科针对 Nexus 数据中心交换机发出危急安全预警
|
||||
======
|
||||
思科围绕着 Nexus 的交换机、Firepower 防火墙和其他设备,发布了 40 个安全报告。
|
||||
|
||||
> 思科围绕着 Nexus 的交换机、Firepower 防火墙和其他设备,发布了 40 个安全报告。
|
||||
|
||||
![Thinkstock][1]
|
||||
|
||||
今天思科发布了 40 个左右的安全报告,但只有其中的一个被评定为“危急”-思科 Nexus 9000 系列应用中心基础设施(ACI)模式数据中心交换机中的一个漏洞,可能会让攻击者隐秘地访问到系统资源。
|
||||
日前,思科发布了 40 个左右的安全报告,但只有其中的一个被评定为“[危急][2]”:思科 Nexus 9000 系列应用中心基础设施(ACI)模式数据中心交换机中的一个漏洞,可能会让攻击者隐秘地访问到系统资源。
|
||||
|
||||
这个新发现的漏洞,被通用漏洞评分系统给到了 9.8 分(满分 10 分),思科表示,它是思科 Nexus 9000 系列的安全 shell (ssh)密钥管理方面的问题,这个漏洞允许远程攻击者以 root 用户的权限来连接到受影响的系统。
|
||||
|
||||
**[ 另请阅读:如何规划一个软件定义的数据中心网络 ][3] ]**
|
||||
思科表示,“**这个漏洞是因为所有的设备都存在一对默认的 ssh 密钥对**,攻击者可以使用提取到的密钥材料,并通过 IPv6 来创建连接到目标设备的 SSH 连接。这个漏洞仅能通过 IPv6 来进行利用,IPv4 不会被攻击”。
|
||||
|
||||
思科表示,“这个漏洞是因为所有的设备都存在一对默认的 ssh 密钥对,攻击者可以使用提取到的密钥材料,并通过 IPv6 来创建连接到目标设备的 SSH 连接。这个漏洞仅能通过 IPv6 来进行利用,IPv4 不会被攻击”。
|
||||
型号为 Nexus 9000 系列且 NX-OS 软件版本在 14.1 之前的设备会受此漏洞的影响,该公司表示没有解决这个问题的变通办法。
|
||||
|
||||
型号为 Nexus 9000 系列且 NX-OS 软件版本在 14.1 之前的设备会受此漏洞的影响,该公司表示没有解决这个问题的办法。
|
||||
|
||||
然而,思科公司已经为解决这个漏洞发布了免费的软件更新。
|
||||
然而,思科公司已经为解决这个漏洞[发布了免费的软件更新][4]。
|
||||
|
||||
该公司同样对 Nexus 9000 系列发布了一个“高危”级别的安全预警报告,报告中表示存在一种攻击,允许攻击者以 root 用户权限在受影响的设备上执行任意操作系统命令。思科表示,如果要用这种方式攻击成功,攻击者需要对应设备的有效的管理员用户凭证。
|
||||
|
||||
思科表示,这个漏洞是由于过于宽泛的系统文件权限造成的。攻击者可以通过向受影响的设备进行认证,构造一个精心设计的命令字符串,并将这个字符串写入到特定位置的文件里。攻击者通过这种方式来利用这个漏洞。
|
||||
|
||||
**[[通过 PluralSight 的综合在线课程成为一名认证信息安全系统工程师。现在提供为期 10 天的免费试用!][6] ]**
|
||||
[思科表示][5],这个漏洞是由于过于宽泛的系统文件权限造成的。攻击者可以通过向受影响的设备进行认证,构造一个精心设计的命令字符串,并将这个字符串写入到特定位置的文件里。攻击者通过这种方式来利用这个漏洞。
|
||||
|
||||
思科发布了解决这个漏洞的软件更新。
|
||||
|
||||
另外两个被评为“高危”级别的漏洞的影响范围同样包括 Nexus 9000 系列:
|
||||
|
||||
思科 Nexus 9000 系列软件后台操作功能中的漏洞,能够允许一个已认证的本地攻击者在受影响的设备上提升到 root 权限。这个漏洞是由于在受影响的设备上用户提供的文件验证不充分。思科表示,攻击者可以通过登录到受影响设备的命令行界面,并在文件系统的特定目录中构造一个精心设计过的文件,以此来利用这个漏洞。
|
||||
- 思科 Nexus 9000 系列软件后台操作功能中的[漏洞][7],能够允许一个已认证的本地攻击者在受影响的设备上提权到 root 权限。这个漏洞是由于在受影响的设备上用户提供的文件验证不充分。思科表示,攻击者可以通过登录到受影响设备的命令行界面,并在文件系统的特定目录中构造一个精心设计过的文件,以此来利用这个漏洞。
|
||||
- 交换机软件后台操作功能中的[弱点][7]能够允许攻击者登录到受影响设备的命令行界面,并在文件系统的特定目录里创建一个精心构造过的文件。思科表示,这个漏洞是由于在受影响的设备上用户提供的文件验证不充分。
|
||||
|
||||
交换机软件后台操作功能中的弱点能够允许攻击者登录到受影响设备的命令行界面,并在文件系统的特定目录里创建一个精心构造过的文件。思科表示,这个漏洞是由于在受影响的设备上用户提供的文件验证不充分。
|
||||
|
||||
|
||||
|
||||
思科同样为这些漏洞发布了软件更新。
|
||||
思科同样为这些漏洞[发布了软件更新][4]。
|
||||
|
||||
此外,这些安全警告中的一部分是针对思科 FirePower 防火墙系列中大量的“高危”漏洞警告。
|
||||
|
||||
例如,思科写道,思科 Firepower 威胁防御软件的 SMB 协议预处理检测引擎中的多个漏洞能够允许未认证的相邻、远程攻击者造成拒绝服务攻击(DoS)的情况。
|
||||
例如,思科[写道][8],思科 Firepower 威胁防御软件的 SMB 协议预处理检测引擎中的多个漏洞能够允许未认证的相邻、远程攻击者造成拒绝服务攻击(DoS)的情况。
|
||||
|
||||
思科表示,思科 Firepower 2100 系列中思科 Firepower 软件里的内部数据包处理功能有另一个漏洞,能够让未认证的远程攻击者造成受影响的设备停止处理流量,从而导致 DOS 的情况。
|
||||
思科表示,思科 Firepower 2100 系列中思科 Firepower 软件里的内部数据包处理功能有[另一个漏洞][9],能够让未认证的远程攻击者造成受影响的设备停止处理流量,从而导致 DOS 的情况。
|
||||
|
||||
软件补丁可用于这些漏洞。
|
||||
[软件补丁][4]可用于这些漏洞。
|
||||
|
||||
其他的产品,比如思科自适应安全虚拟设备和 web 安全设备同样也有高优先级的补丁。
|
||||
|
||||
加入 Facebook 和 LinkedIn 中的网络世界社区,在最上面的主题里做评论。
|
||||
其他的产品,比如思科[自适应安全虚拟设备][10]和 [web 安全设备][11]同样也有高优先级的补丁。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3392858/cisco-issues-critical-security-warning-for-nexus-data-center-switches.html#tk.rss_all
|
||||
via: https://www.networkworld.com/article/3392858/cisco-issues-critical-security-warning-for-nexus-data-center-switches.html
|
||||
|
||||
作者:[Michael Cooney][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[hopefully2333](https://github.com/hopefully2333)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,28 +1,29 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10880-1.html)
|
||||
[#]: subject: (Get started with Libki to manage public user computer access)
|
||||
[#]: via: (https://opensource.com/article/19/5/libki-computer-access)
|
||||
[#]: author: (Don Watkins https://opensource.com/users/don-watkins/users/tony-thomas)
|
||||
|
||||
开始使用 Libk i来管理公共用户访问计算机
|
||||
使用 Libki 来管理公共用户访问计算机
|
||||
======
|
||||
Libki 是一个跨平台的计算机预约和时间管理系统。
|
||||
![][1]
|
||||
> Libki 是一个跨平台的计算机预约和用时管理系统。
|
||||
|
||||
提供公共计算机的图书馆、学校、学院和其他组织需要一种管理用户访问权限的好方法 - 否则,就无法阻止某些人独占机器并确保每个人都有公平的时间。这是 [Libki][2] 要解决的问题。
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/20/230201d26yuo261uu6s61i.jpg)
|
||||
|
||||
Libki 是一个面向 Windows 和 Linux PC 的开源、跨平台的计算机预约和时间管理系统。它提供了一个基于 Web 的服务器和一个基于 Web 的管理系统,员工可以使用它来管理计算机访问,包括创建和删除用户、设置帐户时间限制、登出和禁止用户以及设置访问限制。
|
||||
提供公共计算机的图书馆、学校、学院和其他组织需要一种管理用户访问权限的好方法 —— 否则,就无法阻止某些人独占机器并确保每个人都有公平的用时。这是 [Libki][2] 要解决的问题。
|
||||
|
||||
根据首席开发人员 [Kyle Hall][3] 所说,Libki 主要用于 PC 时间控制,作为 Envisionware 的专有计算机访问控制软件的开源替代品。当用户登录 Libki 管理的计算机时,他们会有一段时间来使用计算机。时间到了之后,他们就会被登出。时间默认设置为 45 分钟,但可以使用基于 Web 的管理系统轻松调整。一些组织在登出用户之前提供 24 小时访问权限,而有的组织则使用它来跟踪使用情况而不设置时间限制。
|
||||
Libki 是一个面向 Windows 和 Linux PC 的开源、跨平台的计算机预约和用时管理系统。它提供了一个基于 Web 的服务器和一个基于 Web 的管理系统,员工可以使用它来管理计算机访问,包括创建和删除用户、设置帐户用时限制、登出和禁止用户以及设置访问限制。
|
||||
|
||||
Kyle 目前是 [ByWater Solutions][4] 的首席开发人员,该公司为图书馆提供开源软件解决方案(包括 Libki)。在职业生涯早期,他在宾夕法尼亚州的[米德维尔公共图书馆][5]担任 IT 技术时开发了 Libki。在其他员工的午休期间,偶尔会要求他关注孩子的房间。图书馆使用纸质注册表来管理对儿童房间计算机的访问,这意味着不断的监督和检查,以确保来到那里的人能够公平地使用。
|
||||
根据其首席开发人员 [Kyle Hall][3] 所说,Libki 主要用于 PC 用时控制,作为 Envisionware 出品的专有计算机访问控制软件的开源替代品。当用户登录 Libki 管理的计算机时,他们会有一段使用计算机的时间。时间到了之后,他们就会被登出。时间默认设置为 45 分钟,但可以使用基于 Web 的管理系统轻松调整。一些组织在登出用户之前提供 24 小时访问权限,而有的组织则使用它来跟踪使用情况而不设置用时限制。
|
||||
|
||||
Kyle 说,“我发现这个系统很麻烦、很尴尬,我想找到一个解决方案。这个解决方案需要同时是 FOSS 和跨平台的。最后,没有现有的软件适合我们的特殊需求,那就是为什么我开发了 Libki。“
|
||||
Kyle 目前是 [ByWater Solutions][4] 的首席开发人员,该公司为图书馆提供开源软件解决方案(包括 Libki)。在职业生涯早期,他在宾夕法尼亚州的[米德维尔公共图书馆][5]担任 IT 技术时开发了 Libki。在其他员工的午休期间,偶尔会要求他关注孩子们的房间。图书馆使用纸质注册表来管理对儿童房间计算机的访问,这意味着不断的监督和检查,以确保来到那里的人能够公平地使用。
|
||||
|
||||
或者,正如 Libki 的网站所宣称的那样,“Libki 的诞生是为了避免与青少年互动,现在允许图书馆员避免与世界各地的青少年互动!”
|
||||
Kyle 说,“我发现这很笨拙而不便的,我想找到一个解决方案。这个解决方案需要同时是 FOSS 和跨平台的。最后,没有现有的软件适合我们的特殊需求,那就是为什么我开发了 Libki。“
|
||||
|
||||
或者,正如 Libki 的网站所宣称的那样,“Libki 的诞生是为了避免与青少年打交道(的麻烦),现在允许图书馆员避免与世界各地的青少年打交道(的麻烦)!”
|
||||
|
||||
### 易于安装和使用
|
||||
|
||||
@ -30,7 +31,7 @@ Kyle 说,“我发现这个系统很麻烦、很尴尬,我想找到一个解
|
||||
|
||||
我计划在我们当地的图书馆支持 Libki,但我想知道在那些没有 IT 相关经验的人或者无法构建和部署服务器的图书馆是怎样的。Kyle 说:“ByWater Solutions 可以云端托管 Libki 服务器,这使得每个人的维护和管理变得更加简单。”
|
||||
|
||||
Kyle 表示,ByWater 并不打算将 Libki 与其最受欢迎的产品,开源集成图书馆系统 (ILS)Koha 或其支持的任何其他[项目][7]捆绑在一起。他说: “Libki 和 Koha 是不同[类型]的软件,满足不同的需求,但它们在图书馆中确实很好地协同工作。事实上,我很早就开发了 Libki 的 SIP2 集成,因此它可以支持使用 Koha 进行单点登录,“ 。
|
||||
Kyle 表示,ByWater 并不打算将 Libki 与其最受欢迎的产品,开源集成图书馆系统 (ILS)Koha 或其支持的任何其他[项目][7]捆绑在一起。他说: “Libki 和 Koha 是不同[类型]的软件,满足不同的需求,但它们在图书馆中确实很好地协同工作。事实上,我很早就开发了 Libki 的 SIP2 集成,因此它可以支持使用 Koha 进行单点登录。“
|
||||
|
||||
### 如何贡献
|
||||
|
||||
@ -40,10 +41,10 @@ Libki 客户端是 GPLv3 许可,Libki 服务器是 AGPLv3 许可。Kyle 说他
|
||||
|
||||
via: https://opensource.com/article/19/5/libki-computer-access
|
||||
|
||||
作者:[Don Watkins ][a]
|
||||
作者:[Don Watkins][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
693
published/201905/20190503 API evolution the right way.md
Normal file
693
published/201905/20190503 API evolution the right way.md
Normal file
@ -0,0 +1,693 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (MjSeven)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10900-1.html)
|
||||
[#]: subject: (API evolution the right way)
|
||||
[#]: via: (https://opensource.com/article/19/5/api-evolution-right-way)
|
||||
[#]: author: (A. Jesse https://opensource.com/users/emptysquare)
|
||||
|
||||
API 演进的正确方式
|
||||
======
|
||||
|
||||
> 负责任的库作者与其用户的十个约定。
|
||||
|
||||
![Browser of things](https://img.linux.net.cn/data/attachment/album/201905/26/134131jnymeg7t7gmo6qcy.jpg)
|
||||
|
||||
想象一下你是一个造物主,为一个生物设计一个身体。出于仁慈,你希望它能随着时间进化:首先,因为它必须对环境的变化作出反应;其次,因为你的智慧在增长,你对这个小东西想到了更好的设计,它不应该永远保持一个样子。
|
||||
|
||||
![Serpents][2]
|
||||
|
||||
然而,这个生物可能有赖于其目前解剖学的特征。你不能无所顾忌地添加翅膀或改变它的身材比例。它需要一个有序的过程来适应新的身体。作为一个负责任的设计者,你如何才能温柔地引导这种生物走向更大的进步呢?
|
||||
|
||||
对于负责任的库维护者也是如此。我们向依赖我们代码的人保证我们的承诺:我们会发布 bug 修复和有用的新特性。如果对库的未来有利,我们有时会删除某些特性。我们会不断创新,但我们不会破坏使用我们库的人的代码。我们怎样才能一次实现所有这些目标呢?
|
||||
|
||||
### 添加有用的特性
|
||||
|
||||
你的库不应该永远保持不变:你应该添加一些特性,使你的库更适合用户。例如,如果你有一个爬行动物类,并且如果有个可以飞行的翅膀是有用的,那就去添加吧。
|
||||
|
||||
```
|
||||
class Reptile:
|
||||
@property
|
||||
def teeth(self):
|
||||
return 'sharp fangs'
|
||||
|
||||
# 如果 wings 是有用的,那就添加它!
|
||||
@property
|
||||
def wings(self):
|
||||
return 'majestic wings'
|
||||
```
|
||||
|
||||
但要注意,特性是有风险的。考虑 Python 标准库中以下功能,看看它出了什么问题。
|
||||
|
||||
```
|
||||
bool(datetime.time(9, 30)) == True
|
||||
bool(datetime.time(0, 0)) == False
|
||||
```
|
||||
|
||||
这很奇怪:将任何时间对象转换为布尔值都会得到 True,但午夜时间除外。(更糟糕的是,时区感知时间的规则更加奇怪。)
|
||||
|
||||
我已经写了十多年的 Python 了,但直到上周才发现这条规则。这种奇怪的行为会在用户代码中引起什么样的 bug?
|
||||
|
||||
比如说一个日历应用程序,它带有一个创建事件的函数。如果一个事件有一个结束时间,那么函数也应该要求它有一个开始时间。
|
||||
|
||||
```
|
||||
def create_event(day,
|
||||
start_time=None,
|
||||
end_time=None):
|
||||
if end_time and not start_time:
|
||||
raise ValueError("Can't pass end_time without start_time")
|
||||
|
||||
# 女巫集会从午夜一直开到凌晨 4 点
|
||||
create_event(datetime.date.today(),
|
||||
datetime.time(0, 0),
|
||||
datetime.time(4, 0))
|
||||
```
|
||||
|
||||
不幸的是,对于女巫来说,从午夜开始的事件无法通过校验。当然,一个了解午夜怪癖的细心程序员可以正确地编写这个函数。
|
||||
|
||||
```
|
||||
def create_event(day,
|
||||
start_time=None,
|
||||
end_time=None):
|
||||
if end_time is not None and start_time is None:
|
||||
raise ValueError("Can't pass end_time without start_time")
|
||||
```
|
||||
|
||||
但这种微妙之处令人担忧。如果一个库作者想要创建一个伤害用户的 API,那么像午夜的布尔转换这样的“特性”很有效。
|
||||
|
||||
![Man being chased by an alligator][3]
|
||||
|
||||
但是,负责任的创建者的目标是使你的库易于正确使用。
|
||||
|
||||
这个功能是由 Tim Peters 在 2002 年首次编写 datetime 模块时造成的。即时是像 Tim 这样的奠基 Python 的高手也会犯错误。[这个怪异之处后来被消除了][4],现在所有时间的布尔值都是 True。
|
||||
|
||||
```
|
||||
# Python 3.5 以后
|
||||
|
||||
bool(datetime.time(9, 30)) == True
|
||||
bool(datetime.time(0, 0)) == True
|
||||
```
|
||||
|
||||
不知道午夜怪癖的古怪之处的程序员现在可以从这种晦涩的 bug 中解脱出来,但是一想到任何依赖于古怪的旧行为的代码现在没有注意变化,我就会感到紧张。如果从来没有实现这个糟糕的特性,情况会更好。这就引出了库维护者的第一个承诺:
|
||||
|
||||
#### 第一个约定:避免糟糕的特性
|
||||
|
||||
最痛苦的变化是你必须删除一个特性。一般来说,避免糟糕特性的一种方法是少添加特性!没有充分的理由,不要使用公共方法、类、功能或属性。因此:
|
||||
|
||||
#### 第二个约定:最小化特性
|
||||
|
||||
特性就像孩子:在充满激情的瞬间孕育,但是它们必须要支持多年(LCTT 译注:我怀疑作者在开车,可是我没有证据)。不要因为你能做傻事就去做傻事。不要画蛇添足!
|
||||
|
||||
![Serpents with and without feathers][5]
|
||||
|
||||
但是,当然,在很多情况下,用户需要你的库中尚未提供的东西,你如何选择合适的功能给他们?以下另一个警示故事。
|
||||
|
||||
### 一个来自 asyncio 的警示故事
|
||||
|
||||
你可能知道,当你调用一个协程函数,它会返回一个协程对象:
|
||||
|
||||
```
|
||||
async def my_coroutine():
|
||||
pass
|
||||
|
||||
print(my_coroutine())
|
||||
```
|
||||
|
||||
```
|
||||
<coroutine object my_coroutine at 0x10bfcbac8>
|
||||
```
|
||||
|
||||
你的代码必须 “<ruby>等待<rt>await</rt></ruby>” 这个对象以此来运行协程。人们很容易忘记这一点,所以 asyncio 的开发人员想要一个“调试模式”来捕捉这个错误。当协程在没有等待的情况下被销毁时,调试模式将打印一个警告,并在其创建的行上进行回溯。
|
||||
|
||||
当 Yury Selivanov 实现调试模式时,他添加了一个“协程装饰器”的基础特性。装饰器是一个函数,它接收一个协程并返回任何内容。Yury 使用它在每个协程上接入警告逻辑,但是其他人可以使用它将协程转换为字符串 “hi!”。
|
||||
|
||||
```
|
||||
import sys
|
||||
|
||||
def my_wrapper(coro):
|
||||
return 'hi!'
|
||||
|
||||
sys.set_coroutine_wrapper(my_wrapper)
|
||||
|
||||
async def my_coroutine():
|
||||
pass
|
||||
|
||||
print(my_coroutine())
|
||||
```
|
||||
|
||||
```
|
||||
hi!
|
||||
```
|
||||
|
||||
这是一个地狱般的定制。它改变了 “<ruby>异步<rt>async</rt></ruby>" 的含义。调用一次 `set_coroutine_wrapper` 将在全局永久改变所有的协程函数。正如 [Nathaniel Smith 所说][6]:“一个有问题的 API” 很容易被误用,必须被删除。如果 asyncio 开发人员能够更好地按照其目标来设计该特性,他们就可以避免删除该特性的痛苦。负责任的创建者必须牢记这一点:
|
||||
|
||||
#### 第三个约定:保持特性单一
|
||||
|
||||
幸运的是,Yury 有良好的判断力,他将该特性标记为临时,所以 asyncio 用户知道不能依赖它。Nathaniel 可以用更单一的功能替换 `set_coroutine_wrapper`,该特性只定制回溯深度。
|
||||
|
||||
```
|
||||
import sys
|
||||
|
||||
sys.set_coroutine_origin_tracking_depth(2)
|
||||
|
||||
async def my_coroutine():
|
||||
pass
|
||||
|
||||
print(my_coroutine())
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
<coroutine object my_coroutine at 0x10bfcbac8>
|
||||
|
||||
RuntimeWarning:'my_coroutine' was never awaited
|
||||
|
||||
Coroutine created at (most recent call last)
|
||||
File "script.py", line 8, in <module>
|
||||
print(my_coroutine())
|
||||
```
|
||||
|
||||
这样好多了。没有可以更改协程的类型的其他全局设置,因此 asyncio 用户无需编写防御代码。造物主应该像 Yury 一样有远见。
|
||||
|
||||
#### 第四个约定:标记实验特征“临时”
|
||||
|
||||
如果你只是预感你的生物需要犄角和四叉舌,那就引入这些特性,但将它们标记为“临时”。
|
||||
|
||||
![Serpent with horns][7]
|
||||
|
||||
你可能会发现犄角是无关紧要的,但是四叉舌是有用的。在库的下一个版本中,你可以删除前者并标记后者为正式的。
|
||||
|
||||
### 删除特性
|
||||
|
||||
无论我们如何明智地指导我们的生物进化,总会有一天想要删除一个正式特征。例如,你可能已经创建了一只蜥蜴,现在你选择删除它的腿。也许你想把这个笨拙的家伙变成一条时尚而现代的蟒蛇。
|
||||
|
||||
![Lizard transformed to snake][8]
|
||||
|
||||
删除特性主要有两个原因。首先,通过用户反馈或者你自己不断增长的智慧,你可能会发现某个特性是个坏主意。午夜怪癖的古怪行为就是这种情况。或者,最初该特性可能已经很好地适应了你的库环境,但现在生态环境发生了变化,也许另一个神发明了哺乳动物,你的生物想要挤进哺乳动物的小洞穴里,吃掉里面美味的哺乳动物,所以它不得不失去双腿。
|
||||
|
||||
![A mouse][9]
|
||||
|
||||
同样,Python 标准库会根据语言本身的变化删除特性。考虑 asyncio 的 Lock 功能,在把 `await` 作为一个关键字添加进来之前,它一直在等待:
|
||||
|
||||
```
|
||||
lock = asyncio.Lock()
|
||||
|
||||
async def critical_section():
|
||||
await lock
|
||||
try:
|
||||
print('holding lock')
|
||||
finally:
|
||||
lock.release()
|
||||
```
|
||||
|
||||
但是现在,我们可以做“异步锁”:
|
||||
|
||||
|
||||
```
|
||||
lock = asyncio.Lock()
|
||||
|
||||
async def critical_section():
|
||||
async with lock:
|
||||
print('holding lock')
|
||||
```
|
||||
|
||||
新方法好多了!很短,并且在一个大函数中使用其他 try-except 块时不容易出错。因为“尽量找一种,最好是唯一一种明显的解决方案”,[旧语法在 Python 3.7 中被弃用][10],并且很快就会被禁止。
|
||||
|
||||
不可避免的是,生态变化会对你的代码产生影响,因此要学会温柔地删除特性。在此之前,请考虑删除它的成本或好处。负责任的维护者不会愿意让用户更改大量代码或逻辑。(还记得 Python 3 在重新添加会 `u` 字符串前缀之前删除它是多么痛苦吗?)如果代码删除是机械性的动作,就像一个简单的搜索和替换,或者如果该特性是危险的,那么它可能值得删除。
|
||||
|
||||
#### 是否删除特性
|
||||
|
||||
![Balance scales][11]
|
||||
|
||||
反对 | 支持
|
||||
---|---
|
||||
代码必须改变 | 改变是机械性的
|
||||
逻辑必须改变 | 特性是危险的
|
||||
|
||||
就我们饥饿的蜥蜴而言,我们决定删除它的腿,这样它就可以滑进老鼠洞里吃掉它。我们该怎么做呢?我们可以删除 `walk` 方法,像下面一样修改代码:
|
||||
|
||||
```
|
||||
class Reptile:
|
||||
def walk(self):
|
||||
print('step step step')
|
||||
```
|
||||
|
||||
变成这样:
|
||||
|
||||
```
|
||||
class Reptile:
|
||||
def slither(self):
|
||||
print('slide slide slide')
|
||||
```
|
||||
|
||||
这不是一个好主意,这个生物习惯于走路!或者,就库而言,你的用户拥有依赖于现有方法的代码。当他们升级到最新库版本时,他们的代码将会崩溃。
|
||||
|
||||
```
|
||||
# 用户的代码,哦,不!
|
||||
Reptile.walk()
|
||||
```
|
||||
|
||||
因此,负责任的创建者承诺:
|
||||
|
||||
#### 第五条预定:温柔地删除
|
||||
|
||||
温柔地删除一个特性需要几个步骤。从用腿走路的蜥蜴开始,首先添加新方法 `slither`。接下来,弃用旧方法。
|
||||
|
||||
```
|
||||
import warnings
|
||||
|
||||
class Reptile:
|
||||
def walk(self):
|
||||
warnings.warn(
|
||||
"walk is deprecated, use slither",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
print('step step step')
|
||||
|
||||
def slither(self):
|
||||
print('slide slide slide')
|
||||
```
|
||||
|
||||
Python 的 warnings 模块非常强大。默认情况下,它会将警告输出到 stderr,每个代码位置只显示一次,但你可以禁用警告或将其转换为异常,以及其它选项。
|
||||
|
||||
一旦将这个警告添加到库中,PyCharm 和其他 IDE 就会使用删除线呈现这个被弃用的方法。用户马上就知道该删除这个方法。
|
||||
|
||||
> Reptile().~~walk()~~
|
||||
|
||||
当他们使用升级后的库运行代码时会发生什么?
|
||||
|
||||
```
|
||||
$ python3 script.py
|
||||
|
||||
DeprecationWarning: walk is deprecated, use slither
|
||||
script.py:14: Reptile().walk()
|
||||
|
||||
step step step
|
||||
```
|
||||
|
||||
默认情况下,他们会在 stderr 上看到警告,但脚本会成功并打印 “step step step”。警告的回溯显示必须修复用户代码的哪一行。(这就是 `stacklevel` 参数的作用:它显示了用户需要更改的调用,而不是库中生成警告的行。)请注意,错误消息有指导意义,它描述了库用户迁移到新版本必须做的事情。
|
||||
|
||||
你的用户可能会希望测试他们的代码,并证明他们没有调用弃用的库方法。仅警告不会使单元测试失败,但异常会失败。Python 有一个命令行选项,可以将弃用警告转换为异常。
|
||||
|
||||
```
|
||||
> python3 -Werror::DeprecationWarning script.py
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "script.py", line 14, in <module>
|
||||
Reptile().walk()
|
||||
File "script.py", line 8, in walk
|
||||
DeprecationWarning, stacklevel=2)
|
||||
DeprecationWarning: walk is deprecated, use slither
|
||||
```
|
||||
|
||||
现在,“step step step” 没有输出出来,因为脚本以一个错误终止。
|
||||
|
||||
因此,一旦你发布了库的一个版本,该版本会警告已启用的 `walk` 方法,你就可以在下一个版本中安全地删除它。对吧?
|
||||
|
||||
考虑一下你的库用户在他们项目的 `requirements` 中可能有什么。
|
||||
|
||||
```
|
||||
# 用户的 requirements.txt 显示 reptile 包的依赖关系
|
||||
reptile
|
||||
```
|
||||
|
||||
下次他们部署代码时,他们将安装最新版本的库。如果他们尚未处理所有的弃用,那么他们的代码将会崩溃,因为代码仍然依赖 `walk`。你需要温柔一点,你必须向用户做出三个承诺:维护更改日志,选择版本化方案和编写升级指南。
|
||||
|
||||
#### 第六个约定:维护变更日志
|
||||
|
||||
你的库必须有更改日志,其主要目的是宣布用户所依赖的功能何时被弃用或删除。
|
||||
|
||||
> **版本 1.1 中的更改**
|
||||
>
|
||||
> **新特性**
|
||||
>
|
||||
> * 新功能 Reptile.slither()
|
||||
>
|
||||
> **弃用**
|
||||
>
|
||||
> * Reptile.walk() 已弃用,将在 2.0 版本中删除,请使用 slither()
|
||||
|
||||
负责任的创建者会使用版本号来表示库发生了怎样的变化,以便用户能够对升级做出明智的决定。“版本化方案”是一种用于交流变化速度的语言。
|
||||
|
||||
#### 第七个约定:选择一个版本化方案
|
||||
|
||||
有两种广泛使用的方案,[语义版本控制][12]和基于时间的版本控制。我推荐任何库都进行语义版本控制。Python 的风格在 [PEP 440][13] 中定义,像 `pip` 这样的工具可以理解语义版本号。
|
||||
|
||||
如果你为库选择语义版本控制,你可以使用版本号温柔地删除腿,例如:
|
||||
|
||||
> 1.0: 第一个“稳定”版,带有 `walk()`
|
||||
> 1.1: 添加 `slither()`,废弃 `walk()`
|
||||
> 2.0: 删除 `walk()`
|
||||
|
||||
你的用户依赖于你的库的版本应该有一个范围,例如:
|
||||
|
||||
```
|
||||
# 用户的 requirements.txt
|
||||
reptile>=1,<2
|
||||
```
|
||||
|
||||
这允许他们在主要版本中自动升级,接收错误修正并可能引发一些弃用警告,但不会升级到**下**个主要版本并冒着更改破坏其代码的风险。
|
||||
|
||||
如果你遵循基于时间的版本控制,则你的版本可能会编号:
|
||||
|
||||
> 2017.06.0: 2017 年 6 月的版本
|
||||
> 2018.11.0: 添加 `slither()`,废弃 `walk()`
|
||||
> 2019.04.0: 删除 `walk()`
|
||||
|
||||
用户可以这样依赖于你的库:
|
||||
|
||||
```
|
||||
# 用户的 requirements.txt,基于时间控制的版本
|
||||
reptile==2018.11.*
|
||||
```
|
||||
|
||||
这非常棒,但你的用户如何知道你的版本方案,以及如何测试代码来进行弃用呢?你必须告诉他们如何升级。
|
||||
|
||||
#### 第八个约定:写一个升级指南
|
||||
|
||||
下面是一个负责任的库创建者如何指导用户:
|
||||
|
||||
> **升级到 2.0**
|
||||
>
|
||||
> **从弃用的 API 迁移**
|
||||
>
|
||||
> 请参阅更改日志以了解已弃用的特性。
|
||||
>
|
||||
> **启用弃用警告**
|
||||
>
|
||||
> 升级到 1.1 并使用以下代码测试代码:
|
||||
>
|
||||
> `python -Werror::DeprecationWarning`
|
||||
>
|
||||
> 现在可以安全地升级了。
|
||||
|
||||
你必须通过向用户显示命令行选项来教会用户如何处理弃用警告。并非所有 Python 程序员都知道这一点 —— 我自己就每次都得查找这个语法。注意,你必须*发布*一个版本,它输出来自每个弃用的 API 的警告,以便用户可以在再次升级之前使用该版本进行测试。在本例中,1.1 版本是小版本。它允许你的用户逐步重写代码,分别修复每个弃用警告,直到他们完全迁移到最新的 API。他们可以彼此独立地测试代码和库的更改,并隔离 bug 的原因。
|
||||
|
||||
如果你选择语义版本控制,则此过渡期将持续到下一个主要版本,从 1.x 到 2.0,或从 2.x 到 3.0 以此类推。删除生物腿部的温柔方法是至少给它一个版本来调整其生活方式。不要一次性把腿删掉!
|
||||
|
||||
![A skink][14]
|
||||
|
||||
版本号、弃用警告、更改日志和升级指南可以协同工作,在不违背与用户约定的情况下温柔地改进你的库。[Twisted 项目的兼容性政策][15] 解释的很漂亮:
|
||||
|
||||
> “先行者总是自由的”
|
||||
>
|
||||
> 运行的应用程序在没有任何警告的情况下都可以升级为 Twisted 的一个次要版本。
|
||||
>
|
||||
> 换句话说,任何运行其测试而不触发 Twisted 警告的应用程序应该能够将其 Twisted 版本升级至少一次,除了可能产生新警告之外没有任何不良影响。
|
||||
>
|
||||
|
||||
现在,我们的造物主已经获得了智慧和力量,可以通过添加方法来添加特性,并温柔地删除它们。我们还可以通过添加参数来添加特性,但这带来了新的难度。你准备好了吗?
|
||||
|
||||
### 添加参数
|
||||
|
||||
想象一下,你只是给了你的蛇形生物一对翅膀。现在你必须允许它选择是滑行还是飞行。目前它的 `move` 功能只接受一个参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
def move(direction):
|
||||
print(f'slither {direction}')
|
||||
|
||||
# 用户的应用
|
||||
move('north')
|
||||
```
|
||||
|
||||
你想要添加一个 `mode` 参数,但如果用户升级库,这会破坏他们的代码,因为他们只传递了一个参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
def move(direction, mode):
|
||||
assert mode in ('slither', 'fly')
|
||||
print(f'{mode} {direction}')
|
||||
|
||||
# 一个用户的代码,出现错误!
|
||||
move('north')
|
||||
```
|
||||
|
||||
一个真正聪明的创建者者会承诺不会以这种方式破坏用户的代码。
|
||||
|
||||
#### 第九条约定:兼容地添加参数
|
||||
|
||||
要保持这个约定,请使用保留原始行为的默认值添加每个新参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
def move(direction, mode='slither'):
|
||||
assert mode in ('slither', 'fly')
|
||||
print(f'{mode} {direction}')
|
||||
|
||||
# 用户的应用
|
||||
move('north')
|
||||
```
|
||||
|
||||
随着时间推移,参数是函数演化的自然历史。它们首先列出最老的参数,每个都有默认值。库用户可以传递关键字参数以选择特定的新行为,并接受所有其他行为的默认值。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
def move(direction,
|
||||
mode='slither',
|
||||
turbo=False,
|
||||
extra_sinuous=False,
|
||||
hail_lyft=False):
|
||||
# ...
|
||||
|
||||
# 用户应用
|
||||
move('north', extra_sinuous=True)
|
||||
```
|
||||
|
||||
但是有一个危险,用户可能会编写如下代码:
|
||||
|
||||
```
|
||||
# 用户应用,简写
|
||||
move('north', 'slither', False, True)
|
||||
```
|
||||
|
||||
如果在你在库的下一个主要版本中去掉其中一个参数,例如 `turbo`,会发生什么?
|
||||
|
||||
```
|
||||
# 你的库代码,下一个主要版本中 "turbo" 被删除
|
||||
def move(direction,
|
||||
mode='slither',
|
||||
extra_sinuous=False,
|
||||
hail_lyft=False):
|
||||
# ...
|
||||
|
||||
# 用户应用,简写
|
||||
move('north', 'slither', False, True)
|
||||
```
|
||||
|
||||
用户的代码仍然能编译,这是一件坏事。代码停止了曲折的移动并开始招呼 Lyft,这不是它的本意。我相信你可以预测我接下来要说的内容:删除参数需要几个步骤。当然,首先弃用 `trubo` 参数。我喜欢这种技术,它可以检测任何用户的代码是否依赖于这个参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
_turbo_default = object()
|
||||
|
||||
def move(direction,
|
||||
mode='slither',
|
||||
turbo=_turbo_default,
|
||||
extra_sinuous=False,
|
||||
hail_lyft=False):
|
||||
if turbo is not _turbo_default:
|
||||
warnings.warn(
|
||||
"'turbo' is deprecated",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
else:
|
||||
# The old default.
|
||||
turbo = False
|
||||
```
|
||||
|
||||
但是你的用户可能不会注意到警告。警告声音不是很大:它们可以在日志文件中被抑制或丢失。用户可能会漫不经心地升级到库的下一个主要版本——那个删除 `turbo` 的版本。他们的代码运行时将没有错误、默默做错误的事情!正如 Python 之禅所说:“错误绝不应该被默默 pass”。实际上,爬行动物的听力很差,所有当它们犯错误时,你必须非常大声地纠正它们。
|
||||
|
||||
![Woman riding an alligator][16]
|
||||
|
||||
保护用户的最佳方法是使用 Python 3 的星型语法,它要求调用者传递关键字参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
# 所有 “*” 后的参数必须以关键字方式传输。
|
||||
def move(direction,
|
||||
*,
|
||||
mode='slither',
|
||||
turbo=False,
|
||||
extra_sinuous=False,
|
||||
hail_lyft=False):
|
||||
# ...
|
||||
|
||||
# 用户代码,简写
|
||||
# 错误!不能使用位置参数,关键字参数是必须的
|
||||
move('north', 'slither', False, True)
|
||||
```
|
||||
|
||||
有了这个星,以下是唯一允许的语法:
|
||||
|
||||
```
|
||||
# 用户代码
|
||||
move('north', extra_sinuous=True)
|
||||
```
|
||||
|
||||
现在,当你删除 `turbo` 时,你可以确定任何依赖于它的用户代码都会明显地提示失败。如果你的库也支持 Python2,这没有什么大不了。你可以模拟星型语法([归功于 Brett Slatkin][17]):
|
||||
|
||||
```
|
||||
# 你的库代码,兼容 Python 2
|
||||
def move(direction, **kwargs):
|
||||
mode = kwargs.pop('mode', 'slither')
|
||||
turbo = kwargs.pop('turbo', False)
|
||||
sinuous = kwargs.pop('extra_sinuous', False)
|
||||
lyft = kwargs.pop('hail_lyft', False)
|
||||
|
||||
if kwargs:
|
||||
raise TypeError('Unexpected kwargs: %r'
|
||||
% kwargs)
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
要求关键字参数是一个明智的选择,但它需要远见。如果允许按位置传递参数,则不能仅在以后的版本中将其转换为仅关键字。所以,现在加上星号。你可以在 asyncio API 中观察到,它在构造函数、方法和函数中普遍使用星号。尽管到目前为止,`Lock` 只接受一个可选参数,但 asyncio 开发人员立即添加了星号。这是幸运的。
|
||||
|
||||
```
|
||||
# In asyncio.
|
||||
class Lock:
|
||||
def __init__(self, *, loop=None):
|
||||
# ...
|
||||
```
|
||||
|
||||
现在,我们已经获得了改变方法和参数的智慧,同时保持与用户的约定。现在是时候尝试最具挑战性的进化了:在不改变方法或参数的情况下改变行为。
|
||||
|
||||
### 改变行为
|
||||
|
||||
假设你创造的生物是一条响尾蛇,你想教它一种新行为。
|
||||
|
||||
![Rattlesnake][18]
|
||||
|
||||
横向移动!这个生物的身体看起来是一样的,但它的行为会发生变化。我们如何为这一进化步骤做好准备?
|
||||
|
||||
![][19]
|
||||
|
||||
*Image by HCA [[CC BY-SA 4.0][20]], [via Wikimedia Commons][21], 由 Opensource.com 修改*
|
||||
|
||||
当行为在没有新函数或新参数的情况下发生更改时,负责任的创建者可以从 Python 标准库中学习。很久以前,os 模块引入了 `stat` 函数来获取文件统计信息,比如创建时间。起初,这个时间总是整数。
|
||||
|
||||
```
|
||||
>>> os.stat('file.txt').st_ctime
|
||||
1540817862
|
||||
```
|
||||
|
||||
有一天,核心开发人员决定在 `os.stat` 中使用浮点数来提供亚秒级精度。但他们担心现有的用户代码还没有做好准备更改。于是他们在 Python 2.3 中创建了一个设置 `stat_float_times`,默认情况下是 `False` 。用户可以将其设置为 True 来选择浮点时间戳。
|
||||
|
||||
```
|
||||
>>> # Python 2.3.
|
||||
>>> os.stat_float_times(True)
|
||||
>>> os.stat('file.txt').st_ctime
|
||||
1540817862.598021
|
||||
```
|
||||
|
||||
从 Python 2.5 开始,浮点时间成为默认值,因此 2.5 及之后版本编写的任何新代码都可以忽略该设置并期望得到浮点数。当然,你可以将其设置为 `False` 以保持旧行为,或将其设置为 `True` 以确保所有 Python 版本都得到浮点数,并为删除 `stat_float_times` 的那一天准备代码。
|
||||
|
||||
多年过去了,在 Python 3.1 中,该设置已被弃用,以便为人们为遥远的未来做好准备,最后,经过数十年的旅程,[这个设置被删除][22]。浮点时间现在是唯一的选择。这是一个漫长的过程,但负责任的神灵是有耐心的,因为我们知道这个渐进的过程很有可能于意外的行为变化拯救用户。
|
||||
|
||||
#### 第十个约定:逐渐改变行为
|
||||
|
||||
以下是步骤:
|
||||
|
||||
* 添加一个标志来选择新行为,默认为 `False`,如果为 `False` 则发出警告
|
||||
* 将默认值更改为 `True`,表示完全弃用标记
|
||||
* 删除该标志
|
||||
|
||||
如果你遵循语义版本控制,版本可能如下:
|
||||
|
||||
库版本 | 库 API | 用户代码
|
||||
---|---|---
|
||||
1.0 | 没有标志 | 预期的旧行为
|
||||
1.1 | 添加标志,默认为 `False`,如果是 `False`,则警告 | 设置标志为 `True`,处理新行为
|
||||
2.0 | 改变默认为 `True`,完全弃用标志 | 处理新行为
|
||||
3.0 | 移除标志 | 处理新行为
|
||||
|
||||
你需要**两**个主要版本来完成该操作。如果你直接从“添加标志,默认为 `False`,如果是 `False` 则发出警告”变到“删除标志”,而没有中间版本,那么用户的代码将无法升级。为 1.1 正确编写的用户代码必须能够升级到下一个版本,除了新警告之外,没有任何不良影响,但如果在下一个版本中删除了该标志,那么该代码将崩溃。一个负责任的神明从不违反扭曲的政策:“先行者总是自由的”。
|
||||
|
||||
### 负责任的创建者
|
||||
|
||||
![Demeter][23]
|
||||
|
||||
我们的 10 个约定大致可以分为三类:
|
||||
|
||||
**谨慎发展**
|
||||
|
||||
1. 避免不良功能
|
||||
2. 最小化特性
|
||||
3. 保持功能单一
|
||||
4. 标记实验特征“临时”
|
||||
5. 温柔删除功能
|
||||
|
||||
**严格记录历史**
|
||||
|
||||
1. 维护更改日志
|
||||
2. 选择版本方案
|
||||
3. 编写升级指南
|
||||
|
||||
**缓慢而明显地改变**
|
||||
|
||||
1. 兼容添加参数
|
||||
2. 逐渐改变行为
|
||||
|
||||
如果你对你所创造的物种保持这些约定,你将成为一个负责任的造物主。你的生物的身体可以随着时间的推移而进化,一直在改善和适应环境的变化,而不是在生物没有准备好就突然改变。如果你维护一个库,请向用户保留这些承诺,这样你就可以在不破坏依赖该库的代码的情况下对库进行更新。
|
||||
|
||||
* * *
|
||||
|
||||
_这篇文章最初是在 [A. Jesse Jiryu Davis 的博客上'][24]出现的,经允许转载。_
|
||||
|
||||
插图参考:
|
||||
|
||||
* [《世界进步》, Delphian Society, 1913][25]
|
||||
* [《走进蛇的历史》, Charles Owen, 1742][26]
|
||||
* [关于哥斯达黎加的 batrachia 和爬行动物,关于尼加拉瓜和秘鲁的爬行动物和鱼类学的记录, Edward Drinker Cope, 1875][27]
|
||||
* [《自然史》, Richard Lydekker et. al., 1897][28]
|
||||
* [Mes Prisons, Silvio Pellico, 1843][29]
|
||||
* [Tierfotoagentur / m.blue-shadow][30]
|
||||
* [洛杉矶公共图书馆, 1930][31]
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/api-evolution-right-way
|
||||
|
||||
作者:[A. Jesse][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/emptysquare
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_desktop_website_checklist_metrics.png?itok=OKKbl1UR (Browser of things)
|
||||
[2]: https://opensource.com/sites/default/files/uploads/praise-the-creator.jpg (Serpents)
|
||||
[3]: https://opensource.com/sites/default/files/uploads/bite.jpg (Man being chased by an alligator)
|
||||
[4]: https://bugs.python.org/issue13936
|
||||
[5]: https://opensource.com/sites/default/files/uploads/feathers.jpg (Serpents with and without feathers)
|
||||
[6]: https://bugs.python.org/issue32591
|
||||
[7]: https://opensource.com/sites/default/files/uploads/horns.jpg (Serpent with horns)
|
||||
[8]: https://opensource.com/sites/default/files/uploads/lizard-to-snake.jpg (Lizard transformed to snake)
|
||||
[9]: https://opensource.com/sites/default/files/uploads/mammal.jpg (A mouse)
|
||||
[10]: https://bugs.python.org/issue32253
|
||||
[11]: https://opensource.com/sites/default/files/uploads/scale.jpg (Balance scales)
|
||||
[12]: https://semver.org
|
||||
[13]: https://www.python.org/dev/peps/pep-0440/
|
||||
[14]: https://opensource.com/sites/default/files/uploads/skink.jpg (A skink)
|
||||
[15]: https://twistedmatrix.com/documents/current/core/development/policy/compatibility-policy.html
|
||||
[16]: https://opensource.com/sites/default/files/uploads/loudly.jpg (Woman riding an alligator)
|
||||
[17]: http://www.informit.com/articles/article.aspx?p=2314818
|
||||
[18]: https://opensource.com/sites/default/files/uploads/rattlesnake.jpg (Rattlesnake)
|
||||
[19]: https://opensource.com/sites/default/files/articles/neonate_sidewinder_sidewinding_with_tracks_unlabeled.png
|
||||
[20]: https://creativecommons.org/licenses/by-sa/4.0
|
||||
[21]: https://commons.wikimedia.org/wiki/File:Neonate_sidewinder_sidewinding_with_tracks_unlabeled.jpg
|
||||
[22]: https://bugs.python.org/issue31827
|
||||
[23]: https://opensource.com/sites/default/files/uploads/demeter.jpg (Demeter)
|
||||
[24]: https://emptysqua.re/blog/api-evolution-the-right-way/
|
||||
[25]: https://www.gutenberg.org/files/42224/42224-h/42224-h.htm
|
||||
[26]: https://publicdomainreview.org/product-att/artist/charles-owen/
|
||||
[27]: https://archive.org/details/onbatrachiarepti00cope/page/n3
|
||||
[28]: https://www.flickr.com/photos/internetarchivebookimages/20556001490
|
||||
[29]: https://www.oldbookillustrations.com/illustrations/stationery/
|
||||
[30]: https://www.alamy.com/mediacomp/ImageDetails.aspx?ref=D7Y61W
|
||||
[31]: https://www.vintag.es/2013/06/riding-alligator-c-1930s.html
|
@ -0,0 +1,82 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10895-1.html)
|
||||
[#]: subject: (Check your spelling at the command line with Ispell)
|
||||
[#]: via: (https://opensource.com/article/19/5/spelling-command-line-ispell)
|
||||
[#]: author: (Scott Nesbitt https://opensource.com/users/scottnesbitt)
|
||||
|
||||
使用 Ispell 在命令行中检查拼写
|
||||
======
|
||||
|
||||
> Ispell 可以帮助你在纯文本中消除超过 50 种语言的拼写错误。
|
||||
|
||||
![Command line prompt](https://img.linux.net.cn/data/attachment/album/201905/24/192644wqqv6d0lztmqoqyl.jpg)
|
||||
|
||||
好的拼写是一种技巧。它是一项需要时间学习和掌握的技能。也就是说,有些人从来没有完全掌握这种技能,我知道有两三个出色的作家就无法完全掌握拼写。
|
||||
|
||||
即使你拼写得很好,偶尔也会输入错字。特别是在最后期限前如果你快速敲击键盘,那就更是如此。无论你的拼写的是什么,通过拼写检查器检查你所写的内容总是一个好主意。
|
||||
|
||||
我用[纯文本][2]完成了我的大部分写作,并经常使用名为 [Aspell][3] 的命令行拼写检查器来完成这项工作。Aspell 不是唯一的工具。你可能还想要看下不错的 [Ispell][4]。
|
||||
|
||||
### 入门
|
||||
|
||||
自 1971 年以来,Ispell 就以各种形式出现过。不要被它的年龄欺骗。Ispell 仍然是一个可以在 21 世纪高效使用的应用。
|
||||
|
||||
在开始之前,请打开终端窗口并输入 `which ispell` 来检查计算机上是否安装了 Ispell。如果未安装,请打开发行版的软件包管理器并从那里安装 Ispell。
|
||||
|
||||
不要忘记为你使用的语言安装词典。我唯一使用的语言是英语,所以我只需下载美国和英国英语字典。你可以不局限于我的(也是唯一的)母语。Ispell 有[超过 50 种语言的词典][5]。
|
||||
|
||||
![Installing Ispell dictionaries][6]
|
||||
|
||||
### 使用 Ispell
|
||||
|
||||
如果你还没有猜到,Ispell 只能用在文本文件。这包括用 HTML、LaTeX 和 [nroff 或 troff][7] 标记的文档。之后会有更多相关内容。
|
||||
|
||||
要开始使用,请打开终端窗口并进入包含要运行拼写检查的文件的目录。输入 `ispell` 后跟文件名,然后按回车键。
|
||||
|
||||
![Checking spelling with Ispell][8]
|
||||
|
||||
Ispell 高亮了它无法识别的第一个词。如果单词拼写错误,Ispell 通常会提供一个或多个备选方案。按下 `R`,然后按下正确选择旁边的数字。在上面的截图中,我按了 `R` 和 `0` 来修复错误。
|
||||
|
||||
另一方面,如果单词拼写正确,请按下 `A` 然后移动到下一个拼写错误的单词。
|
||||
|
||||
继续这样做直到到达文件的末尾。Ispell 会保存你的更改,创建你刚检查的文件的备份(扩展名为 `.bak`),然后关闭。
|
||||
|
||||
### 其他几个选项
|
||||
|
||||
此示例说明了 Ispell 的基本用法。这个程序有[很多选项][9],有些你*可能*会用到,而另一些你*可能永远*不会使用。让我们快速看下我经常使用的一些。
|
||||
|
||||
之前我提到过 Ispell 可以用于某些标记语言。你需要告诉它文件的格式。启动 Ispell 时,为 TeX 或 LaTeX 文件添加 `-t`,为 HTML 文件添加 `-H`,对于 groff 或 troff 文件添加 `-n`。例如,如果输入 `ispell -t myReport.tex`,Ispell 将忽略所有标记。
|
||||
|
||||
如果你不想在检查文件后创建备份文件,请将 `-x` 添加到命令行。例如,`ispell -x myFile.txt`。
|
||||
|
||||
如果 Ispell 遇到拼写正确但不在其字典中的单词,比如名字,会发生什么?你可以按 `I` 将该单词添加到个人单词列表中。这会将单词保存到 `/home` 目录下的 `.ispell_default` 的文件中。
|
||||
|
||||
这些是我在使用 Ispell 时最有用的选项,但请查看 [Ispell 的手册页][9]以了解其所有选项。
|
||||
|
||||
Ispell 比 Aspell 或其他命令行拼写检查器更好或者更快么?我会说它不比其他的差或者慢。Ispell 不是适合所有人。它也许也不适合你。但有更多选择也不错,不是么?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/spelling-command-line-ispell
|
||||
|
||||
作者:[Scott Nesbitt][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/scottnesbitt
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/command_line_prompt.png?itok=wbGiJ_yg (Command line prompt)
|
||||
[2]: https://plaintextproject.online
|
||||
[3]: https://opensource.com/article/18/2/how-check-spelling-linux-command-line-aspell
|
||||
[4]: https://www.cs.hmc.edu/~geoff/ispell.html
|
||||
[5]: https://www.cs.hmc.edu/~geoff/ispell-dictionaries.html
|
||||
[6]: https://opensource.com/sites/default/files/uploads/ispell-install-dictionaries.png (Installing Ispell dictionaries)
|
||||
[7]: https://opensource.com/article/18/2/how-format-academic-papers-linux-groff-me
|
||||
[8]: https://opensource.com/sites/default/files/uploads/ispell-checking.png (Checking spelling with Ispell)
|
||||
[9]: https://www.cs.hmc.edu/~geoff/ispell-man.html
|
@ -1,26 +1,27 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Say goodbye to boilerplate in Python with attrs)
|
||||
[#]: via: (https://opensource.com/article/19/5/python-attrs)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez/users/moshez)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez/users/moshez)
|
||||
|
||||
使用 attrs 来告别 Python 中的样板
|
||||
======
|
||||
在我们覆盖 7 个 PyPI 库的系列文章中了解更多解决 Python 问题的信息。
|
||||
![Programming at a browser, orange hands][1]
|
||||
|
||||
Python是当今使用最多[流行的编程语言] [2]之一 - 并且有充分的理由:它是开源的,它具有广泛的用途(例如Web编程,业务应用程序,游戏,科学编程等等)更多),它有一个充满活力和专注的社区支持它。这个社区是我们在[Python Package Index] [3](PyPI)中提供如此庞大,多样化的软件包的原因,以扩展和改进Python并解决不可避免的问题。
|
||||
> 在我们覆盖 7 个 PyPI 库的系列文章中了解更多解决 Python 问题的信息。
|
||||
|
||||
在本系列中,我们将介绍七个可以帮助你解决常见 Python 问题的 PyPI 库。今天,我们将研究 [**attrs**][4],这是一个帮助你快速编写简洁,正确的代码的 Python 包。
|
||||
![Programming at a browser, orange hands](https://img.linux.net.cn/data/attachment/album/201905/18/211211lhqqbemqwkeqc2bb.jpg)
|
||||
|
||||
Python是当今使用最多[流行的编程语言][2]之一,因为:它是开源的,它具有广泛的用途(例如 Web 编程、业务应用、游戏、科学编程等等),它有一个充满活力和专注的社区支持它。这个社区是我们在 [Python Package Index][3](PyPI)中提供如此庞大、多样化的软件包的原因,用以扩展和改进 Python。并解决不可避免的问题。
|
||||
|
||||
在本系列中,我们将介绍七个可以帮助你解决常见 Python 问题的 PyPI 库。今天,我们将研究 [attrs][4],这是一个帮助你快速编写简洁、正确的代码的 Python 包。
|
||||
|
||||
### attrs
|
||||
|
||||
如果你已经写过一段时间的 Python,那么你可能习惯这样写代码:
|
||||
|
||||
|
||||
```
|
||||
class Book(object):
|
||||
|
||||
@ -30,7 +31,7 @@ class Book(object):
|
||||
self.author = author
|
||||
```
|
||||
|
||||
接着写一个 **__repr__** 函数。否则,很难记录 **Book** 的实例:
|
||||
接着写一个 `__repr__` 函数。否则,很难记录 `Book` 的实例:
|
||||
|
||||
|
||||
```
|
||||
@ -38,11 +39,10 @@ def __repr__(self):
|
||||
return f"Book({self.isbn}, {self.name}, {self.author})"
|
||||
```
|
||||
|
||||
接下来你会写一个好看的 docstring 来记录期望的类型。但是你注意到你忘了添加 **edition** 和 **published_year** 属性,所以你必须在五个地方修改它们。
|
||||
接下来你会写一个好看的 docstring 来记录期望的类型。但是你注意到你忘了添加 `edition` 和 `published_year` 属性,所以你必须在五个地方修改它们。
|
||||
|
||||
如果你不必这么做如何?
|
||||
|
||||
|
||||
```
|
||||
@attr.s(auto_attribs=True)
|
||||
class Book(object):
|
||||
@ -53,11 +53,10 @@ class Book(object):
|
||||
edition: int
|
||||
```
|
||||
|
||||
使用新的类型注释语法注释类型属性,**attrs** 会检测注释并创建一个类。
|
||||
使用新的类型注释语法注释类型属性,`attrs` 会检测注释并创建一个类。
|
||||
|
||||
ISBN 有特定格式。如果我们想强行使用该格式怎么办?
|
||||
|
||||
|
||||
```
|
||||
@attr.s(auto_attribs=True)
|
||||
class Book(object):
|
||||
@ -73,14 +72,14 @@ class Book(object):
|
||||
edition: int
|
||||
```
|
||||
|
||||
**attrs** 库也对[不可变风格编程][5]支持良好。将第一行改成 **@attr.s(auto_attribs=True, frozen=True)** 意味着 **Book** 现在是不可变的:尝试修改一个属性将会引发一个异常。相反,比如,如果希望将发布日期向后一年,我们可以修改成 **attr.evolve(old_book, published_year=old_book.published_year+1)** 来得到一个_新的_实例。
|
||||
`attrs` 库也对[不可变式编程][5]支持良好。将第一行改成 `@attr.s(auto_attribs=True, frozen=True)` 意味着 `Book` 现在是不可变的:尝试修改一个属性将会引发一个异常。相反,比如,如果希望将发布日期向后一年,我们可以修改成 `attr.evolve(old_book, published_year=old_book.published_year+1)` 来得到一个*新的*实例。
|
||||
|
||||
本系列的下一篇文章我们将来看下 **singledispatch**,一个能让你向 Python 库添加方法的库。
|
||||
本系列的下一篇文章我们将来看下 `singledispatch`,一个能让你向 Python 库添加方法的库。
|
||||
|
||||
#### 查看本系列先前的文章
|
||||
|
||||
* [Cython][6]
|
||||
* [Black][7]
|
||||
* [Cython][6]
|
||||
* [Black][7]
|
||||
|
||||
|
||||
|
||||
@ -88,10 +87,10 @@ class Book(object):
|
||||
|
||||
via: https://opensource.com/article/19/5/python-attrs
|
||||
|
||||
作者:[Moshe Zadka ][a]
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
@ -102,5 +101,5 @@ via: https://opensource.com/article/19/5/python-attrs
|
||||
[3]: https://pypi.org/
|
||||
[4]: https://pypi.org/project/attrs/
|
||||
[5]: https://opensource.com/article/18/10/functional-programming-python-immutable-data-structures
|
||||
[6]: https://opensource.com/article/19/4/7-python-problems-solved-cython
|
||||
[7]: https://opensource.com/article/19/4/python-problems-solved-black
|
||||
[6]: https://linux.cn/article-10859-1.html
|
||||
[7]: https://linux.cn/article-10864-1.html
|
@ -0,0 +1,105 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10887-1.html)
|
||||
[#]: subject: (Add methods retroactively in Python with singledispatch)
|
||||
[#]: via: (https://opensource.com/article/19/5/python-singledispatch)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez)
|
||||
|
||||
使用 singledispatch 在 Python 中追溯地添加方法
|
||||
======
|
||||
|
||||
> 在我们覆盖 7 个 PyPI 库的系列文章中了解更多解决 Python 问题的信息。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/23/093515sgmu4auml9caz54l.jpg)
|
||||
|
||||
Python 是当今使用最多[流行的编程语言][2]之一,因为:它是开源的,它具有广泛的用途(例如 Web 编程、业务应用、游戏、科学编程等等),它有一个充满活力和专注的社区支持它。这个社区是我们在 [Python Package Index][3](PyPI)中提供如此庞大、多样化的软件包的原因,用以扩展和改进 Python。并解决不可避免的问题。
|
||||
|
||||
在本系列中,我们将介绍七个可以帮助你解决常见 Python 问题的 PyPI 库。今天,我们将研究 [singledispatch][4],这是一个能让你追溯地向 Python 库添加方法的库。
|
||||
|
||||
### singledispatch
|
||||
|
||||
想象一下,你有一个有 Circle、Square 等类的“形状”库。
|
||||
|
||||
Circle 类有半径、Square 有边、Rectangle 有高和宽。我们的库已经存在,我们不想改变它。
|
||||
|
||||
然而,我们想给库添加一个面积计算。如果我们不会和其他人共享这个库,我们只需添加 `area` 方法,这样我们就能调用 `shape.area()` 而无需关心是什么形状。
|
||||
|
||||
虽然可以进入类并添加一个方法,但这是一个坏主意:没有人希望他们的类会被添加新的方法,程序会因奇怪的方式出错。
|
||||
|
||||
相反,functools 中的 `singledispatch` 函数可以帮助我们。
|
||||
|
||||
|
||||
```
|
||||
@singledispatch
|
||||
def get_area(shape):
|
||||
raise NotImplementedError("cannot calculate area for unknown shape",
|
||||
shape)
|
||||
```
|
||||
|
||||
`get_area` 函数的“基类”实现会报错。这保证了如果我们出现一个新的形状时,我们会明确地报错而不是返回一个无意义的结果。
|
||||
|
||||
|
||||
```
|
||||
@get_area.register(Square)
|
||||
def _get_area_square(shape):
|
||||
return shape.side ** 2
|
||||
@get_area.register(Circle)
|
||||
def _get_area_circle(shape):
|
||||
return math.pi * (shape.radius ** 2)
|
||||
```
|
||||
|
||||
这种方式的好处是如果某人写了一个匹配我们代码的*新*形状,它们可以自己实现 `get_area`。
|
||||
|
||||
|
||||
```
|
||||
from area_calculator import get_area
|
||||
|
||||
@attr.s(auto_attribs=True, frozen=True)
|
||||
class Ellipse:
|
||||
horizontal_axis: float
|
||||
vertical_axis: float
|
||||
|
||||
@get_area.register(Ellipse)
|
||||
def _get_area_ellipse(shape):
|
||||
return math.pi * shape.horizontal_axis * shape.vertical_axis
|
||||
```
|
||||
|
||||
*调用* `get_area` 很直接。
|
||||
|
||||
|
||||
```
|
||||
print(get_area(shape))
|
||||
```
|
||||
|
||||
这意味着我们可以将大量的 `if isintance()`/`elif isinstance()` 的代码以这种方式修改,而无需修改接口。下一次你要修改 if isinstance,你试试 `singledispatch!
|
||||
|
||||
在本系列的下一篇文章中,我们将介绍 tox,一个用于自动化 Python 代码测试的工具。
|
||||
|
||||
#### 回顾本系列的前几篇文章:
|
||||
|
||||
* [Cython][5]
|
||||
* [Black][6]
|
||||
* [attrs][7]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/python-singledispatch
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/computer_code_programming_laptop.jpg?itok=ormv35tV
|
||||
[2]: https://opensource.com/article/18/5/numbers-python-community-trends
|
||||
[3]: https://pypi.org/
|
||||
[4]: https://pypi.org/project/singledispatch/
|
||||
[5]: https://linux.cn/article-10859-1.html
|
||||
[6]: https://linux.cn/article-10864-1.html
|
||||
[7]: https://linux.cn/article-10871-1.html
|
@ -0,0 +1,219 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (Moelf)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10881-1.html)
|
||||
[#]: subject: (Using the force at the Linux command line)
|
||||
[#]: via: (https://opensource.com/article/19/5/may-the-force-linux)
|
||||
[#]: author: (Alan Formy-Duval https://opensource.com/users/alanfdoss)
|
||||
|
||||
在 Linux 命令行下使用“原力”
|
||||
======
|
||||
|
||||
> 和绝地武士的原力一样,`-f` 参数是很强大的,并伴随着潜在的毁灭性,在你能用好的时候又很便利。
|
||||
|
||||
![Fireworks](https://img.linux.net.cn/data/attachment/album/201905/21/083913jqbwn4ywq1jqnb9y.jpg)
|
||||
|
||||
近些年来,科幻发烧友开始在每年的 5 月 4 日庆祝[星战节][2],其口号是绝地武士的祝福语”愿<ruby>原力<rt>Force</rt></ruby>和你同在“。虽然大多数 Linux 用户可能不是绝地武士,但我们依然可以使用<ruby>原力<rt>Force</rt></ruby>。自然,如果尤达大师只是叫天行者卢克输入什么 “man X-Wing 战机“、“man 原力”,或者 RTFM(去读原力手册,肯定是这个意思对不对),那这电影肯定没啥意思。(LCTT 译注:RTFM 是 “Read The Fucking Manual” 的缩写 —— 读读该死的手册吧)。
|
||||
|
||||
很多 Linux 命令都有 `-f` 选项,意思你现在肯定也知道了,原力(LCTT 译注:force 选项原意是“强制”)!很多时候你先尝试执行命令然后失败了,或者提示你需要补充输入更多选项。通常这都是为了保护你试着改变的文件,或者告诉用户该设备正忙或文件已经存在之类的。
|
||||
|
||||
如果你不想被这些提醒打扰或者压根就不在乎,就使用原力吧!
|
||||
|
||||
不过要小心,通常使用原力选项是摧毁性的。所以用户一定要格外注意!并且确保你知道自己在做什么!用原力就要承担后果!
|
||||
|
||||
以下是一些常见 Linux 命令的原力选项和它们的效果,以及常见使用场景。
|
||||
|
||||
### cp
|
||||
|
||||
`cp` 是 “copy” 的缩写,这是个被用来复制文件或者目录的命令。其 [man 页面][3] 说:
|
||||
|
||||
> -f, --force
|
||||
>
|
||||
> 如果已经存在的目标文件无法被打开,删除它并重试
|
||||
|
||||
你可能会用它来处理只读状态的文件:
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ ls -l
|
||||
total 8
|
||||
-rw-rw---- 1 alan alan 13 May 1 12:24 Hoth
|
||||
-r--r----- 1 alan alan 14 May 1 12:23 Naboo
|
||||
[alan@workstation ~]$ cat Hoth Naboo
|
||||
Icy Planet
|
||||
|
||||
Green Planet
|
||||
```
|
||||
|
||||
如果你想要复制一个叫做 `Hoth` 的文件到 `Naboo`,但因为 `Naboo` 目前是只读状态,`cp` 命令不会执行:
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ cp Hoth Naboo
|
||||
cp: cannot create regular file 'Naboo': Permission denied
|
||||
```
|
||||
|
||||
但通过使用原力,`cp` 会强制执行。`Hoth` 的内容和文件权限会直接被复制到 `Naboo`:
|
||||
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ cp -f Hoth Naboo
|
||||
[alan@workstation ~]$ cat Hoth Naboo
|
||||
Icy Planet
|
||||
|
||||
Icy Planet
|
||||
|
||||
[alan@workstation ~]$ ls -l
|
||||
total 8
|
||||
-rw-rw---- 1 alan alan 12 May 1 12:32 Hoth
|
||||
-rw-rw---- 1 alan alan 12 May 1 12:38 Naboo
|
||||
```
|
||||
|
||||
### ln
|
||||
|
||||
`ln` 命令是用来在文件之间建立链接的,其 [man 页面][4] 描述的原力选项如下:
|
||||
|
||||
|
||||
> -f, --force
|
||||
>
|
||||
> 移除当前存在的文件
|
||||
|
||||
|
||||
假设莱娅公主在维护一个 Java 应用服务器,并且她又一个存放这所有 Java 版本的目录,比如:
|
||||
|
||||
```
|
||||
leia@workstation:/usr/lib/java$ ls -lt
|
||||
total 28
|
||||
lrwxrwxrwx 1 leia leia 12 Mar 5 2018 jdk -> jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Mar 5 2018 jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Aug 28 2017 jdk1.8.0_144
|
||||
```
|
||||
|
||||
正如你所看到的,这里有很多个版本的 JDK,并有一个符号链接指向最新版的 JDK。她接着用一个脚本来安装最新版本的 JDK。但是如果没有原力选项的话以下命令是不会成功的:
|
||||
|
||||
```
|
||||
tar xvzmf jdk1.8.0_181.tar.gz -C jdk1.8.0_181/
|
||||
ln -vs jdk1.8.0_181 jdk
|
||||
```
|
||||
|
||||
`tar` 命令会解压 .gz 文件到一个特定的目标目录,但 `ln` 命令会失败,因为这个链接已经存在了。这样的结果是该符号链接不会指向最新版本的 JDK:
|
||||
|
||||
```
|
||||
leia@workstation:/usr/lib/java$ ln -vs jdk1.8.0_181 jdk
|
||||
ln: failed to create symbolic link 'jdk/jdk1.8.0_181': File exists
|
||||
leia@workstation:/usr/lib/java$ ls -lt
|
||||
total 28
|
||||
drwxr-x--- 2 leia leia 4096 May 1 15:44 jdk1.8.0_181
|
||||
lrwxrwxrwx 1 leia leia 12 Mar 5 2018 jdk -> jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Mar 5 2018 jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Aug 28 2017 jdk1.8.0_144
|
||||
```
|
||||
|
||||
她可以通过使用原力选项强制 `ln` 更新链接,但这里她还需要使用 `-n`,`-n` 是因为这个情况下链接其实指向一个目录而非文件。这样的话,链接就会正确指向最新版本的JDK了。
|
||||
|
||||
```
|
||||
leia@workstation:/usr/lib/java$ ln -vsnf jdk1.8.0_181 jdk
|
||||
'jdk' -> 'jdk1.8.0_181'
|
||||
leia@workstation:/usr/lib/java$ ls -lt
|
||||
total 28
|
||||
lrwxrwxrwx 1 leia leia 12 May 1 16:13 jdk -> jdk1.8.0_181
|
||||
drwxr-x--- 2 leia leia 4096 May 1 15:44 jdk1.8.0_181
|
||||
drwxr-xr-x 8 leia leia 4096 Mar 5 2018 jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Aug 28 2017 jdk1.8.0_144
|
||||
```
|
||||
|
||||
你可以配置 Java 应用使其一直使用在 `/usr/lib/java/jdk` 处的 JDK,而不用每次升级都更新。
|
||||
|
||||
### rm
|
||||
|
||||
`rm` 命令是 “remove” 的缩写(也叫做删除,因为某些系统 `del` 命令也干这事)。其 [man 页面][5] 对原力选项的描述如下:
|
||||
|
||||
|
||||
> -f, --force
|
||||
>
|
||||
> 无视不存在的文件或者参数,不向用户确认
|
||||
|
||||
如果你尝试删除一个只读的文件,`rm` 会寻求用户的确认:
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ ls -l
|
||||
total 4
|
||||
-r--r----- 1 alan alan 16 May 1 11:38 B-wing
|
||||
[alan@workstation ~]$ rm B-wing
|
||||
rm: remove write-protected regular file 'B-wing'?
|
||||
```
|
||||
|
||||
你一定要输入 `y` 或者 `n` 来回答确认才能让 `rm` 命令继续。如果你使用原力选项,`rm` 就不会寻求你的确认而直接删除文件:
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ rm -f B-wing
|
||||
[alan@workstation ~]$ ls -l
|
||||
total 0
|
||||
[alan@workstation ~]$
|
||||
```
|
||||
|
||||
最常见的 `rm` 原力选项用法是用来删除目录。 `-r`(递归)选项会让 `rm` 删除目录,当和原力选项结合起来,它会删除这个文件夹及其内容而无需用户确认。
|
||||
|
||||
`rm` 命令和一些选项结合起来是致命的,一直以来互联网上都有关于误用 `rm` 删除整个系统之类的玩笑和鬼故事。比如最出名的一不当心执行 `rm -rf .` 会直接删除目录和文件(没有用户确认)。(LCTT 译注:真的这么干过的校对飘过~~请按下回车前再三确认:我是谁,我在哪里,我在干什么)
|
||||
|
||||
### userdel
|
||||
|
||||
`userdel` 命令使用来删除用户的。其 [man 页面][6] 是这样描述它的原力选项的:
|
||||
|
||||
> -f, --force
|
||||
>
|
||||
> 这个选项会强制移除用户,即便用户当前处于登入状态。它同时还会强制
|
||||
删除用户的目录和邮件存储,即便这个用户目录被别人共享或者邮件存储并不
|
||||
属于这个用户。如果 `USERGROUPS_ENAB` 在 `/etc/login.defs` 里是 `yes`
|
||||
并且有一个组和此用户同名的话,这个组也会被移除,即便这个组还是别
|
||||
的用户的主要用户组也一样。
|
||||
>
|
||||
> 注意:这个选项有风险并可能让系统处于不稳定状态。
|
||||
|
||||
当欧比旺抵达穆斯塔法星的时候,他知道自己的使命。他需要删掉达斯·维达的用户账户——而达斯还在里面呢。
|
||||
|
||||
```
|
||||
[root@workstation ~]# ps -fu darth
|
||||
UID PID PPID C STIME TTY TIME CMD
|
||||
darth 7663 7655 0 13:28 pts/3 00:00:00 -bash
|
||||
[root@workstation ~]# userdel darth
|
||||
userdel: user darth is currently used by process 7663
|
||||
```
|
||||
|
||||
因为达斯还登在系统里,欧比旺需要使用原力选项操作 `userdel`。这能强制删除当前登入的用户。
|
||||
|
||||
```
|
||||
[root@workstation ~]# userdel -f darth
|
||||
userdel: user darth is currently used by process 7663
|
||||
[root@workstation ~]# finger darth
|
||||
finger: darth: no such user.
|
||||
[root@workstation ~]# ps -fu darth
|
||||
error: user name does not exist
|
||||
```
|
||||
|
||||
正如我们所见到的一样,`finger` 和 `ps` 命令让我们确认了达斯已经被删除了。
|
||||
|
||||
### 在 Shell 脚本里使用原力
|
||||
|
||||
很多命令都有原力选项,而在 shell 脚本里他们特别有用。因为我们经常使用脚本完成定期或者自动化的任务,避免用户输入至关重要,不然的话自动任务就无法完成了
|
||||
|
||||
我希望上面的几个例子能帮你理解一些需要使用原力的情况。你在命令行使用原力或把它们写入脚本之前应当完全理解它们的作用。误用原力会有毁灭性的后果——时常是对整个系统,甚至不仅限于一台设备。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/may-the-force-linux
|
||||
|
||||
作者:[Alan Formy-Duval][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[Jerry Ling](https://github.com/Moelf)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alanfdoss
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/fireworks_light_art_design.jpg?itok=hfx9i4By (Fireworks)
|
||||
[2]: https://www.starwars.com/star-wars-day
|
||||
[3]: http://man7.org/linux/man-pages/man1/cp.1.html
|
||||
[4]: http://man7.org/linux/man-pages/man1/ln.1.html
|
||||
[5]: http://man7.org/linux/man-pages/man1/rm.1.html
|
||||
[6]: http://man7.org/linux/man-pages/man8/userdel.8.html
|
@ -0,0 +1,244 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (tomjlw)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10892-1.html)
|
||||
[#]: subject: (Duc – A Collection Of Tools To Inspect And Visualize Disk Usage)
|
||||
[#]: via: (https://www.ostechnix.com/duc-a-collection-of-tools-to-inspect-and-visualize-disk-usage/)
|
||||
[#]: author: (sk https://www.ostechnix.com/author/sk/)
|
||||
|
||||
Duc:一个能够可视化洞察硬盘使用情况的工具包
|
||||
======
|
||||
|
||||
![Duc:一个能够洞察并可视化硬盘使用情况的工具包][1]
|
||||
|
||||
Duc 是一个在类 Unix 操作系统上可以用来索引、洞察及可视化硬盘使用情况的工具包。别把它当成一个仅能用漂亮图表展现硬盘使用情况的 CLI 工具。它对巨大的文件系统也支持的很好。Duc 已在由超过五亿个文件和几 PB 的存储组成的系统上测试过,没有任何问题。
|
||||
|
||||
Duc 是一个快速而且灵活的工具。它将你的硬盘使用情况存在一个优化过的数据库里,这样你就可以在索引完成后迅速找到你的数据。此外,它自带不同的用户交互界面与后端以访问数据库并绘制图表。
|
||||
|
||||
以下列出的是目前支持的用户界面(UI):
|
||||
|
||||
1. 命令行界面(`duc ls`)
|
||||
2. Ncurses 控制台界面(`duc ui`)
|
||||
3. X11 GUI(`duc gui`)
|
||||
4. OpenGL GUI(`duc gui`)
|
||||
|
||||
支持的后端数据库:
|
||||
|
||||
* Tokyocabinet
|
||||
* Leveldb
|
||||
* Sqlite3
|
||||
|
||||
Duc 默认使用 Tokyocabinet 作为后端数据库。
|
||||
|
||||
### 安装 Duc
|
||||
|
||||
Duc 可以从 Debian 以及其衍生品例如 Ubuntu 的默认仓库中获取。因此在基于 DEB 的系统上安装 Duc 是小菜一碟。
|
||||
|
||||
```
|
||||
$ sudo apt-get install duc
|
||||
```
|
||||
|
||||
在其它 Linux 发行版上你需要像以下所展示的那样手动从源代码编译安装 Duc。
|
||||
|
||||
可以从 Github 上的[发行][2]页面下载最新的 Duc 源代码的 .tgz 文件。在写这篇教程的时候,最新的版本是1.4.4。
|
||||
|
||||
```
|
||||
$ wget https://github.com/zevv/duc/releases/download/1.4.4/duc-1.4.4.tar.gz
|
||||
```
|
||||
|
||||
然后一个接一个地运行以下命令来安装 DUC。
|
||||
|
||||
```
|
||||
$ tar -xzf duc-1.4.4.tar.gz
|
||||
$ cd duc-1.4.4
|
||||
$ ./configure
|
||||
$ make
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
### 使用 Duc
|
||||
|
||||
`duc` 的典型用法是:
|
||||
|
||||
```
|
||||
$ duc <subcommand> <options>
|
||||
```
|
||||
|
||||
你可以通过运行以下命令来浏览总的选项列表以及子命令:
|
||||
|
||||
```
|
||||
$ duc help
|
||||
```
|
||||
|
||||
你也可以像下面这样了解一个特定子命令的用法。
|
||||
|
||||
```
|
||||
$ duc help <subcommand>
|
||||
```
|
||||
|
||||
要查看所有命令与其选项的列表,仅需运行:
|
||||
|
||||
```
|
||||
$ duc help --all
|
||||
```
|
||||
|
||||
让我们看看一些 `duc` 工具的特定用法。
|
||||
|
||||
### 创建索引(数据库)
|
||||
|
||||
首先,你需要创建一个你文件系统的索引文件(数据库)。使用 `duc index` 命令以创建索引文件。
|
||||
|
||||
比如说,要创建你的 `/home` 目录的索引,仅需运行:
|
||||
|
||||
```
|
||||
$ duc index /home
|
||||
```
|
||||
|
||||
上述命令将会创建你的 `/home` 目录的索引,并将其保存在 `$HOME/.duc.db` 文件中。如果你以后需要往 `/home` 目录添加新的文件或目录,只要在之后重新运行一下上面的命令来重建索引。
|
||||
|
||||
### 查询索引
|
||||
|
||||
Duc 有不同的子命令来查询并探索索引。
|
||||
|
||||
要查看可访问的索引列表,运行:
|
||||
|
||||
```
|
||||
$ duc info
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```
|
||||
Date Time Files Dirs Size Path
|
||||
2019-04-09 15:45:55 3.5K 305 654.6M /home
|
||||
```
|
||||
|
||||
如你在上述输出所见,我已经索引好了 `/home` 目录。
|
||||
|
||||
要列出当前工作目录中所有的文件和目录,你可以这样做:
|
||||
|
||||
```
|
||||
$ duc ls
|
||||
```
|
||||
|
||||
要列出指定的目录,例如 `/home/sk/Downloads` 中的文件/目录,仅需像下面这样将路径作为参数传过去。
|
||||
|
||||
```
|
||||
$ duc ls /home/sk/Downloads
|
||||
```
|
||||
|
||||
类似的,运行 `duc ui` 命令来打开基于 ncurses 的控制台用户界面以探索文件系统使用情况,运行`duc gui` 以打开图形界面(X11)来探索文件系统。
|
||||
|
||||
要了解更多子命令的用法,仅需参考帮助部分。
|
||||
|
||||
```
|
||||
$ duc help ls
|
||||
```
|
||||
|
||||
上述命令将会展现 `ls` 子命令的帮助部分。
|
||||
|
||||
### 可视化硬盘使用状况
|
||||
|
||||
在之前的部分我们以及看到如何用 duc 子命令列出文件和目录。在此之外,你甚至可以用一张漂亮的图表展示文件大小。
|
||||
|
||||
要展示所提供目录的图表,像以下这样使用 `ls` 子命令。
|
||||
|
||||
```
|
||||
$ duc ls -Fg /home/sk
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
![使用 “duc ls” 命令可视化硬盘使用情况][3]
|
||||
|
||||
如你在上述输出所见,`ls` 子命令查询 duc 数据库并列出了所提供目录包含的文件与目录的大小,在这里就是 `/home/sk/`。
|
||||
|
||||
这里 `-F` 选项是往条目中用来添加文件类型指示符(`/`),`-g` 选项是用来绘制每个条目相对大小的图表。
|
||||
|
||||
请注意如果未提供任何路径,就会使用当前工作目录。
|
||||
|
||||
你可以使用 `-R` 选项来用[树状结构][4]浏览硬盘使用情况。
|
||||
|
||||
```
|
||||
$ duc ls -R /home/sk
|
||||
```
|
||||
|
||||
![用树状结构可视化硬盘使用情况][5]
|
||||
|
||||
要查询 duc 数据库并打开基于 ncurses 的控制台以探索所提供的目录,像以下这样使用 `ui` 子命令。
|
||||
|
||||
```
|
||||
$ duc ui /home/sk
|
||||
```
|
||||
|
||||
![][6]
|
||||
|
||||
类似的,我们使用 `gui *` 子命令来查询 duc 数据库以及打开一个图形界面(X11)来了解指定路径的硬盘使用情况。
|
||||
|
||||
```
|
||||
$ duc gui /home/sk
|
||||
```
|
||||
|
||||
![][7]
|
||||
|
||||
像我之前所提到的,我们可以像下面这样了解更多关于特定子命令的用法。
|
||||
|
||||
```
|
||||
$ duc help <子命令名字>
|
||||
```
|
||||
|
||||
我仅仅覆盖了基本用法的部分,参考 man 页面了解关于 `duc` 工具的更多细节。
|
||||
|
||||
```
|
||||
$ man duc
|
||||
```
|
||||
|
||||
相关阅读:
|
||||
|
||||
* [Filelight – 在你的 Linux 系统上可视化硬盘使用情况][8]
|
||||
* [一些好的 du 命令的替代品][9]
|
||||
* [如何在 Linux 中用 Ncdu 检查硬盘使用情况][10]
|
||||
* [Agedu——发现 Linux 中被浪费的硬盘空间][11]
|
||||
* [如何在 Linux 中找到目录大小][12]
|
||||
* [为初学者打造的带有示例的 df 命令教程][13]
|
||||
|
||||
### 总结
|
||||
|
||||
Duc 是一款简单却有用的硬盘用量查看器。如果你想要快速简便地知道哪个文件/目录占用你的硬盘空间,Duc 可能是一个好的选择。你还等什么呢?获取这个工具,扫描你的文件系统,摆脱无用的文件/目录。
|
||||
|
||||
现在就到此为止了。希望这篇文章有用处。更多好东西马上就到。保持关注!
|
||||
|
||||
欢呼吧!
|
||||
|
||||
资源:
|
||||
|
||||
* [Duc 网站][14]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/duc-a-collection-of-tools-to-inspect-and-visualize-disk-usage/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[tomjlw](https://github.com/tomjlw)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/04/duc-720x340.png
|
||||
[2]: https://github.com/zevv/duc/releases
|
||||
[3]: http://www.ostechnix.com/wp-content/uploads/2019/04/duc-1-1.png
|
||||
[4]: https://www.ostechnix.com/view-directory-tree-structure-linux/
|
||||
[5]: http://www.ostechnix.com/wp-content/uploads/2019/04/duc-2.png
|
||||
[6]: http://www.ostechnix.com/wp-content/uploads/2019/04/duc-3.png
|
||||
[7]: http://www.ostechnix.com/wp-content/uploads/2019/04/duc-4.png
|
||||
[8]: https://www.ostechnix.com/filelight-visualize-disk-usage-on-your-linux-system/
|
||||
[9]: https://www.ostechnix.com/some-good-alternatives-to-du-command/
|
||||
[10]: https://www.ostechnix.com/check-disk-space-usage-linux-using-ncdu/
|
||||
[11]: https://www.ostechnix.com/agedu-find-out-wasted-disk-space-in-linux/
|
||||
[12]: https://www.ostechnix.com/find-size-directory-linux/
|
||||
[13]: https://www.ostechnix.com/the-df-command-tutorial-with-examples-for-beginners/
|
||||
[14]: https://duc.zevv.nl/
|
@ -1,18 +1,18 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10872-1.html)
|
||||
[#]: subject: (How to Add Application Shortcuts on Ubuntu Desktop)
|
||||
[#]: via: (https://itsfoss.com/ubuntu-desktop-shortcut/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
在 Ubuntu 桌面如何添加应用快捷方式
|
||||
如何在 Ubuntu 桌面手动添加应用快捷方式
|
||||
===============================
|
||||
|
||||
_**在这篇快速指南中,你将学到如何在 Ubuntu 桌面和其他使用 GNOME 桌面的发行版中添加应用图标。**_
|
||||
> 在这篇快速指南中,你将学到如何在 Ubuntu 桌面和其他使用 GNOME 桌面的发行版中添加应用图标。
|
||||
|
||||
一个经典的桌面操作系统在 ‘桌面屏’ 上总是有图标的。这些桌面图标包括文件管理器,回收站和应用图标。
|
||||
一个经典的桌面操作系统在“桌面屏”上总是有图标的。这些桌面图标包括文件管理器、回收站和应用图标。
|
||||
|
||||
当在 Windows 中安装应用时,一些程序会询问你是否在桌面创建一个快捷方式。但在 Linux 系统中不是这样。
|
||||
|
||||
@ -26,43 +26,37 @@ _**在这篇快速指南中,你将学到如何在 Ubuntu 桌面和其他使用
|
||||
|
||||
![][5]
|
||||
|
||||
个人来讲,我更喜欢为应用图标准备的 Ubuntu 启动器方式。如果我经常使用一个程序,我会添加到启动器。但是我知道不是每个人都有相同的偏好并且少数人更喜欢桌面的快捷方式。
|
||||
个人来讲,我更喜欢为应用图标准备的 Ubuntu 启动器方式。如果我经常使用一个程序,我会添加到启动器。但是我知道不是每个人都有相同的偏好,可能少数人更喜欢桌面的快捷方式。
|
||||
|
||||
让我们看在桌面中创建应用快捷方式的最简单方式。
|
||||
|
||||
免责声明
|
||||
> 免责声明
|
||||
|
||||
这篇指南已经在 Ubuntu18.04 LTS 的 GNOME 桌面上测试过了。它可能在其他发行版和桌面环境上也能发挥作用,但你必须自己尝试。一些 GNOME 特定步骤可能会变,所以请在[其他桌面环境][7]尝试时注意。
|
||||
|
||||
[Subscribe to our YouTube Channel for More Linux Videos][8]
|
||||
> 这篇指南已经在 Ubuntu 18.04 LTS 的 GNOME 桌面上测试过了。它可能在其他发行版和桌面环境上也能发挥作用,但你必须自己尝试。一些 GNOME 特定步骤可能会变,所以请在[其他桌面环境][7]尝试时注意。
|
||||
|
||||
#### 准备
|
||||
|
||||
首先最重要的事是确保你有 GNOME 桌面的图标权限。
|
||||
|
||||
如果你跟随 Ubuntu 18.04 自定义提示,你会知道如何安装 GNOME Tweaks 工具。在这个工具中,确保你设置 ‘Show Icons’ 选项为允许。
|
||||
如果你跟随 Ubuntu 18.04 自定义提示,你会知道如何安装 GNOME Tweaks 工具。在这个工具中,确保你设置“Show Icons”选项为启用。
|
||||
|
||||
![Allow icons on desktop in GNOME][9]
|
||||
|
||||
一旦你确保已经设置,是时候在桌面添加应用快捷方式了。
|
||||
|
||||
[][10]
|
||||
|
||||
建议阅读在双启动中如何替代一个 Linux 发行版 [保留 Home分区]
|
||||
|
||||
#### 第一步:定位应用的 .desktop 文件
|
||||
|
||||
到 Files -> Other Location -> Computer。
|
||||
到 “Files -> Other Location -> Computer”。
|
||||
|
||||
![Go to Other Locations -> Computer][11]
|
||||
|
||||
从这里,到目录 usr -> share -> applications。你会在这里看到几个你已经安装的 [Ubuntu 应用][12]。即使你没有看到图标,你应该看到被命名为 应用名.desktop 形式的文件。
|
||||
从这里,到目录 “usr -> share -> applications”。你会在这里看到几个你已经安装的 [Ubuntu 应用][12]。即使你没有看到图标,你应该看到被命名为“应用名.desktop”形式的文件。
|
||||
|
||||
![Application Shortcuts][13]
|
||||
|
||||
#### 第二步:拷贝 .desktop 文件到桌面
|
||||
|
||||
现在你要做的只是查找应用图标(或者它的 desktop 文件)。当你找到后,拖文件到桌面或者拷贝文件(使用 Ctrl+C 快捷方式)并在桌面粘贴(使用 Ctrl+V 快捷方式)。
|
||||
现在你要做的只是查找应用图标(或者它的 desktop 文件)。当你找到后,拖文件到桌面或者拷贝文件(使用 `Ctrl+C` 快捷方式)并在桌面粘贴(使用 `Ctrl+V` 快捷方式)。
|
||||
|
||||
![Add .desktop file to the desktop][14]
|
||||
|
||||
@ -70,7 +64,7 @@ _**在这篇快速指南中,你将学到如何在 Ubuntu 桌面和其他使用
|
||||
|
||||
当你这么做,你应该在桌面上看到一个图标的文本文件而不是应用 logo。别担心,一会就不一样了。
|
||||
|
||||
你要做的就是双击桌面的那个文件。它将警告你它是一个 ‘未信任的应用启动器’,点击信任并启动。
|
||||
你要做的就是双击桌面的那个文件。它将警告你它是一个“未信任的应用启动器’,点击“信任并启动”。
|
||||
|
||||
![Launch Desktop Shortcut][15]
|
||||
|
||||
@ -80,20 +74,16 @@ _**在这篇快速指南中,你将学到如何在 Ubuntu 桌面和其他使用
|
||||
|
||||
#### Ubuntu 19.04 或者 GNOME 3.32 用户的疑难杂症
|
||||
|
||||
如果你使用 Ubuntu 19.04 或者 GNOME 3.32,你的 .desktop 文件可能根本不会启动。你应该右击 .desktop 文件并选择 “Allow Launching”。
|
||||
如果你使用 Ubuntu 19.04 或者 GNOME 3.32,你的 .desktop 文件可能根本不会启动。你应该右击 .desktop 文件并选择 “允许启动”。
|
||||
|
||||
在这之后,你应该能够启动应用并且桌面上的应用快捷方式能够正常显示了。
|
||||
|
||||
**总结**
|
||||
### 总结
|
||||
|
||||
如果你不喜欢桌面的某个应用启动器,选择删除就是了。它会删除应用快捷方式,但是应用仍安全的保留在你的系统中。
|
||||
|
||||
我希望你发现这篇快速指南有帮助并喜欢在 Ubuntu 桌面上的应用快捷方式。
|
||||
|
||||
[][17]
|
||||
|
||||
建议阅读在 Ubuntu 中如何安装和设置 Nemo 为默认的文件管理器。
|
||||
|
||||
如果你有问题或建议,请在下方评论让我知道。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -103,7 +93,7 @@ via: https://itsfoss.com/ubuntu-desktop-shortcut/
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,8 +1,8 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10870-1.html)
|
||||
[#]: subject: (PHP in 2019)
|
||||
[#]: via: (https://stitcher.io/blog/php-in-2019)
|
||||
[#]: author: (Brent https://stitcher.io/blog/php-in-2019)
|
||||
@ -10,75 +10,75 @@
|
||||
9102 年的 PHP
|
||||
======
|
||||
|
||||
你还记得篇流行的《[PHP:糟糕设计的分形][3]》博客文章吗?我第一次读它时,我在一个有很多遗留的 PHP 项目的糟糕地方工作。这篇文章让我去思考我是否应该放弃并去做与编程完全不同的事情。
|
||||
你还记得篇流行的博客文章《[PHP:设计糟糕的分形][3]》吗?我第一次读到它时,我在一个有很多遗留的 PHP 项目的糟糕地方工作。这篇文章让我觉得我是否应该放弃,并去做与编程完全不同的事情。
|
||||
|
||||
幸运的是,我之后很快就换了工作,更重要的是,自从 5.x 版本以来,PHP 成功地进步了很多。今天,我在向那些不再使用 PHP 编程,或者陷入遗留项目的人们致敬。
|
||||
还好,我之后很快就换了工作,更重要的是,自从 5.x 版本以来,PHP 成功地进步了很多。今天,我在向那些不再使用 PHP 编程,或者陷入遗留项目的人们致意。
|
||||
|
||||
剧透:今天有些事情仍然很糟糕,就像几乎每种编程语言都有它的怪癖一样。许多核心功能仍然有不一致的调用方法,仍然有令人困惑的配置设置,仍有许多开发人员在那里写蹩脚的代码 —— 因为他们必须如此,或因为他们不知道更好的写法。
|
||||
剧透:今天有些事情仍然很糟糕,就像几乎每种编程语言都有它的怪癖一样。许多核心功能仍然有不一致的调用方法,仍然有令人困惑的配置设置,仍然有许多开发人员在那里写蹩脚的代码 —— 因为他们必须如此,或是他们不知道更好的写法。
|
||||
|
||||
今天我想看看好的一面:让我们关注已经发生变化的事情以及编写干净而可维护的 PHP 代码的方法。在此之前,我想请你暂时搁置任何偏见。
|
||||
今天我想看看好的一面:让我们关注已经发生变化的事情,以及编写干净而可维护的 PHP 代码的方法。在此之前,我想请你暂时搁置任何偏见。
|
||||
|
||||
之后,你可以像以前一样自由地对 PHP 发表你的看法。虽然你可能会对 PHP 在过去的几年里的一些改进感到惊讶。
|
||||
然后,你可以像以前一样对 PHP 自由吐槽。虽然,你可能会对 PHP 在过去的几年里的一些改进感到惊讶。(LCTT 译注:说实话,我是真的感到吃惊)
|
||||
|
||||
### 提前看结论
|
||||
|
||||
* PHP 在积极开发,每年都有新版本
|
||||
* 自 PHP 5 时代以来的性能翻倍,如果不是三倍的话
|
||||
* PHP 在积极地开发,每年都有新版本
|
||||
* 自 PHP 5 时代以来的性能已经翻倍,如果不是三倍的话
|
||||
* 有一个非常活跃的框架、包和平台的生态系统
|
||||
* PHP 在过去几年中添加了许多新功能,并且这种语言在不断发展
|
||||
* 像静态分析这样的工具在过去几年中已经成熟,并且一直保持增长
|
||||
|
||||
更新:人们让我展示一些实际的代码。我觉得这没问题!这是我的一个爱好项目的[源代码][4],用 PHP 和 Laravel 编写;[这里][5]列出了我们在办公室维护的几百个自由开源软件包。这两者都是现代 PHP 项目的好例子。
|
||||
更新:人们让我展示一些实际的代码。我觉得这没问题!这是我的一个业余项目的[源代码][4],用 PHP 和 Laravel 编写的;[这里][5]列出了我们在办公室维护的几百个自由开源软件包。这两者都是现代 PHP 项目的好例子。
|
||||
|
||||
开始吧。
|
||||
那让我们开始吧。
|
||||
|
||||
### 历史总结
|
||||
|
||||
出于更好地衡量的目的,让我们快速回顾一下如今的 PHP 发布周期。我们现在的 PHP 为 7.3,预计在 2019 年底为 7.4。PHP 8.0 将是 7.4 之后的下一个版本。
|
||||
|
||||
自从 5.x 时代以来,核心团队试图保持每年的发布周期,并且在过去的四年中成功地做到了。
|
||||
自从 5.x 时代以来,核心团队试图保持每年发布一个版本的周期,并且他们在过去的四年中成功地做到了这一点。
|
||||
|
||||
一般来说,每个新版本都会在两年内得到积极支持,并再获得一年以上的“安全修复”。其目标是激励 PHP 开发人员尽可能保持最新:例如,每年进行小规模升级比在 5.4 到 7.0 之间跳转更容易。
|
||||
|
||||
可以在 [这里][6] 找到 PHP 时间轴的活动概述。
|
||||
|
||||
最后,PHP 5.6 是最新的 5.x 版本,而 7.0 是 6.x 的下一个版本。 如果你想知道 PHP 6 发生了什么,你可以听 [PHP Roundtable 播客][7]。
|
||||
最后,PHP 5.6 是最新的 5.x 版本,而 8.0 是当前的下一个大版本。如果你想知道 PHP 6 发生了什么,你可以听听 [PHP Roundtable 播客][7]。
|
||||
|
||||
了解了这个,让我们揭穿一些关于现代 PHP 的常见误解。
|
||||
|
||||
### PHP的性能
|
||||
### PHP 的性能
|
||||
|
||||
早在 5.x 时代,PHP 的表现就是……嗯,平均水平。但是在 7.0 版本中,PHP 的核心部分从头开始重写,导致性能提升了两到三倍!
|
||||
早在 5.x 时代,PHP 的表现就是……嗯,平均水平。但是在 7.0 版本中,PHP 从头开始重写了核心部分,导致其性能提升了两到三倍!
|
||||
|
||||
但光是嘴说是不够的。我们来看看基准测试。幸运的是,人们花了很多时间来对 PHP 性能进行基准测试。 我发现 [Kinsta][8] 有一个很好的更新列表。
|
||||
但光是嘴说是不够的。让我们来看看基准测试。幸运的是,人们花了很多时间对 PHP 性能进行了基准测试。 我发现 [Kinsta][8] 有一个很好的更新的测试列表。
|
||||
|
||||
自 7.0 升级以来,性能就只有提升而没有回退。PHP Web 应用程序的性能就可与其他语言中的 Web 框架相提并论,甚至在某些情况下更好。你可以看看这个[广泛的基准测试套件][9]。
|
||||
自 7.0 升级以来,性能就一直在提升而没有回退。PHP Web 应用程序的性能可与其它语言中的 Web 框架相提并论,甚至在某些情况下更好。你可以看看这个[广泛的基准测试套件][9]。
|
||||
|
||||
当然 PHP 框架不会胜过 C 和 Rust,但它们比 Rails 或 Django 要好得多,并且与 ExpressJS 相当。
|
||||
|
||||
### 框架和生态系统
|
||||
|
||||
说到框架:PHP 可不仅仅是 WordPress。让我告诉你,某些专业的 PHP 开发人员:WordPress 绝不代表当代的 PHP 生态系统。
|
||||
说到框架:PHP 可不仅仅是 WordPress。让我告诉你 —— 某些专业的 PHP 开发人员:WordPress 绝不代表当代的 PHP 生态系统。
|
||||
|
||||
一般来说,有两个主要的 Web 应用程序框架,[Symfony][10] 和 [Laravel][11],以及一些较小的应用程序框架。当然还有 Zend、Yii、Cake、Code Igniter 等等,但是如果你想知道现代 PHP 开发是怎么样的,这两者之一都是很好的对象。
|
||||
一般来说,有两个主要的 Web 应用程序框架,[Symfony][10] 和 [Laravel][11],以及一些较小的应用程序框架。当然还有 Zend、Yii、Cake、Code Igniter 等等,但是如果你想知道现代 PHP 开发是怎么样的,这两者之一都是很好的选择。
|
||||
|
||||
这两个框架都有一个庞大的包和产品的生态系统。从管理面板和 CRM 到独立软件包,从 CI 到分析器,以及几个 Web 套接字服务器、队列管理器、支付集成等众多服务。老实说,要列出的内容太多了。
|
||||
|
||||
这些框架虽然适用于实际开发。如果你只是需要个内容管理系统(CMS),WordPress 和 CraftCMS 等平台会越来越好。
|
||||
这些框架虽然适用于实际开发。如果你只是需要个内容管理系统(CMS),WordPress 和 CraftCMS 等平台就够了。
|
||||
|
||||
衡量 PHP 生态系统当前状态的一种方法是查看 Packagist,这是 PHP 的主要软件包存储库。它已呈指数级增长。每天下载量达到 2500 万次,这可以说 PHP 生态系统已不再是以前的小型弱势群体。
|
||||
衡量 PHP 生态系统当前状态的一种方法是查看 Packagist,这是 PHP 主要的软件包存储库。它现在呈指数级增长。每天下载量达到了 2500 万次,可以说 PHP 生态系统已不再是以前的小型弱势群体了。
|
||||
|
||||
请查看此图表,它列出一段时间内的软件包和版本数量。它也可以在 [Packagist 网站][12]上找到。
|
||||
请查看此图表,它列出一段时间内的软件包和版本数量变化。它也可以在 [Packagist 网站][12]上找到它。
|
||||
|
||||
![][13]
|
||||
|
||||
除了应用程序框架和 CMS 之外,我们还看到过去几年里异步框架的兴起。
|
||||
|
||||
这些是用 PHP 或其他语言编写的框架和服务器,允许用户运行真正的异步 PHP。这些例子包括 [Swoole][14](创始人韩天峰),以及 [Amp][15] 和 [ReactPHP][16]。
|
||||
这些是用 PHP 或其他语言编写的框架和服务器,允许用户运行真正的异步 PHP,这些例子包括 [Swoole][14](创始人韩天峰),以及 [Amp][15] 和 [ReactPHP][16]。
|
||||
|
||||
我们已经进入了异步世界冒险,像 Web 套接字和具有大量 IO 的应用程序之类的东西在 PHP 世界中已经变得非常重要。
|
||||
我们已经进入了异步的世界,像 Web 套接字和具有大量 I/O 的应用程序之类的东西在 PHP 世界中已经变得非常重要。
|
||||
|
||||
在内部邮件列表里(核心开发人员讨论语言开发的地方)已经谈到了[将 libuv 添加到核心][17]。如果你还不知道 libuv:Node.js 全赖它提供了异步性。
|
||||
在内部邮件列表里(PHP 核心开发人员讨论语言开发的地方)已经谈到了[将 libuv 添加到核心][17]。如果你还不知道 libuv:Node.js 全有赖它提供异步性。
|
||||
|
||||
### 语言本身
|
||||
|
||||
@ -90,7 +90,7 @@
|
||||
+ [Trait](https://www.php.net/manual/en/language.oop5.traits.php)(一种代码重用方式)
|
||||
+ [属性类型](https://stitcher.io/blog/new-in-php-74#typed-properties-rfc)
|
||||
+ [展开操作符](https://wiki.php.net/rfc/argument_unpacking)(参数解包 `...`)
|
||||
+ [JIT 编译器](https://wiki.php.net/rfc/jit)
|
||||
+ [JIT 编译器](https://wiki.php.net/rfc/jit)(即时编译器)
|
||||
+ [FFI](https://wiki.php.net/rfc/ffi)(外部函数接口)
|
||||
+ [匿名类](https://www.php.net/manual/en/language.oop5.anonymous.php)
|
||||
+ [返回类型声明](https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration)
|
||||
@ -98,31 +98,31 @@
|
||||
+ [生成器](https://wiki.php.net/rfc/generators)
|
||||
+ [等等](https://www.php.net/ChangeLog-7.php)
|
||||
|
||||
当我们讨论语言功能时,我们还要谈谈当今语言的发展过程。虽然社区可以提出 RFC,但是得有一个活跃的志愿者核心团队才能推着语言前进。
|
||||
当我们讨论语言功能时,我们还要谈谈当今该语言的发展过程。虽然社区可以提出 RFC,但是得有一个活跃的志愿者核心团队才能推着它前进。
|
||||
|
||||
接下来,这些 RFC 将在“内部”邮件列表中进行讨论,这个邮件列表也可以[在线阅读][18]。在添加新语言功能之前,必须进行投票。只有得到了至少 2/3 多数的 RFC 才能进入核心。
|
||||
接下来,这些 RFC 将在“内部”邮件列表中进行讨论,这个邮件列表也可以[在线阅读][18]。在添加新的语言特性之前,必须进行投票。只有得到了至少 2/3 多数同意的 RFC 才能进入核心。
|
||||
|
||||
可能有大约 100 人能投票,但不需要对每个 RFC 进行投票。核心团队的成员当然可以投票,他们是维护代码库的人。除了他们之外,还有一群人从 PHP 社区中被单独挑选出来。这些人包括 PHP 文档的维护者,对 PHP 项目整体有贡献的人,以及 PHP 社区中的杰出开发人员。
|
||||
可能有大约 100 人能够投票,但不需要每个人对每个 RFC 进行投票。核心团队的成员当然可以投票,他们是维护代码库的人。除了他们之外,还有一群人从 PHP 社区中被单独挑选出来。这些人包括 PHP 文档的维护者,对 PHP 项目整体有贡献的人,以及 PHP 社区中的杰出开发人员。
|
||||
|
||||
虽然大多数核心开发都是在自愿的基础上完成的,但其中一位核心 PHP 开发人员 Nikita Popov 最近受雇于 [JetBrains][19] 全职从事于该语言。另一个例子是 Linux 基金会最近决定[投资 Zend 框架][20]。像这样的雇佣和收购确保了 PHP 未来发展的稳定性。
|
||||
虽然大多数核心开发都是在自愿的基础上完成的,但其中一位核心 PHP 开发人员 Nikita Popov 最近受雇于 [JetBrains][19] 全职从事于 PHP 语言的开发。另一个例子是 Linux 基金会最近决定[投资 Zend 框架][20]。像这样的雇佣和收购确保了 PHP 未来发展的稳定性。
|
||||
|
||||
### 工具
|
||||
|
||||
除了核心本身,我们看到过去几年中围绕它的工具有所增加。浮现于我脑海中的是静态分析器,如由 Vimeo 创建 [Psalm][21],以及 [Phan][22] 和 [PHPStan][23]。
|
||||
除了核心本身,我们看到过去几年中围绕它的工具有所增加。首先浮现于我脑海中的是静态分析器,比如由 Vimeo 创建 [Psalm][21],以及 [Phan][22] 和 [PHPStan][23]。
|
||||
|
||||
这些工具将静态分析你的 PHP 代码并报告任何的类型错误和可能的错误等。在某种程度上,它们提供的功能可以与 TypeScript 进行比较,但是现在这种语言不能<ruby>转译<rt>transpiling</rt></ruby>,因此不支持使用自定义语法。
|
||||
这些工具将静态分析你的 PHP 代码并报告任何类型错误和可能的错误等。在某种程度上,它们提供的功能可以与 TypeScript 进行比较,但是现在这种语言不能<ruby>转译<rt>transpiling</rt></ruby>,因此不支持使用自定义语法。
|
||||
|
||||
尽管这意味着我们需要依赖 docblocks,但是 PHP 的原创建者 Rasmus Lerdorf 确实提到了[添加静态分析引擎][24]到核心的想法。虽然会有很多潜力,但这是一项艰巨的任务。
|
||||
尽管这意味着我们需要依赖 docblocks,但是 PHP 之父 Rasmus Lerdorf 确实提到了[添加静态分析引擎][24]到核心的想法。虽然会有很多潜力,但这是一项艰巨的任务。
|
||||
|
||||
说到转译,以及受到 JavaScript 社区的启发;他们已经努力在用户领域中扩展 PHP 语法。一个名为 [Pre][25] 的项目正是如此:允许新的 PHP 语法转译为普通的 PHP 代码。
|
||||
说到转译,以及受到 JavaScript 社区的启发;他们已经努力在用户领域中扩展 PHP 语法。一个名为 [Pre][25] 的项目正是如此:允许将新的 PHP 语法转译为普通的 PHP 代码。
|
||||
|
||||
虽然这个思路已经在 JavaScript 世界中被证明了,但如果提供了适当的 IDE 和静态分析支持,它就能在 PHP 中工作了。这是一个非常有趣的想法,但必须发展起来才能称之为“主流”。
|
||||
|
||||
### 结语
|
||||
|
||||
尽管如此,你仍然可以将 PHP 视为一种糟糕的语言。虽然这种语言肯定有它的缺点和与之而来的 20 年的遗产;但我可以放胆地说,我喜欢和它一起工作。
|
||||
尽管如此,你仍然可以将 PHP 视为一种糟糕的语言。虽然这种语言肯定有它的缺点和背负了 20 年的遗产;但我可以放胆地说,我喜欢用它工作。
|
||||
|
||||
根据我的经验,我能够创建可靠、可维护和高质量的软件。我工作的客户对最终结果感到满意,就像我一样。
|
||||
根据我的经验,我能够创建可靠、可维护和高质量的软件。我工作的客户对最终结果感到满意,“俺也一样”。
|
||||
|
||||
尽管仍然可以用 PHP 做很多乱七八糟的事情,但我认为如果明智和正确地使用的话,它是 Web 开发的绝佳选择。
|
||||
|
||||
@ -135,7 +135,7 @@ via: https://stitcher.io/blog/php-in-2019
|
||||
作者:[Brent][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,120 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (tomjlw)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10888-1.html)
|
||||
[#]: subject: (How to SSH into a Raspberry Pi [Beginner’s Tip])
|
||||
[#]: via: (https://itsfoss.com/ssh-into-raspberry/)
|
||||
[#]: author: (Chinmay https://itsfoss.com/author/chinmay/)
|
||||
|
||||
新手教程:如何 SSH 进入树莓派
|
||||
======
|
||||
|
||||
> 在这篇树莓派文章中,你将学到如何在树莓派中启用 SSH 以及之后如何通过 SSH 进入树莓派。
|
||||
|
||||
在你可以用[树莓派][1]做的所有事情中,将其作为一个家庭网络的服务器是十分流行的做法。小体积与低功耗使它成为运行轻量级服务器的完美设备。
|
||||
|
||||
在这种情况下你做得到的事情之一是能够每次在树莓派上无须接上显示器、键盘、鼠标以及走到放置你的树莓派的地方就可以运行指令。
|
||||
|
||||
你可以从其它任意电脑、笔记本、台式机甚至你的手机通过 SSH([Secure Shell][2])登入你的树莓派来做到这一点。让我展示给你看:
|
||||
|
||||
### 如何 SSH 进入树莓派
|
||||
|
||||
![][3]
|
||||
|
||||
我假设你已经[在你的树莓派上运行 Raspbian][4] 并已经成功通过有线或者无线网连进网络了。你的树莓派接入网络这点是很重要的,否则你无法通过 SSH 连接树莓派(抱歉说出这种显而易见的事实)。
|
||||
|
||||
#### 步骤一:在树莓派上启用 SSH
|
||||
|
||||
SSH 在树莓派上是默认关闭的,因此在你安装好全新的 Raspbian 后打开树莓派时,你需要启用它。
|
||||
|
||||
首先通过菜单进入树莓派的配置界面。
|
||||
|
||||
![树莓派菜单,树莓派配置][5]
|
||||
|
||||
现在进入<ruby>接口<rt>interfaces</rt></ruby>标签,启动 SSH 并重启你的树莓派。
|
||||
|
||||
![在树莓派上启动 SSH][6]
|
||||
|
||||
你也可以通过终端直接启动 SSH。仅需输入命令 `sudo raspi-config` 然后进入高级设置以启用 SSH。
|
||||
|
||||
#### 步骤二: 找到树莓派的 IP 地址
|
||||
|
||||
在大多数情况下,你的树莓派会被分配一个看起来长得像 `192.168.x.x` 或者 `10.x.x.x` 的本地 IP 地址。你可以[使用多种 Linux 命令来找到 IP 地址][7]。
|
||||
|
||||
我在这使用古老而好用的 `ifconfig` 命令,但是你也可以使用 `ip address`。
|
||||
|
||||
```
|
||||
ifconfig
|
||||
```
|
||||
|
||||
![树莓派网络配置][9]
|
||||
|
||||
这行命令展现了所有活跃中的网络适配器以及其配置的列表。第一个条目(`eth0`)展示了例如`192.168.2.105` 的有效 IP 地址。我用有线网将我的树莓派连入网络,因此这里显示的是 `eth0`。如果你用无线网的话在叫做 `wlan0` 的条目下查看。
|
||||
|
||||
你也可以用其他方法例如查看你的路由器或者调制解调器的网络设备表以找到 IP 地址。
|
||||
|
||||
#### 步骤三:SSH 进你的树莓派
|
||||
|
||||
既然你已经启用了 SSH 功能并且找到了 IP 地址,你可以从任何电脑 SSH 进入你的树莓派。你同样需要树莓派的用户名和密码。
|
||||
|
||||
默认用户名和密码是:
|
||||
|
||||
* 用户名:`pi`
|
||||
* 密码:`raspberry`
|
||||
|
||||
如果你已改变了默认的密码,那就使用新的而不是以上的密码。理想状态下你必须改变默认的密码。在过去,有一款[恶意软件感染数千使用默认用户名和密码的树莓派设备][8]。
|
||||
|
||||
(在 Mac 或 Linux 上)从你想要 SSH 进树莓派的电脑上打开终端输入以下命令,在 Windows 上,你可以用类似 [Putty][10] 的 SSH 客户端。
|
||||
|
||||
这里,使用你在之前步骤中找到的 IP 地址。
|
||||
|
||||
```
|
||||
ssh [受保护的邮件]
|
||||
```
|
||||
|
||||
> 注意: 确保你的树莓派和你用来 SSH 进入树莓派的电脑接入了同一个网络。
|
||||
|
||||
![通过命令行 SSH][11]
|
||||
|
||||
第一次你会看到一个警告,输入 `yes` 并按下回车。
|
||||
|
||||
![输入密码 \(默认是 ‘raspberry‘\)][12]
|
||||
|
||||
现在,输入密码按下回车。
|
||||
|
||||
![成功通过 SSH 登入][13]
|
||||
|
||||
成功登入你将会看到树莓派的终端。现在你可以通过这个终端无需物理上访问你的树莓派就可以远程(在当前网络内)在它上面运行指令。
|
||||
|
||||
在此之上你也可以设置 SSH 密钥这样每次通过 SSH 登入时就可以无需输入密码,但那完全是另一个话题了。
|
||||
|
||||
我希望你通过跟着这个教程已能够 SSH 进入你的树莓派。在下方评论中让我知道你打算用你的树莓派做些什么!
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/ssh-into-raspberry/
|
||||
|
||||
作者:[Chinmay][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[tomjlw](https://github.com/tomjlw)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/chinmay/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.raspberrypi.org/
|
||||
[2]: https://en.wikipedia.org/wiki/Secure_Shell
|
||||
[3]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/ssh-into-raspberry-pi.png?resize=800%2C450&ssl=1
|
||||
[4]: https://itsfoss.com/tutorial-how-to-install-raspberry-pi-os-raspbian-wheezy/
|
||||
[5]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/Raspberry-pi-configuration.png?ssl=1
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/enable-ssh-raspberry-pi.png?ssl=1
|
||||
[7]: https://linuxhandbook.com/find-ip-address/
|
||||
[8]: https://itsfoss.com/raspberry-pi-malware-threat/
|
||||
[9]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/ifconfig-rapberry-pi.png?ssl=1
|
||||
[10]: https://itsfoss.com/putty-linux/
|
||||
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-pi-warning.png?fit=800%2C199&ssl=1
|
||||
[12]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-pi-password.png?fit=800%2C202&ssl=1
|
||||
[13]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-Pi-successful-login.png?fit=800%2C306&ssl=1
|
||||
[14]: https://itsfoss.com/speed-up-ubuntu-unity-on-low-end-system/
|
117
published/201905/20190516 Building Smaller Container Images.md
Normal file
117
published/201905/20190516 Building Smaller Container Images.md
Normal file
@ -0,0 +1,117 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10885-1.html)
|
||||
[#]: subject: (Building Smaller Container Images)
|
||||
[#]: via: (https://fedoramagazine.org/building-smaller-container-images/)
|
||||
[#]: author: (Muayyad Alsadi https://fedoramagazine.org/author/alsadi/)
|
||||
|
||||
构建更小的容器镜像的技巧
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Linux 容器已经成为一个热门话题,保证容器镜像较小被认为是一个好习惯。本文提供了有关如何构建较小 Fedora 容器镜像的一些技巧。
|
||||
|
||||
### microdnf
|
||||
|
||||
Fedora 的 DNF 是用 Python 编写的,因为它有各种各样的插件,因此它的设计是可扩展的。但是 有一个 Fedora 基本容器镜像替代品,它使用一个较小的名为 [microdnf][2] 的包管理器,使用 C 编写。要在 Dockerfile 中使用这个最小的镜像,`FROM` 行应该如下所示:
|
||||
|
||||
```
|
||||
FROM registry.fedoraproject.org/fedora-minimal:30
|
||||
```
|
||||
|
||||
如果你的镜像不需要像 Python 这样的典型 DNF 依赖项,例如,如果你在制作 NodeJS 镜像时,那么这是一个重要的节省项。
|
||||
|
||||
### 在一个层中安装和清理
|
||||
|
||||
为了节省空间,使用 `dnf clean all` 或其 microdnf 等效的 `microdnf clean all` 删除仓库元数据非常重要。但是你不应该分两步执行此操作,因为这实际上会将这些文件保存在容器镜像中,然后在另一层中将其标记为删除。要正确地执行此操作,你应该像这样一步完成安装和清理:
|
||||
|
||||
```
|
||||
FROM registry.fedoraproject.org/fedora-minimal:30
|
||||
RUN microdnf install nodejs && microdnf clean all
|
||||
```
|
||||
|
||||
### 使用 microdnf 进行模块化
|
||||
|
||||
模块化是一种给你选择不同堆栈版本的方法。例如,你可能需要在项目中用非 LTS 的 NodeJS v11,旧的 LTS NodeJS v8 用于另一个,最新的 LTS NodeJS v10 用于另一个。你可以使用冒号指定流。
|
||||
|
||||
```
|
||||
# dnf module list
|
||||
# dnf module install nodejs:8
|
||||
```
|
||||
|
||||
`dnf module install` 命令意味着两个命令,一个启用流,另一个是从它安装 nodejs。
|
||||
|
||||
```
|
||||
# dnf module enable nodejs:8
|
||||
# dnf install nodejs
|
||||
```
|
||||
|
||||
尽管 `microdnf` 不提供与模块化相关的任何命令,但是可以启用带有配置文件的模块,并且 libdnf(被 microdnf 使用)[似乎][3]支持模块化流。该文件看起来像这样:
|
||||
|
||||
```
|
||||
/etc/dnf/modules.d/nodejs.module
|
||||
[nodejs]
|
||||
name=nodejs
|
||||
stream=8
|
||||
profiles=
|
||||
state=enabled
|
||||
```
|
||||
|
||||
使用模块化的 `microdnf` 的完整 Dockerfile 如下所示:
|
||||
|
||||
```
|
||||
FROM registry.fedoraproject.org/fedora-minimal:30
|
||||
RUN \
|
||||
echo -e "[nodejs]\nname=nodejs\nstream=8\nprofiles=\nstate=enabled\n" > /etc/dnf/modules.d/nodejs.module && \
|
||||
microdnf install nodejs zopfli findutils busybox && \
|
||||
microdnf clean all
|
||||
```
|
||||
|
||||
### 多阶段构建
|
||||
|
||||
在许多情况下,你可能需要大量的无需用于运行软件的构建时依赖项,例如构建一个静态链接依赖项的 Go 二进制文件。多阶段构建是分离应用构建和应用运行时的有效方法。
|
||||
|
||||
例如,下面的 Dockerfile 构建了一个 Go 应用 [confd][4]。
|
||||
|
||||
```
|
||||
# building container
|
||||
FROM registry.fedoraproject.org/fedora-minimal AS build
|
||||
RUN mkdir /go && microdnf install golang && microdnf clean all
|
||||
WORKDIR /go
|
||||
RUN export GOPATH=/go; CGO_ENABLED=0 go get github.com/kelseyhightower/confd
|
||||
|
||||
FROM registry.fedoraproject.org/fedora-minimal
|
||||
WORKDIR /
|
||||
COPY --from=build /go/bin/confd /usr/local/bin
|
||||
CMD ["confd"]
|
||||
```
|
||||
|
||||
通过在 `FROM` 指令之后添加 `AS` 并从基本容器镜像中添加另一个 `FROM` 然后使用 `COPY --from=` 指令将内容从*构建*的容器复制到第二个容器来完成多阶段构建。
|
||||
|
||||
可以使用 `podman` 构建并运行此 Dockerfile:
|
||||
|
||||
```
|
||||
$ podman build -t myconfd .
|
||||
$ podman run -it myconfd
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/building-smaller-container-images/
|
||||
|
||||
作者:[Muayyad Alsadi][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/alsadi/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/05/smaller-container-images-816x345.jpg
|
||||
[2]: https://github.com/rpm-software-management/microdnf
|
||||
[3]: https://bugzilla.redhat.com/show_bug.cgi?id=1575626
|
||||
[4]: https://github.com/kelseyhightower/confd
|
@ -0,0 +1,156 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10906-1.html)
|
||||
[#]: subject: (Querying 10 years of GitHub data with GHTorrent and Libraries.io)
|
||||
[#]: via: (https://opensource.com/article/19/5/chaossearch-github-ghtorrent)
|
||||
[#]: author: (Pete Cheslock https://opensource.com/users/petecheslock/users/ghaff/users/payalsingh/users/davidmstokes)
|
||||
|
||||
用 GHTorrent 和 Libraries.io 查询 10 年的 GitHub 数据
|
||||
======
|
||||
|
||||
> 有一种方法可以在没有任何本地基础设施的情况下使用开源数据集探索 GitHub 数据。
|
||||
|
||||
![magnifying glass on computer screen](https://img.linux.net.cn/data/attachment/album/201905/27/220200jlzrlz333vkfl8ok.jpg)
|
||||
|
||||
我一直在寻找新的数据集,以用它们来展示我们团队工作的力量。[CHAOSSEARCH][2] 可以将你的 [Amazon S3][3] 对象存储数据转换为完全可搜索的 [Elasticsearch][4] 式集群。使用 Elasticsearch API 或 [Kibana][5] 等工具,你可以查询你所要找的任何数据。
|
||||
|
||||
当我找到 [GHTorrent][6] 项目进行探索时,我很兴奋。GHTorrent 旨在通过 GitHub API 构建所有可用数据的离线版本。如果你喜欢数据集,这是一个值得一看的项目,甚至你可以考虑[捐赠一个 GitHub API 密钥][7]。
|
||||
|
||||
### 访问 GHTorrent 数据
|
||||
|
||||
有许多方法可以访问和使用 [GHTorrent 的数据][8],它以 [NDJSON][9] 格式提供。这个项目可以以多种形式提供数据,包括用于恢复到 [MySQL][11] 数据库的 [CSV][10],可以转储所有对象的 [MongoDB][12],以及用于将数据直接导出到 Google 对象存储中的 Google Big Query(免费)。 有一点需要注意:这个数据集有从 2008 年到 2017 年几乎完整的数据集,但从 2017 年到现在的数据还不完整。这将影响我们确定性查询的能力,但它仍然是一个令人兴奋的信息量。
|
||||
|
||||
我选择 Google Big Query 来避免自己运行任何数据库,那么我就可以很快下载包括用户和项目在内的完整数据库。CHAOSSEARCH 可以原生分析 NDJSON 格式,因此在将数据上传到 Amazon S3 之后,我能够在几分钟内对其进行索引。CHAOSSEARCH 平台不要求用户设置索引模式或定义其数据的映射,它可以发现所有字段本身(字符串、整数等)。
|
||||
|
||||
随着我的数据完全索引并准备好进行搜索和聚合,我想深入了解看看我们可以发现什么,比如哪些软件语言是 GitHub 项目最受欢迎的。
|
||||
|
||||
(关于格式化的说明:下面这是一个有效的 JSON 查询,我们不会在这里正确格式化以避免滚动疲劳。要正确格式化它,你可以在本地复制它并发送到命令行实用程序,如 [jq][13]。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"date_histogram":{"field":"root.created_at","interval":"1M","time_zone":"America/New_York","min_doc_count":1}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["root.created_at","root.updated_at"],"query":{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"root.language":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
对于那些近年来跟踪开源语言状态的人来说,这个结果并不令人惊讶。
|
||||
|
||||
![Which software languages are the most popular on GitHub.][14]
|
||||
|
||||
[JavaScript][15] 仍然是卫冕冠军,虽然有些人认为 JavaScript 正在逐渐消失,但它仍然是 800 磅重的大猩猩,很可能会保持这种状态一段时间。[Java][16] 面临类似的谣言,但这些数据表明它是开源生态系统的重要组成部分。
|
||||
|
||||
考虑到像 [Docker][17] 和 [Kubernetes][18] 这样的项目的流行,你可能会想,“Go([Golang][19])怎么样?”这是一个提醒的好时机,这里讨论的 GitHub 数据集包含一些空缺,最明显的是在 2017 年之后我看到 Golang 项目随处可见,而这里并没有显示。我希望用完整的 GitHub 数据集重复此搜索,看看它是否会改变排名。
|
||||
|
||||
现在让我们来探讨项目创建的速度。 (提醒:这是为了便于阅读而合并的有效 JSON。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"date_histogram":{"field":"root.created_at","interval":"1M","time_zone":"America/New_York","min_doc_count":1}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["root.created_at","root.updated_at"],"query":{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"root.language":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
我们可以看到创建新项目的速度,也会给人留下深刻的印象,从 2012 年左右开始大幅增长:
|
||||
|
||||
![The rate at which new projects are created on GitHub.][20]
|
||||
|
||||
既然我知道了创建项目的速度以及用于创建这些项目的最流行的语言,我还想知道这些项目选择的开源许可证。遗憾的是,这个 GitHub 项目数据集中并不存在这些数据,但是 [Tidelift][21] 的精彩团队在 [Libraries.io][22] [数据][23] 里发布了一个 GitHub 项目的详细列表,包括使用的许可证以及其中有关开源软件状态的其他详细信息。将此数据集导入 CHAOSSEARCH 只花了几分钟,让我看看哪些开源软件许可证在 GitHub 上最受欢迎:
|
||||
|
||||
(提醒:这是为了便于阅读而合并的有效 JSON。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"terms":{"field":"Repository License","size":10,"order":{"_count":"desc"}}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["Created Timestamp","Last synced Timestamp","Latest Release Publish Timestamp","Updated Timestamp"],"query":{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"Repository License":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
结果显示了一些重要的异常值:
|
||||
|
||||
![Which open source software licenses are the most popular on GitHub.][24]
|
||||
|
||||
如你所见,[MIT 许可证][25] 和 [Apache 2.0 许可证][26] 的开源项目远远超过了其他大多数开源许可证,而 [各种 BSD 和 GPL 许可证][27] 则差得很远。鉴于 GitHub 的开放模式,我不能说我对这些结果感到惊讶。我猜想是用户(而不是公司)创建了大多数项目,并且他们使用 MIT 许可证可以使其他人轻松地使用、共享和贡献。而鉴于有不少公司希望确保其商标得到尊重并为其业务提供开源组件,那么 Apache 2.0 许可证数量高企的背后也是有道理的。
|
||||
|
||||
现在我确定了最受欢迎的许可证,我很想看看最少使用的许可证。通过调整我的上一个查询,我将前 10 名逆转为最后 10 名,并且只找到了两个使用 [伊利诺伊大学 - NCSA 开源许可证][28] 的项目。我之前从未听说过这个许可证,但它与 Apache 2.0 非常接近。看到所有 GitHub 项目中使用了多少个不同的软件许可证,这很有意思。
|
||||
|
||||
![The University of Illinois/NCSA open source license.][29]
|
||||
|
||||
之后,我针对特定语言(JavaScript)来查看最常用的许可证。(提醒:这是为了便于阅读而合并的有效JSON。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"terms":{"field":"Repository License","size":10,"order":{"_count":"desc"}}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["Created Timestamp","Last synced Timestamp","Latest Release Publish Timestamp","Updated Timestamp"],"query":{"bool":{"must":[{"match_phrase":{"Repository Language":{"query":"JavaScript"}}}],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"Repository License":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
这个输出有一些意外。
|
||||
|
||||
![The most popular open source licenses used for GitHub JavaScript projects.][30]
|
||||
|
||||
尽管使用 `npm init` 创建的 [NPM][31] 模块的默认许可证是来自 [Internet Systems Consortium(ISC)][32] 的许可证,但你可以看到相当多的这些项目使用 MIT 以及 Apache 2.0 的开源许可证。
|
||||
|
||||
由于 Libraries.io 数据集中包含丰富的开源项目内容,并且由于 GHTorrent 数据缺少最近几年的数据(因此缺少有关 Golang 项目的任何细节),因此我决定运行类似的查询来查看 Golang 项目是如何许可他们的代码的。
|
||||
|
||||
(提醒:这是为了便于阅读而合并的有效 JSON。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"terms":{"field":"Repository License","size":10,"order":{"_count":"desc"}}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["Created Timestamp","Last synced Timestamp","Latest Release Publish Timestamp","Updated Timestamp"],"query":{"bool":{"must":[{"match_phrase":{"Repository Language":{"query":"Go"}}}],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"Repository License":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
结果与 Javascript 完全不同。
|
||||
|
||||
![How Golang projects license their GitHub code.][33]
|
||||
|
||||
Golang 项目与 JavaScript 项目惊人逆转 —— 使用 Apache 2.0 的 Golang 项目几乎是 MIT 许可证的三倍。虽然很难准确地解释为什么会出现这种情况,但在过去的几年中,Golang 已经出现了大规模的增长,特别是在开源和商业化的项目和软件产品公司中。
|
||||
|
||||
正如我们上面所了解的,这些公司中的许多公司都希望强制执行其商标策略,因此转向 Apache 2.0 许可证是有道理的。
|
||||
|
||||
#### 总结
|
||||
|
||||
最后,我通过深入了解 GitHub 用户和项目的数据找到了一些有趣的结果。其中一些我肯定会猜到,但是一些结果对我来说也是惊喜,特别是像很少使用的 NCSA 许可证这样的异常值。
|
||||
|
||||
总而言之,你可以看到 CHAOSSEARCH 平台能够快速轻松地找到有趣问题的复杂答案。我无需自己运行任何数据库就可以深入研究这个数据集,甚至可以在 Amazon S3 上以低成本的方式存储数据,因此无需维护。 现在,我可以随时查询有关这些数据的任何其他问题。
|
||||
|
||||
你对数据提出了哪些其他问题,以及你使用了哪些数据集?请在评论或推特上告诉我 [@petecheslock] [34]。
|
||||
|
||||
本文的一个版本最初发布在 [CHAOSSEARCH][35],有更多结果可供发现。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/chaossearch-github-ghtorrent
|
||||
|
||||
作者:[Pete Cheslock][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/petecheslock/users/ghaff/users/payalsingh/users/davidmstokes
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/search_find_code_issue_bug_programming.png?itok=XPrh7fa0 (magnifying glass on computer screen)
|
||||
[2]: https://chaossearch.io/
|
||||
[3]: https://aws.amazon.com/s3/
|
||||
[4]: https://www.elastic.co/
|
||||
[5]: https://www.elastic.co/products/kibana
|
||||
[6]: http://ghtorrent.org
|
||||
[7]: http://ghtorrent.org/services.html
|
||||
[8]: http://ghtorrent.org/downloads.html
|
||||
[9]: http://ndjson.org
|
||||
[10]: https://en.wikipedia.org/wiki/Comma-separated_values
|
||||
[11]: https://en.wikipedia.org/wiki/MySQL
|
||||
[12]: https://www.mongodb.com/
|
||||
[13]: https://stedolan.github.io/jq/
|
||||
[14]: https://opensource.com/sites/default/files/uploads/github-1_500.png (Which software languages are the most popular on GitHub.)
|
||||
[15]: https://en.wikipedia.org/wiki/JavaScript
|
||||
[16]: /resources/java
|
||||
[17]: /resources/what-docker
|
||||
[18]: /resources/what-is-kubernetes
|
||||
[19]: https://golang.org/
|
||||
[20]: https://opensource.com/sites/default/files/uploads/github-2_500.png (The rate at which new projects are created on GitHub.)
|
||||
[21]: https://tidelift.com
|
||||
[22]: http://libraries.io/
|
||||
[23]: https://libraries.io/data
|
||||
[24]: https://opensource.com/sites/default/files/uploads/github-3_500.png (Which open source software licenses are the most popular on GitHub.)
|
||||
[25]: https://opensource.org/licenses/MIT
|
||||
[26]: https://opensource.org/licenses/Apache-2.0
|
||||
[27]: https://opensource.org/licenses
|
||||
[28]: https://tldrlegal.com/license/university-of-illinois---ncsa-open-source-license-(ncsa)
|
||||
[29]: https://opensource.com/sites/default/files/uploads/github-4_500_0.png (The University of Illinois/NCSA open source license.)
|
||||
[30]: https://opensource.com/sites/default/files/uploads/github-5_500_0.png (The most popular open source licenses used for GitHub JavaScript projects.)
|
||||
[31]: https://www.npmjs.com/
|
||||
[32]: https://en.wikipedia.org/wiki/ISC_license
|
||||
[33]: https://opensource.com/sites/default/files/uploads/github-6_500.png (How Golang projects license their GitHub code.)
|
||||
[34]: https://twitter.com/petecheslock
|
||||
[35]: https://chaossearch.io/blog/where-are-the-github-users-part-1/
|
@ -0,0 +1,102 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (zhs852)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10897-1.html)
|
||||
[#]: subject: (Change Power Modes in Ubuntu with Slimbook Battery Optimizer)
|
||||
[#]: via: (https://itsfoss.com/slimbook-battry-optimizer-ubuntu/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
在 Ubuntu 中使用 Slimbook Battery Optimizer 切换电源模式
|
||||
======
|
||||
|
||||
> Slimbook Battery Optimizer 是一个美观实用的指示器小程序,它可以让你在安装了 Linux 的笔记本上快速切换电源模式来延长续航时间。
|
||||
|
||||
[Slimbook][1] 是一个销售 [预装 Linux 的笔记本电脑][2] 的西班牙电脑制造商,他们发布了一款好用的小程序,用来在基于 Ubuntu 的 Linux 发行版下调整电池性能。
|
||||
|
||||
因为 Slimbook 销售他们自己的 Linux 系统,所以他们制作了一些在 Linux 上用于调整他们自己硬件性能的小工具。Battery Optimizer 就是这样一个工具。
|
||||
|
||||
要使用这个实用小程序,你不必购买 Slimbook 的产品,因为 Slimbook 已经将它在 [他们的官方 PPA 源][3] 发行了。
|
||||
|
||||
### Slimbook Battery Optimizer 简介
|
||||
|
||||
这个程序叫 Slimbook Battery。它是一个常驻顶栏的指示器小程序,使得你可以快速切换电源模式。
|
||||
|
||||
![Slimbook Battery Mode Ubuntu][4]
|
||||
|
||||
你可能在 Windows 中见过类似的程序。Slimbook Battery 和它们一样,提供了类似的电源计划:
|
||||
|
||||
* 节能:最大程度延长电池续航时间
|
||||
* 平衡:性能与节能间的最佳平衡
|
||||
* 高性能:最大程度提高性能
|
||||
|
||||
你可以在高级模式中配置这些模式:
|
||||
|
||||
![配置多种多样的电源模式][5]
|
||||
|
||||
如果你觉得你把设置调乱了,你可以用“恢复默认设置”的按钮还原它。
|
||||
|
||||
你也可以修改像程序自启或默认电源模式这样的通用设置。
|
||||
|
||||
![Slimbook Battery 通用设置][6]
|
||||
|
||||
Slimbook 有专门为多种电源管理参数提供的页面。如果你希望自己配置,请参照 [此页][7]。
|
||||
|
||||
不过,我认为 Slimbook 的界面需要一些改进。例如,某些页面上的“问题标记”的图标应该改为可点击的,以此提供更多信息。然而,在我写这篇文章时,那个标记仍然无法点击。
|
||||
|
||||
总的来说,Slimbook Battery 是一个小巧精美的软件,你可以用它来快速切换电源模式。如果你决定在 Ubuntu 及其衍生发行版上(比如 Linux Mint 或 elementary OS 等),你可以使用官方 [PPA 源][8]。
|
||||
|
||||
#### 在基于 Ubuntu 的发行版上安装 Slimbook Battery
|
||||
|
||||
打开终端,一步一步地使用以下命令:
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:slimbook/slimbook
|
||||
sudo apt update
|
||||
sudo apt install slimbookbattery
|
||||
```
|
||||
|
||||
安装好之后,在菜单中搜索 Slimbook Battery:
|
||||
|
||||
![启动 Slimbook Battery Optimizer][10]
|
||||
|
||||
在你点击它之后,你会发现它出现在了顶栏。你可以在这里选择你希望使用的电源模式。
|
||||
|
||||
![Slimbook Battery 电源模式][4]
|
||||
|
||||
#### 卸载 Slimbook Battery
|
||||
|
||||
如果你不再使用它,你可以通过以下命令来卸载它:
|
||||
|
||||
```
|
||||
sudo apt remove slimbookbattery
|
||||
sudo add-apt-repository -r ppa:slimbook/slimbook
|
||||
```
|
||||
|
||||
在我看来,这样的应用程序为某些特定的目的服务,这是值得鼓励的。这个工具给了你一条调整电源模式的捷径,和调整性能的更多选项。
|
||||
|
||||
你用过 Slimbook Battery 吗?你觉得它如何?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/slimbook-battry-optimizer-ubuntu/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[zhs852](https://github.com/zhs852)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://slimbook.es/en/
|
||||
[2]: https://itsfoss.com/get-linux-laptops/
|
||||
[3]: https://launchpad.net/~slimbook/+archive/ubuntu/slimbook
|
||||
[4]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/slimbook-battery-mode-ubuntu.jpg?resize=800%2C400&ssl=1
|
||||
[5]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/slimbook-battery-optimizer-2.jpg?ssl=1
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/slimbook-battery-optimizer-1.jpg?ssl=1
|
||||
[7]: https://slimbook.es/en/tutoriales/aplicaciones-slimbook/398-slimbook-battery-3-application-for-optimize-battery-of-your-laptop
|
||||
[8]: https://itsfoss.com/ppa-guide/
|
||||
[9]: https://itsfoss.com/ubuntu-forums-hacked-again/
|
||||
[10]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/slimbook-battery-optimizer.jpg?ssl=1
|
@ -0,0 +1,118 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10894-1.html)
|
||||
[#]: subject: (PiShrink – Make Raspberry Pi Images Smaller)
|
||||
[#]: via: (https://www.ostechnix.com/pishrink-make-raspberry-pi-images-smaller/)
|
||||
[#]: author: (sk https://www.ostechnix.com/author/sk/)
|
||||
|
||||
PiShrink:使树莓派镜像更小
|
||||
======
|
||||
|
||||
![Make Raspberry Pi Images Smaller With PiShrink In Linux][1]
|
||||
|
||||
树莓派不需要过多介绍。它是一款小巧、价格实惠,只有信用卡大小的电脑,它可以连接到显示器或电视。我们可以连接一个标准的键盘和鼠标,并将其用作一台成熟的台式计算机来完成日常任务,如互联网浏览、播放视频/玩游戏、文字处理和电子表格制作等。它主要是为学校的计算机科学教学而开发的。如今,树莓派被广泛用于大学、中小型组织和研究所来教授编码。
|
||||
|
||||
如果你有一台树莓派,你可能需要了解一个名为 PiShrink 的 bash 脚本,该脚本可使树莓派镜像更小。PiShrink 将自动缩小镜像,然后在启动时将其调整为 SD 卡的最大大小。这能更快地将镜像复制到 SD 卡中,同时缩小的镜像将更好地压缩。这对于将大容量镜像放入 SD 卡非常有用。在这个简短的指南中,我们将学习如何在类 Unix 系统中将树莓派镜像缩小到更小。
|
||||
|
||||
### 安装 PiShrink
|
||||
|
||||
要在 Linux 机器上安装 PiShrink,请先使用以下命令下载最新版本:
|
||||
|
||||
```
|
||||
$ wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
|
||||
```
|
||||
|
||||
接下来,将下载的 PiShrink 变成二进制可执行文件:
|
||||
|
||||
```
|
||||
$ chmod +x pishrink.sh
|
||||
```
|
||||
|
||||
最后,移动到目录:
|
||||
|
||||
```
|
||||
$ sudo mv pishrink.sh /usr/local/bin/
|
||||
```
|
||||
|
||||
### 使树莓派镜像更小
|
||||
|
||||
你可能已经知道,Raspbian 是所有树莓派型号的官方操作系统。树莓派基金会为 PC 和 Mac 开发了树莓派桌面版本。你可以创建一个 live CD,并在虚拟机中运行它,甚至也可以将其安装在桌面上。树莓派也有少量非官方操作系统镜像。为了测试,我从[官方下载页面][2]下载了官方的 Raspbian 系统。
|
||||
|
||||
解压下载的系统镜像:
|
||||
|
||||
```
|
||||
$ unzip 2019-04-08-raspbian-stretch-lite.zip
|
||||
```
|
||||
|
||||
上面的命令将提取当前目录中 `2019-04-08-raspbian-stretch-lite.zip` 文件的内容。
|
||||
|
||||
让我们看下提取文件的实际大小:
|
||||
|
||||
```
|
||||
$ du -h 2019-04-08-raspbian-stretch-lite.img
|
||||
1.7G 2019-04-08-raspbian-stretch-lite.img
|
||||
```
|
||||
|
||||
如你所见,提取的树莓派系统镜像大小为 1.7G。
|
||||
|
||||
现在,使用 PiShrink 缩小此文件的大小,如下所示:
|
||||
|
||||
```
|
||||
$ sudo pishrink.sh 2019-04-08-raspbian-stretch-lite.img
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```
|
||||
Creating new /etc/rc.local
|
||||
rootfs: 39795/107072 files (0.1% non-contiguous), 239386/428032 blocks
|
||||
resize2fs 1.45.0 (6-Mar-2019)
|
||||
resize2fs 1.45.0 (6-Mar-2019)
|
||||
Resizing the filesystem on /dev/loop1 to 280763 (4k) blocks.
|
||||
Begin pass 3 (max = 14)
|
||||
Scanning inode table XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
Begin pass 4 (max = 3728)
|
||||
Updating inode references XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
The filesystem on /dev/loop1 is now 280763 (4k) blocks long.
|
||||
|
||||
Shrunk 2019-04-08-raspbian-stretch-lite.img from 1.7G to 1.2G
|
||||
```
|
||||
|
||||
正如你在上面的输出中看到的,树莓派镜像的大小已减少到 1.2G。
|
||||
|
||||
你还可以使用 `-s` 标志跳过该过程的自动扩展部分。
|
||||
|
||||
```
|
||||
$ sudo pishrink.sh -s 2019-04-08-raspbian-stretch-lite.img newpi.img
|
||||
```
|
||||
|
||||
这将创建一个源镜像文件(即 `2019-04-08-raspbian-stretch-lite.img`)的副本到一个新镜像文件(`newpi.img`)并进行处理。有关更多详细信息,请查看最后给出的官方 GitHub 页面。
|
||||
|
||||
就是这些了。希望本文有用。还有更多好东西,敬请期待!
|
||||
|
||||
|
||||
资源:
|
||||
|
||||
* [PiShrink 的 GitHub 仓库][4]
|
||||
* [树莓派网站][5]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/pishrink-make-raspberry-pi-images-smaller/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/05/pishrink-720x340.png
|
||||
[2]: https://www.raspberrypi.org/downloads/
|
||||
[3]: http://www.ostechnix.com/wp-content/uploads/2019/05/pishrink-1.png
|
||||
[4]: https://github.com/Drewsif/PiShrink
|
||||
[5]: https://www.raspberrypi.org/
|
@ -0,0 +1,396 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10889-1.html)
|
||||
[#]: subject: (xsos – A Tool To Read SOSReport In Linux)
|
||||
[#]: via: (https://www.2daygeek.com/xsos-a-tool-to-read-sosreport-in-linux/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
xsos:一个在 Linux 上阅读 SOSReport 的工具
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/23/133305accwpsvhk1epsisc.jpg)
|
||||
|
||||
我们都已经知道 [SOSReport][1]。它用来收集可用于诊断的系统信息。Redhat 的支持服务建议我们在提交案例时提供 SOSReport 来分析当前的系统状态。
|
||||
|
||||
它会收集全部类型的报告,以帮助用户找出问题的根本原因。我们可以轻松地提取和阅读 SOSReport,但它很难阅读。因为它的每个部分都是一个单独的文件。
|
||||
|
||||
那么,在 Linux 中使用语法高亮显示阅读所有这些内容的最佳方法是什么。是的,这可以通过 `xsos` 工具做到。
|
||||
|
||||
### sosreport
|
||||
|
||||
`sosreport` 命令是一个从运行中的系统(尤其是 RHEL 和 OEL 系统)收集大量配置细节、系统信息和诊断信息的工具。它可以帮助技术支持工程师在很多方面分析系统。
|
||||
|
||||
此报告包含有关系统的大量信息,例如引导信息、文件系统、内存、主机名、已安装的 RPM、系统 IP、网络详细信息、操作系统版本、已安装的内核、已加载的内核模块、打开的文件列表、PCI 设备列表、挂载点及其细节、运行中的进程信息、进程树输出、系统路由、位于 `/etc` 文件夹中的所有配置文件,以及位于 `/var` 文件夹中的所有日志文件。
|
||||
|
||||
这将需要一段时间来生成报告,这取决于你的系统安装和配置。
|
||||
|
||||
完成后,`sosreport` 将在 `/tmp` 目录下生成一个压缩的归档文件。
|
||||
|
||||
### xsos
|
||||
|
||||
[xsos][3] 是一个帮助用户轻松读取 Linux 系统上的 `sosreport` 的工具。另一方面,我们可以说它是 `sosreport` 考官。
|
||||
|
||||
它可以立即从 `sosreport` 或正在运行的系统中汇总系统信息。
|
||||
|
||||
`xsos` 将尝试简化、解析、计算并格式化来自数十个文件(和命令)的数据,以便为你提供有关系统的详细概述。
|
||||
|
||||
你可以通过运行以下命令立即汇总系统信息。
|
||||
|
||||
```
|
||||
# curl -Lo ./xsos bit.ly/xsos-direct; chmod +x ./xsos; ./xsos -ya
|
||||
```
|
||||
|
||||
![][5]
|
||||
|
||||
### 如何在 Linux 上安装 xsos
|
||||
|
||||
我们可以使用以下两种方法轻松安装 `xsos`。
|
||||
|
||||
如果你正在寻找最新的前沿版本。使用以下步骤:
|
||||
|
||||
```
|
||||
# curl -Lo /usr/local/bin/xsos bit.ly/xsos-direct
|
||||
# chmod +x /usr/local/bin/xsos
|
||||
```
|
||||
|
||||
下面是安装 `xsos` 的推荐方法。它将从 rpm 文件安装 `xsos`。
|
||||
|
||||
```
|
||||
# yum install http://people.redhat.com/rsawhill/rpms/latest-rsawaroha-release.rpm
|
||||
# yum install xsos
|
||||
```
|
||||
|
||||
### 如何在 Linux 上使用 xsos
|
||||
|
||||
一旦通过上述方法之一安装了 xsos。只需运行 `xsos` 命令,不带任何选项,它们会显示有关系统的基本信息。
|
||||
|
||||
```
|
||||
# xsos
|
||||
|
||||
OS
|
||||
Hostname: CentOS7.2daygeek.com
|
||||
Distro: [redhat-release] CentOS Linux release 7.6.1810 (Core)
|
||||
[centos-release] CentOS Linux release 7.6.1810 (Core)
|
||||
[os-release] CentOS Linux 7 (Core) 7 (Core)
|
||||
RHN: (missing)
|
||||
RHSM: (missing)
|
||||
YUM: 2 enabled plugins: fastestmirror, langpacks
|
||||
Runlevel: N 5 (default graphical)
|
||||
SELinux: enforcing (default enforcing)
|
||||
Arch: mach=x86_64 cpu=x86_64 platform=x86_64
|
||||
Kernel:
|
||||
Booted kernel: 3.10.0-957.el7.x86_64
|
||||
GRUB default: 3.10.0-957.el7.x86_64
|
||||
Build version:
|
||||
Linux version 3.10.0-957.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red
|
||||
Hat 4.8.5-36) (GCC) ) #1 SMP Thu Nov 8 23:39:32 UTC 2018
|
||||
Booted kernel cmdline:
|
||||
root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
|
||||
LANG=en_US.UTF-8
|
||||
GRUB default kernel cmdline:
|
||||
root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
|
||||
LANG=en_US.UTF-8
|
||||
Taint-check: 0 (kernel untainted)
|
||||
- - - - - - - - - - - - - - - - - - -
|
||||
Sys time: Sun May 12 10:05:21 CDT 2019
|
||||
Boot time: Sun May 12 09:50:20 CDT 2019 (epoch: 1557672620)
|
||||
Time Zone: America/Chicago
|
||||
Uptime: 15 min, 1 user
|
||||
LoadAvg: [1 CPU] 0.00 (0%), 0.04 (4%), 0.09 (9%)
|
||||
/proc/stat:
|
||||
procs_running: 2 procs_blocked: 0 processes [Since boot]: 6423
|
||||
cpu [Utilization since boot]:
|
||||
us 1%, ni 0%, sys 1%, idle 99%, iowait 0%, irq 0%, sftirq 0%, steal 0%
|
||||
```
|
||||
|
||||
### 如何使用 xsos 命令在 Linux 中查看生成的 SOSReport 输出?
|
||||
|
||||
我们需要份 SOSReport 以使用 `xsos` 命令进一步阅读。
|
||||
|
||||
是的,我已经生成了一个 SOSReport,文件如下。
|
||||
|
||||
```
|
||||
# ls -lls -lh /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa.tar.xz
|
||||
9.8M -rw-------. 1 root root 9.8M May 12 10:13 /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa.tar.xz
|
||||
```
|
||||
|
||||
运行如下命令解开它。
|
||||
|
||||
```
|
||||
# tar xf sosreport-CentOS7-01-1005-2019-05-12-pomeqsa.tar.xz
|
||||
```
|
||||
|
||||
要查看全部信息,带上 `-a` 或 `--all` 开关运行 `xsos`:
|
||||
|
||||
```
|
||||
# xsos --all /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
```
|
||||
|
||||
要查看 BIOS 信息,带上 `-b` 或 `--bios` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --bios /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
DMIDECODE
|
||||
BIOS:
|
||||
Vend: innotek GmbH
|
||||
Vers: VirtualBox
|
||||
Date: 12/01/2006
|
||||
BIOS Rev:
|
||||
FW Rev:
|
||||
System:
|
||||
Mfr: innotek GmbH
|
||||
Prod: VirtualBox
|
||||
Vers: 1.2
|
||||
Ser: 0
|
||||
UUID: 002f47b8-2af2-48f5-be1d-67b67e03514c
|
||||
CPU:
|
||||
0 of 0 CPU sockets populated, 0 cores/0 threads per CPU
|
||||
0 total cores, 0 total threads
|
||||
Mfr:
|
||||
Fam:
|
||||
Freq:
|
||||
Vers:
|
||||
Memory:
|
||||
Total: 0 MiB (0 GiB)
|
||||
DIMMs: 0 of 0 populated
|
||||
MaxCapacity: 0 MiB (0 GiB / 0.00 TiB)
|
||||
```
|
||||
|
||||
要查看系统基本信息,如主机名、发行版、SELinux、内核信息、正常运行时间等,请使用 `-o` 或 `--os` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --os /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
OS
|
||||
Hostname: CentOS7.2daygeek.com
|
||||
Distro: [redhat-release] CentOS Linux release 7.6.1810 (Core)
|
||||
[centos-release] CentOS Linux release 7.6.1810 (Core)
|
||||
[os-release] CentOS Linux 7 (Core) 7 (Core)
|
||||
RHN: (missing)
|
||||
RHSM: (missing)
|
||||
YUM: 2 enabled plugins: fastestmirror, langpacks
|
||||
SELinux: enforcing (default enforcing)
|
||||
Arch: mach=x86_64 cpu=x86_64 platform=x86_64
|
||||
Kernel:
|
||||
Booted kernel: 3.10.0-957.el7.x86_64
|
||||
GRUB default: 3.10.0-957.el7.x86_64
|
||||
Build version:
|
||||
Linux version 3.10.0-957.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red
|
||||
Hat 4.8.5-36) (GCC) ) #1 SMP Thu Nov 8 23:39:32 UTC 2018
|
||||
Booted kernel cmdline:
|
||||
root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
|
||||
LANG=en_US.UTF-8
|
||||
GRUB default kernel cmdline:
|
||||
root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
|
||||
LANG=en_US.UTF-8
|
||||
Taint-check: 536870912 (see https://access.redhat.com/solutions/40594)
|
||||
29 TECH_PREVIEW: Technology Preview code is loaded
|
||||
- - - - - - - - - - - - - - - - - - -
|
||||
Sys time: Sun May 12 10:12:22 CDT 2019
|
||||
Boot time: Sun May 12 09:50:20 CDT 2019 (epoch: 1557672620)
|
||||
Time Zone: America/Chicago
|
||||
Uptime: 22 min, 1 user
|
||||
LoadAvg: [1 CPU] 1.19 (119%), 0.27 (27%), 0.14 (14%)
|
||||
/proc/stat:
|
||||
procs_running: 8 procs_blocked: 2 processes [Since boot]: 9005
|
||||
cpu [Utilization since boot]:
|
||||
us 1%, ni 0%, sys 1%, idle 99%, iowait 0%, irq 0%, sftirq 0%, steal 0%
|
||||
```
|
||||
|
||||
要查看 kdump 配置,请使用 `-k` 或 `--kdump` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --kdump /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
KDUMP CONFIG
|
||||
kexec-tools rpm version:
|
||||
kexec-tools-2.0.15-21.el7.x86_64
|
||||
Service enablement:
|
||||
UNIT STATE
|
||||
kdump.service enabled
|
||||
kdump initrd/initramfs:
|
||||
13585734 Feb 19 05:51 initramfs-3.10.0-957.el7.x86_64kdump.img
|
||||
Memory reservation config:
|
||||
/proc/cmdline { crashkernel=auto }
|
||||
GRUB default { crashkernel=auto }
|
||||
Actual memory reservation per /proc/iomem:
|
||||
2a000000-340fffff : Crash kernel
|
||||
kdump.conf:
|
||||
path /var/crash
|
||||
core_collector makedumpfile -l --message-level 1 -d 31
|
||||
kdump.conf "path" available space:
|
||||
System MemTotal (uncompressed core size) { 1.80 GiB }
|
||||
Available free space on target path's fs { 22.68 GiB } (fs=/)
|
||||
Panic sysctls:
|
||||
kernel.sysrq [bitmask] = "16" (see proc man page)
|
||||
kernel.panic [secs] = 0 (no autoreboot on panic)
|
||||
kernel.hung_task_panic = 0
|
||||
kernel.panic_on_oops = 1
|
||||
kernel.panic_on_io_nmi = 0
|
||||
kernel.panic_on_unrecovered_nmi = 0
|
||||
kernel.panic_on_stackoverflow = 0
|
||||
kernel.softlockup_panic = 0
|
||||
kernel.unknown_nmi_panic = 0
|
||||
kernel.nmi_watchdog = 1
|
||||
vm.panic_on_oom [0-2] = 0 (no panic)
|
||||
```
|
||||
|
||||
要查看有关 CPU 的信息,请使用 `-c` 或 `--cpu` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --cpu /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
CPU
|
||||
1 logical processors
|
||||
1 Intel Core i7-6700HQ CPU @ 2.60GHz (flags: aes,constant_tsc,ht,lm,nx,pae,rdrand)
|
||||
```
|
||||
|
||||
要查看内存利用情况,请使用 `-m` 或 `--mem` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --mem /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
MEMORY
|
||||
Stats graphed as percent of MemTotal:
|
||||
MemUsed ▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊..................... 58.8%
|
||||
Buffers .................................................. 0.6%
|
||||
Cached ▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊................................... 29.9%
|
||||
HugePages .................................................. 0.0%
|
||||
Dirty .................................................. 0.7%
|
||||
RAM:
|
||||
1.8 GiB total ram
|
||||
1.1 GiB (59%) used
|
||||
0.5 GiB (28%) used excluding Buffers/Cached
|
||||
0.01 GiB (1%) dirty
|
||||
HugePages:
|
||||
No ram pre-allocated to HugePages
|
||||
LowMem/Slab/PageTables/Shmem:
|
||||
0.09 GiB (5%) of total ram used for Slab
|
||||
0.02 GiB (1%) of total ram used for PageTables
|
||||
0.01 GiB (1%) of total ram used for Shmem
|
||||
Swap:
|
||||
0 GiB (0%) used of 2 GiB total
|
||||
```
|
||||
|
||||
要查看添加的磁盘信息,请使用 `-d` 和 `-disks` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --disks /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
STORAGE
|
||||
Whole Disks from /proc/partitions:
|
||||
2 disks, totaling 40 GiB (0.04 TiB)
|
||||
- - - - - - - - - - - - - - - - - - - - -
|
||||
Disk Size in GiB
|
||||
---- -----------
|
||||
sda 30
|
||||
sdb 10
|
||||
```
|
||||
|
||||
要查看网络接口配置,请使用 `-e` 或 `--ethtool` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --ethtool /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
ETHTOOL
|
||||
Interface Status:
|
||||
enp0s10 0000:00:0a.0 link=up 1000Mb/s full (autoneg=Y) rx ring 256/4096 drv e1000 v7.3.21-k8-NAPI / fw UNKNOWN
|
||||
enp0s9 0000:00:09.0 link=up 1000Mb/s full (autoneg=Y) rx ring 256/4096 drv e1000 v7.3.21-k8-NAPI / fw UNKNOWN
|
||||
virbr0 N/A link=DOWN rx ring UNKNOWN drv bridge v2.3 / fw N/A
|
||||
virbr0-nic tap link=DOWN rx ring UNKNOWN drv tun v1.6 / fw UNKNOWN
|
||||
```
|
||||
|
||||
要查看有关 IP 地址的信息,请使用 `-i` 或 `--ip` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --ip /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
IP4
|
||||
Interface Master IF MAC Address MTU State IPv4 Address
|
||||
========= ========= ================= ====== ===== ==================
|
||||
lo - - 65536 up 127.0.0.1/8
|
||||
enp0s9 - 08:00:27:0b:bc:e9 1500 up 192.168.1.8/24
|
||||
enp0s10 - 08:00:27:b2:08:91 1500 up 192.168.1.9/24
|
||||
virbr0 - 52:54:00:ae:01:94 1500 up 192.168.122.1/24
|
||||
virbr0-nic virbr0 52:54:00:ae:01:94 1500 DOWN -
|
||||
|
||||
IP6
|
||||
Interface Master IF MAC Address MTU State IPv6 Address Scope
|
||||
========= ========= ================= ====== ===== =========================================== =====
|
||||
lo - - 65536 up ::1/128 host
|
||||
enp0s9 - 08:00:27:0b:bc:e9 1500 up fe80::945b:8333:f4bc:9723/64 link
|
||||
enp0s10 - 08:00:27:b2:08:91 1500 up fe80::7ed4:1fab:23c3:3790/64 link
|
||||
virbr0 - 52:54:00:ae:01:94 1500 up - -
|
||||
virbr0-nic virbr0 52:54:00:ae:01:94 1500 DOWN - -
|
||||
```
|
||||
|
||||
要通过 `ps` 查看正在运行的进程,请使用 `-p` 或 `--ps` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --ps /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
PS CHECK
|
||||
Total number of threads/processes:
|
||||
501 / 171
|
||||
Top users of CPU & MEM:
|
||||
USER %CPU %MEM RSS
|
||||
root 20.6% 14.1% 0.30 GiB
|
||||
gdm 0.3% 16.8% 0.33 GiB
|
||||
postfix 0.0% 0.6% 0.01 GiB
|
||||
polkitd 0.0% 0.6% 0.01 GiB
|
||||
daygeek 0.0% 0.2% 0.00 GiB
|
||||
colord 0.0% 0.4% 0.01 GiB
|
||||
Uninteruptible sleep threads/processes (0/0):
|
||||
[None]
|
||||
Defunct zombie threads/processes (0/0):
|
||||
[None]
|
||||
Top CPU-using processes:
|
||||
USER PID %CPU %MEM VSZ-MiB RSS-MiB TTY STAT START TIME COMMAND
|
||||
root 6542 15.6 4.2 875 78 pts/0 Sl+ 10:11 0:07 /usr/bin/python /sbin/sosreport
|
||||
root 7582 3.0 0.1 10 2 pts/0 S 10:12 0:00 /bin/bash /usr/sbin/dracut --print-cmdline
|
||||
root 7969 0.7 0.1 95 4 ? Ss 10:12 0:00 /usr/sbin/certmonger -S -p
|
||||
root 7889 0.4 0.2 24 4 ? Ss 10:12 0:00 /usr/lib/systemd/systemd-hostnamed
|
||||
gdm 3866 0.3 7.1 2856 131 ? Sl 09:50 0:04 /usr/bin/gnome-shell
|
||||
root 8553 0.2 0.1 47 3 ? S 10:12 0:00 /usr/lib/systemd/systemd-udevd
|
||||
root 6971 0.2 0.4 342 9 ? Sl 10:12 0:00 /usr/sbin/abrt-dbus -t133
|
||||
root 3200 0.2 0.9 982 18 ? Ssl 09:50 0:02 /usr/sbin/libvirtd
|
||||
root 2855 0.1 0.1 88 3 ? Ss 09:50 0:01 /sbin/rngd -f
|
||||
rtkit 2826 0.0 0.0 194 2 ? SNsl 09:50 0:00 /usr/libexec/rtkit-daemon
|
||||
Top MEM-using processes:
|
||||
USER PID %CPU %MEM VSZ-MiB RSS-MiB TTY STAT START TIME COMMAND
|
||||
gdm 3866 0.3 7.1 2856 131 ? Sl 09:50 0:04 /usr/bin/gnome-shell
|
||||
root 6542 15.6 4.2 875 78 pts/0 Sl+ 10:11 0:07 /usr/bin/python /sbin/sosreport
|
||||
root 3264 0.0 1.2 271 23 tty1 Ssl+ 09:50 0:00 /usr/bin/X :0 -background
|
||||
root 3200 0.2 0.9 982 18 ? Ssl 09:50 0:02 /usr/sbin/libvirtd
|
||||
root 3189 0.0 0.9 560 17 ? Ssl 09:50 0:00 /usr/bin/python2 -Es /usr/sbin/tuned
|
||||
gdm 4072 0.0 0.9 988 17 ? Sl 09:50 0:00 /usr/libexec/gsd-media-keys
|
||||
gdm 4076 0.0 0.8 625 16 ? Sl 09:50 0:00 /usr/libexec/gsd-power
|
||||
gdm 4056 0.0 0.8 697 16 ? Sl 09:50 0:00 /usr/libexec/gsd-color
|
||||
root 2853 0.0 0.7 622 14 ? Ssl 09:50 0:00 /usr/sbin/NetworkManager --no-daemon
|
||||
gdm 4110 0.0 0.7 544 14 ? Sl 09:50 0:00 /usr/libexec/gsd-wacom
|
||||
Top thread-spawning processes:
|
||||
# USER PID %CPU %MEM VSZ-MiB RSS-MiB TTY STAT START TIME COMMAND
|
||||
17 root 3200 0.2 0.9 982 18 ? - 09:50 0:02 /usr/sbin/libvirtd
|
||||
12 root 6542 16.1 4.5 876 83 pts/0 - 10:11 0:07 /usr/bin/python /sbin/sosreport
|
||||
10 gdm 3866 0.3 7.1 2856 131 ? - 09:50 0:04 /usr/bin/gnome-shell
|
||||
7 polkitd 2864 0.0 0.6 602 13 ? - 09:50 0:01 /usr/lib/polkit-1/polkitd --no-debug
|
||||
6 root 2865 0.0 0.0 203 1 ? - 09:50 0:00 /usr/sbin/gssproxy -D
|
||||
5 root 3189 0.0 0.9 560 17 ? - 09:50 0:00 /usr/bin/python2 -Es /usr/sbin/tuned
|
||||
5 root 2823 0.0 0.3 443 6 ? - 09:50 0:00 /usr/libexec/udisks2/udisksd
|
||||
5 gdm 4102 0.0 0.2 461 5 ? - 09:50 0:00 /usr/libexec/gsd-smartcard
|
||||
4 root 3215 0.0 0.2 470 4 ? - 09:50 0:00 /usr/sbin/gdm
|
||||
4 gdm 4106 0.0 0.2 444 5 ? - 09:50 0:00 /usr/libexec/gsd-sound
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/xsos-a-tool-to-read-sosreport-in-linux/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/how-to-create-collect-sosreport-in-linux/
|
||||
[2]: https://www.2daygeek.com/oswbb-how-to-install-and-configure-oswatcher-black-box-for-system-diagnostics/
|
||||
[3]: https://github.com/ryran/xsos
|
||||
[4]: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[5]: https://www.2daygeek.com/wp-content/uploads/2019/05/xsos-a-tool-to-read-sosreport-in-linux-1.jpg
|
40
published/20190524 Spell Checking Comments.md
Normal file
40
published/20190524 Spell Checking Comments.md
Normal file
@ -0,0 +1,40 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lujun9972)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11294-1.html)
|
||||
[#]: subject: (Spell Checking Comments)
|
||||
[#]: via: (https://emacsredux.com/blog/2019/05/24/spell-checking-comments/)
|
||||
[#]: author: (Bozhidar Batsov https://emacsredux.com)
|
||||
|
||||
Emacs 注释中的拼写检查
|
||||
======
|
||||
|
||||
我出了名的容易拼错单词(特别是在播客当中)。谢天谢地 Emacs 内置了一个名为 `flyspell` 的超棒模式来帮助像我这样的可怜的打字员。flyspell 会在你输入时突出显示拼错的单词 (也就是实时的) 并提供有用的快捷键来快速修复该错误。
|
||||
|
||||
大多输入通常会对派生自 `text-mode`(比如 `markdown-mode`,`adoc-mode` )的主模式启用 `flyspell`,但是它对程序员也有所帮助,可以指出他在注释中的错误。所需要的只是启用 `flyspell-prog-mode`。我通常在所有的编程模式中(至少在 `prog-mode` 派生的模式中)都启用它:
|
||||
|
||||
```
|
||||
(add-hook 'prog-mode-hook #'flyspell-prog-mode)
|
||||
```
|
||||
|
||||
现在当你在注释中输入错误时,就会得到即时反馈了。要修复单词只需要将光标置于单词后,然后按下 `C-c $` (`M-x flyspell-correct-word-before-point`)。(还有许多其他方法可以用 `flyspell` 来纠正拼写错误的单词,但为了简单起见,我们暂时忽略它们。)
|
||||
|
||||
![flyspell_prog_mode.gif][1]
|
||||
|
||||
今天的分享就到这里!我要继续修正这些讨厌的拼写错误了!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://emacsredux.com/blog/2019/05/24/spell-checking-comments/
|
||||
|
||||
作者:[Bozhidar Batsov][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lujun9972](https://github.com/lujun9972)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://emacsredux.com
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://emacsredux.com/assets/images/flyspell_prog_mode.gif
|
@ -0,0 +1,484 @@
|
||||
从零写一个时间序列数据库
|
||||
==================
|
||||
|
||||
编者按:Prometheus 是 CNCF 旗下的开源监控告警解决方案,它已经成为 Kubernetes 生态圈中的核心监控系统。本文作者 Fabian Reinartz 是 Prometheus 的核心开发者,这篇文章是其于 2017 年写的一篇关于 Prometheus 中的时间序列数据库的设计思考,虽然写作时间有点久了,但是其中的考虑和思路非常值得参考。长文预警,请坐下来慢慢品味。
|
||||
|
||||
---
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/11/180646l7cqbhazqs7nsqsn.jpg)
|
||||
|
||||
我从事监控工作。特别是在 [Prometheus][2] 上,监控系统包含一个自定义的时间序列数据库,并且集成在 [Kubernetes][3] 上。
|
||||
|
||||
在许多方面上 Kubernetes 展现出了 Prometheus 所有的设计用途。它使得<ruby>持续部署<rt>continuous deployments</rt></ruby>,<ruby>弹性伸缩<rt>auto scaling</rt></ruby>和其他<ruby>高动态环境<rt>highly dynamic environments</rt></ruby>下的功能可以轻易地访问。查询语句和操作模型以及其它概念决策使得 Prometheus 特别适合这种环境。但是,如果监控的工作负载动态程度显著地增加,这就会给监控系统本身带来新的压力。考虑到这一点,我们就可以特别致力于在高动态或<ruby>瞬态服务<rt>transient services</rt></ruby>环境下提升它的表现,而不是回过头来解决 Prometheus 已经解决的很好的问题。
|
||||
|
||||
Prometheus 的存储层在历史以来都展现出卓越的性能,单一服务器就能够以每秒数百万个时间序列的速度摄入多达一百万个样本,同时只占用了很少的磁盘空间。尽管当前的存储做的很好,但我依旧提出一个新设计的存储子系统,它可以修正现存解决方案的缺点,并具备处理更大规模数据的能力。
|
||||
|
||||
> 备注:我没有数据库方面的背景。我说的东西可能是错的并让你误入歧途。你可以在 Freenode 的 #prometheus 频道上对我(fabxc)提出你的批评。
|
||||
|
||||
## 问题,难题,问题域
|
||||
|
||||
首先,快速地概览一下我们要完成的东西和它的关键难题。我们可以先看一下 Prometheus 当前的做法 ,它为什么做的这么好,以及我们打算用新设计解决哪些问题。
|
||||
|
||||
### 时间序列数据
|
||||
|
||||
我们有一个收集一段时间数据的系统。
|
||||
|
||||
```
|
||||
identifier -> (t0, v0), (t1, v1), (t2, v2), (t3, v3), ....
|
||||
```
|
||||
|
||||
每个数据点是一个时间戳和值的元组。在监控中,时间戳是一个整数,值可以是任意数字。64 位浮点数对于计数器和测量值来说是一个好的表示方法,因此我们将会使用它。一系列严格单调递增的时间戳数据点是一个序列,它由标识符所引用。我们的标识符是一个带有<ruby>标签维度<rt>label dimensions</rt></ruby>字典的度量名称。标签维度划分了单一指标的测量空间。每一个指标名称加上一个唯一标签集就成了它自己的时间序列,它有一个与之关联的<ruby>数据流<rt>value stream</rt></ruby>。
|
||||
|
||||
这是一个典型的<ruby>序列标识符<rt>series identifier</rt></ruby>集,它是统计请求指标的一部分:
|
||||
|
||||
```
|
||||
requests_total{path="/status", method="GET", instance=”10.0.0.1:80”}
|
||||
requests_total{path="/status", method="POST", instance=”10.0.0.3:80”}
|
||||
requests_total{path="/", method="GET", instance=”10.0.0.2:80”}
|
||||
```
|
||||
|
||||
让我们简化一下表示方法:度量名称可以当作另一个维度标签,在我们的例子中是 `__name__`。对于查询语句,可以对它进行特殊处理,但与我们存储的方式无关,我们后面也会见到。
|
||||
|
||||
```
|
||||
{__name__="requests_total", path="/status", method="GET", instance=”10.0.0.1:80”}
|
||||
{__name__="requests_total", path="/status", method="POST", instance=”10.0.0.3:80”}
|
||||
{__name__="requests_total", path="/", method="GET", instance=”10.0.0.2:80”}
|
||||
```
|
||||
|
||||
我们想通过标签来查询时间序列数据。在最简单的情况下,使用 `{__name__="requests_total"}` 选择所有属于 `requests_total` 指标的数据。对于所有选择的序列,我们在给定的时间窗口内获取数据点。
|
||||
|
||||
在更复杂的语句中,我们或许想一次性选择满足多个标签的序列,并且表示比相等条件更复杂的情况。例如,非语句(`method!="GET"`)或正则表达式匹配(`method=~"PUT|POST"`)。
|
||||
|
||||
这些在很大程度上定义了存储的数据和它的获取方式。
|
||||
|
||||
### 纵与横
|
||||
|
||||
在简化的视图中,所有的数据点可以分布在二维平面上。水平维度代表着时间,序列标识符域经纵轴展开。
|
||||
|
||||
```
|
||||
series
|
||||
^
|
||||
| . . . . . . . . . . . . . . . . . . . . . . {__name__="request_total", method="GET"}
|
||||
| . . . . . . . . . . . . . . . . . . . . . . {__name__="request_total", method="POST"}
|
||||
| . . . . . . .
|
||||
| . . . . . . . . . . . . . . . . . . . ...
|
||||
| . . . . . . . . . . . . . . . . . . . . .
|
||||
| . . . . . . . . . . . . . . . . . . . . . {__name__="errors_total", method="POST"}
|
||||
| . . . . . . . . . . . . . . . . . {__name__="errors_total", method="GET"}
|
||||
| . . . . . . . . . . . . . .
|
||||
| . . . . . . . . . . . . . . . . . . . ...
|
||||
| . . . . . . . . . . . . . . . . . . . .
|
||||
v
|
||||
<-------------------- time --------------------->
|
||||
```
|
||||
|
||||
Prometheus 通过定期地抓取一组时间序列的当前值来获取数据点。我们从中获取到的实体称为目标。因此,写入模式完全地垂直且高度并发,因为来自每个目标的样本是独立摄入的。
|
||||
|
||||
这里提供一些测量的规模:单一 Prometheus 实例从数万个目标中收集数据点,每个数据点都暴露在数百到数千个不同的时间序列中。
|
||||
|
||||
在每秒采集数百万数据点这种规模下,批量写入是一个不能妥协的性能要求。在磁盘上分散地写入单个数据点会相当地缓慢。因此,我们想要按顺序写入更大的数据块。
|
||||
|
||||
对于旋转式磁盘,它的磁头始终得在物理上向不同的扇区上移动,这是一个不足为奇的事实。而虽然我们都知道 SSD 具有快速随机写入的特点,但事实上它不能修改单个字节,只能写入一页或更多页的 4KiB 数据量。这就意味着写入 16 字节的样本相当于写入满满一个 4Kib 的页。这一行为就是所谓的[写入放大][4],这种特性会损耗你的 SSD。因此它不仅影响速度,而且还毫不夸张地在几天或几个周内破坏掉你的硬件。
|
||||
|
||||
关于此问题更深层次的资料,[“Coding for SSDs”系列][5]博客是极好的资源。让我们想想主要的用处:顺序写入和批量写入分别对于旋转式磁盘和 SSD 来说都是理想的写入模式。大道至简。
|
||||
|
||||
查询模式比起写入模式明显更不同。我们可以查询单一序列的一个数据点,也可以对 10000 个序列查询一个数据点,还可以查询一个序列几个周的数据点,甚至是 10000 个序列几个周的数据点。因此在我们的二维平面上,查询范围不是完全水平或垂直的,而是二者形成矩形似的组合。
|
||||
|
||||
[记录规则][6]可以减轻已知查询的问题,但对于<ruby>点对点<rt>ad-hoc</rt></ruby>查询来说并不是一个通用的解决方法。
|
||||
|
||||
我们知道我们想要批量地写入,但我们得到的仅仅是一系列垂直数据点的集合。当查询一段时间窗口内的数据点时,我们不仅很难弄清楚在哪才能找到这些单独的点,而且不得不从磁盘上大量随机的地方读取。也许一条查询语句会有数百万的样本,即使在最快的 SSD 上也会很慢。读入也会从磁盘上获取更多的数据而不仅仅是 16 字节的样本。SSD 会加载一整页,HDD 至少会读取整个扇区。不论哪一种,我们都在浪费宝贵的读取吞吐量。
|
||||
|
||||
因此在理想情况下,同一序列的样本将按顺序存储,这样我们就能通过尽可能少的读取来扫描它们。最重要的是,我们仅需要知道序列的起始位置就能访问所有的数据点。
|
||||
|
||||
显然,将收集到的数据写入磁盘的理想模式与能够显著提高查询效率的布局之间存在着明显的抵触。这是我们 TSDB 需要解决的一个基本问题。
|
||||
|
||||
#### 当前的解决方法
|
||||
|
||||
是时候看一下当前 Prometheus 是如何存储数据来解决这一问题的,让我们称它为“V2”。
|
||||
|
||||
我们创建一个时间序列的文件,它包含所有样本并按顺序存储。因为每几秒附加一个样本数据到所有文件中非常昂贵,我们在内存中打包 1Kib 样本序列的数据块,一旦打包完成就附加这些数据块到单独的文件中。这一方法解决了大部分问题。写入目前是批量的,样本也是按顺序存储的。基于给定的同一序列的样本相对之前的数据仅发生非常小的改变这一特性,它还支持非常高效的压缩格式。Facebook 在他们 Gorilla TSDB 上的论文中描述了一个相似的基于数据块的方法,并且[引入了一种压缩格式][7],它能够减少 16 字节的样本到平均 1.37 字节。V2 存储使用了包含 Gorilla 变体等在内的各种压缩格式。
|
||||
|
||||
```
|
||||
+----------+---------+---------+---------+---------+ series A
|
||||
+----------+---------+---------+---------+---------+
|
||||
+----------+---------+---------+---------+---------+ series B
|
||||
+----------+---------+---------+---------+---------+
|
||||
. . .
|
||||
+----------+---------+---------+---------+---------+---------+ series XYZ
|
||||
+----------+---------+---------+---------+---------+---------+
|
||||
chunk 1 chunk 2 chunk 3 ...
|
||||
```
|
||||
|
||||
尽管基于块存储的方法非常棒,但为每个序列保存一个独立的文件会给 V2 存储带来麻烦,因为:
|
||||
|
||||
* 实际上,我们需要的文件比当前收集数据的时间序列数量要多得多。多出的部分在<ruby>序列分流<rt>Series Churn</rt></ruby>上。有几百万个文件,迟早会使用光文件系统中的 [inode][1]。这种情况我们只能通过重新格式化来恢复磁盘,这种方式是最具有破坏性的。我们通常不想为了适应一个应用程序而格式化磁盘。
|
||||
* 即使是分块写入,每秒也会产生数千块的数据块并且准备持久化。这依然需要每秒数千次的磁盘写入。尽管通过为每个序列打包好多个块来缓解,但这反过来还是增加了等待持久化数据的总内存占用。
|
||||
* 要保持所有文件打开来进行读写是不可行的。特别是因为 99% 的数据在 24 小时之后不再会被查询到。如果查询它,我们就得打开数千个文件,找到并读取相关的数据点到内存中,然后再关掉。这样做就会引起很高的查询延迟,数据块缓存加剧会导致新的问题,这一点在“资源消耗”一节另作讲述。
|
||||
* 最终,旧的数据需要被删除,并且数据需要从数百万文件的头部删除。这就意味着删除实际上是写密集型操作。此外,循环遍历数百万文件并且进行分析通常会导致这一过程花费数小时。当它完成时,可能又得重新来过。喔天,继续删除旧文件又会进一步导致 SSD 产生写入放大。
|
||||
* 目前所积累的数据块仅维持在内存中。如果应用崩溃,数据就会丢失。为了避免这种情况,内存状态会定期的保存在磁盘上,这比我们能接受数据丢失窗口要长的多。恢复检查点也会花费数分钟,导致很长的重启周期。
|
||||
|
||||
我们能够从现有的设计中学到的关键部分是数据块的概念,我们当然希望保留这个概念。最新的数据块会保持在内存中一般也是好的主意。毕竟,最新的数据会大量的查询到。
|
||||
|
||||
一个时间序列对应一个文件,这个概念是我们想要替换掉的。
|
||||
|
||||
### 序列分流
|
||||
|
||||
在 Prometheus 的<ruby>上下文<rt>context</rt></ruby>中,我们使用术语<ruby>序列分流<rt>series churn</rt></ruby>来描述一个时间序列集合变得不活跃,即不再接收数据点,取而代之的是出现一组新的活跃序列。
|
||||
|
||||
例如,由给定微服务实例产生的所有序列都有一个相应的“instance”标签来标识其来源。如果我们为微服务执行了<ruby>滚动更新<rt>rolling update</rt></ruby>,并且为每个实例替换一个新的版本,序列分流便会发生。在更加动态的环境中,这些事情基本上每小时都会发生。像 Kubernetes 这样的<ruby>集群编排<rt>Cluster orchestration</rt></ruby>系统允许应用连续性的自动伸缩和频繁的滚动更新,这样也许会创建成千上万个新的应用程序实例,并且伴随着全新的时间序列集合,每天都是如此。
|
||||
|
||||
```
|
||||
series
|
||||
^
|
||||
| . . . . . .
|
||||
| . . . . . .
|
||||
| . . . . . .
|
||||
| . . . . . . .
|
||||
| . . . . . . .
|
||||
| . . . . . . .
|
||||
| . . . . . .
|
||||
| . . . . . .
|
||||
| . . . . .
|
||||
| . . . . .
|
||||
| . . . . .
|
||||
v
|
||||
<-------------------- time --------------------->
|
||||
```
|
||||
|
||||
所以即便整个基础设施的规模基本保持不变,过一段时间后数据库内的时间序列还是会成线性增长。尽管 Prometheus 很愿意采集 1000 万个时间序列数据,但要想在 10 亿个序列中找到数据,查询效果还是会受到严重的影响。
|
||||
|
||||
#### 当前解决方案
|
||||
|
||||
当前 Prometheus 的 V2 存储系统对所有当前保存的序列拥有基于 LevelDB 的索引。它允许查询语句含有给定的<ruby>标签对<rt>label pair</rt></ruby>,但是缺乏可伸缩的方法来从不同的标签选集中组合查询结果。
|
||||
|
||||
例如,从所有的序列中选择标签 `__name__="requests_total"` 非常高效,但是选择 `instance="A" AND __name__="requests_total"` 就有了可伸缩性的问题。我们稍后会重新考虑导致这一点的原因和能够提升查找延迟的调整方法。
|
||||
|
||||
事实上正是这个问题才催生出了对更好的存储系统的最初探索。Prometheus 需要为查找亿万个时间序列改进索引方法。
|
||||
|
||||
### 资源消耗
|
||||
|
||||
当试图扩展 Prometheus(或其他任何事情,真的)时,资源消耗是永恒不变的话题之一。但真正困扰用户的并不是对资源的绝对渴求。事实上,由于给定的需求,Prometheus 管理着令人难以置信的吞吐量。问题更在于面对变化时的相对未知性与不稳定性。通过其架构设计,V2 存储系统缓慢地构建了样本数据块,这一点导致内存占用随时间递增。当数据块完成之后,它们可以写到磁盘上并从内存中清除。最终,Prometheus 的内存使用到达稳定状态。直到监测环境发生了改变——每次我们扩展应用或者进行滚动更新,序列分流都会增加内存、CPU、磁盘 I/O 的使用。
|
||||
|
||||
如果变更正在进行,那么它最终还是会到达一个稳定的状态,但比起更加静态的环境,它的资源消耗会显著地提高。过渡时间通常为数个小时,而且难以确定最大资源使用量。
|
||||
|
||||
为每个时间序列保存一个文件这种方法也使得一个单个查询就很容易崩溃 Prometheus 进程。当查询的数据没有缓存在内存中,查询的序列文件就会被打开,然后将含有相关数据点的数据块读入内存。如果数据量超出内存可用量,Prometheus 就会因 OOM 被杀死而退出。
|
||||
|
||||
在查询语句完成之后,加载的数据便可以被再次释放掉,但通常会缓存更长的时间,以便更快地查询相同的数据。后者看起来是件不错的事情。
|
||||
|
||||
最后,我们看看之前提到的 SSD 的写入放大,以及 Prometheus 是如何通过批量写入来解决这个问题的。尽管如此,在许多地方还是存在因为批量太小以及数据未精确对齐页边界而导致的写入放大。对于更大规模的 Prometheus 服务器,现实当中会发现缩减硬件寿命的问题。这一点对于高写入吞吐量的数据库应用来说仍然相当普遍,但我们应该放眼看看是否可以解决它。
|
||||
|
||||
### 重新开始
|
||||
|
||||
到目前为止我们对于问题域、V2 存储系统是如何解决它的,以及设计上存在的问题有了一个清晰的认识。我们也看到了许多很棒的想法,这些或多或少都可以拿来直接使用。V2 存储系统相当数量的问题都可以通过改进和部分的重新设计来解决,但为了好玩(当然,在我仔细的验证想法之后),我决定试着写一个完整的时间序列数据库——从头开始,即向文件系统写入字节。
|
||||
|
||||
性能与资源使用这种最关键的部分直接影响了存储格式的选取。我们需要为数据找到正确的算法和磁盘布局来实现一个高性能的存储层。
|
||||
|
||||
这就是我解决问题的捷径——跳过令人头疼、失败的想法,数不尽的草图,泪水与绝望。
|
||||
|
||||
### V3—宏观设计
|
||||
|
||||
我们存储系统的宏观布局是什么?简而言之,是当我们在数据文件夹里运行 `tree` 命令时显示的一切。看看它能给我们带来怎样一副惊喜的画面。
|
||||
|
||||
```
|
||||
$ tree ./data
|
||||
./data
|
||||
+-- b-000001
|
||||
| +-- chunks
|
||||
| | +-- 000001
|
||||
| | +-- 000002
|
||||
| | +-- 000003
|
||||
| +-- index
|
||||
| +-- meta.json
|
||||
+-- b-000004
|
||||
| +-- chunks
|
||||
| | +-- 000001
|
||||
| +-- index
|
||||
| +-- meta.json
|
||||
+-- b-000005
|
||||
| +-- chunks
|
||||
| | +-- 000001
|
||||
| +-- index
|
||||
| +-- meta.json
|
||||
+-- b-000006
|
||||
+-- meta.json
|
||||
+-- wal
|
||||
+-- 000001
|
||||
+-- 000002
|
||||
+-- 000003
|
||||
```
|
||||
|
||||
在最顶层,我们有一系列以 `b-` 为前缀编号的<ruby>块<rt>block</rt></ruby>。每个块中显然保存了索引文件和含有更多编号文件的 `chunk` 文件夹。`chunks` 目录只包含不同序列<ruby>数据点的原始块<rt>raw chunks of data points</rt><ruby>。与 V2 存储系统一样,这使得通过时间窗口读取序列数据非常高效并且允许我们使用相同的有效压缩算法。这一点被证实行之有效,我们也打算沿用。显然,这里并不存在含有单个序列的文件,而是一堆保存着许多序列的数据块。
|
||||
|
||||
`index` 文件的存在应该不足为奇。让我们假设它拥有黑魔法,可以让我们找到标签、可能的值、整个时间序列和存放数据点的数据块。
|
||||
|
||||
但为什么这里有好几个文件夹都是索引和块文件的布局?并且为什么存在最后一个包含 `wal` 文件夹?理解这两个疑问便能解决九成的问题。
|
||||
|
||||
#### 许多小型数据库
|
||||
|
||||
我们分割横轴,即将时间域分割为不重叠的块。每一块扮演着完全独立的数据库,它包含该时间窗口所有的时间序列数据。因此,它拥有自己的索引和一系列块文件。
|
||||
|
||||
```
|
||||
|
||||
t0 t1 t2 t3 now
|
||||
+-----------+ +-----------+ +-----------+ +-----------+
|
||||
| | | | | | | | +------------+
|
||||
| | | | | | | mutable | <--- write ---- ┤ Prometheus |
|
||||
| | | | | | | | +------------+
|
||||
+-----------+ +-----------+ +-----------+ +-----------+ ^
|
||||
+--------------+-------+------+--------------+ |
|
||||
| query
|
||||
| |
|
||||
merge -------------------------------------------------+
|
||||
```
|
||||
|
||||
每一块的数据都是<ruby>不可变的<rt>immutable</rt></ruby>。当然,当我们采集新数据时,我们必须能向最近的块中添加新的序列和样本。对于该数据块,所有新的数据都将写入内存中的数据库中,它与我们的持久化的数据块一样提供了查找属性。内存中的数据结构可以高效地更新。为了防止数据丢失,所有传入的数据同样被写入临时的<ruby>预写日志<rt>write ahead log</rt></ruby>中,这就是 `wal` 文件夹中的一些列文件,我们可以在重新启动时通过它们重新填充内存数据库。
|
||||
|
||||
所有这些文件都带有序列化格式,有我们所期望的所有东西:许多标志、偏移量、变体和 CRC32 校验和。纸上得来终觉浅,绝知此事要躬行。
|
||||
|
||||
这种布局允许我们扩展查询范围到所有相关的块上。每个块上的部分结果最终合并成完整的结果。
|
||||
|
||||
这种横向分割增加了一些很棒的功能:
|
||||
|
||||
* 当查询一个时间范围,我们可以简单地忽略所有范围之外的数据块。通过减少需要检查的数据集,它可以初步解决序列分流的问题。
|
||||
* 当完成一个块,我们可以通过顺序的写入大文件从内存数据库中保存数据。这样可以避免任何的写入放大,并且 SSD 与 HDD 均适用。
|
||||
* 我们延续了 V2 存储系统的一个好的特性,最近使用而被多次查询的数据块,总是保留在内存中。
|
||||
* 很好,我们也不再受限于 1KiB 的数据块尺寸,以使数据在磁盘上更好地对齐。我们可以挑选对单个数据点和压缩格式最合理的尺寸。
|
||||
* 删除旧数据变得极为简单快捷。我们仅仅只需删除一个文件夹。记住,在旧的存储系统中我们不得不花数个小时分析并重写数亿个文件。
|
||||
|
||||
每个块还包含了 `meta.json` 文件。它简单地保存了关于块的存储状态和包含的数据,以便轻松了解存储状态及其包含的数据。
|
||||
|
||||
##### mmap
|
||||
|
||||
将数百万个小文件合并为少数几个大文件使得我们用很小的开销就能保持所有的文件都打开。这就解除了对 [mmap(2)][8] 的使用的阻碍,这是一个允许我们通过文件透明地回传虚拟内存的系统调用。简单起见,你可以将其视为<ruby>交换空间<rt>swap space</rt></ruby>,只是我们所有的数据已经保存在了磁盘上,并且当数据换出内存后不再会发生写入。
|
||||
|
||||
这意味着我们可以当作所有数据库的内容都视为在内存中却不占用任何物理内存。仅当我们访问数据库文件某些字节范围时,操作系统才会从磁盘上<ruby>惰性加载<rt>lazy load</rt></ruby>页数据。这使得我们将所有数据持久化相关的内存管理都交给了操作系统。通常,操作系统更有资格作出这样的决定,因为它可以全面了解整个机器和进程。查询的数据可以相当积极的缓存进内存,但内存压力会使得页被换出。如果机器拥有未使用的内存,Prometheus 目前将会高兴地缓存整个数据库,但是一旦其他进程需要,它就会立刻返回那些内存。
|
||||
|
||||
因此,查询不再轻易地使我们的进程 OOM,因为查询的是更多的持久化的数据而不是装入内存中的数据。内存缓存大小变得完全自适应,并且仅当查询真正需要时数据才会被加载。
|
||||
|
||||
就个人理解,这就是当今大多数数据库的工作方式,如果磁盘格式允许,这是一种理想的方式,——除非有人自信能在这个过程中超越操作系统。我们做了很少的工作但确实从外面获得了很多功能。
|
||||
|
||||
#### 压缩
|
||||
|
||||
存储系统需要定期“切”出新块并将之前完成的块写入到磁盘中。仅在块成功的持久化之后,才会被删除之前用来恢复内存块的日志文件(wal)。
|
||||
|
||||
我们希望将每个块的保存时间设置的相对短一些(通常配置为 2 小时),以避免内存中积累太多的数据。当查询多个块,我们必须将它们的结果合并为一个整体的结果。合并过程显然会消耗资源,一个星期的查询不应该由超过 80 个的部分结果所组成。
|
||||
|
||||
为了实现两者,我们引入<ruby>压缩<rt>compaction</rt></ruby>。压缩描述了一个过程:取一个或更多个数据块并将其写入一个可能更大的块中。它也可以在此过程中修改现有的数据。例如,清除已经删除的数据,或重建样本块以提升查询性能。
|
||||
|
||||
```
|
||||
|
||||
t0 t1 t2 t3 t4 now
|
||||
+------------+ +----------+ +-----------+ +-----------+ +-----------+
|
||||
| 1 | | 2 | | 3 | | 4 | | 5 mutable | before
|
||||
+------------+ +----------+ +-----------+ +-----------+ +-----------+
|
||||
+-----------------------------------------+ +-----------+ +-----------+
|
||||
| 1 compacted | | 4 | | 5 mutable | after (option A)
|
||||
+-----------------------------------------+ +-----------+ +-----------+
|
||||
+--------------------------+ +--------------------------+ +-----------+
|
||||
| 1 compacted | | 3 compacted | | 5 mutable | after (option B)
|
||||
+--------------------------+ +--------------------------+ +-----------+
|
||||
```
|
||||
|
||||
在这个例子中我们有顺序块 `[1,2,3,4]`。块 1、2、3 可以压缩在一起,新的布局将会是 `[1,4]`。或者,将它们成对压缩为 `[1,3]`。所有的时间序列数据仍然存在,但现在整体上保存在更少的块中。这极大程度地缩减了查询时间的消耗,因为需要合并的部分查询结果变得更少了。
|
||||
|
||||
#### 保留
|
||||
|
||||
我们看到了删除旧的数据在 V2 存储系统中是一个缓慢的过程,并且消耗 CPU、内存和磁盘。如何才能在我们基于块的设计上清除旧的数据?相当简单,只要删除我们配置的保留时间窗口里没有数据的块文件夹即可。在下面的例子中,块 1 可以被安全地删除,而块 2 则必须一直保留,直到它落在保留窗口边界之外。
|
||||
|
||||
```
|
||||
|
|
||||
+------------+ +----+-----+ +-----------+ +-----------+ +-----------+
|
||||
| 1 | | 2 | | | 3 | | 4 | | 5 | . . .
|
||||
+------------+ +----+-----+ +-----------+ +-----------+ +-----------+
|
||||
|
|
||||
|
|
||||
retention boundary
|
||||
```
|
||||
|
||||
随着我们不断压缩先前压缩的块,旧数据越大,块可能变得越大。因此必须为其设置一个上限,以防数据块扩展到整个数据库而损失我们设计的最初优势。
|
||||
|
||||
方便的是,这一点也限制了部分存在于保留窗口内部分存在于保留窗口外的块的磁盘消耗总量。例如上面例子中的块 2。当设置了最大块尺寸为总保留窗口的 10% 后,我们保留块 2 的总开销也有了 10% 的上限。
|
||||
|
||||
总结一下,保留与删除从非常昂贵到了几乎没有成本。
|
||||
|
||||
> 如果你读到这里并有一些数据库的背景知识,现在你也许会问:这些都是最新的技术吗?——并不是;而且可能还会做的更好。
|
||||
>
|
||||
> 在内存中批量处理数据,在预写日志中跟踪,并定期写入到磁盘的模式在现在相当普遍。
|
||||
>
|
||||
> 我们看到的好处无论在什么领域的数据里都是适用的。遵循这一方法最著名的开源案例是 LevelDB、Cassandra、InfluxDB 和 HBase。关键是避免重复发明劣质的轮子,采用经过验证的方法,并正确地运用它们。
|
||||
>
|
||||
> 脱离场景添加你自己的黑魔法是一种不太可能的情况。
|
||||
|
||||
### 索引
|
||||
|
||||
研究存储改进的最初想法是解决序列分流的问题。基于块的布局减少了查询所要考虑的序列总数。因此假设我们索引查找的复杂度是 `O(n^2)`,我们就要设法减少 n 个相当数量的复杂度,之后就相当于改进 `O(n^2)` 复杂度。——恩,等等……糟糕。
|
||||
|
||||
快速回顾一下“算法 101”课上提醒我们的,在理论上它并未带来任何好处。如果之前就很糟糕,那么现在也一样。理论是如此的残酷。
|
||||
|
||||
实际上,我们大多数的查询已经可以相当快响应。但是,跨越整个时间范围的查询仍然很慢,尽管只需要找到少部分数据。追溯到所有这些工作之前,最初我用来解决这个问题的想法是:我们需要一个更大容量的[倒排索引][9]。
|
||||
|
||||
倒排索引基于数据项内容的子集提供了一种快速的查找方式。简单地说,我可以通过标签 `app="nginx"` 查找所有的序列而无需遍历每个文件来看它是否包含该标签。
|
||||
|
||||
为此,每个序列被赋上一个唯一的 ID ,通过该 ID 可以恒定时间内检索它(`O(1)`)。在这个例子中 ID 就是我们的正向索引。
|
||||
|
||||
> 示例:如果 ID 为 10、29、9 的序列包含标签 `app="nginx"`,那么 “nginx”的倒排索引就是简单的列表 `[10, 29, 9]`,它就能用来快速地获取所有包含标签的序列。即使有 200 多亿个数据序列也不会影响查找速度。
|
||||
|
||||
简而言之,如果 `n` 是我们序列总数,`m` 是给定查询结果的大小,使用索引的查询复杂度现在就是 `O(m)`。查询语句依据它获取数据的数量 `m` 而不是被搜索的数据体 `n` 进行缩放是一个很好的特性,因为 `m` 一般相当小。
|
||||
|
||||
为了简单起见,我们假设可以在恒定时间内查找到倒排索引对应的列表。
|
||||
|
||||
实际上,这几乎就是 V2 存储系统具有的倒排索引,也是提供在数百万序列中查询性能的最低需求。敏锐的人会注意到,在最坏情况下,所有的序列都含有标签,因此 `m` 又成了 `O(n)`。这一点在预料之中,也相当合理。如果你查询所有的数据,它自然就会花费更多时间。一旦我们牵扯上了更复杂的查询语句就会有问题出现。
|
||||
|
||||
#### 标签组合
|
||||
|
||||
与数百万个序列相关的标签很常见。假设横向扩展着数百个实例的“foo”微服务,并且每个实例拥有数千个序列。每个序列都会带有标签 `app="foo"`。当然,用户通常不会查询所有的序列而是会通过进一步的标签来限制查询。例如,我想知道服务实例接收到了多少请求,那么查询语句便是 `__name__="requests_total" AND app="foo"`。
|
||||
|
||||
为了找到满足两个标签选择子的所有序列,我们得到每一个标签的倒排索引列表并取其交集。结果集通常会比任何一个输入列表小一个数量级。因为每个输入列表最坏情况下的大小为 `O(n)`,所以在嵌套地为每个列表进行<ruby>暴力求解<rt>brute force solution</rt><ruby>下,运行时间为 `O(n^2)`。相同的成本也适用于其他的集合操作,例如取并集(`app="foo" OR app="bar"`)。当在查询语句上添加更多标签选择子,耗费就会指数增长到 `O(n^3)`、`O(n^4)`、`O(n^5)`……`O(n^k)`。通过改变执行顺序,可以使用很多技巧以优化运行效率。越复杂,越是需要关于数据特征和标签之间相关性的知识。这引入了大量的复杂度,但是并没有减少算法的最坏运行时间。
|
||||
|
||||
这便是 V2 存储系统使用的基本方法,幸运的是,看似微小的改动就能获得显著的提升。如果我们假设倒排索引中的 ID 都是排序好的会怎么样?
|
||||
|
||||
假设这个例子的列表用于我们最初的查询:
|
||||
|
||||
```
|
||||
__name__="requests_total" -> [ 9999, 1000, 1001, 2000000, 2000001, 2000002, 2000003 ]
|
||||
app="foo" -> [ 1, 3, 10, 11, 12, 100, 311, 320, 1000, 1001, 10002 ]
|
||||
|
||||
intersection => [ 1000, 1001 ]
|
||||
```
|
||||
|
||||
它的交集相当小。我们可以为每个列表的起始位置设置游标,每次从最小的游标处移动来找到交集。当二者的数字相等,我们就添加它到结果中并移动二者的游标。总体上,我们以锯齿形扫描两个列表,因此总耗费是 `O(2n)=O(n)`,因为我们总是在一个列表上移动。
|
||||
|
||||
两个以上列表的不同集合操作也类似。因此 `k` 个集合操作仅仅改变了因子 `O(k*n)` 而不是最坏情况下查找运行时间的指数 `O(n^k)`。
|
||||
|
||||
我在这里所描述的是几乎所有[全文搜索引擎][10]使用的标准搜索索引的简化版本。每个序列描述符都视作一个简短的“文档”,每个标签(名称 + 固定值)作为其中的“单词”。我们可以忽略搜索引擎索引中通常遇到的很多附加数据,例如单词位置和和频率。
|
||||
|
||||
关于改进实际运行时间的方法似乎存在无穷无尽的研究,它们通常都是对输入数据做一些假设。不出意料的是,还有大量技术来压缩倒排索引,其中各有利弊。因为我们的“文档”比较小,而且“单词”在所有的序列里大量重复,压缩变得几乎无关紧要。例如,一个真实的数据集约有 440 万个序列与大约 12 个标签,每个标签拥有少于 5000 个单独的标签。对于最初的存储版本,我们坚持使用基本的方法而不压缩,仅做微小的调整来跳过大范围非交叉的 ID。
|
||||
|
||||
尽管维持排序好的 ID 听起来很简单,但实践过程中不是总能完成的。例如,V2 存储系统为新的序列赋上一个哈希值来当作 ID,我们就不能轻易地排序倒排索引。
|
||||
|
||||
另一个艰巨的任务是当磁盘上的数据被更新或删除掉后修改其索引。通常,最简单的方法是重新计算并写入,但是要保证数据库在此期间可查询且具有一致性。V3 存储系统通过每块上具有的独立不可变索引来解决这一问题,该索引仅通过压缩时的重写来进行修改。只有可变块上的索引需要被更新,它完全保存在内存中。
|
||||
|
||||
## 基准测试
|
||||
|
||||
我从存储的基准测试开始了初步的开发,它基于现实世界数据集中提取的大约 440 万个序列描述符,并生成合成数据点以输入到这些序列中。这个阶段的开发仅仅测试了单独的存储系统,对于快速找到性能瓶颈和高并发负载场景下的触发死锁至关重要。
|
||||
|
||||
在完成概念性的开发实施之后,该基准测试能够在我的 Macbook Pro 上维持每秒 2000 万的吞吐量 —— 并且这都是在打开着十几个 Chrome 的页面和 Slack 的时候。因此,尽管这听起来都很棒,它这也表明推动这项测试没有的进一步价值(或者是没有在高随机环境下运行)。毕竟,它是合成的数据,因此在除了良好的第一印象外没有多大价值。比起最初的设计目标高出 20 倍,是时候将它部署到真正的 Prometheus 服务器上了,为它添加更多现实环境中的开销和场景。
|
||||
|
||||
我们实际上没有可重现的 Prometheus 基准测试配置,特别是没有对于不同版本的 A/B 测试。亡羊补牢为时不晚,[不过现在就有一个了][11]!
|
||||
|
||||
我们的工具可以让我们声明性地定义基准测试场景,然后部署到 AWS 的 Kubernetes 集群上。尽管对于全面的基准测试来说不是最好环境,但它肯定比 64 核 128GB 内存的专用<ruby>裸机服务器<rt>bare metal servers</rt></ruby>更能反映出我们的用户群体。
|
||||
|
||||
我们部署了两个 Prometheus 1.5.2 服务器(V2 存储系统)和两个来自 2.0 开发分支的 Prometheus (V3 存储系统)。每个 Prometheus 运行在配备 SSD 的专用服务器上。我们将横向扩展的应用部署在了工作节点上,并且让其暴露典型的微服务度量。此外,Kubernetes 集群本身和节点也被监控着。整套系统由另一个 Meta-Prometheus 所监督,它监控每个 Prometheus 的健康状况和性能。
|
||||
|
||||
为了模拟序列分流,微服务定期的扩展和收缩来移除旧的 pod 并衍生新的 pod,生成新的序列。通过选择“典型”的查询来模拟查询负载,对每个 Prometheus 版本都执行一次。
|
||||
|
||||
总体上,伸缩与查询的负载以及采样频率极大的超出了 Prometheus 的生产部署。例如,我们每隔 15 分钟换出 60% 的微服务实例去产生序列分流。在现代的基础设施上,一天仅大约会发生 1-5 次。这就保证了我们的 V3 设计足以处理未来几年的工作负载。就结果而言,Prometheus 1.5.2 和 2.0 之间的性能差异在极端的环境下会变得更大。
|
||||
|
||||
总而言之,我们每秒从 850 个目标里收集大约 11 万份样本,每次暴露 50 万个序列。
|
||||
|
||||
在此系统运行一段时间之后,我们可以看一下数字。我们评估了两个版本在 12 个小时之后到达稳定时的几个指标。
|
||||
|
||||
> 请注意从 Prometheus 图形界面的截图中轻微截断的 Y 轴
|
||||
|
||||
![Heap usage GB](https://fabxc.org/tsdb/assets/heap_usage.png)
|
||||
|
||||
*堆内存使用(GB)*
|
||||
|
||||
内存资源的使用对用户来说是最为困扰的问题,因为它相对的不可预测且可能导致进程崩溃。
|
||||
|
||||
显然,查询的服务器正在消耗内存,这很大程度上归咎于查询引擎的开销,这一点可以当作以后优化的主题。总的来说,Prometheus 2.0 的内存消耗减少了 3-4 倍。大约 6 小时之后,在 Prometheus 1.5 上有一个明显的峰值,与我们设置的 6 小时的保留边界相对应。因为删除操作成本非常高,所以资源消耗急剧提升。这一点在下面几张图中均有体现。
|
||||
|
||||
![CPU usage cores](https://fabxc.org/tsdb/assets/cpu_usage.png)
|
||||
|
||||
*CPU 使用(核心/秒)*
|
||||
|
||||
类似的模式也体现在 CPU 使用上,但是查询的服务器与非查询的服务器之间的差异尤为明显。每秒获取大约 11 万个数据需要 0.5 核心/秒的 CPU 资源,比起评估查询所花费的 CPU 时间,我们的新存储系统 CPU 消耗可忽略不计。总的来说,新存储需要的 CPU 资源减少了 3 到 10 倍。
|
||||
|
||||
![Disk writes](https://fabxc.org/tsdb/assets/disk_writes.png)
|
||||
|
||||
*磁盘写入(MB/秒)*
|
||||
|
||||
迄今为止最引人注目和意想不到的改进表现在我们的磁盘写入利用率上。这就清楚的说明了为什么 Prometheus 1.5 很容易造成 SSD 损耗。我们看到最初的上升发生在第一个块被持久化到序列文件中的时期,然后一旦删除操作引发了重写就会带来第二个上升。令人惊讶的是,查询的服务器与非查询的服务器显示出了非常不同的利用率。
|
||||
|
||||
在另一方面,Prometheus 2.0 每秒仅向其预写日志写入大约一兆字节。当块被压缩到磁盘时,写入定期地出现峰值。这在总体上节省了:惊人的 97-99%。
|
||||
|
||||
![Disk usage](https://fabxc.org/tsdb/assets/disk_usage.png)
|
||||
|
||||
*磁盘大小(GB)*
|
||||
|
||||
与磁盘写入密切相关的是总磁盘空间占用量。由于我们对样本(这是我们的大部分数据)几乎使用了相同的压缩算法,因此磁盘占用量应当相同。在更为稳定的系统中,这样做很大程度上是正确地,但是因为我们需要处理高的序列分流,所以还要考虑每个序列的开销。
|
||||
|
||||
如我们所见,Prometheus 1.5 在这两个版本达到稳定状态之前,使用的存储空间因其保留操作而急速上升。Prometheus 2.0 似乎在每个序列上的开销显著降低。我们可以清楚的看到预写日志线性地充满整个存储空间,然后当压缩完成后瞬间下降。事实上对于两个 Prometheus 2.0 服务器,它们的曲线并不是完全匹配的,这一点需要进一步的调查。
|
||||
|
||||
前景大好。剩下最重要的部分是查询延迟。新的索引应当优化了查找的复杂度。没有实质上发生改变的是处理数据的过程,例如 `rate()` 函数或聚合。这些就是查询引擎要做的东西了。
|
||||
|
||||
![Query latency](https://fabxc.org/tsdb/assets/query_latency.png)
|
||||
|
||||
*第 99 个百分位查询延迟(秒)*
|
||||
|
||||
数据完全符合预期。在 Prometheus 1.5 上,查询延迟随着存储的序列而增加。只有在保留操作开始且旧的序列被删除后才会趋于稳定。作为对比,Prometheus 2.0 从一开始就保持在合适的位置。
|
||||
|
||||
我们需要花一些心思在数据是如何被采集上,对服务器发出的查询请求通过对以下方面的估计来选择:范围查询和即时查询的组合,进行更轻或更重的计算,访问更多或更少的文件。它并不需要代表真实世界里查询的分布。也不能代表冷数据的查询性能,我们可以假设所有的样本数据都是保存在内存中的热数据。
|
||||
|
||||
尽管如此,我们可以相当自信地说,整体查询效果对序列分流变得非常有弹性,并且在高压基准测试场景下提升了 4 倍的性能。在更为静态的环境下,我们可以假设查询时间大多数花费在了查询引擎上,改善程度明显较低。
|
||||
|
||||
![Ingestion rate](https://fabxc.org/tsdb/assets/ingestion_rate.png)
|
||||
|
||||
*摄入的样本/秒*
|
||||
|
||||
最后,快速地看一下不同 Prometheus 服务器的摄入率。我们可以看到搭载 V3 存储系统的两个服务器具有相同的摄入速率。在几个小时之后变得不稳定,这是因为不同的基准测试集群节点由于高负载变得无响应,与 Prometheus 实例无关。(两个 2.0 的曲线完全匹配这一事实希望足够具有说服力)
|
||||
|
||||
尽管还有更多 CPU 和内存资源,两个 Prometheus 1.5.2 服务器的摄入率大大降低。序列分流的高压导致了无法采集更多的数据。
|
||||
|
||||
那么现在每秒可以摄入的<ruby>绝对最大<rt>absolute maximum</rt></ruby>样本数是多少?
|
||||
|
||||
但是现在你可以摄取的每秒绝对最大样本数是多少?
|
||||
|
||||
我不知道 —— 虽然这是一个相当容易的优化指标,但除了稳固的基线性能之外,它并不是特别有意义。
|
||||
|
||||
有很多因素都会影响 Prometheus 数据流量,而且没有一个单独的数字能够描述捕获质量。最大摄入率在历史上是一个导致基准出现偏差的度量,并且忽视了更多重要的层面,例如查询性能和对序列分流的弹性。关于资源使用线性增长的大致猜想通过一些基本的测试被证实。很容易推断出其中的原因。
|
||||
|
||||
我们的基准测试模拟了高动态环境下 Prometheus 的压力,它比起真实世界中的更大。结果表明,虽然运行在没有优化的云服务器上,但是已经超出了预期的效果。最终,成功将取决于用户反馈而不是基准数字。
|
||||
|
||||
> 注意:在撰写本文的同时,Prometheus 1.6 正在开发当中,它允许更可靠地配置最大内存使用量,并且可能会显著地减少整体的消耗,有利于稍微提高 CPU 使用率。我没有重复对此进行测试,因为整体结果变化不大,尤其是面对高序列分流的情况。
|
||||
|
||||
## 总结
|
||||
|
||||
Prometheus 开始应对高基数序列与单独样本的吞吐量。这仍然是一项富有挑战性的任务,但是新的存储系统似乎向我们展示了未来的一些好东西。
|
||||
|
||||
第一个配备 V3 存储系统的 [alpha 版本 Prometheus 2.0][12] 已经可以用来测试了。在早期阶段预计还会出现崩溃,死锁和其他 bug。
|
||||
|
||||
存储系统的代码可以在[这个单独的项目中找到][13]。Prometheus 对于寻找高效本地存储时间序列数据库的应用来说可能非常有用,这一点令人非常惊讶。
|
||||
|
||||
> 这里需要感谢很多人作出的贡献,以下排名不分先后:
|
||||
|
||||
> Bjoern Rabenstein 和 Julius Volz 在 V2 存储引擎上的打磨工作以及 V3 存储系统的反馈,这为新一代的设计奠定了基础。
|
||||
|
||||
> Wilhelm Bierbaum 对新设计不断的建议与见解作出了很大的贡献。Brian Brazil 不断的反馈确保了我们最终得到的是语义上合理的方法。与 Peter Bourgon 深刻的讨论验证了设计并形成了这篇文章。
|
||||
|
||||
> 别忘了我们整个 CoreOS 团队与公司对于这项工作的赞助与支持。感谢所有那些听我一遍遍唠叨 SSD、浮点数、序列化格式的同学。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fabxc.org/blog/2017-04-10-writing-a-tsdb/
|
||||
|
||||
作者:[Fabian Reinartz][a]
|
||||
译者:[LuuMing](https://github.com/LuuMing)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://twitter.com/fabxc
|
||||
[1]:https://en.wikipedia.org/wiki/Inode
|
||||
[2]:https://prometheus.io/
|
||||
[3]:https://kubernetes.io/
|
||||
[4]:https://en.wikipedia.org/wiki/Write_amplification
|
||||
[5]:http://codecapsule.com/2014/02/12/coding-for-ssds-part-1-introduction-and-table-of-contents/
|
||||
[6]:https://prometheus.io/docs/practices/rules/
|
||||
[7]:http://www.vldb.org/pvldb/vol8/p1816-teller.pdf
|
||||
[8]:https://en.wikipedia.org/wiki/Mmap
|
||||
[9]:https://en.wikipedia.org/wiki/Inverted_index
|
||||
[10]:https://en.wikipedia.org/wiki/Search_engine_indexing#Inverted_indices
|
||||
[11]:https://github.com/prometheus/prombench
|
||||
[12]:https://prometheus.io/blog/2017/04/10/promehteus-20-sneak-peak/
|
||||
[13]:https://github.com/prometheus/tsdb
|
149
published/201906/20170414 5 projects for Raspberry Pi at home.md
Normal file
149
published/201906/20170414 5 projects for Raspberry Pi at home.md
Normal file
@ -0,0 +1,149 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10936-1.html)
|
||||
[#]: subject: (5 projects for Raspberry Pi at home)
|
||||
[#]: via: (https://opensource.com/article/17/4/5-projects-raspberry-pi-home)
|
||||
[#]: author: (Ben Nuttall https://opensource.com/users/bennuttall)
|
||||
|
||||
5 个可在家中使用的树莓派项目
|
||||
======================================
|
||||
|
||||
![5 projects for Raspberry Pi at home][1]
|
||||
|
||||
[树莓派][2] 电脑可被用来进行多种设置用于不同的目的。显然它在教育市场帮助学生在教室和创客空间中学习编程与创客技巧方面占有一席之地,它在工作场所和工厂中有大量行业应用。我打算介绍五个你可能想要在你的家中构建的项目。
|
||||
|
||||
### 媒体中心
|
||||
|
||||
在家中人们常用树莓派作为媒体中心来服务多媒体文件。它很容易搭建,树莓派提供了大量的 GPU(图形处理单元)运算能力来在大屏电视上渲染你的高清电视节目和电影。将 [Kodi][3](从前的 XBMC)运行在树莓派上是一个很棒的方式,它可以播放你的硬盘或网络存储上的任何媒体。你同样可以安装一个插件来播放 YouTube 视频。
|
||||
|
||||
还有几个略微不同的选择,最常见的是 [OSMC][4](开源媒体中心)和 [LibreELEC][5],都是基于 Kodi 的。它们在放映媒体内容方面表现的都非常好,但是 OSMC 有一个更酷炫的用户界面,而 LibreElec 更轻量级。你要做的只是选择一个发行版,下载镜像并安装到一个 SD 卡中(或者仅仅使用 [NOOBS][6]),启动,然后就准备好了。
|
||||
|
||||
![LibreElec ][7]
|
||||
|
||||
*LibreElec;树莓派基金会, CC BY-SA*
|
||||
|
||||
![OSMC][8]
|
||||
|
||||
*OSMC.tv, 版权所有, 授权使用*
|
||||
|
||||
在往下走之前,你需要决定[使用哪种树莓派][9]。这些发行版在任何树莓派(1、2、3 或 Zero)上都能运行,视频播放在这些树莓派中的任何一个上都能胜任。除了 Pi 3(和 Zero W)有内置 Wi-Fi,唯一可察觉的不同是用户界面的反应速度,在 Pi 3 上更快。Pi 2 也不会慢太多,所以如果你不需要 Wi-Fi 它也是可以的,但是当切换菜单时,你会注意到 Pi 3 比 Pi 1 和 Zero 表现的更好。
|
||||
|
||||
### SSH 网关
|
||||
|
||||
如果你想从外部网络访问你的家庭局域网的电脑和设备,你必须打开这些设备的端口来允许外部访问。在互联网中开放这些端口有安全风险,意味着你总是你总是处于被攻击、滥用或者其他各种未授权访问的风险中。然而,如果你在你的网络中安装一个树莓派,并且设置端口映射来仅允许通过 SSH 访问树莓派,你可以这么用来作为一个安全的网关来跳到网络中的其他树莓派和 PC。
|
||||
|
||||
大多数路由允许你配置端口映射规则。你需要给你的树莓派一个固定的内网 IP 地址来设置你的路由器端口 22 映射到你的树莓派端口 22。如果你的网络服务提供商给你提供了一个静态 IP 地址,你能够通过 SSH 和主机的 IP 地址访问(例如,`ssh pi@123.45.56.78`)。如果你有一个域名,你可以配置一个子域名指向这个 IP 地址,所以你没必要记住它(例如,`ssh pi@home.mydomain.com`)。
|
||||
|
||||
![][11]
|
||||
|
||||
然而,如果你不想将树莓派暴露在互联网上,你应该非常小心,不要让你的网络处于危险之中。如果你遵循一些简单的步骤来使它更安全:
|
||||
|
||||
1. 大多数人建议你更换你的登录密码(有道理,默认密码 “raspberry” 是众所周知的),但是这不能阻挡暴力攻击。你可以改变你的密码并添加一个双重验证(所以你需要你的密码*和*一个手机生成的与时间相关的密码),这么做更安全。但是,我相信最好的方法阻止入侵者访问你的树莓派是在你的 SSH 配置中[禁止密码认证][12],这样只能通过 SSH 密匙进入。这意味着任何试图猜测你的密码尝试登录的人都不会成功。只有你的私有密匙可以访问。简单来说,很多人建议将 SSH 端口从默认的 22 换成其他的,但是通过简单的 [Nmap][13] 扫描你的 IP 地址,你信任的 SSH 端口就会暴露。
|
||||
2. 最好,不要在这个树莓派上运行其他的软件,这样你不会意外暴露其他东西。如果你想要运行其他软件,你最好在网络中的其他树莓派上运行,它们没有暴露在互联网上。确保你经常升级来保证你的包是最新的,尤其是 `openssh-server` 包,这样你的安全缺陷就被打补丁了。
|
||||
3. 安装 [sshblack][14] 或 [fail2ban][15] 来将任何表露出恶意的用户加入黑名单,例如试图暴力破解你的 SSH 密码。
|
||||
|
||||
使树莓派安全后,让它在线,你将可以在世界的任何地方登录你的网络。一旦你登录到你的树莓派,你可以用 SSH 访问本地网络上的局域网地址(例如,192.168.1.31)访问其他设备。如果你在这些设备上有密码,用密码就好了。如果它们同样只允许 SSH 密匙,你需要确保你的密匙通过 SSH 转发,使用 `-A` 参数:`ssh -A pi@123.45.67.89`。
|
||||
|
||||
### CCTV / 宠物相机
|
||||
|
||||
另一个很棒的家庭项目是安装一个相机模块来拍照和录视频,录制并保存文件,在内网或者外网中进行流式传输。你想这么做有很多原因,但两个常见的情况是一个家庭安防相机或监控你的宠物。
|
||||
|
||||
[树莓派相机模块][16] 是一个优秀的配件。它提供全高清的相片和视频,包括很多高级配置,很[容易编程][17]。[红外线相机][18]用于这种目的是非常理想的,通过一个红外线 LED(树莓派可以控制的),你就能够在黑暗中看见东西。
|
||||
|
||||
如果你想通过一定频率拍摄静态图片来留意某件事,你可以仅仅写一个简短的 [Python][19] 脚本或者使用命令行工具 [raspistill][20], 在 [Cron][21] 中规划它多次运行。你可能想将它们保存到 [Dropbox][22] 或另一个网络服务,上传到一个网络服务器,你甚至可以创建一个[web 应用][23]来显示他们。
|
||||
|
||||
如果你想要在内网或外网中流式传输视频,那也相当简单。在 [picamera 文档][24]中(在 “web streaming” 章节)有一个简单的 MJPEG(Motion JPEG)例子。简单下载或者拷贝代码到文件中,运行并访问树莓派的 IP 地址的 8000 端口,你会看见你的相机的直播输出。
|
||||
|
||||
有一个更高级的流式传输项目 [pistreaming][25] 也可以,它通过在网络服务器中用 [JSMpeg][26] (一个 JavaScript 视频播放器)和一个用于相机流的单独运行的 websocket。这种方法性能更好,并且和之前的例子一样简单,但是如果要在互联网中流式传输,则需要包含更多代码,并且需要你开放两个端口。
|
||||
|
||||
一旦你的网络流建立起来,你可以将你的相机放在你想要的地方。我用一个来观察我的宠物龟:
|
||||
|
||||
![Tortoise ][27]
|
||||
|
||||
*Ben Nuttall, CC BY-SA*
|
||||
|
||||
如果你想控制相机位置,你可以用一个舵机。一个优雅的方案是用 Pimoroni 的 [Pan-Tilt HAT][28],它可以让你简单的在二维方向上移动相机。为了与 pistreaming 集成,可以看看该项目的 [pantilthat 分支][29].
|
||||
|
||||
![Pan-tilt][30]
|
||||
|
||||
*Pimoroni.com, Copyright, 授权使用*
|
||||
|
||||
如果你想将你的树莓派放到户外,你将需要一个防水的外围附件,并且需要一种给树莓派供电的方式。POE(通过以太网提供电力)电缆是一个不错的实现方式。
|
||||
|
||||
### 家庭自动化或物联网
|
||||
|
||||
现在是 2017 年(LCTT 译注:此文发表时间),到处都有很多物联网设备,尤其是家中。我们的电灯有 Wi-Fi,我们的面包烤箱比过去更智能,我们的茶壶处于俄国攻击的风险中,除非你确保你的设备安全,不然别将没有必要的设备连接到互联网,之后你可以在家中充分的利用物联网设备来完成自动化任务。
|
||||
|
||||
市场上有大量你可以购买或订阅的服务,像 Nest Thermostat 或 Philips Hue 电灯泡,允许你通过你的手机控制你的温度或者你的亮度,无论你是否在家。你可以用一个树莓派来催动这些设备的电源,通过一系列规则包括时间甚至是传感器来完成自动交互。用 Philips Hue,你做不到的当你进房间时打开灯光,但是有一个树莓派和一个运动传感器,你可以用 Python API 来打开灯光。类似地,当你在家的时候你可以通过配置你的 Nest 打开加热系统,但是如果你想在房间里至少有两个人时才打开呢?写一些 Python 代码来检查网络中有哪些手机,如果至少有两个,告诉 Nest 来打开加热器。
|
||||
|
||||
不用选择集成已存在的物联网设备,你可以用简单的组件来做的更多。一个自制的窃贼警报器,一个自动化的鸡笼门开关,一个夜灯,一个音乐盒,一个定时的加热灯,一个自动化的备份服务器,一个打印服务器,或者任何你能想到的。
|
||||
|
||||
### Tor 协议和屏蔽广告
|
||||
|
||||
Adafruit 的 [Onion Pi][31] 是一个 [Tor][32] 协议来使你的网络通讯匿名,允许你使用互联网而不用担心窥探者和各种形式的监视。跟随 Adafruit 的指南来设置 Onion Pi,你会找到一个舒服的匿名的浏览体验。
|
||||
|
||||
![Onion-Pi][33]
|
||||
|
||||
*Onion-pi from Adafruit, Copyright, 授权使用*
|
||||
|
||||
![Pi-hole][34]
|
||||
|
||||
可以在你的网络中安装一个树莓派来拦截所有的网络交通并过滤所有广告。简单下载 [Pi-hole][35] 软件到 Pi 中,你的网络中的所有设备都将没有广告(甚至屏蔽你的移动设备应用内的广告)。
|
||||
|
||||
树莓派在家中有很多用法。你在家里用树莓派来干什么?你想用它干什么?
|
||||
|
||||
在下方评论让我们知道。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/17/4/5-projects-raspberry-pi-home
|
||||
|
||||
作者:[Ben Nuttall][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/bennuttall
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/raspberry_pi_home_automation.png?itok=2TnmJpD8 (5 projects for Raspberry Pi at home)
|
||||
[2]: https://www.raspberrypi.org/
|
||||
[3]: https://kodi.tv/
|
||||
[4]: https://osmc.tv/
|
||||
[5]: https://libreelec.tv/
|
||||
[6]: https://www.raspberrypi.org/downloads/noobs/
|
||||
[7]: https://opensource.com/sites/default/files/libreelec_0.png (LibreElec )
|
||||
[8]: https://opensource.com/sites/default/files/osmc.png (OSMC)
|
||||
[9]: https://opensource.com/life/16/10/which-raspberry-pi-should-you-choose-your-project
|
||||
[10]: mailto:pi@home.mydomain.com
|
||||
[11]: https://opensource.com/sites/default/files/resize/screenshot_from_2017-04-07_15-13-01-700x380.png
|
||||
[12]: http://stackoverflow.com/questions/20898384/ssh-disable-password-authentication
|
||||
[13]: https://nmap.org/
|
||||
[14]: http://www.pettingers.org/code/sshblack.html
|
||||
[15]: https://www.fail2ban.org/wiki/index.php/Main_Page
|
||||
[16]: https://www.raspberrypi.org/products/camera-module-v2/
|
||||
[17]: https://opensource.com/life/15/6/raspberry-pi-camera-projects
|
||||
[18]: https://www.raspberrypi.org/products/pi-noir-camera-v2/
|
||||
[19]: http://picamera.readthedocs.io/
|
||||
[20]: https://www.raspberrypi.org/documentation/usage/camera/raspicam/raspistill.md
|
||||
[21]: https://www.raspberrypi.org/documentation/linux/usage/cron.md
|
||||
[22]: https://github.com/RZRZR/plant-cam
|
||||
[23]: https://github.com/bennuttall/bett-bot
|
||||
[24]: http://picamera.readthedocs.io/en/release-1.13/recipes2.html#web-streaming
|
||||
[25]: https://github.com/waveform80/pistreaming
|
||||
[26]: http://jsmpeg.com/
|
||||
[27]: https://opensource.com/sites/default/files/tortoise.jpg (Tortoise)
|
||||
[28]: https://shop.pimoroni.com/products/pan-tilt-hat
|
||||
[29]: https://github.com/waveform80/pistreaming/tree/pantilthat
|
||||
[30]: https://opensource.com/sites/default/files/pan-tilt.gif (Pan-tilt)
|
||||
[31]: https://learn.adafruit.com/onion-pi/overview
|
||||
[32]: https://www.torproject.org/
|
||||
[33]: https://opensource.com/sites/default/files/onion-pi.jpg (Onion-Pi)
|
||||
[34]: https://opensource.com/sites/default/files/resize/pi-hole-250x250.png (Pi-hole)
|
||||
[35]: https://pi-hole.net/
|
||||
|
||||
|
||||
|
@ -0,0 +1,103 @@
|
||||
为 man 手册页编写解析器的备忘录
|
||||
======
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/11/235607fiqfqapvpzqhh8n1.jpg)
|
||||
|
||||
我一般都很喜欢无所事事,但有时候太无聊了也不行 —— 2015 年的一个星期天下午就是这样,我决定开始写一个开源项目来让我不那么无聊。
|
||||
|
||||
在我寻求创意时,我偶然发现了一个请求,要求构建一个由 [Mathias Bynens][2] 提出的“[按 Web 标准构建的 Man 手册页查看器][1]”。没有考虑太多,我开始使用 JavaScript 编写一个手册页解析器,经过大量的反复思考,最终做出了一个 [Jroff][3]。
|
||||
|
||||
那时候,我非常熟悉手册页这个概念,而且使用过很多次,但我知道的仅止于此,我不知道它们是如何生成的,或者是否有一个标准。在经过两年后,我有了一些关于此事的想法。
|
||||
|
||||
### man 手册页是如何写的
|
||||
|
||||
当时令我感到惊讶的第一件事是,手册页的核心只是存储在系统某处的纯文本文件(你可以使用 `manpath` 命令检查这些目录)。
|
||||
|
||||
此文件中不仅包含文档,还包含使用了 20 世纪 70 年代名为 `troff` 的排版系统的格式化信息。
|
||||
|
||||
> troff 及其 GNU 实现 groff 是处理文档的文本描述以生成适合打印的排版版本的程序。**它更像是“你所描述的即你得到的”,而不是你所见即所得的。**
|
||||
>
|
||||
> - 摘自 [troff.org][4]
|
||||
|
||||
如果你对排版格式毫不熟悉,可以将它们视为 steroids 期刊用的 Markdown,但其灵活性带来的就是更复杂的语法:
|
||||
|
||||
![groff-compressor][5]
|
||||
|
||||
`groff` 文件可以手工编写,也可以使用许多不同的工具从其他格式生成,如 Markdown、Latex、HTML 等。
|
||||
|
||||
为什么 `groff` 和 man 手册页绑在一起是有历史原因的,其格式[随时间有变化][6],它的血统由一系列类似命名的程序组成:RUNOFF > roff > nroff > troff > groff。
|
||||
|
||||
但这并不一定意味着 `groff` 与手册页有多紧密的关系,它是一种通用格式,已被用于[书籍][7],甚至用于[照相排版][8]。
|
||||
|
||||
此外,值得注意的是 `groff` 也可以调用后处理器将其中间输出结果转换为最终格式,这对于终端显示来说不一定是 ascii !一些支持的格式是:TeX DVI、HTML、Canon、HP LaserJet4 兼容格式、PostScript、utf8 等等。
|
||||
|
||||
### 宏
|
||||
|
||||
该格式的其他很酷的功能是它的可扩展性,你可以编写宏来增强其基本功能。
|
||||
|
||||
鉴于 *nix 系统的悠久历史,有几个可以根据你想要生成的输出而将特定功能组合在一起的宏包,例如 `man`、`mdoc`、`mom`、`ms`、`mm` 等等。
|
||||
|
||||
手册页通常使用 `man` 和 `mdoc` 宏包编写。
|
||||
|
||||
区分原生的 `groff` 命令和宏的方式是通过标准 `groff` 包大写其宏名称。对于 `man` 宏包,每个宏的名称都是大写的,如 `.PP`、`.TH`、`.SH` 等。对于 `mdoc` 宏包,只有第一个字母是大写的: `.Pp`、`.Dt`、`.Sh`。
|
||||
|
||||
![groff-example][9]
|
||||
|
||||
### 挑战
|
||||
|
||||
无论你是考虑编写自己的 `groff` 解析器,还是只是好奇,这些都是我发现的一些更具挑战性的问题。
|
||||
|
||||
#### 上下文敏感的语法
|
||||
|
||||
表面上,`groff` 的语法是上下文无关的,遗憾的是,因为宏描述的是主体不透明的令牌,所以包中的宏集合本身可能不会实现上下文无关的语法。
|
||||
|
||||
这导致我在那时做不出来一个解析器生成器(不管好坏)。
|
||||
|
||||
#### 嵌套的宏
|
||||
|
||||
`mdoc` 宏包中的大多数宏都是可调用的,这差不多意味着宏可以用作其他宏的参数,例如,你看看这个:
|
||||
|
||||
* 宏 `Fl`(Flag)会在其参数中添加破折号,因此 `Fl s` 会生成 `-s`
|
||||
* 宏 `Ar`(Argument)提供了定义参数的工具
|
||||
* 宏 `Op`(Optional)会将其参数括在括号中,因为这是将某些东西定义为可选的标准习惯用法
|
||||
* 以下组合 `.Op Fl s Ar file ` 将生成 `[-s file]`,因为 `Op` 宏可以嵌套。
|
||||
|
||||
#### 缺乏适合初学者的资源
|
||||
|
||||
让我感到困惑的是缺乏一个规范的、定义明确的、清晰的来源,网上有很多信息,这些信息对读者来说很重要,需要时间来掌握。
|
||||
|
||||
### 有趣的宏
|
||||
|
||||
总结一下,我会向你提供一个非常简短的宏列表,我在开发 jroff 时发现它很有趣:
|
||||
|
||||
`man` 宏包:
|
||||
|
||||
* `.TH`:用 `man` 宏包编写手册页时,你的第一个不是注释的行必须是这个宏,它接受五个参数:`title`、`section`、`date`、`source`、`manual`。
|
||||
* `.BI`:粗体加斜体(特别适用于函数格式)
|
||||
* `.BR`:粗体加正体(特别适用于参考其他手册页)
|
||||
|
||||
`mdoc` 宏包:
|
||||
|
||||
* `.Dd`、`.Dt`、`.Os`:类似于 `man` 宏包需要 `.TH`,`mdoc` 宏也需要这三个宏,需要按特定顺序使用。它们的缩写分别代表:文档日期、文档标题和操作系统。
|
||||
* `.Bl`、`.It`、`.El`:这三个宏用于创建列表,它们的名称不言自明:开始列表、项目和结束列表。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://monades.roperzh.com/memories-writing-parser-man-pages/
|
||||
|
||||
作者:[Roberto Dip][a]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://monades.roperzh.com
|
||||
[1]:https://github.com/h5bp/lazyweb-requests/issues/114
|
||||
[2]:https://mathiasbynens.be/
|
||||
[3]:jroff
|
||||
[4]:https://www.troff.org/
|
||||
[5]:https://user-images.githubusercontent.com/4419992/37868021-2e74027c-2f7f-11e8-894b-80829ce39435.gif
|
||||
[6]:https://manpages.bsd.lv/history.html
|
||||
[7]:https://rkrishnan.org/posts/2016-03-07-how-is-gopl-typeset.html
|
||||
[8]:https://en.wikipedia.org/wiki/Phototypesetting
|
||||
[9]:https://user-images.githubusercontent.com/4419992/37866838-e602ad78-2f6e-11e8-97a9-2a4494c766ae.jpg
|
@ -0,0 +1,191 @@
|
||||
如何使用 GParted 实用工具缩放根分区
|
||||
======
|
||||
|
||||
今天,我们将讨论磁盘分区。这是 Linux 中的一个好话题。这允许用户来重新调整在 Linux 中的活动 root 分区。
|
||||
|
||||
在这篇文章中,我们将教你如何使用 GParted 缩放在 Linux 上的活动根分区。
|
||||
|
||||
比如说,当我们安装 Ubuntu 操作系统时,并没有恰当地配置,我们的系统仅有 30 GB 磁盘。我们需要安装另一个操作系统,因此我们想在其中制作第二个分区。
|
||||
|
||||
虽然不建议重新调整活动分区。然而,我们要执行这个操作,因为没有其它方法来释放系统分区。
|
||||
|
||||
> 注意:在执行这个动作前,确保你备份了重要的数据,因为如果一些东西出错(例如,电源故障或你的系统重启),你可以得以保留你的数据。
|
||||
|
||||
### Gparted 是什么
|
||||
|
||||
[GParted][1] 是一个自由的分区管理器,它使你能够缩放、复制和移动分区,而不丢失数据。通过使用 GParted 的 Live 可启动镜像,我们可以使用 GParted 应用程序的所有功能。GParted Live 可以使你能够在 GNU/Linux 以及其它的操作系统上使用 GParted,例如,Windows 或 Mac OS X 。
|
||||
|
||||
#### 1) 使用 df 命令检查磁盘空间利用率
|
||||
|
||||
我只是想使用 `df` 命令向你显示我的分区。`df` 命令输出清楚地表明我仅有一个分区。
|
||||
|
||||
```
|
||||
$ df -h
|
||||
Filesystem Size Used Avail Use% Mounted on
|
||||
/dev/sda1 30G 3.4G 26.2G 16% /
|
||||
none 4.0K 0 4.0K 0% /sys/fs/cgroup
|
||||
udev 487M 4.0K 487M 1% /dev
|
||||
tmpfs 100M 844K 99M 1% /run
|
||||
none 5.0M 0 5.0M 0% /run/lock
|
||||
none 498M 152K 497M 1% /run/shm
|
||||
none 100M 52K 100M 1% /run/user
|
||||
```
|
||||
|
||||
#### 2) 使用 fdisk 命令检查磁盘分区
|
||||
|
||||
我将使用 `fdisk` 命令验证这一点。
|
||||
|
||||
```
|
||||
$ sudo fdisk -l
|
||||
[sudo] password for daygeek:
|
||||
|
||||
Disk /dev/sda: 33.1 GB, 33129218048 bytes
|
||||
255 heads, 63 sectors/track, 4027 cylinders, total 64705504 sectors
|
||||
Units = sectors of 1 * 512 = 512 bytes
|
||||
Sector size (logical/physical): 512 bytes / 512 bytes
|
||||
I/O size (minimum/optimal): 512 bytes / 512 bytes
|
||||
Disk identifier: 0x000473a3
|
||||
|
||||
Device Boot Start End Blocks Id System
|
||||
/dev/sda1 * 2048 62609407 31303680 83 Linux
|
||||
/dev/sda2 62611454 64704511 1046529 5 Extended
|
||||
/dev/sda5 62611456 64704511 1046528 82 Linux swap / Solaris
|
||||
```
|
||||
|
||||
#### 3) 下载 GParted live ISO 镜像
|
||||
|
||||
使用下面的命令来执行下载 GParted live ISO。
|
||||
|
||||
```
|
||||
$ wget https://downloads.sourceforge.net/gparted/gparted-live-0.31.0-1-amd64.iso
|
||||
```
|
||||
|
||||
#### 4) 使用 GParted Live 安装介质启动你的系统
|
||||
|
||||
使用 GParted Live 安装介质(如烧录的 CD/DVD 或 USB 或 ISO 镜像)启动你的系统。你将获得类似于下面屏幕的输出。在这里选择 “GParted Live (Default settings)” ,并敲击回车按键。
|
||||
|
||||
![][3]
|
||||
|
||||
#### 5) 键盘选择
|
||||
|
||||
默认情况下,它选择第二个选项,按下回车即可。
|
||||
|
||||
![][4]
|
||||
|
||||
#### 6) 语言选择
|
||||
|
||||
默认情况下,它选择 “33” 美国英语,按下回车即可。
|
||||
|
||||
![][5]
|
||||
|
||||
#### 7) 模式选择(图形用户界面或命令行)
|
||||
|
||||
默认情况下,它选择 “0” 图形用户界面模式,按下回车即可。
|
||||
|
||||
![][6]
|
||||
|
||||
#### 8) 加载 GParted Live 屏幕
|
||||
|
||||
现在,GParted Live 屏幕已经加载,它显示我以前创建的分区列表。
|
||||
|
||||
![][7]
|
||||
|
||||
#### 9) 如何重新调整根分区大小
|
||||
|
||||
选择你想重新调整大小的根分区,在这里仅有一个分区,所以我将编辑这个分区以便于安装另一个操作系统。
|
||||
|
||||
![][8]
|
||||
|
||||
为做到这一点,按下 “Resize/Move” 按钮来重新调整分区大小。
|
||||
|
||||
![][9]
|
||||
|
||||
现在,在第一个框中输入你想从这个分区中取出的大小。我将索要 “10GB”,所以,我添加 “10240MB”,并让该对话框的其余部分为默认值,然后点击 “Resize/Move” 按钮。
|
||||
|
||||
![][10]
|
||||
|
||||
它将再次要求你确认重新调整分区的大小,因为你正在编辑活动的系统分区,然后点击 “Ok”。
|
||||
|
||||
![][11]
|
||||
|
||||
分区从 30GB 缩小到 20GB 已经成功。也显示 10GB 未分配的磁盘空间。
|
||||
|
||||
![][12]
|
||||
|
||||
最后点击 “Apply” 按钮来执行下面剩余的操作。
|
||||
|
||||
![][13]
|
||||
|
||||
`e2fsck` 是一个文件系统检查实用程序,自动修复文件系统中与 HDD 相关的坏扇道、I/O 错误。
|
||||
|
||||
![][14]
|
||||
|
||||
`resize2fs` 程序将重新调整 ext2、ext3 或 ext4 文件系统的大小。它可以被用于扩大或缩小一个位于设备上的未挂载的文件系统。
|
||||
|
||||
![][15]
|
||||
|
||||
`e2image` 程序将保存位于设备上的关键的 ext2、ext3 或 ext4 文件系统的元数据到一个指定文件中。
|
||||
|
||||
![][16]
|
||||
|
||||
所有的操作完成,关闭对话框。
|
||||
|
||||
![][17]
|
||||
|
||||
现在,我们可以看到未分配的 “10GB” 磁盘分区。
|
||||
|
||||
![][18]
|
||||
|
||||
重启系统来检查这一结果。
|
||||
|
||||
![][19]
|
||||
|
||||
#### 10) 检查剩余空间
|
||||
|
||||
重新登录系统,并使用 `fdisk` 命令来查看在分区中可用的空间。是的,我可以看到这个分区上未分配的 “10GB” 磁盘空间。
|
||||
|
||||
```
|
||||
$ sudo parted /dev/sda print free
|
||||
[sudo] password for daygeek:
|
||||
Model: ATA VBOX HARDDISK (scsi)
|
||||
Disk /dev/sda: 32.2GB
|
||||
Sector size (logical/physical): 512B/512B
|
||||
Partition Table: msdos
|
||||
Disk Flags:
|
||||
|
||||
Number Start End Size Type File system Flags
|
||||
32.3kB 10.7GB 10.7GB Free Space
|
||||
1 10.7GB 32.2GB 21.5GB primary ext4 boot
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.2daygeek.com/author/magesh/
|
||||
[1]:https://gparted.org/
|
||||
[2]:data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[3]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-1.png
|
||||
[4]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-2.png
|
||||
[5]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-3.png
|
||||
[6]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-4.png
|
||||
[7]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-5.png
|
||||
[8]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-6.png
|
||||
[9]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-7.png
|
||||
[10]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-8.png
|
||||
[11]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-9.png
|
||||
[12]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-10.png
|
||||
[13]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-11.png
|
||||
[14]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-12.png
|
||||
[15]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-13.png
|
||||
[16]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-14.png
|
||||
[17]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-15.png
|
||||
[18]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-16.png
|
||||
[19]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-17.png
|
@ -0,0 +1,172 @@
|
||||
BootISO:从 ISO 文件中创建一个可启动的 USB 设备
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/16/110109qq0b7atyaped3ij2.jpg)
|
||||
|
||||
为了安装操作系统,我们中的大多数人(包括我)经常从 ISO 文件中创建一个可启动的 USB 设备。为达到这个目的,在 Linux 中有很多自由可用的应用程序。甚至在过去我们写了几篇介绍这种实用程序的文章。
|
||||
|
||||
每个人使用不同的应用程序,每个应用程序有它们自己的特色和功能。在这些应用程序中,一些应用程序属于 CLI 程序,一些应用程序则是 GUI 的。
|
||||
|
||||
今天,我们将讨论名为 BootISO 的实用程序类似工具。它是一个简单的 bash 脚本,允许用户来从 ISO 文件中创建一个可启动的 USB 设备。
|
||||
|
||||
很多 Linux 管理员使用 `dd` 命令开创建可启动的 ISO ,它是一个著名的原生方法,但是与此同时,它也是一个非常危险的命令。因此,小心,当你用 `dd` 命令执行一些动作时。
|
||||
|
||||
建议阅读:
|
||||
|
||||
- [Etcher:从一个 ISO 镜像中创建一个可启动的 USB 驱动器 & SD 卡的简单方法][1]
|
||||
- [在 Linux 上使用 dd 命令来从一个 ISO 镜像中创建一个可启动的 USB 驱动器][2]
|
||||
|
||||
### BootISO 是什么
|
||||
|
||||
[BootISO][3] 是一个简单的 bash 脚本,允许用户来安全的从一个 ISO 文件中创建一个可启动的 USB 设备,它是用 bash 编写的。
|
||||
|
||||
它不提供任何图形用户界面而是提供了大量的选项,可以让初学者顺利地在 Linux 上来创建一个可启动的 USB 设备。因为它是一个智能工具,能自动地选择连接到系统上的 USB 设备。
|
||||
|
||||
当系统有多个 USB 设备连接,它将打印出列表。当你手动选择了另一个硬盘而不是 USB 时,在这种情况下,它将安全地退出,而不会在硬盘上写入任何东西。
|
||||
|
||||
这个脚本也将检查依赖关系,并提示用户安装,它可以与所有的软件包管理器一起工作,例如 apt-get、yum、dnf、pacman 和 zypper。
|
||||
|
||||
### BootISO 的功能
|
||||
|
||||
* 它检查选择的 ISO 是否是正确的 mime 类型。如果不是,那么退出。
|
||||
* 如果你选择除 USB 设备以外的任何其它的磁盘(本地硬盘),BootISO 将自动地退出。
|
||||
* 当你有多个驱动器时,BootISO 允许用户选择想要使用的 USB 驱动器。
|
||||
* 在擦除和分区 USB 设备前,BootISO 会提示用户确认。
|
||||
* BootISO 将正确地处理来自一个命令的任何错误,并退出。
|
||||
* BootISO 在遇到问题退出时将调用一个清理例行程序。
|
||||
|
||||
### 如何在 Linux 中安装 BootISO
|
||||
|
||||
在 Linux 中安装 BootISO 有几个可用的方法,但是,我建议用户使用下面的方法安装。
|
||||
|
||||
```
|
||||
$ curl -L https://git.io/bootiso -O
|
||||
$ chmod +x bootiso
|
||||
$ sudo mv bootiso /usr/local/bin/
|
||||
```
|
||||
|
||||
一旦 BootISO 已经安装,运行下面的命令来列出可用的 USB 设备。
|
||||
|
||||
```
|
||||
$ bootiso -l
|
||||
|
||||
Listing USB drives available in your system:
|
||||
NAME HOTPLUG SIZE STATE TYPE
|
||||
sdd 1 32G running disk
|
||||
```
|
||||
|
||||
如果你仅有一个 USB 设备,那么简单地运行下面的命令来从一个 ISO 文件中创建一个可启动的 USB 设备。
|
||||
|
||||
```
|
||||
$ bootiso /path/to/iso file
|
||||
```
|
||||
|
||||
```
|
||||
$ bootiso /opt/iso_images/archlinux-2018.05.01-x86_64.iso
|
||||
Granting root privileges for bootiso.
|
||||
Listing USB drives available in your system:
|
||||
NAME HOTPLUG SIZE STATE TYPE
|
||||
sdd 1 32G running disk
|
||||
Autoselecting `sdd' (only USB device candidate)
|
||||
The selected device `/dev/sdd' is connected through USB.
|
||||
Created ISO mount point at `/tmp/iso.vXo'
|
||||
`bootiso' is about to wipe out the content of device `/dev/sdd'.
|
||||
Are you sure you want to proceed? (y/n)>y
|
||||
Erasing contents of /dev/sdd...
|
||||
Creating FAT32 partition on `/dev/sdd1'...
|
||||
Created USB device mount point at `/tmp/usb.0j5'
|
||||
Copying files from ISO to USB device with `rsync'
|
||||
Synchronizing writes on device `/dev/sdd'
|
||||
`bootiso' took 250 seconds to write ISO to USB device with `rsync' method.
|
||||
ISO succesfully unmounted.
|
||||
USB device succesfully unmounted.
|
||||
USB device succesfully ejected.
|
||||
You can safely remove it !
|
||||
```
|
||||
|
||||
当你有多个 USB 设备时,可以使用 `--device` 选项指明你的设备名称。
|
||||
|
||||
```
|
||||
$ bootiso -d /dev/sde /opt/iso_images/archlinux-2018.05.01-x86_64.iso
|
||||
```
|
||||
|
||||
默认情况下,BootISO 使用 `rsync` 命令来执行所有的动作,如果你想使用 `dd` 命令代替它,使用下面的格式。
|
||||
|
||||
```
|
||||
$ bootiso --dd -d /dev/sde /opt/iso_images/archlinux-2018.05.01-x86_64.iso
|
||||
```
|
||||
|
||||
如果你想跳过 mime 类型检查,BootISO 实用程序带有下面的选项。
|
||||
|
||||
```
|
||||
$ bootiso --no-mime-check -d /dev/sde /opt/iso_images/archlinux-2018.05.01-x86_64.iso
|
||||
```
|
||||
|
||||
为 BootISO 添加下面的选项来跳过在擦除和分区 USB 设备前的用户确认。
|
||||
|
||||
```
|
||||
$ bootiso -y -d /dev/sde /opt/iso_images/archlinux-2018.05.01-x86_64.iso
|
||||
```
|
||||
|
||||
连同 `-y` 选项一起,启用自动选择 USB 设备。
|
||||
|
||||
```
|
||||
$ bootiso -y -a /opt/iso_images/archlinux-2018.05.01-x86_64.iso
|
||||
```
|
||||
|
||||
为知道更多的 BootISO 选项,运行下面的命令。
|
||||
|
||||
```
|
||||
$ bootiso -h
|
||||
Create a bootable USB from any ISO securely.
|
||||
Usage: bootiso [...]
|
||||
|
||||
Options
|
||||
|
||||
-h, --help, help Display this help message and exit.
|
||||
-v, --version Display version and exit.
|
||||
-d, --device Select block file as USB device.
|
||||
If is not connected through USB, `bootiso' will fail and exit.
|
||||
Device block files are usually situated in /dev/sXX or /dev/hXX.
|
||||
You will be prompted to select a device if you don't use this option.
|
||||
-b, --bootloader Install a bootloader with syslinux (safe mode) for non-hybrid ISOs. Does not work with `--dd' option.
|
||||
-y, --assume-yes `bootiso' won't prompt the user for confirmation before erasing and partitioning USB device.
|
||||
Use at your own risks.
|
||||
-a, --autoselect Enable autoselecting USB devices in conjunction with -y option.
|
||||
Autoselect will automatically select a USB drive device if there is exactly one connected to the system.
|
||||
Enabled by default when neither -d nor --no-usb-check options are given.
|
||||
-J, --no-eject Do not eject device after unmounting.
|
||||
-l, --list-usb-drives List available USB drives.
|
||||
-M, --no-mime-check `bootiso' won't assert that selected ISO file has the right mime-type.
|
||||
-s, --strict-mime-check Disallow loose application/octet-stream mime type in ISO file.
|
||||
-- POSIX end of options.
|
||||
--dd Use `dd' utility instead of mounting + `rsync'.
|
||||
Does not allow bootloader installation with syslinux.
|
||||
--no-usb-check `bootiso' won't assert that selected device is a USB (connected through USB bus).
|
||||
Use at your own risks.
|
||||
|
||||
Readme
|
||||
|
||||
Bootiso v2.5.2.
|
||||
Author: Jules Samuel Randolph
|
||||
Bugs and new features: https://github.com/jsamr/bootiso/issues
|
||||
If you like bootiso, please help the community by making it visible:
|
||||
* star the project at https://github.com/jsamr/bootiso
|
||||
* upvote those SE post: https://goo.gl/BNRmvm https://goo.gl/YDBvFe
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/bootiso-a-simple-bash-script-to-securely-create-a-bootable-usb-device-in-linux-from-iso-file/
|
||||
|
||||
作者:[Prakash Subramanian][a]
|
||||
选题:[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/) 荣誉推出
|
||||
|
||||
[a]:https://www.2daygeek.com/author/prakash/
|
||||
[1]:https://www.2daygeek.com/etcher-easy-way-to-create-a-bootable-usb-drive-sd-card-from-an-iso-image-on-linux/
|
||||
[2]:https://www.2daygeek.com/create-a-bootable-usb-drive-from-an-iso-image-using-dd-command-on-linux/
|
||||
[3]:https://github.com/jsamr/bootiso
|
@ -1,21 +1,22 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lujun9972)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10977-1.html)
|
||||
[#]: subject: (Get desktop notifications from Emacs shell commands ·)
|
||||
[#]: via: (https://blog.hoetzel.info/post/eshell-notifications/)
|
||||
[#]: author: (Jürgen Hötzel https://blog.hoetzel.info)
|
||||
|
||||
Get desktop notifications from Emacs shell commands ·
|
||||
让 Emacs shell 命令发送桌面通知
|
||||
======
|
||||
When interacting with the operating systems I always use [Eshell][1] because it integrates seamlessly with Emacs, supports (remote) [TRAMP][2] file names and also works nice on Windows.
|
||||
|
||||
After starting shell commands (like long running build jobs) I often lose track the task when switching buffers.
|
||||
我总是使用 [Eshell][1] 来与操作系统进行交互,因为它与 Emacs 无缝整合、支持处理 (远程) [TRAMP][2] 文件,而且在 Windows 上也能工作得很好。
|
||||
|
||||
Thanks to Emacs [hooks][3] mechanism you can customize Emacs to call a elisp function when an external command finishes.
|
||||
启动 shell 命令后 (比如耗时严重的构建任务) 我经常会由于切换缓冲区而忘了追踪任务的运行状态。
|
||||
|
||||
I use [John Wiegleys][4] excellent [alert][5] package to send desktop notifications:
|
||||
多亏了 Emacs 的 [钩子][3] 机制,你可以配置 Emacs 在某个外部命令完成后调用一个 elisp 函数。
|
||||
|
||||
我使用 [John Wiegleys][4] 所编写的超棒的 [alert][5] 包来发送桌面通知:
|
||||
|
||||
```
|
||||
(require 'alert)
|
||||
@ -32,7 +33,7 @@ I use [John Wiegleys][4] excellent [alert][5] package to send desktop notificati
|
||||
(add-hook 'eshell-kill-hook #'eshell-command-alert)
|
||||
```
|
||||
|
||||
[alert][5] rules can be setup programmatically. In my case I only want to get notified if the corresponding buffer is not visible:
|
||||
[alert][5] 的规则可以用程序来设置。就我这个情况来看,我只需要当对应的缓冲区不可见时得到通知:
|
||||
|
||||
```
|
||||
(alert-add-rule :status '(buried) ;only send alert when buffer not visible
|
||||
@ -40,7 +41,8 @@ I use [John Wiegleys][4] excellent [alert][5] package to send desktop notificati
|
||||
:style 'notifications)
|
||||
```
|
||||
|
||||
This even works on [TRAMP][2] buffers. Below is a screenshot showing a Gnome desktop notification of a failed `make` command.
|
||||
|
||||
这甚至对于 [TRAMP][2] 也一样生效。下面这个截屏展示了失败的 `make` 命令产生的 Gnome 桌面通知。
|
||||
|
||||
![../../img/eshell.png][6]
|
||||
|
||||
@ -51,7 +53,7 @@ via: https://blog.hoetzel.info/post/eshell-notifications/
|
||||
作者:[Jürgen Hötzel][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lujun9972](https://github.com/lujun9972)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
@ -62,4 +64,4 @@ via: https://blog.hoetzel.info/post/eshell-notifications/
|
||||
[3]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Hooks.html (hooks)
|
||||
[4]: https://github.com/jwiegley (John Wiegleys)
|
||||
[5]: https://github.com/jwiegley/alert (alert)
|
||||
[6]: https://blog.hoetzel.info/img/eshell.png (../../img/eshell.png)
|
||||
[6]: https://blog.hoetzel.info/img/eshell.png
|
@ -0,0 +1,56 @@
|
||||
一条日志消息的现代生活
|
||||
======
|
||||
|
||||
> 从一条日志消息的角度来巡览现代分布式系统。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/18/193030frxkcoccjhorz42o.jpg)
|
||||
|
||||
混沌系统往往是不可预测的。在构建像分布式系统这样复杂的东西时,这一点尤其明显。如果不加以控制,这种不可预测性会无止境的浪费时间。因此,分布式系统的每个组件,无论多小,都必须设计成以简化的方式组合在一起。
|
||||
|
||||
[Kubernetes][1] 为抽象计算资源提供了一个很有前景的模型 —— 但即使是它也必须与其他分布式平台(如 [Apache Kafka][2])协调一致,以确保可靠的数据传输。如果有人要整合这两个平台,它会如何运作?此外,如果你通过这样的系统跟踪像日志消息这么简单的东西,它会是什么样子?本文将重点介绍来自在 [OKD][3] 内运行的应用程序的日志消息如何通过 Kafka 进入数据仓库(OKD 是为 Red Hat OpenShift 提供支持的 Kubernetes 的原初社区发行版)。
|
||||
|
||||
### OKD 定义的环境
|
||||
|
||||
这样的旅程始于 OKD,因为该容器平台完全覆盖了它抽象的硬件。这意味着日志消息等待由驻留在容器中的应用程序写入 stdout 或 stderr 流。从那里,日志消息被容器引擎(例如 [CRI-O][4])重定向到节点的文件系统。
|
||||
|
||||
![](https://opensource.com/sites/default/files/uploads/logmessagepathway.png)
|
||||
|
||||
在 OpenShift 中,一个或多个容器封装在称为 pod(豆荚)的虚拟计算节点中。实际上,在 OKD 中运行的所有应用程序都被抽象为 pod。这允许应用程序以统一的方式操纵。这也大大简化了分布式组件之间的通信,因为 pod 可以通过 IP 地址和[负载均衡服务][5]进行系统寻址。因此,当日志消息由日志收集器应用程序从节点的文件系统获取时,它可以很容易地传递到在 OpenShift 中运行的另一个 pod 中。
|
||||
|
||||
### 在豆荚里的两个豌豆
|
||||
|
||||
为了确保可以在整个分布式系统中四处传播日志消息,日志收集器需要将日志消息传递到在 OpenShift 中运行的 Kafka 集群数据中心。通过 Kafka,日志消息可以以可靠且容错的方式低延迟传递给消费应用程序。但是,为了在 OKD 定义的环境中获得 Kafka 的好处,Kafka 需要完全集成到 OKD 中。
|
||||
|
||||
运行 [Strimzi 操作子][6]将所有 Kafka 组件实例化为 pod,并将它们集成在 OKD 环境中运行。 这包括用于排队日志消息的 Kafka 代理,用于从 Kafka 代理读取和写入的 Kafka 连接器,以及用于管理 Kafka 集群状态的 Zookeeper 节点。Strimzi 还可以将日志收集器实例化兼做 Kafka 连接器,允许日志收集器将日志消息直接提供给在 OKD 中运行的 Kafka 代理 pod。
|
||||
|
||||
### 在 OKD 内的 Kafka
|
||||
|
||||
当日志收集器 pod 将日志消息传递给 Kafka 代理时,收集器会写到单个代理分区,并将日志消息附加到该分区的末尾。使用 Kafka 的一个优点是它将日志收集器与日志的最终目标分离。由于解耦,日志收集器不关心日志最后是放在 [Elasticsearch][7]、Hadoop、Amazon S3 中的某个还是全都。Kafka 与所有基础设施连接良好,因此 Kafka 连接器可以在任何需要的地方获取日志消息。
|
||||
|
||||
一旦写入 Kafka 代理的分区,该日志消息就会在 Kafka 集群内的跨代理分区复制。这是它的一个非常强大的概念;结合平台的自愈功能,它创建了一个非常有弹性的分布式系统。例如,当节点变得不可用时,(故障)节点上运行的应用程序几乎立即在健康节点上生成。因此,即使带有 Kafka 代理的节点丢失或损坏,日志消息也能保证存活在尽可能多的节点上,并且新的 Kafka 代理将快速原位取代。
|
||||
|
||||
### 存储起来
|
||||
|
||||
在日志消息被提交到 Kafka 主题后,它将等待 Kafka 连接器使用它,该连接器将日志消息中继到分析引擎或日志记录仓库。在传递到其最终目的地时,可以分析日志消息以进行异常检测,也可以查询日志以立即进行根本原因分析,或用于其他目的。无论哪种方式,日志消息都由 Kafka 以安全可靠的方式传送到目的地。
|
||||
|
||||
OKD 和 Kafka 是正在迅速发展的功能强大的分布式平台。创建能够在不影响性能的情况下抽象出分布式计算的复杂特性的系统至关重要。毕竟,如果我们不能简化单一日志消息的旅程,我们怎么能夸耀全系统的效率呢?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/9/life-log-message
|
||||
|
||||
作者:[Josef Karásek][a]
|
||||
选题:[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/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jkarasek
|
||||
[1]: https://kubernetes.io/
|
||||
[2]: https://kafka.apache.org/
|
||||
[3]: https://www.okd.io/
|
||||
[4]: http://cri-o.io/
|
||||
[5]: https://kubernetes.io/docs/concepts/services-networking/service/
|
||||
[6]: http://strimzi.io/
|
||||
[7]: https://www.elastic.co/
|
@ -0,0 +1,178 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10969-1.html)
|
||||
[#]: subject: (GoAccess – A Real-Time Web Server Log Analyzer And Interactive Viewer)
|
||||
[#]: via: (https://www.2daygeek.com/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer/)
|
||||
[#]: author: (Vinoth Kumar https://www.2daygeek.com/author/vinoth/)
|
||||
|
||||
GoAccess:一个实时的 Web 日志分析器及交互式查看器
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/12/222616h80pl0k0tt811071.jpg)
|
||||
|
||||
分析日志文件对于 Linux 管理员来说是一件非常令人头疼的事情,因为它记录了很多东西。大多数新手和初级管理员都不知道如何分析。如果你在分析日志方面拥有很多知识,那么你就成了 *NIX 系统高手。
|
||||
|
||||
Linux 中有许多工具可以轻松分析日志。GoAccess 是允许用户轻松分析 Web 服务器日志的工具之一。我们将在本文中详细讨论 GoAccess 工具。
|
||||
|
||||
### GoAccess
|
||||
|
||||
GoAccess 是一个实时 Web 日志分析器和交互式查看器,可以在 *nix 系统中的终端运行或通过浏览器访问。
|
||||
|
||||
GoAccess 需要的依赖极少,它是用 C 语言编写的,只需要 ncurses。
|
||||
|
||||
它支持 Apache、Nginx 和 Lighttpd 日志。它为需要动态可视化服务器报告的系统管理员即时提供了快速且有价值的 HTTP 统计信息。
|
||||
|
||||
GoAccess 可以解析指定的 Web 日志文件并将数据输出到 X 终端和浏览器。
|
||||
|
||||
GoAccess 被设计成一个基于终端的快速日志分析器。其核心思想是实时快速分析和查看 Web 服务器统计信息,而无需使用浏览器。
|
||||
|
||||
默认输出是在终端输出,它也能够生成完整的、自包含的实时 HTML 报告,以及 JSON 和 CSV 报告。
|
||||
|
||||
GoAccess 支持任何自定义日志格式,并包含以下预定义日志格式选项:Apache/Nginx 中的组合日志格式 XLF/ELF,Apache 中的通用日志格式 CLF,但不限于此。
|
||||
|
||||
### GoAccess 功能
|
||||
|
||||
* 完全实时:所有指标在终端上每 200 毫秒更新一次,在 HTML 输出上每秒更新一次。
|
||||
* 跟踪应用程序响应时间:跟踪服务请求所需的时间。如果你想跟踪减慢了网站速度的网页,则非常有用。
|
||||
* 访问者:按小时或日期确定最慢运行的请求的点击量、访问者数、带宽数和指标。
|
||||
* 按虚拟主机的度量标准:如果有多个虚拟主机(`Server`),它提供了一个面板,可显示哪些虚拟主机正在消耗大部分 Web 服务器资源。
|
||||
|
||||
### 如何安装 GoAccess?
|
||||
|
||||
我建议用户在包管理器的帮助下从发行版官方的存储库安装 GoAccess。它在大多数发行版官方存储库中都可用。
|
||||
|
||||
我们知道,我们在标准发行方式的发行版中得到的是过时的软件包,而滚动发行方式的发行版总是包含最新的软件包。
|
||||
|
||||
如果你使用标准发行方式的发行版运行操作系统,我建议你检查替代选项,如 PPA 或 GoAccess 官方维护者存储库等,以获取最新的软件包。
|
||||
|
||||
对于 Debian / Ubuntu 系统,使用 [APT-GET 命令][1]或 [APT 命令][2]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# apt install goaccess
|
||||
```
|
||||
|
||||
要获取最新的 GoAccess 包,请使用以下 GoAccess 官方存储库。
|
||||
|
||||
```
|
||||
$ echo "deb https://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee -a /etc/apt/sources.list.d/goaccess.list
|
||||
$ wget -O - https://deb.goaccess.io/gnugpg.key | sudo apt-key add -
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install goaccess
|
||||
```
|
||||
|
||||
对于 RHEL / CentOS 系统,使用 [YUM 包管理器][3]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# yum install goaccess
|
||||
```
|
||||
|
||||
对于 Fedora 系统,使用 [DNF 包管理器][4]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# dnf install goaccess
|
||||
```
|
||||
|
||||
对于基于 ArchLinux / Manjaro 的系统,使用 [Pacman 包管理器][5]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# pacman -S goaccess
|
||||
```
|
||||
|
||||
对于 openSUSE Leap 系统,使用[Zypper 包管理器][6]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# zypper install goaccess
|
||||
# zypper ar -f obs://server:http
|
||||
# zypper ref && zypper in goaccess
|
||||
```
|
||||
|
||||
### 如何使用 GoAccess?
|
||||
|
||||
成功安装 GoAccess 后。只需输入 `goaccess` 命令,然后输入 Web 服务器日志位置即可查看。
|
||||
|
||||
```
|
||||
# goaccess [options] /path/to/Web Server/access.log
|
||||
# goaccess /var/log/apache/2daygeek_access.log
|
||||
```
|
||||
|
||||
执行上述命令时,它会要求您选择日志格式配置。
|
||||
|
||||
![][8]
|
||||
|
||||
我用 Apache 访问日志对此进行了测试。Apache 日志被分为十五个部分。详情如下。主要部分显示了这十五个部分的摘要。
|
||||
|
||||
以下屏幕截图包括四个部分,例如唯一身份访问者、请求的文件、静态请求、未找到的网址。
|
||||
|
||||
![][10]
|
||||
|
||||
以下屏幕截图包括四个部分,例如访客主机名和 IP、操作系统、浏览器、时间分布。
|
||||
|
||||
![][10]
|
||||
|
||||
以下屏幕截图包括四个部分,例如来源网址、来源网站,Google 的搜索引擎结果、HTTP状态代码。
|
||||
|
||||
![][11]
|
||||
|
||||
如果要生成 html 报告,请使用以下命令。最初我在尝试生成 html 报告时遇到错误。
|
||||
|
||||
```
|
||||
# goaccess 2daygeek_access.log -a > report.html
|
||||
|
||||
GoAccess - version 1.3 - Nov 23 2018 11:28:19
|
||||
Config file: No config file used
|
||||
|
||||
Fatal error has occurred
|
||||
Error occurred at: src/parser.c - parse_log - 2764
|
||||
No time format was found on your conf file.Parsing... [0] [0/s]
|
||||
```
|
||||
|
||||
它说“你的 conf 文件没有找到时间格式”。要解决此问题,请为其添加 “COMBINED” 日志格式选项。
|
||||
|
||||
```
|
||||
# goaccess -f 2daygeek_access.log --log-format=COMBINED -o 2daygeek.html
|
||||
Parsing...[0,165] [50,165/s]
|
||||
```
|
||||
|
||||
![][12]
|
||||
|
||||
GoAccess 也允许你访问和分析实时日志并进行过滤和解析。
|
||||
|
||||
```
|
||||
# tail -f /var/log/apache/2daygeek_access.log | goaccess -
|
||||
```
|
||||
|
||||
更多细节请参考其 man 手册页或帮助。
|
||||
|
||||
```
|
||||
# man goaccess
|
||||
或
|
||||
# goaccess --help
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer/
|
||||
|
||||
作者:[Vinoth Kumar][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/vinoth/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[2]: https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[3]: https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/
|
||||
[4]: https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[5]: https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/
|
||||
[6]: https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/
|
||||
[7]: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[8]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-1.png
|
||||
[9]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-2.png
|
||||
[10]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-3.png
|
||||
[11]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-4.png
|
||||
[12]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-5.png
|
@ -0,0 +1,140 @@
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "qfzy1233"
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-11028-1.html"
|
||||
[#]: subject: "Top 5 Linux Distributions for Productivity"
|
||||
[#]: via: "https://www.linux.com/blog/learn/2019/1/top-5-linux-distributions-productivity"
|
||||
[#]: author: "Jack Wallen https://www.linux.com/users/jlwallen"
|
||||
|
||||
5 个最具生产力的 Linux 发行版
|
||||
======
|
||||
|
||||
> 如果你正在寻找一个适合开发工作的完美环境,我敢说你找不到比 Pop!_OS 更好的选择。
|
||||
|
||||
![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/productivity_main.jpg?itok=2IKyg_7_)
|
||||
|
||||
必须承认的是,这样的一个热门话题其实很难被总结的话题。为什么呢?首先,Linux 在就是一种有生产力的操作系统。由于它极强的可靠性和稳定的平台,使得完成工作变得很容易。其次为了衡量工作的效率,你需要考虑到哪项工作需要得到生产力方面的助推。是日常办公?开发类工作?学校事务?数据挖掘?或者是人力资源?你可以看到这个问题有多复杂。
|
||||
|
||||
然而,这并不意味着某些发行版无法更好地配置将底层操作系统呈现为一个有效的平台来完成工作。恰恰相反,许多发行版在偏离生产力这条道路上越走越远,所以你不会意识到你自己处在工作的窘境中,而是继续挖掘自己的潜力在工期结束之前拼命赶上进度。这些 Linux 发行版可以帮助你化繁为简,因此或许可以减少你工作流程中的痛点。
|
||||
|
||||
让我们来看一下这些发行版并为你找出适合你的最佳选择。为了更具条理,我按照生产力诉求把它们分成了几类。这项任务本身也是一种挑战,因为每个人在生产力提升上的需要是千差万别的。然而,我所关注的是下列的几项:
|
||||
|
||||
* 常规:适于那些只需要有效地完成多项工作的人。
|
||||
* 设计:适于那些从事设计创造和图像处理的人。
|
||||
* 开发:适于那些使用 Linux 桌面发行版来进行编程工作的人。
|
||||
* 运维:适于那些需要一个发行版来促进其执行系统管理任务的人。
|
||||
* 教育:适于那些需要桌面发行版可以助力他们在教育领域更高效的人。
|
||||
|
||||
诚然,有很多很多类别的发行版可供挑选,其中的很多可能用起来十分得心应手,但这五种或许是你最为需要的。
|
||||
|
||||
### 常规
|
||||
|
||||
对于常规的生产力诉求来说,你不会找到比 [Ubuntu][1] 更为高效的了。在这个类别中首推 Ubuntu 最主要的原因是因为它实现了桌面操作系统、软件、服务的无缝集成。你可能会问为什么我不选择同类别的 Linux Mint 呢?因为 Ubuntu 现在默认的的桌面环境为 GNOME 桌面,而它拥有 GNOME 许多扩展程序的优势的加成(图 1)。
|
||||
|
||||
![GNOME Clipboard][3]
|
||||
|
||||
*图 1:运行中的 GNOME 桌面的剪切板管理工具。*
|
||||
|
||||
这些扩展程序在提升生产力方面做了很多努力(所以 Ubuntu 比 Linux Mint 获得了更多的认可)。但是 Ubuntu 不仅仅是装了一个普通的 GNOME 桌面。事实上,他们致力于将它改进的更为轻量化、更为高效、以及用户友好度更高、开箱即用。总而言之,由于 Ubuntu 正确的融合了多种特性,开箱即用,完善的软件支持(仅对工作方面而言),这些特性使它几乎成为了生产力领域最为完美的一个平台。
|
||||
|
||||
不管你是要写一篇文档,制作一张电子表格,写一个新的软件,开发公司的网站,设计商用的图形,管理一个服务器或是网络,抑或是在你的公司内从事人力资源管理工作,Ubuntu 都可以满足你的需求。Ubuntu 桌面发行版也并不要求你耗费很大的精力才能开始开始开展工作……它直接就能使用(并且工作的十分优秀)。最后,得益于它是基于 Debian 的,使得在 Ubuntu 上安装第三方的软件十分简便。
|
||||
|
||||
很难不支持这一发行版独占生产力发行版列表的鳌头,尽管 Ubuntu 几乎已经成为几乎所有“某某类顶级发行版”列表的榜首。
|
||||
|
||||
### 设计
|
||||
|
||||
如果你正在寻求提升你的平面设计效率,你不能错过 [Fedora 设计套件][5]。这一 Fedora 衍生版是由负责 Fedora 相关的艺术作品的团队亲自操刀制作的。虽然其默认选择的应用程序并不是一个庞大的工具集合,但它所包含的工具都是创建和处理图像专用的。
|
||||
|
||||
有了 GIMP、Inkscape、Darktable、Krita、Entangle、Blender、Pitivi、Scribus 等应用程序(图 2),你可以找到完成图像编辑工作所需要的一切。但是 Fedora 设计套件并不仅限于此。这个桌面平台还包括一堆教程,涵盖了许多已安装的应用程序。对于任何想要尽可能提高效率的人来说,这将是一些非常有用的信息。不过,我要说的是,GNOME 收藏夹中的教程并没有超乎[此页中][6]链接的内容。
|
||||
|
||||
![Fedora Design Suite Favorites][8]
|
||||
|
||||
*图 2:Fedora 设计套件收藏夹菜单包含了许多工具,可以让你用于图形设计。*
|
||||
|
||||
那些使用数码相机的用户肯定会喜欢 Entangle 应用程序,它可以让你在电脑上控制单反相机。
|
||||
|
||||
### 开发
|
||||
|
||||
几乎所有的 Linux 发行版都是程序员的绝佳平台。然而,有一种特定的发行版脱颖而出,并超越了其他发行版,它将是你见过的用于编程类最有效率的工具之一。这个操作系统来自 [System76][9](LCTT 译注:一家美国的计算机制造商),名为 [Pop!\_OS][10]。Pop!\_OS 是专门为创作者定制的,但不是针对艺术类。相反,Pop!\_OS 面向专门从事开发、编程和软件制作的程序员。如果你需要一个既能完美的胜任开发工作又包含符合使用习惯的桌面操作系统的开发环境,Pop!\_OS 将会是你的不二选择。(图 3)
|
||||
|
||||
可能会让你感到惊讶(考虑到这个操作系统是多么“年轻”)的是 Pop!\_OS 也是你将使用的基于 GNOME 平台的最稳定系统的之一。这意味着 Pop!\_OS 不只是为创作者和创客准备的,也是为任何想要一个可靠的操作系统的人准备的。你可以下载针对你的硬件的专门 ISO 文件,这一点是许多用户十分欣赏的。如果你有英特尔硬件,[下载][10] Intel 或 AMD 的版本。如果你的显卡是 NVIDIA,请下载该特定版本。不管怎样,你肯定会得到针对不同平台进行特殊定制的稳定版本。
|
||||
|
||||
![Pop!_OS][12]
|
||||
|
||||
*图 3:装有 GNOME 桌面的 Pop!_OS 一览。*
|
||||
|
||||
有趣的是,在 Pop!\_OS 中,你不会找到太多预装的开发工具。你也不会找到 IDE 或许多其他开发工具。但是,你可以在 Pop 商店中中找到所需的所有开发工具。
|
||||
|
||||
### 运维
|
||||
|
||||
如果你正在寻找适合系统管理的最具生产力的发行版,[Debian][13] 将会是你的不二之选。为什么这么说呢?因为 Debian 不仅仅拥有无与伦比的可靠性,它也是众多能从苦海中将你解救出来的最好的一个发行版。Debian 是易用性和无限可能性的完美结合。最重要的是,因为它是许多其他发行版的基础,所以可以打赌,如果你需要一个任务的管理工具,那么它一定支持 Debian 系统。当然,我们讨论的是一般的系统管理任务,这意味着大多数时候你需要使用终端窗口 SSH 连接到服务器(图 4),或者在浏览器上使用网络上基于 web 的 GUI 工具。既然如此为什么还要使用一个增加复杂性的桌面呢(比如 Fedora 中的 SELinux 或 openSUSE 中的 YaST)呢?所以,应选择更为简洁易用的那一种。
|
||||
|
||||
![Debian][15]
|
||||
|
||||
*图 4:在 Debian 系统上通过 SSH 连接到远程服务器。*
|
||||
|
||||
你可以选择你想要的不同的桌面(包括 GNOME、Xfce、KDE、Cinnamon、MATE、LXDE),可以确保你所使用的桌面外观最适合你的工作习惯。
|
||||
|
||||
### 教育
|
||||
|
||||
如果你是一名老师或学生,抑或是其他从事与教育相关工作的人士,你需要适当的工具来提高生产力。之前,有 Edubuntu 这样的版本。这一版本位列教育类相关发行版排名的前列。然而,自从 Ubuntu 14.04 版之后这一发行版就再也没有更新。还好,现在有一款基于 openSUSE 的新的以教育为基础的发行版有望夺摘得桂冠。这一改版叫做 [openSUSE:Education-Li-f-e][16](Li-f-e:Linux For Education - 图 5),它基于 openSUSE Leap 42.1 (所以它可能稍微有一点过时)。
|
||||
|
||||
openSUSE:Education-Li-f-e 包含了以下工具:
|
||||
|
||||
* Brain Workshop(大脑工坊):一种基于 dual n-back 模式的大脑训练软件(LCTT 译注:dual n-back 训练是一种科学的智力训练方法,可以改善人的工作记忆和流体智力)
|
||||
* GCompris:一种针对青少年的教育软件包
|
||||
* gElemental:一款元素周期表查看工具
|
||||
* iGNUit:一款通用的记忆卡片工具
|
||||
* Little Wizard:基于 Pascal 语言的少儿编程开发环境
|
||||
* Stellarium:天文模拟器
|
||||
* TuxMath:数学入门游戏
|
||||
* TuxPaint:一款少儿绘画软件
|
||||
* TuxType:一款为少儿准备的打字入门软件
|
||||
* wxMaxima:一个跨平台的计算机代数系统
|
||||
* Inkscape:矢量图形编辑软件
|
||||
* GIMP:图像处理软件(LCTT 译注:被誉为 Linux 上的 PhotoShop)
|
||||
* Pencil:GUI 模型制作工具
|
||||
* Hugin:全景照片拼接及 HDR 效果混合软件
|
||||
|
||||
|
||||
![Education][18]
|
||||
|
||||
*图 5:openSUSE:Education-Li-f-e 发行版拥有大量的工具可以帮你在学校中变得更为高效。*
|
||||
|
||||
同时还集成在 openSUSE:Education-Li-f-e 中的还有 [KIWI-LTSP Server][19] 。KIWI-LTSP 服务器是一个灵活的、经济高效的解决方案,旨在使全世界的学校、企业和组织能够轻松地安装和部署桌面工作站。虽然这可能不会直接帮助学生变得更具生产力,但它肯定会使教育机构在部署供学生使用的桌面时更有效率。有关配置 KIWI-LTSP 的更多信息,请查看 openSUSE [KIWI-LTSP 快速入门指南][20]。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/blog/learn/2019/1/top-5-linux-distributions-productivity
|
||||
|
||||
作者:[Jack Wallen][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[qfzy1233](https://github.com/qfzy1233)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/users/jlwallen
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ubuntu.com/
|
||||
[2]: /files/images/productivity1jpg
|
||||
[3]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/productivity_1.jpg?itok=yxez3X1w "GNOME Clipboard"
|
||||
[4]: /licenses/category/used-permission
|
||||
[5]: https://labs.fedoraproject.org/en/design-suite/
|
||||
[6]: https://fedoraproject.org/wiki/Design_Suite/Tutorials
|
||||
[7]: /files/images/productivity2jpg
|
||||
[8]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/productivity_2.jpg?itok=ke0b8qyH "Fedora Design Suite Favorites"
|
||||
[9]: https://system76.com/
|
||||
[10]: https://system76.com/pop
|
||||
[11]: /files/images/productivity3jpg-0
|
||||
[12]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/productivity_3_0.jpg?itok=8UkCUfsD "Pop!_OS"
|
||||
[13]: https://www.debian.org/
|
||||
[14]: /files/images/productivity4jpg
|
||||
[15]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/productivity_4.jpg?itok=c9yD3Xw2 "Debian"
|
||||
[16]: https://en.opensuse.org/openSUSE:Education-Li-f-e
|
||||
[17]: /files/images/productivity5jpg
|
||||
[18]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/productivity_5.jpg?itok=oAFtV8nT "Education"
|
||||
[19]: https://en.opensuse.org/Portal:KIWI-LTSP
|
||||
[20]: https://en.opensuse.org/SDB:KIWI-LTSP_quick_start
|
||||
[21]: https://training.linuxfoundation.org/linux-courses/system-administration-training/introduction-to-linux
|
@ -0,0 +1,126 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (luuming)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11004-1.html)
|
||||
[#]: subject: (5 Good Open Source Speech Recognition/Speech-to-Text Systems)
|
||||
[#]: via: (https://fosspost.org/lists/open-source-speech-recognition-speech-to-text)
|
||||
[#]: author: (Simon James https://fosspost.org/author/simonjames)
|
||||
|
||||
5 款不错的开源语音识别/语音文字转换系统
|
||||
======
|
||||
|
||||
![](https://i0.wp.com/fosspost.org/wp-content/uploads/2019/02/open-source-speech-recognition-speech-to-text.png?resize=1237%2C527&ssl=1)
|
||||
|
||||
<ruby>语音文字转换<rt>speech-to-text</rt></ruby>(STT)系统就像它名字所蕴含的意思那样,是一种将说出的单词转换为文本文件以供后续用途的方式。
|
||||
|
||||
语音文字转换技术非常有用。它可以用到许多应用中,例如自动转录,使用自己的声音写书籍或文本,用生成的文本文件和其他工具做复杂的分析等。
|
||||
|
||||
在过去,语音文字转换技术以专有软件和库为主导,要么没有开源替代品,要么有着严格的限制,也没有社区。这一点正在发生改变,当今有许多开源语音文字转换工具和库可以让你随时使用。
|
||||
|
||||
这里我列出了 5 个。
|
||||
|
||||
### 开源语音识别库
|
||||
|
||||
#### DeepSpeech 项目
|
||||
|
||||
![5 Good Open Source Speech Recognition/Speech-to-Text Systems 15 open source speech recognition][1]
|
||||
|
||||
该项目由 Firefox 浏览器的开发组织 Mozilla 团队开发。它是 100% 的自由开源软件,其名字暗示使用了 TensorFlow 机器学习框架实现去功能。
|
||||
|
||||
换句话说,你可以用它训练自己的模型获得更好的效果,甚至可以用它来转换其它的语言。你也可以轻松的将它集成到自己的 Tensorflow 机器学习项目中。可惜的是项目当前默认仅支持英语。
|
||||
|
||||
它也支持许多编程语言,例如 Python(3.6)。可以让你在数秒之内完成工作:
|
||||
|
||||
```
|
||||
pip3 install deepspeech
|
||||
deepspeech --model models/output_graph.pbmm --alphabet models/alphabet.txt --lm models/lm.binary --trie models/trie --audio my_audio_file.wav
|
||||
```
|
||||
|
||||
你也可以通过 `npm` 安装它:
|
||||
|
||||
```
|
||||
npm install deepspeech
|
||||
```
|
||||
|
||||
- [项目主页][2]
|
||||
|
||||
#### Kaldi
|
||||
|
||||
![5 Good Open Source Speech Recognition/Speech-to-Text Systems 17 open source speech recognition][3]
|
||||
|
||||
Kaldi 是一个用 C++ 编写的开源语音识别软件,并且在 Apache 公共许可证下发布。它可以运行在 Windows、macOS 和 Linux 上。它的开发始于 2009。
|
||||
|
||||
Kaldi 超过其他语音识别软件的主要特点是可扩展和模块化。社区提供了大量的可以用来完成你的任务的第三方模块。Kaldi 也支持深度神经网络,并且在它的网站上提供了[出色的文档][4]。
|
||||
|
||||
虽然代码主要由 C++ 完成,但它通过 Bash 和 Python 脚本进行了封装。因此,如果你仅仅想使用基本的语音到文字转换功能,你就会发现通过 Python 或 Bash 能够轻易的实现。
|
||||
|
||||
- [项目主页][5]
|
||||
|
||||
#### Julius
|
||||
|
||||
![5 Good Open Source Speech Recognition/Speech-to-Text Systems 19 open source speech recognition][6]
|
||||
|
||||
它可能是有史以来最古老的语音识别软件之一。它的开发始于 1991 年的京都大学,之后在 2005 年将所有权转移到了一个独立的项目组。
|
||||
|
||||
Julius 的主要特点包括了执行实时 STT 的能力,低内存占用(20000 单词少于 64 MB),能够输出<ruby>最优词<rt>N-best word</rt></ruby>和<ruby>词图<rt>Word-graph</rt></ruby>,能够作为服务器单元运行等等。这款软件主要为学术和研究所设计。由 C 语言写成,并且可以运行在 Linux、Windows、macOS 甚至 Android(在智能手机上)。
|
||||
|
||||
它当前仅支持英语和日语。软件应该能够从 Linux 发行版的仓库中轻松安装。只要在软件包管理器中搜索 julius 即可。最新的版本[发布][7]于本文发布前大约一个半月之前。
|
||||
|
||||
- [项目主页][8]
|
||||
|
||||
#### Wav2Letter++
|
||||
|
||||
![5 Good Open Source Speech Recognition/Speech-to-Text Systems 21 open source speech recognition][9]
|
||||
|
||||
如果你在寻找一个更加时髦的,那么这款一定适合。Wav2Letter++ 是一款由 Facebook 的 AI 研究团队于 2 个月之前发布的开源语言识别软件。代码在 BSD 许可证下发布。
|
||||
|
||||
Facebook 描述它的库是“最快、<ruby>最先进<rt>state-of-the-art</rt></ruby>的语音识别系统”。构建它时的理念使其默认针对性能进行了优化。Facebook 最新的机器学习库 [FlashLight][11] 也被用作 Wav2Letter++ 的底层核心。
|
||||
|
||||
Wav2Letter++ 需要你先为所描述的语言建立一个模型来训练算法。没有任何一种语言(包括英语)的预训练模型,它仅仅是个机器学习驱动的文本语音转换工具,它用 C++ 写成,因此被命名为 Wav2Letter++。
|
||||
|
||||
- [项目主页][12]
|
||||
|
||||
#### DeepSpeech2
|
||||
|
||||
![5 Good Open Source Speech Recognition/Speech-to-Text Systems 23 open source speech recognition][13]
|
||||
|
||||
中国软件巨头百度的研究人员也在开发他们自己的语音文字转换引擎,叫做“DeepSpeech2”。它是一个端对端的开源引擎,使用“PaddlePaddle”深度学习框架进行英语或汉语的文字转换。代码在 BSD 许可证下发布。
|
||||
|
||||
该引擎可以在你想用的任何模型和任何语言上训练。模型并未随代码一同发布。你要像其他软件那样自己建立模型。DeepSpeech2 的源代码由 Python 写成,如果你使用过就会非常容易上手。
|
||||
|
||||
- [项目主页][14]
|
||||
|
||||
### 总结
|
||||
|
||||
语音识别领域仍然主要由专有软件巨头所占据,比如 Google 和 IBM(它们为此提供了闭源商业服务),但是开源同类软件很有前途。这 5 款开源语音识别引擎应当能够帮助你构建应用,随着时间推移,它们会不断地发展。在几年之后,我们希望开源成为这些技术中的常态,就像其他行业那样。
|
||||
|
||||
如果你对清单有其他的建议或评论,我们很乐意在下面听到。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fosspost.org/lists/open-source-speech-recognition-speech-to-text
|
||||
|
||||
作者:[Simon James][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[LuuMing](https://github.com/LuuMing)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fosspost.org/author/simonjames
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://i0.wp.com/fosspost.org/wp-content/uploads/2019/02/hero_speech-machine-learning2.png?resize=820%2C280&ssl=1 (5 Good Open Source Speech Recognition/Speech-to-Text Systems 16 open source speech recognition)
|
||||
[2]: https://github.com/mozilla/DeepSpeech
|
||||
[3]: https://i0.wp.com/fosspost.org/wp-content/uploads/2019/02/Screenshot-at-2019-02-19-1134.png?resize=591%2C138&ssl=1 (5 Good Open Source Speech Recognition/Speech-to-Text Systems 18 open source speech recognition)
|
||||
[4]: http://kaldi-asr.org/doc/index.html
|
||||
[5]: http://kaldi-asr.org
|
||||
[6]: https://i2.wp.com/fosspost.org/wp-content/uploads/2019/02/mic_web.png?resize=385%2C100&ssl=1 (5 Good Open Source Speech Recognition/Speech-to-Text Systems 20 open source speech recognition)
|
||||
[7]: https://github.com/julius-speech/julius/releases
|
||||
[8]: https://github.com/julius-speech/julius
|
||||
[9]: https://i2.wp.com/fosspost.org/wp-content/uploads/2019/02/fully_convolutional_ASR.png?resize=850%2C177&ssl=1 (5 Good Open Source Speech Recognition/Speech-to-Text Systems 22 open source speech recognition)
|
||||
[10]: https://code.fb.com/ai-research/wav2letter/
|
||||
[11]: https://github.com/facebookresearch/flashlight
|
||||
[12]: https://github.com/facebookresearch/wav2letter
|
||||
[13]: https://i2.wp.com/fosspost.org/wp-content/uploads/2019/02/ds2.png?resize=850%2C313&ssl=1 (5 Good Open Source Speech Recognition/Speech-to-Text Systems 24 open source speech recognition)
|
||||
[14]: https://github.com/PaddlePaddle/DeepSpeech
|
@ -0,0 +1,163 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10956-1.html)
|
||||
[#]: subject: (Blockchain 2.0 – Explaining Smart Contracts And Its Types [Part 5])
|
||||
[#]: via: (https://www.ostechnix.com/blockchain-2-0-explaining-smart-contracts-and-its-types/)
|
||||
[#]: author: (editor https://www.ostechnix.com/author/editor/)
|
||||
|
||||
区块链 2.0:智能合约及其类型(五)
|
||||
======
|
||||
|
||||
![Explaining Smart Contracts And Its Types][1]
|
||||
|
||||
这是 区块链 2.0 系列的第 5 篇文章。本系列的前一篇文章探讨了我们如何[在房地产行业实现区块链][2]。本文简要探讨了区块链及相关技术领域内的<ruby>智能合约<rt>Smart Contract</rt></ruby>主题。智能合约是在区块链上验证和创建新“数据块”的基本协议,它被吹捧为该系统未来发展和应用的焦点。 然而,像所有“万灵药”一样,它不是一切的答案。我们将从基础知识中探索这个概念,以了解“智能合约”是什么以及它们不是什么。
|
||||
|
||||
### 不断发展的合同
|
||||
|
||||
这个世界建立在合同(合约)之上。在当前社会,没有合约的使用和再利用,地球上任何个人或公司都无法运作。订立、维护和执行合同的任务变得如此复杂,以至于整个司法和法律系统都必须以“合同法”的名义建立起来以支持它。事实上,大多数合同都是由一个“可信的”第三方监督,以确保最终的利益攸关者按照达成的条件得到妥善处理。有些合同甚至涉及到了第三方受益人。此类合同旨在对不是合同的活跃(或参与)方的第三方产生影响。解决和争论合同义务占据了民事诉讼所涉及的大部分法律纠纷。当然,更好的处理合同的方式来对于个人和企业来说都是天赐之物。更不用说它将以核查和证明的名义节省政府的巨大的[文书工作][7] [^1]。
|
||||
|
||||
本系列中的大多数文章都研究了如何利用现有的区块链技术。相比之下,这篇文章将更多地讲述对未来几年的预期。关于“智能合约”的讨论源于前一篇文章中提出的财产讨论。当前这篇文章旨在概述区块链自动执行“智能”可执行程序的能力。务实地处理这个问题意味着我们首先必须定义和探索这些“智能合约”是什么,以及它们如何适应现有的合同系统。我们将在下一篇题为“区块链 2.0:正在进行的项目”的文章中查看当前该领域正在进行的主要应用和项目。
|
||||
|
||||
### 定义智能合约
|
||||
|
||||
[本系列的第一篇文章][3]从基本的角度来看待区块链,将其看作由数据块组成的“分布式分类账本”,这些数据块是:
|
||||
|
||||
* 防篡改
|
||||
* 不可否认(意味着每个数据块都是由某人显式创建的,并且该人不能否认相同的责任)
|
||||
* 安全,且能抵御传统的网络攻击方法
|
||||
* 几乎是永久性的(当然这取决于区块链协议层)
|
||||
* 高度冗余,通过存在于多个网络节点或参与者系统上,其中一个节点的故障不会以任何方式影响系统的功能,并且,
|
||||
* 根据应用的不同可以提供更快的处理速度。
|
||||
|
||||
由于每个数据实例都是安全存储和通过适当的凭证访问的,因此区块链网络可以为精确验证事实和信息提供简便的基础,而无需第三方监督。区块链 2.0 开发也允许“分布式应用程序(DApp)”(我们将在接下来的文章中详细介绍这个术语)。这些分布式应用程序要求存在网络上并在其上运行。当用户需要它们时就会调用它们,并通过使用已经过审核并存储在区块链上的信息来执行它们。
|
||||
|
||||
上面的最后一段为智能合约的定义提供了基础。<ruby>数字商会<rt>The Chamber for Digital Commerce</rt></ruby>提供了一个许多专家都同意的智能合约定义。
|
||||
|
||||
> “(智能合约是一种)计算机代码,在发生指定条件时,能够根据预先指定的功能自动运行。该代码可以在分布式分类帐本上存储和处理,并将产生的任何更改写入分布式分类帐本” [^2]。
|
||||
|
||||
智能合约如上所述是一种简单的计算机程序,就像 “if-then” 或 “if-else if” 语句一样工作。关于其“智能”的方面来自这样一个事实,即该程序的预定义输入来自区块链分类账本,如上所述,它是一个记录信息的安全可靠的来源。如有必要,程序可以调用外部服务或来源以获取信息,以验证操作条款,并且仅在满足所有预定义条件后才执行。
|
||||
|
||||
必须记住,与其名称所暗示的不同,智能合约通常不是自治实体,严格来说,也不是合同。1996 年,Nick Szabo 很早就提到了智能合约,他将其与接受付款并交付用户选择的产品的自动售货机进行了比较。可以在[这里][4]查看全文。此外,人们正在制定允许智能合约进入主流合同使用的法律框架,因此目前该技术的使用仅限于法律监督不那么明确和严格的领域 [^4]。
|
||||
|
||||
### 智能合约的主要类型
|
||||
|
||||
假设读者对合同和计算机编程有基本的了解,并且基于我们对智能合约的定义,我们可以将智能合约和协议粗略地分类为以下主要类别。
|
||||
|
||||
#### 1、智能法律合约
|
||||
|
||||
这大概是最明显的一种。大多数(如果不是全部)合同都具有法律效力。在不涉及太多技术问题的情况下,智能法律合约是涉及到严格的法律追索权的合同,以防参与合同的当事人不履行其交易的目的。如前所述,不同国家和地区的现行法律框架对区块链上的智能和自动化合约缺乏足够的支持,其法律地位也不明确。但是,一旦制定了法律,就可以订立智能合约,以简化目前涉及严格监管的流程,如金融和房地产市场交易、政府补贴、国际贸易等。
|
||||
|
||||
#### 2、DAO
|
||||
|
||||
<ruby>去中心化自治组织<rt>Decentralized Autonomous Organization</rt></ruby>,即DAO,可以粗略地定义为区块链上存在的社区。该社区可以通过一组规则来定义,这些规则通过智能合约来体现并放入代码中。然后,每个参与者的每一个行动都将受到这些规则的约束,其任务是在程序中断的情况下执行并获得追索权。许多智能合约构成了这些规则,它们协同监管和监督参与者。
|
||||
|
||||
名为“创世纪 DAO” 的 DAO 是由以太坊参与者于 2016 年 5 月创建。该社区旨在成为众筹和风险投资平台。在极短的时间内,他们设法筹集了惊人的 1.5 亿美元。然而,由于黑客在系统中发现了漏洞,并设法从众筹投资者手中窃取价值约 5000 万美元的以太币。这次黑客破坏的后果导致以太坊区块链[分裂为两个][8],以太坊和以太坊经典。
|
||||
|
||||
#### 3、应用逻辑合约(ALC)
|
||||
|
||||
如果你已经听说过与区块链相结合的物联网,那么很可能它涉及到了<ruby>应用逻辑合约<rt>Application logic contract</rt></ruby>,即 ALC。此类智能合约包含特定于应用的代码,这些代码可以与区块链上的其他智能合约和程序一起工作。它们有助于与设备进行通信并验证设备之间的通信(在物联网领域)。ALC 是每个多功能智能合约的关键部分,并且大多数都是在一个管理程序下工作。在这里引用的大多数例子中,它们到处都能找到[应用][9] [^6]。
|
||||
|
||||
*由于该领域还在开发中,因此目前所说的任何定义或标准最多只能说是变化而模糊的。*
|
||||
|
||||
### 智能合约是如何工作的?
|
||||
|
||||
为简化起见,让我们用个例子来说明。
|
||||
|
||||
约翰和彼得是两个争论足球比赛得分的人。他们对比赛结果持有相互矛盾的看法,他们都支持不同的球队(这是背景情况)。由于他们两个都需要去其他地方并且无法看完比赛,所以约翰认为如果 A 队在比赛中击败 B 队,他就*支付*给彼得 100 美元。彼得*考虑*之后*接受*了该赌注,同时明确表示他们必须接受这些条款。但是,他们没有兑现该赌注的相互信任,也没有时间和钱来指定第三方监督赌注。
|
||||
|
||||
假设约翰和彼得都使用像 [Etherparty][5] 这样的智能合约平台,它可以在合约谈判时自动结算赌注,他们都会将基于区块链的身份链接到该合约,并设置条款,明确表示一旦比赛结束,该程序将找出获胜方是谁,并自动将该金额从输家中归入获胜者银行账户。一旦比赛结束并且媒体报道同样的结果,该程序将在互联网上搜索规定的来源,确定哪支球队获胜,将其与合约条款联系起来,在这种情况下,如果 A 队赢了彼得将从约翰哪里得到钱,也就是说将约翰的 100 美元转移到彼得的账户。执行完毕后,除非另有说明,否则智能合约将终止并在未来所有的时间内处于非活动状态。
|
||||
|
||||
抛开例子的简单不说,这种情况涉及到一个经典的合同,而参与者选择使用智能合约实现了相同目的。所有的智能合约基本上都遵循类似的原则,对程序进行编码,以便在预定义的参数上执行,并且只抛出预期的输出。智能合同咨询的外部来源可以是有时被称为 IT 世界中的<ruby>神谕<rt>Oracle</rt></ruby>。神谕是当今全球许多智能合约系统的常见部分。
|
||||
|
||||
在这种情况下使用智能合约使参与者可以获得以下好处:
|
||||
|
||||
* 它比在一起并手动结算更快。
|
||||
* 从其中删除了信任问题。
|
||||
* 消除了受信任的第三方代表有关各方处理和解的必要性。
|
||||
* 执行时无需任何费用。
|
||||
* 在如何处理参数和敏感数据方面是安全的。
|
||||
* 相关数据将永久保留在他们运行的区块链平台中,未来可以通过调用相同的函数并为其提供更多输入来设置投注。
|
||||
* 随着时间的推移,假设约翰和彼得变得赌博成瘾,该程序可以帮助他们开发可靠的统计数据来衡量他们的连胜纪录。
|
||||
|
||||
现在我们知道**什么是智能合约**和**它们如何工作**,我们还没有解决**为什么我们需要它们**。
|
||||
|
||||
### 智能合约的需要
|
||||
|
||||
正如之前的例子我们重点提到过的,出于各种原因,我们需要智能合约。
|
||||
|
||||
#### 透明度
|
||||
|
||||
交易对手非常清楚所涉及的条款和条件。此外,由于程序或智能合约的执行涉及某些明确的输入,因此用户可以非常直接地核实会影响他们和合约受益人的因素。
|
||||
|
||||
#### 时间效率
|
||||
|
||||
如上所述,智能合约一旦被控制变量或用户调用所触发,就立即开始工作。由于数据是通过区块链和网络中的其它来源即时提供给系统,因此执行不需要任何时间来验证和处理信息并解决交易。例如,转移土地所有权契约,这是一个涉及手工核实大量文书工作并且需要数周时间的过程,可以在几分钟甚至几秒钟内通过智能合约程序来处理文件和相关各方。
|
||||
|
||||
#### 精度
|
||||
|
||||
由于平台基本上只是计算机代码和预定义的内容,因此不存在主观错误,所有结果都是精确的,完全没有人为错误。
|
||||
|
||||
#### 安全
|
||||
|
||||
区块链的一个固有特征是每个数据块都是安全加密的。这意味着为了实现冗余,即使数据存储在网络上的多个节点上,**也只有数据所有者才能访问以查看和使用数据**。类似地,利用区块链在过程中存储重要变量和结果,所有过程都将是完全安全和防篡改的。同样也通过按时间顺序为审计人员提供原始的、未经更改的和不可否认的数据版本,简化了审计和法规事务。
|
||||
|
||||
#### 信任
|
||||
|
||||
这个文章系列开篇说到区块链为互联网及其上运行的服务增加了急需的信任层。智能合约在任何情况下都不会在执行协议时表现出偏见或主观性,这意味着所涉及的各方对结果完全有约束力,并且可以不附带任何条件地信任该系统。这也意味着,在具有重要价值的传统合同中所需的“可信第三方”,在此处不需要。当事人之间的犯规和监督将成为过去的问题。
|
||||
|
||||
#### 成本效益
|
||||
|
||||
如示例中所强调的,使用智能合约需要最低的成本。企业通常有专门从事使其交易合法并遵守法规的行政人员。如果交易涉及多方,则重复工作是不可避免的。智能合约基本上使前者无关紧要,并且消除了重复,因为双方可以同时完成尽职调查。
|
||||
|
||||
### 智能合约的应用
|
||||
|
||||
基本上,如果两个或多个参与方使用共同的区块链平台,并就一组原则或业务逻辑达成一致,他们可以一起在区块链上创建一个智能合约,并且在没有人为干预的情况下执行。没有人可以篡改所设置的条件,如果原始代码允许,任何更改都会加上时间戳并带有编辑者的指纹,从而增加了问责制。想象一下,在更大的企业级规模上出现类似的情况,你就会明白智能合约的能力是什么,实际上从 2016 年开始的 **Capgemini 研究** 发现智能合约实际上可能是**“未来几年的”** [^8] 商业主流。商业的应用涉及保险、金融市场、物联网、贷款、身份管理系统、托管账户、雇佣合同以及专利和版税合同等用途。像以太坊这样的区块链平台,是一个设计时就考虑了智能合约的系统,它允许个人私人用户免费使用智能合约。
|
||||
|
||||
通过对处理智能合约的公司的探讨,本系列的下一篇文章中将更全面地概述智能合约在当前技术问题上的应用。
|
||||
|
||||
### 那么,它有什么缺点呢?
|
||||
|
||||
这并不是说对智能合约的使用没有任何顾虑。这种担忧实际上也减缓了这方面的发展。所有区块链的防篡改性质实质上使得,如果所涉及的各方需要在没有重大改革或法律追索的情况下,几乎不可能修改或添加现有条款的新条款。
|
||||
|
||||
其次,即使公有链上的活动是开放的,所有人都可以看到和观察。交易中涉及的各方的个人身份并不总是已知的。这种匿名性造成在任何一方违约的情况下法律有罪不罚的问题,特别是因为现行法律和立法者并不完全适应现代技术。
|
||||
|
||||
第三,区块链和智能合约在很多方面仍然存在安全缺陷,因为对其所以涉及的技术仍处于发展的初期阶段。 对代码和平台的这种缺乏经验最终导致了 2016 年的 DAO 事件。
|
||||
|
||||
所有这些都可能导致企业或公司在需要调整区块链以供其使用时需要大量的初始投资。然而,这些是最初的一次性投资,并且随之而来的是潜在的节约,这才是人们感兴趣的。
|
||||
|
||||
### 结论
|
||||
|
||||
目前的法律框架并没有真正支持一个全面的智能合约的社会,并且由于显然的原因,在不久的将来也不会支持。一个解决方案是选择**“混合”合约**,它将传统的法律文本和文件与在为此目的设计的区块链上运行的智能合约代码相结合。然而,即使是混合合约仍然很大程度上尚未得到探索,因为需要创新的立法机构才能实现这些合约。这里简要提到的应用以及更多内容将在[本系列的下一篇文章][6]中详细探讨。
|
||||
|
||||
[^1]: S. C. A. Chamber of Digital Commerce, “Smart contracts – Is the law ready,” no. September, 2018.
|
||||
[^2]: S. C. A. Chamber of Digital Commerce, “Smart contracts – Is the law ready,” no. September, 2018.
|
||||
[^4]: Cardozo Blockchain Project, “‘Smart Contracts’ & Legal Enforceability,” vol. 2, p. 28, 2018.
|
||||
[^6]: F. Idelberger, G. Governatori, R. Riveret, and G. Sartor, “Evaluation of Logic-Based Smart Contracts for Blockchain Systems,” 2016, pp. 167–183.
|
||||
[^8]: B. Cant et al., “Smart Contracts in Financial Services : Getting from Hype to Reality,” Capgemini Consult., pp. 1–24, 2016.
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/blockchain-2-0-explaining-smart-contracts-and-its-types/
|
||||
|
||||
作者:[ostechnix][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/editor/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/03/smart-contracts-720x340.png
|
||||
[2]: https://linux.cn/article-10914-1.html
|
||||
[3]: https://linux.cn/article-10650-1.html
|
||||
[4]: http://www.fon.hum.uva.nl/rob/Courses/InformationInSpeech/CDROM/Literature/LOTwinterschool2006/szabo.best.vwh.net/smart_contracts_2.html
|
||||
[5]: https://etherparty.com/
|
||||
[6]: https://www.ostechnix.com/blockchain-2-0-ongoing-projects-the-state-of-smart-contracts-now/
|
||||
[7]: http://www.legal-glossary.org/
|
||||
[8]: https://futurism.com/the-dao-heist-undone-97-of-eth-holders-vote-for-the-hard-fork/
|
||||
[9]: https://www.everestgrp.com/2016-10-types-smart-contracts-based-applications-market-insights-36573.html/
|
@ -0,0 +1,127 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (tomjlw)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10939-1.html)
|
||||
[#]: subject: (How to build a mobile particulate matter sensor with a Raspberry Pi)
|
||||
[#]: via: (https://opensource.com/article/19/3/mobile-particulate-matter-sensor)
|
||||
[#]: author: (Stephan Tetzel https://opensource.com/users/stephan)
|
||||
|
||||
如何用树莓派搭建一个颗粒物传感器
|
||||
======
|
||||
|
||||
> 用树莓派、一个廉价的传感器和一个便宜的屏幕监测空气质量。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/05/005121bbveeavwgyc1i1gk.jpg)
|
||||
|
||||
大约一年前,我写了一篇关于如何使用树莓派和廉价传感器测量[空气质量][2]的文章。我们这几年已在学校里和私下使用了这个项目。然而它有一个缺点:由于它基于无线/有线网,因此它不是便携的。如果你的树莓派、你的智能手机和电脑不在同一个网络的话,你甚至都不能访问传感器测量的数据。
|
||||
|
||||
为了弥补这一缺陷,我们给树莓派添加了一块小屏幕,这样我们就可以直接从该设备上读取数据。以下是我们如何为我们的移动细颗粒物传感器搭建并配置好屏幕。
|
||||
|
||||
### 为树莓派搭建好屏幕
|
||||
|
||||
在[亚马逊][3]、阿里巴巴以及其它来源有许多可以买到的树莓派屏幕,从 ePaper 屏幕到可触控 LCD。我们选择了一个便宜的带触控功能且分辨率为 320*480 像素的[3.5英寸 LCD][3],可以直接插进树莓派的 GPIO 引脚。3.5 英寸屏幕和树莓派几乎一样大,这一点不错。
|
||||
|
||||
当你第一次启动屏幕打开树莓派的时候,会因为缺少驱动屏幕会保持白屏。你得首先为屏幕安装[合适的驱动][5]。通过 SSH 登入并执行以下命令:
|
||||
|
||||
```
|
||||
$ rm -rf LCD-show
|
||||
$ git clone <https://github.com/goodtft/LCD-show.git>
|
||||
$ chmod -R 755 LCD-show
|
||||
$ cd LCD-show/
|
||||
```
|
||||
|
||||
为你的屏幕执行合适的命令以安装驱动。例如这是给我们 MPI3501 型屏幕的命令:
|
||||
|
||||
```
|
||||
$ sudo ./LCD35-show
|
||||
```
|
||||
|
||||
这行命令会安装合适的驱动并重启树莓派。
|
||||
|
||||
### 安装 PIXEL 桌面并设置自动启动
|
||||
|
||||
以下是我们想要我们项目能够做到的事情:如果树莓派启动,我们想要展现一个有我们空气质量测量数据的网站。
|
||||
|
||||
首先,安装树莓派的[PIXEL 桌面环境][6]:
|
||||
|
||||
```
|
||||
$ sudo apt install raspberrypi-ui-mods
|
||||
```
|
||||
|
||||
然后安装 Chromium 浏览器以显示网站:
|
||||
|
||||
```
|
||||
$ sudo apt install chromium-browser
|
||||
```
|
||||
|
||||
需要自动登录以使测量数据在启动后直接显示;否则你将只会看到登录界面。然而树莓派用户并没有默认设置好自动登录。你可以用 `raspi-config` 工具设置自动登录:
|
||||
|
||||
```
|
||||
$ sudo raspi-config
|
||||
```
|
||||
|
||||
在菜单中,选择:“3 Boot Options → B1 Desktop / CLI → B4 Desktop Autologin”。
|
||||
|
||||
在启动后用 Chromium 打开我们的网站这块少了一步。创建文件夹 `/home/pi/.config/lxsession/LXDE-pi/`:
|
||||
|
||||
```
|
||||
$ mkdir -p /home/pi/config/lxsession/LXDE-pi/
|
||||
```
|
||||
|
||||
然后在该文件夹里创建 `autostart` 文件:
|
||||
|
||||
```
|
||||
$ nano /home/pi/.config/lxsession/LXDE-pi/autostart
|
||||
```
|
||||
|
||||
并粘贴以下代码:
|
||||
|
||||
```
|
||||
#@unclutter
|
||||
@xset s off
|
||||
@xset -dpms
|
||||
@xset s noblank
|
||||
|
||||
# Open Chromium in Full Screen Mode
|
||||
@chromium-browser --incognito --kiosk <http://localhost>
|
||||
```
|
||||
|
||||
如果你想要隐藏鼠标指针,你得安装 `unclutter` 包并移除 `autostart` 文件开头的注释。
|
||||
|
||||
```
|
||||
$ sudo apt install unclutter
|
||||
```
|
||||
|
||||
![移动颗粒物传感器][7]
|
||||
|
||||
我对去年的代码做了些小修改。因此如果你之前搭建过空气质量项目,确保用[原文章][2]中的指导为 AQI 网站重新下载脚本和文件。
|
||||
|
||||
通过添加触摸屏,你现在拥有了一个便携的颗粒物传感器!我们在学校用它来检查教室里的空气质量或者进行比较测量。使用这种配置,你无需再依赖网络连接或 WLAN。你可以在任何地方使用这个小型测量站——你甚至可以使用移动电源以摆脱电网。
|
||||
|
||||
* * *
|
||||
|
||||
这篇文章原来在<ruby>[开源学校解决方案][8]<rt>Open Scool Solutions</rt></ruby>上发表,获得许可重新发布。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/3/mobile-particulate-matter-sensor
|
||||
|
||||
作者:[Stephan Tetzel][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[tomjlw](https://github.com/tomjlw)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/stephan
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/talk_chat_team_mobile_desktop.png?itok=d7sRtKfQ (Team communication, chat)
|
||||
[2]: https://linux.cn/article-9620-1.html
|
||||
[3]: https://www.amazon.com/gp/search/ref=as_li_qf_sp_sr_tl?ie=UTF8&tag=openschoolsol-20&keywords=lcd%20raspberry&index=aps&camp=1789&creative=9325&linkCode=ur2&linkId=51d6d7676e10d6c7db203c4a8b3b529a
|
||||
[4]: https://amzn.to/2CcvgpC
|
||||
[5]: https://github.com/goodtft/LCD-show
|
||||
[6]: https://linux.cn/article-8459-1.html
|
||||
[7]: https://opensource.com/sites/default/files/uploads/mobile-aqi-sensor.jpg (Mobile particulate matter sensor)
|
||||
[8]: https://openschoolsolutions.org/mobile-particulate-matter-sensor/
|
||||
|
@ -0,0 +1,64 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10984-1.html)
|
||||
[#]: subject: (Running LEDs in reverse could cool computers)
|
||||
[#]: via: (https://www.networkworld.com/article/3386876/running-leds-in-reverse-could-cool-computers.html#tk.rss_all)
|
||||
[#]: author: (Patrick Nelson https://www.networkworld.com/author/Patrick-Nelson/)
|
||||
|
||||
反向运行 LED 能够冷却计算机
|
||||
======
|
||||
|
||||
> 电子产品的小型化正在触及其极限,部分原因在于热量管理。许多人现在都在积极地尝试解决这个问题。其中一种正在探索的途径是反向运行的 LED。
|
||||
|
||||
![monsitj / Getty Images][1]
|
||||
|
||||
寻找更有效的冷却计算机的方法,几乎与渴望发现更好的电池化学成分一样,在科学家的研究日程中也处于重要位置。
|
||||
|
||||
更多的冷却手段对于降低成本至关重要。冷却技术也使得在较小的空间中可以进行更强大的处理,其有限的处理能力应该是进行计算而不是浪费热量。冷却技术可以阻止热量引起的故障,从而延长部件的使用寿命,并且可以促进环保的数据中心 —— 更少的热量意味着对环境的影响更小。
|
||||
|
||||
如何从微处理器中消除热量是科学家们一直在探索的一个方向,他们认为他们已经提出了一个简单而不寻常、且反直觉的解决方案。他们说可以运行一个发光二极管(LED)的变体,其电极反转可以迫使该元件表现得像处于异常低温下工作一样。如果将其置于较热的电子设备旁边,然后引入纳米级间隙,可以使 LED 吸收热量。
|
||||
|
||||
“一旦 LED 反向偏置,它就会像一个非常低温的物体一样,吸收光子,”密歇根大学机械工程教授埃德加·梅霍夫在宣布了这一突破的[新闻稿][4]中说。 “与此同时,该间隙可防止热量返回,从而产生冷却效果。”
|
||||
|
||||
研究人员表示,LED 和相邻的电子设备(在这种情况下是热量计,通常用于测量热能)必须非常接近。他们说他们已经能够证明达到了每平方米 6 瓦的冷却功率。他们解释说,这是差不多是地球表面所接受到的阳光的能量。
|
||||
|
||||
物联网(IoT)设备和智能手机可能是最终将受益于这种 LED 改造的电子产品。这两种设备都需要在更小的空间中容纳更多的计算功率。
|
||||
|
||||
“从微处理器中可以移除的热量开始限制在给定空间内容纳的功率,”密歇根大学的公告说。
|
||||
|
||||
### 材料科学和冷却计算机
|
||||
|
||||
[我之前写过关于新形式的计算机冷却的文章][5]。源自材料科学的外来材料是正在探索的想法之一。美国能源部劳伦斯伯克利国家实验室表示,钠铋(Na3Bi)可用于晶体管设计。这种新物质带电荷,重要的是具有可调节性;但是,它不需要像超导体那样进行冷却。
|
||||
|
||||
事实上,这是超导体的一个问题。不幸的是,它们比大多数电子设备需要更多的冷却 —— 通过极端冷却消除电阻。
|
||||
|
||||
另外,[康斯坦茨大学的德国研究人员][6]表示他们很快将拥有超导体驱动的计算机,没有废热。他们计划使用电子自旋 —— 一种新的电子物理维度,可以提高效率。该大学去年在一份新闻稿中表示,这种方法“显著降低了计算中心的能耗”。
|
||||
|
||||
另一种减少热量的方法可能是用嵌入在微处理器上的[螺旋和回路来取代传统的散热器][7]。宾汉姆顿大学的科学家们表示,印在芯片上的微小通道可以为冷却剂提供单独的通道。
|
||||
|
||||
康斯坦茨大学说:“半导体技术的小型化正在接近其物理极限。”热管理现在被科学家提上了议事日程。这是“小型化的一大挑战”。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3386876/running-leds-in-reverse-could-cool-computers.html#tk.rss_all
|
||||
|
||||
作者:[Patrick Nelson][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Patrick-Nelson/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2019/02/big_data_center_server_racks_storage_binary_analytics_by_monsitj_gettyimages-944444446_3x2-100787357-large.jpg
|
||||
[2]: https://www.networkworld.com/article/3242807/data-center/top-10-data-center-predictions-idc.html#nww-fsb
|
||||
[3]: https://www.networkworld.com/newsletters/signup.html#nww-fsb
|
||||
[4]: https://news.umich.edu/running-an-led-in-reverse-could-cool-future-computers/
|
||||
[5]: https://www.networkworld.com/article/3326831/computers-could-soon-run-cold-no-heat-generated.html
|
||||
[6]: https://www.uni-konstanz.de/en/university/news-and-media/current-announcements/news/news-in-detail/Supercomputer-ohne-Abwaerme/
|
||||
[7]: https://www.networkworld.com/article/3322956/chip-cooling-breakthrough-will-reduce-data-center-power-costs.html
|
||||
[8]: https://www.facebook.com/NetworkWorld/
|
||||
[9]: https://www.linkedin.com/company/network-world
|
@ -0,0 +1,100 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11013-1.html)
|
||||
[#]: subject: (Blockchain 2.0 – Ongoing Projects (The State Of Smart Contracts Now) [Part 6])
|
||||
[#]: via: (https://www.ostechnix.com/blockchain-2-0-ongoing-projects-the-state-of-smart-contracts-now/)
|
||||
[#]: author: (editor https://www.ostechnix.com/author/editor/)
|
||||
|
||||
区块链 2.0:智能合约如今的发展(六)
|
||||
======
|
||||
|
||||
![The State Of Smart Contracts Now][1]
|
||||
|
||||
继续我们的[前面的关于智能合约的文章][2],这篇文章旨在讨论智能合约的发展形势,重点介绍目前正在该领域进行开发的一些项目和公司。如本系列前一篇文章中讨论的,智能合约是在区块链网络上存在并执行的程序。我们探讨了智能合约的工作原理以及它们优于传统数字平台的原因。这里描述的公司分布于各种各样的行业中,但是大多涉及到身份管理系统、金融服务、众筹系统等,因为这些是被认为最适合切换到基于区块链的数据库系统的领域。
|
||||
|
||||
### 开放平台
|
||||
|
||||
诸如 [Counterparty][8] 和 Solidity(以太坊)等平台是完全公用的构建模块,开发者可以以之创建自己的智能合约。大量的开发人员参与此类项目使这些项目成为开发智能合约、设计自己的加密货币令牌系统,以及创建区块链运行协议的事实标准。许多值得称赞的项目都来源于它们。摩根大通派生自以太坊的 [Quorum][9],就是一个例子。而瑞波是另一个例子。
|
||||
|
||||
### 管理金融交易
|
||||
|
||||
通过互联网转账加密货币被吹捧为在未来几年会成为常态。与此相关的不足之处是:
|
||||
|
||||
* 身份和钱包地址是匿名的。如果接收方不履行交易,则付款人没有任何第一追索权。
|
||||
* 错误交易(如果无法追踪任何交易)。
|
||||
* 密码生成的哈希密钥很难用于人类,人为错误是主要关注点。
|
||||
|
||||
在这种情况下,可以让其他人暂时接受该交易并在接受尽职调查后与接收方结算。
|
||||
|
||||
[EscrowMyEther][10] 和 [PAYFAIR][11] 是两个这样的托管平台。基本上,托管公司采用商定的金额并向接收方发送令牌。一旦接收方通过相同的托管平台提供付款人想要的内容,两者都会确认并最终付款。 这些得到了自由职业者和业余爱好者收藏家广泛在线使用。
|
||||
|
||||
### 金融服务
|
||||
|
||||
小额融资和小额保险项目的发展将改善世界上大多数贫穷或没有银行账户的人的银行金融服务。据估计,社会中较贫穷的“无银行账户”人群可以为银行和机构的增加 3800 亿美元收入 [^5]。这一金额要远远超过银行切换到区块链分布式账本技术(DLT)预期可以节省的运营费用。
|
||||
|
||||
位于美国中西部的 BankQu Inc. 的口号是“通过身份而尊严”。他们的平台允许个人建立他们自己的数字身份记录,其中所有交易将在区块链上实时审查和处理。在底层代码上记录并为其用户构建唯一的在线标识,从而实现超快速的交易和结算。BankQu 案例研究探讨了他们如何以这种方式帮助个人和公司,可以在[这里][3]看到。
|
||||
|
||||
[Stratumn][12] 正在帮助保险公司通过自动化早期由人类微观管理的任务来提供更好的保险服务。通过自动化、端到端可追溯性和高效的数据隐私方法,他们彻底改变了保险索赔的结算方式。改善客户体验以及显著降低成本为客户和相关的公司带来双赢局面。
|
||||
|
||||
法国保险公司 [AXA][14] 目前正在试行类似的努力。其产品 [fizzy][13] 允许用户以少量费用订阅其服务并输入他们的航班详细信息。如果航班延误或遇到其他问题,该程序会自动搜索在线数据库,检查保险条款并将保险金额记入用户的帐户。这样就用户或客户无需在手动检查条款后提出索赔,并且就长期而言,一旦这样的系统成为主流,就增加了航空公司的责任心。
|
||||
|
||||
### 跟踪所有权
|
||||
|
||||
理论上可以利用 DLT 中的带时间戳的数据块来跟踪媒体的创建到最终用户消费。Peertracks 公司和 Mycelia 公司目前正在帮助音乐家发布内容,而不必担心其内容被盗或被滥用。他们帮助艺术家直接向粉丝和客户销售,同时获得工作报酬,而无需通过权利和唱片公司 [^9]。
|
||||
|
||||
### 身份管理平台
|
||||
|
||||
基于区块链的身份管理平台可以将你的身份存储在分布式分类帐本中。设置帐户后,会对其进行安全加密,然后将其发送给所有参与节点。但是,作为数据块的所有者,只有该用户才能访问该数据。一旦你在网络上建立身份并开始交易,网络中的自动程序将验证与你的帐户关联的先前所有的交易,在检查要求后将其发送给监管备案,并在程序认为交易合法时自动执行结算。这里的好处是,由于区块链上的数据是防篡改的,而智能合约以零偏差(或主观性)检查输入,如前所述,交易不需要任何人的监督或批准,并且需要小心是即刻生效的。
|
||||
|
||||
像 [ShoCard][15] 、[Credits][16] 和 [OneName][17] 这样的初创公司目前正在推出类似的服务,目前正在与政府和社会机构进行谈判,以便将它们整合到主流用途中。
|
||||
|
||||
开发商的其他独立项目如 Chris Ellis 和 David Duccini 分别开发或提出了替代的身份管理系统,分别是 “[世界公民][4]”和 [IDCoin][5]。Ellis 先生甚至通过在区块链网络上创建护照来证明他的工作能力。
|
||||
|
||||
### 资源共享
|
||||
|
||||
[Share & Charge][18] ([Slock.It][19]) 是一家欧洲的区块链初创公司。他们的移动应用程序允许房主和其他个人投入资金建立充电站与其他正在寻找快速充电的人分享他们的资源。这不仅使业主能够收回他们的一些投资,而且还允许 EV 司机在其近地域获得更多的充电点,从而允许供应商以方便的方式满足需求。一旦“客户”完成对其车辆的充电,相关的硬件就会创建一个由数据组成的安全时间戳块,并且在该平台上工作的智能合约会自动将相应的金额记入所有者账户。记录所有此类交易的跟踪并保持适当的安全验证。有兴趣的读者可以看一下[这里][6],了解他们产品背后的技术角度。该公司的平台将逐步使用户能够与有需要的个人分享其他产品和服务,并从中获得被动收入。
|
||||
|
||||
我们在这里看到的公司,以及一个很短的正在进行中的项目的清单,这些项目利用智能合约和区块链数据库系统。诸如此类的平台有助于构建一个安全的“盒子”,其中包含仅由用户自己、其上的代码或智能合约访问的信息。基于触发器对信息进行实时审查、检查,并且算法由系统执行。这样的平台人为监督最小化,这是在安全数字自动化方面朝着正确方向迈出的急需的一步,这在以前从未被考虑过如此规模。
|
||||
|
||||
下一篇文章将阐述不同类型的区块链。单击以下链接以了解有关此主题的更多信息。
|
||||
|
||||
* [区块链 2.0:公有链与私有链的比较][7]
|
||||
|
||||
|
||||
[^5]: B. Pani, “Blockchain Powered Financial Inclusion,” 2016.
|
||||
[^9]: M. Gates, “Blockchain. Ultimate guide to understanding blockchain bitcoin cryptocurrencies smart-contracts and the future of money.pdf.” 2017.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/blockchain-2-0-ongoing-projects-the-state-of-smart-contracts-now/
|
||||
|
||||
作者:[ostechnix][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/editor/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/04/State-Of-Smart-Contracts-720x340.png
|
||||
[2]: https://linux.cn/article-10956-1.html
|
||||
[3]: https://banqu.co/case-study/
|
||||
[4]: https://github.com/MrChrisJ/World-Citizenship
|
||||
[5]: https://github.com/IDCoin/IDCoin
|
||||
[6]: https://blog.slock.it/share-charge-smart-contracts-the-technical-angle-58b93ce80f15
|
||||
[7]: https://www.ostechnix.com/blockchain-2-0-public-vs-private-blockchain-comparison/
|
||||
[8]: https://counterparty.io/platform/
|
||||
[9]: https://www.jpmorgan.com/global/Quorum
|
||||
[10]: http://escrowmyether.com/
|
||||
[11]: https://payfair.io/
|
||||
[12]: https://stratumn.com/business-case/insurance-claim-automation-across-europe/
|
||||
[13]: https://fizzy.axa/en-gb/
|
||||
[14]: https://group.axa.com/en/newsroom/news/axa-goes-blockchain-with-fizzy
|
||||
[15]: https://techcrunch.com/2015/05/05/shocard-is-a-digital-identity-card-on-the-blockchain/
|
||||
[16]: https://techcrunch.com/2014/10/31/your-next-passport-could-be-on-the-blockchain/
|
||||
[17]: https://wiki.namecoin.org/index.php?title=OneName
|
||||
[18]: https://blog.slock.it/share-charge-launches-its-app-on-boards-over-1-000-charging-stations-on-the-blockchain-ba8275390309
|
||||
[19]: https://slock.it/
|
56
published/201906/20190409 5 Linux rookie mistakes.md
Normal file
56
published/201906/20190409 5 Linux rookie mistakes.md
Normal file
@ -0,0 +1,56 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10952-1.html)
|
||||
[#]: subject: (5 Linux rookie mistakes)
|
||||
[#]: via: (https://opensource.com/article/19/4/linux-rookie-mistakes)
|
||||
[#]: author: (Jen Wike Huger https://opensource.com/users/jen-wike/users/bcotton/users/petercheer/users/greg-p/users/greg-p)
|
||||
|
||||
5 个 Linux 新手会犯的失误
|
||||
======
|
||||
|
||||
> Linux 爱好者们分享了他们犯下的一些最大错误。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/09/103635akfkghwh5mp58g68.jpg)
|
||||
|
||||
终身学习是明智的 —— 它可以让你的思维敏捷,让你在就业市场上更具竞争力。但是有些技能比其他技能更难学,尤其是那些小菜鸟错误,当你尝试修复它们时可能会花费你很多时间,给你带来很大困扰。
|
||||
|
||||
以学习 [Linux][2] 为例。如果你习惯于在 Windows 或 MacOS 图形界面中工作,那么转移到 Linux,要将不熟悉的命令输入到终端中,可能会有很大的学习曲线。但是,其回报是值得的,因为已经有数以百万计的人们已经证明了这一点。
|
||||
|
||||
也就是说,这趟学习之旅并不是一帆风顺的。我们让一些 Linux 爱好者回想了一下他们刚开始使用 Linux 的时候,并告诉我们他们犯下的最大错误。
|
||||
|
||||
“不要进入[任何类型的命令行界面(CLI)工作]时就期望命令会以合理或一致的方式工作,因为这可能会导致你感到挫折。这不是因为设计选择不当 —— 虽然当你在键盘上敲击时就像在敲在你的脑袋上一样 —— 而是反映了这些系统是历经了几代的软件和操作系统的发展而陆续添加完成的事实。顺其自然,写下或记住你需要的命令,并且(尽量不要)在[事情不是你所期望的][3]时感到沮丧。” —— [Gina Likins] [4]
|
||||
|
||||
“尽可能简单地复制和粘贴命令以使事情顺利进行,首先阅读命令,至少对将要执行的操作有一个大致的了解,特别是如果有管道命令时,如果有多个管道更要特别注意。有很多破坏性的命令看起来无害 —— 直到你意识到它们能做什么(例如 `rm`、`dd`),而你不会想要意外破坏什么东西(别问我怎么知道)。” —— [Katie McLaughlin] [5]
|
||||
|
||||
“在我的 Linux 之旅的早期,我并不知道我所处在文件系统中的位置的重要性。我正在删除一些我认为是我的主目录的文件,我输入了 `sudo rm -rf *`,然后就删除了我系统上的所有启动文件。现在,我经常使用 `pwd` 来确保我在发出这样的命令之前确认我在哪里。幸运的是,我能够使用 USB 驱动器启动被搞坏的笔记本电脑并恢复我的文件。” —— [Don Watkins] [6]
|
||||
|
||||
“不要因为你认为‘权限很难理解’而你希望应用程序可以访问某些内容时就将整个文件系统的权限重置为 [777][7]。”—— [Matthew Helmke] [8]
|
||||
|
||||
“我从我的系统中删除一个软件包,而我没有检查它依赖的其他软件包。我只是让它删除它想删除要的东西,最终导致我的一些重要程序崩溃并变得不可用。” —— [Kedar Vijay Kulkarni] [9]
|
||||
|
||||
你在学习使用 Linux 时犯过什么错误?请在评论中分享。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/linux-rookie-mistakes
|
||||
|
||||
作者:[Jen Wike Huger][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jen-wike/users/bcotton/users/petercheer/users/greg-p/users/greg-p
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/mistake_bug_fix_find_error.png?itok=PZaz3dga (magnifying glass on computer screen, finding a bug in the code)
|
||||
[2]: https://opensource.com/resources/linux
|
||||
[3]: https://lintqueen.com/2017/07/02/learning-while-frustrated/
|
||||
[4]: https://opensource.com/users/lintqueen
|
||||
[5]: https://opensource.com/users/glasnt
|
||||
[6]: https://opensource.com/users/don-watkins
|
||||
[7]: https://www.maketecheasier.com/file-permissions-what-does-chmod-777-means/
|
||||
[8]: https://twitter.com/matthewhelmke
|
||||
[9]: https://opensource.com/users/kkulkarn
|
108
published/201906/20190409 5 open source mobile apps.md
Normal file
108
published/201906/20190409 5 open source mobile apps.md
Normal file
@ -0,0 +1,108 @@
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "fuzheng1998"
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-10931-1.html"
|
||||
[#]: subject: "5 open source mobile apps"
|
||||
[#]: via: "https://opensource.com/article/19/4/mobile-apps"
|
||||
[#]: author: "Chris Hermansen https://opensource.com/users/clhermansen/users/bcotton/users/clhermansen/users/bcotton/users/clhermansen"
|
||||
|
||||
5 个可以满足你的生产力、沟通和娱乐需求的开源手机应用
|
||||
======
|
||||
|
||||
> 你可以依靠这些应用来满足你的生产力、沟通和娱乐需求。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/03/001949brnq19j5qeqn3onv.jpg)
|
||||
|
||||
像世界上大多数人一样,我的手似乎就没有离开过手机。多亏了我从 Google Play 和 F-Droid 安装的开源移动应用程序,让我的 Android 设备好像提供了无限的沟通、生产力和娱乐服务一样。
|
||||
|
||||
在我的手机上的许多开源应用程序中,当想听音乐、与朋友/家人和同事联系、或者在旅途中完成工作时,以下五个是我一直使用的。
|
||||
|
||||
### MPDroid
|
||||
|
||||
一个音乐播放器进程 (MPD)的 Android 控制器。
|
||||
|
||||
![MPDroid][2]
|
||||
|
||||
MPD 是将音乐从小型音乐服务器电脑传输到大型的黑色立体声音箱的好方法。它直连 ALSA,因此可以通过 ALSA 硬件接口与数模转换器(DAC)对话,它可以通过我的网络进行控制——但是用什么东西控制呢?好吧,事实证明 MPDroid 是一个很棒的 MPD 控制器。它可以管理我的音乐数据库,显示专辑封面,处理播放列表,并支持互联网广播。而且它是开源的,所以如果某些东西不好用的话……
|
||||
|
||||
MPDroid 可在 [Google Play][4] 和 [F-Droid][5] 上找到。
|
||||
|
||||
### RadioDroid
|
||||
|
||||
一台能单独使用及与 Chromecast 搭配使用的 Android 网络收音机。
|
||||
|
||||
![RadioDroid][6]
|
||||
|
||||
RadioDroid 是一个网络收音机,而 MPDroid 则管理我音乐的数据库;从本质上讲,RadioDroid 是 [Internet-Radio.com][7] 的一个前端。此外,通过将耳机插入 Android 设备,通过耳机插孔或 USB 将 Android 设备直接连接到立体声系统,或通过兼容设备使用其 Chromecast 功能,可以享受 RadioDroid。这是一个查看芬兰天气情况,听取排名前 40 的西班牙语音乐,或收到到最新新闻消息的好方法。
|
||||
|
||||
RadioDroid 可在 [Google Play][8] 和 [F-Droid][9] 上找到。
|
||||
|
||||
### Signal
|
||||
|
||||
一个支持 Android、iOS,还有桌面系统的安全即时消息客户端。
|
||||
|
||||
![Signal][10]
|
||||
|
||||
如果你喜欢 WhatsApp,但是因为它与 Facebook [日益密切][11]的关系而感到困扰,那么 Signal 应该是你的下一个产品。Signal 的唯一问题是说服你的朋友们最好用 Signal 取代 WhatsApp。但除此之外,它有一个与 WhatsApp 类似的界面;很棒的语音和视频通话;很好的加密;恰到好处的匿名;并且它受到了一个不打算通过使用软件来获利的基金会的支持。为什么不喜欢它呢?
|
||||
|
||||
Signal 可用于 [Android][12]、[iOS][13] 和 [桌面][14]。
|
||||
|
||||
### ConnectBot
|
||||
|
||||
Android SSH 客户端。
|
||||
|
||||
![ConnectBot][15]
|
||||
|
||||
有时我离电脑很远,但我需要登录服务器才能办事。[ConnectBot][16] 是将 SSH 会话搬到手机上的绝佳解决方案。
|
||||
|
||||
ConnectBot 可在 [Google Play][17] 上找到。
|
||||
|
||||
### Termux
|
||||
|
||||
有多种熟悉的功能的安卓终端模拟器。
|
||||
|
||||
![Termux][18]
|
||||
|
||||
你是否需要在手机上运行 `awk` 脚本?[Termux][19] 是个解决方案。如果你需要做终端类的工作,而且你不想一直保持与远程计算机的 SSH 连接,请使用 ConnectBot 将文件放到手机上,然后退出会话,在 Termux 中执行你的操作,用 ConnectBot 发回结果。
|
||||
|
||||
Termux 可在 [Google Play][20] 和 [F-Droid][21] 上找到。
|
||||
|
||||
* * *
|
||||
|
||||
你最喜欢用于工作或娱乐的开源移动应用是什么呢?请在评论中分享它们。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/mobile-apps
|
||||
|
||||
作者:[Chris Hermansen][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[fuzheng1998](https://github.com/fuzheng1998)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/clhermansen/users/bcotton/users/clhermansen/users/bcotton/users/clhermansen
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003588_01_rd3os.combacktoschoolserieshe_rh_041x_0.png?itok=tfg6_I78
|
||||
[2]: https://opensource.com/sites/default/files/uploads/mpdroid.jpg "MPDroid"
|
||||
[3]: https://opensource.com/article/17/4/fun-new-gadget
|
||||
[4]: https://play.google.com/store/apps/details?id=com.namelessdev.mpdroid&hl=en_US
|
||||
[5]: https://f-droid.org/en/packages/com.namelessdev.mpdroid/
|
||||
[6]: https://opensource.com/sites/default/files/uploads/radiodroid.png "RadioDroid"
|
||||
[7]: https://www.internet-radio.com/
|
||||
[8]: https://play.google.com/store/apps/details?id=net.programmierecke.radiodroid2
|
||||
[9]: https://f-droid.org/en/packages/net.programmierecke.radiodroid2/
|
||||
[10]: https://opensource.com/sites/default/files/uploads/signal.png "Signal"
|
||||
[11]: https://opensource.com/article/19/3/open-messenger-client
|
||||
[12]: https://play.google.com/store/apps/details?id=org.thoughtcrime.securesms
|
||||
[13]: https://itunes.apple.com/us/app/signal-private-messenger/id874139669?mt=8
|
||||
[14]: https://signal.org/download/
|
||||
[15]: https://opensource.com/sites/default/files/uploads/connectbot.png "ConnectBot"
|
||||
[16]: https://connectbot.org/
|
||||
[17]: https://play.google.com/store/apps/details?id=org.connectbot
|
||||
[18]: https://opensource.com/sites/default/files/uploads/termux.jpg "Termux"
|
||||
[19]: https://termux.com/
|
||||
[20]: https://play.google.com/store/apps/details?id=com.termux
|
||||
[21]: https://f-droid.org/packages/com.termux/
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user