seconds to wait between updates
+ -p, --precise attempt run command in precise intervals
+ -t, --no-title turn off header
+ -x, --exec pass command to exec instead of "sh -c"
+
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+```
+
+### 使用 tail -f
+
+`tail -f` 命令与 `watch` 有一些相同之处。它也会在添加文件时显示文件的底部和其他内容。你不必一次又一次地运行 `tail` 命令,而是运行一个命令并获得可重复更新显示视图的结果。例如,你可以使用如下命令查看系统日志:
+
+```
+$ tail -f /var/log/syslog
+```
+
+某些文件(如 `/var/log/wtmp`)不适合这种类型的处理,因为它们的格式不是普通文本文件,但是通过组合 `watch` 和 `tail`,你可以获得类似的结果,如下所示:
+
+```
+watch 'who /var/log/wtmp | tail -20'
+```
+
+无论有多少用户仍处于登录状态,此命令都将只显示最近的 5 次登录。如果发生其他登录,显示结果将添加一行记录并删除顶行记录。
+
+```
+Every 60.0s: who /var/log/wtmp | tail -5 dragonfly: Thu Feb 27 12:46:07 2020
+
+shs pts/0 2020-02-27 08:07 (192.168.0.5)
+nemo pts/1 2020-02-27 08:26 (192.168.0.5)
+shs pts/1 2020-02-27 10:58 (192.168.0.5)
+nemo pts/1 2020-02-27 11:34 (192.168.0.5)
+dory pts/1 2020-02-27 12:14 (192.168.0.5)
+```
+
+对你有时可能想要监视的信息,无论监视进程、登录名还是系统资源,`watch` 和 `tail -f` 命令都可以提供自动更新视图,从而使监视任务变得更加容易。
+
+--------------------------------------------------------------------------------
+
+via: https://www.networkworld.com/article/3529891/watching-activity-on-linux-with-watch-and-tail-commands.html
+
+作者:[Sandra Henry-Stocker][a]
+选题:[lujun9972][b]
+译者:[Starryi](https://github.com/Starryi)
+校对:[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/newsletters/signup.html
+[2]: https://www.networkworld.com/article/3440100/take-the-intelligent-route-with-consumption-based-storage.html?utm_source=IDG&utm_medium=promotions&utm_campaign=HPE21620&utm_content=sidebar ( Take the Intelligent Route with Consumption-Based Storage)
+[3]: https://www.facebook.com/NetworkWorld/
+[4]: https://www.linkedin.com/company/network-world
diff --git a/published/202007/20200701 Customizing Bash.md b/published/202007/20200701 Customizing Bash.md
old mode 100755
new mode 100644
diff --git a/published/20200807 A Beginner-s Guide to Open Source.md b/published/20200807 A Beginner-s Guide to Open Source.md
new file mode 100644
index 0000000000..a51f88eefe
--- /dev/null
+++ b/published/20200807 A Beginner-s Guide to Open Source.md
@@ -0,0 +1,114 @@
+[#]: subject: "A Beginner's Guide to Open Source"
+[#]: via: "https://ruthikegah.xyz/a-beginners-guide-to-open-source"
+[#]: author: "Ruth Ikegah https://hashnode.com/@ikegah_ruth"
+[#]: collector: "lkxed"
+[#]: translator: "lkxed"
+[#]: reviewer: "wxy"
+[#]: publisher: "wxy"
+[#]: url: "https://linux.cn/article-14600-1.html"
+
+开源新手指南
+======
+
+
+
+作为一名技术人员,你应该时不时会看到“开源”这个词。你有可能在浏览推文、博文时看到过它,也有可能是在学习某一门编程语言或使用某个工具时,看到它的部分介绍写着:这个工具/语言是“开源”的。总之,开源无处不在。
+
+在本文中,我将介绍下面这三个话题:
+
+* 什么是开源
+* 贡献于开源的好处
+* 如何开始贡献
+
+### 什么是开源
+
+开源指的是这样一些软件、项目或社区:它们允许人们修改和分享,因为它们的设计目的就是为了让所有人都能访问。举一个关于菜谱的例子:你可以做你从未发明过的菜,因为发明这个菜谱的人公开了它。大多数时候,你也可以根据自己的口味烹饪,而不会呛到喉咙(开个玩笑)。
+
+> 开源软件(OSS)是指源代码可供他人查看、复制、学习、修改或分享的软件。
+
+下面是开源软件和语言的一些例子:
+
+* Linux 操作系统
+* Google 的 Android 操作系统
+* Firefox 浏览器
+* VLC 媒体播放器
+* Python 语言、PHP 语言、MySQL 数据库
+
+与开源软件相反的是专有软件 / 闭源软件,只有软件的创造者才能自由使用,其他人若想使用,就得先获得法律许可才行。例如 Adobe Photoshop、微软 Office 等。
+
+> 开源不仅限于软件或代码,技术领域的任何人都可以为开源做出贡献(各个角色)。有了开源,就有了透明度、可靠性、灵活性,并允许开放合作。
+
+### 贡献于开源的好处
+
+向开源项目或软件做贡献意味着“免费”让该项目变得更好。你应该会问自己,为什么我要关心或向自己强调“免费”呢?如果你是新手,你可以阅读 [Edidiong Asikpo][2] 的故事,她在 [这篇文章][3] 中说明了为什么开源是她成长的催化剂。
+
+贡献开源的好处有很多,这里是其中一部分:
+
+* 它能够帮助你提高现有的技能,特别是对于新手而言,因为它允许你边做边学。
+* 无论身在何处,你都可以与世界各地的优秀科技人士协作或共事。
+* 你可以公开自己的想法,从而改善软件、项目或社区,让世界变得更美好。
+* 你可以通过贡献开源来得到大家的认可,或者成为独特或伟大事物的一部分(获得自豪感)。
+* 它让你有机会成为一个人才济济、活力四射的社区的一分子,你可以从中汲取灵感,并结识志同道合的人。
+* 你可以因为贡献开源而获得报酬(OoO)!比如你可以参与一些实习,包括 [谷歌编程之夏][4]、[Outreachy][5]、[谷歌文档季][6],以及 Open Collective 的 [赏金计划][7] 等。(LCTT 译注:国内也有类似的开源实习机会,如“开源之夏”。)
+
+### 如何开始贡献
+
+我相信你会对上面提到的最后一点感兴趣吧(^o^),那么,你该如何开始为开源软件做贡献呢?
+
+是时候介绍一下 GitHub 了!
+
+Github 是开源项目协作的大本营,因此它是一个开始贡献开源的好地方。没听说过 GitHub?没有关系!它提供了文档和指南,很容易就可以上手。不过我还是要提醒你,学习是一个循序渐进的过程,不要太心急喔。
+
+Github 以公共存储库的形式容纳了许多开源项目。对于某个项目,你可以提交一个议题,来说明你注意到的错误或问题(或进一步提出改进意见),也可以创建一个拉取请求(PR),并说明你的更正和改进。
+
+我不建议你在 GitHub 上搜索项目来开始贡献,这将是相当令人沮丧的。尽管你可以限定项目使用的编程语言来简化搜索过程,但仍然会有一大堆东西出现在你眼前。(LCCT 译注:对于可爱的小萌新来说,这实在是难以承受 >…<。)
+
+为了更精准地找到适合自己的项目,这里有一些可供开始的途径:
+
+* [First-timers only][8]:一个很好的资源网站,你可以在上面找到新手友好的开源项目来开始贡献。(设计师朋友,我没有忘记你!你可以查看 [Open Source Design][9] 这个网站,在上面也能找到新手友好的开源设计项目!)
+* 你可以创建你自己的开源项目,把你美妙的想法变成现实,并允许其他人的合作和贡献。[这里][10] 有关于如何创建开源项目的指南。
+* 加入一个社区:你可以成为某个社区的成员,这也是传播开源思想的一种方式。你可以在谷歌上搜索当地的开源社区,并积极加入其中。
+
+最后,我想给出几个有用的提示,供你在贡献开源项目时参考:
+
+* 在加入之前,先对项目、社区或组织做一些研究;当你在做的时候,针对不清楚的地方提出问题。
+* 当你加入社区时,尽量积极地介绍自己,并说明你能帮助项目的地方。
+* **不要**认为自己无法为项目提供任何帮助,停止这种念头!你有很好的想法可以分享!
+* 在存储库中看看别人提交的议题,(如果有的话)看看你能在哪些方面提供帮助,你可以关注带有“good first issue”、“help-wanted”、“first-timers only”等标签的议题。
+* 在开始贡献之前,一定要先看一下贡献指南,这样你在贡献时就不会有冲突。
+
+> 哪怕只是使用一个开源工具也是一种贡献;参加一个开源活动也是一种贡献;做开源项目的志愿者,或者为开源项目提供赞助也是一种贡献。
+
+我想用非洲开源节的口号来结束:“未来是开放的”,所以快上车吧!
+
+感谢阅读!
+
+如果你还有疑问或需要帮助,请在 [这里][11] 联系我,我很乐意和你讨论开源,并帮助你做出首次贡献!
+
+**LCTT 译注:读了这篇文章,你是不是想要马上投身于开源贡献呢?那么请考虑加入“Linux 中国翻译组(LCTT)”吧!我们有能帮助你快速上手翻译的 [维基][12] ,有热心友爱的 QQ 群,你甚至还能够在我们的官网上获得属于自己的译者专页……心动了吗?那就立刻行动起来吧!阅读 [维基][12] 以了解如何加入我们~**
+
+--------------------------------------------------------------------------------
+
+via: https://ruthikegah.xyz/a-beginners-guide-to-open-source
+
+作者:[Ruth Ikegah][a]
+选题:[lkxed][b]
+译者:[lkxed](https://github.com/lkxed)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://hashnode.com/@ikegah_ruth
+[b]: https://github.com/lkxed
+[1]: https://ruthikegah.xyz/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1596742204400%2Fk9AJL1oNC.jpeg%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp&w=3840&q=75
+[2]: https://hashnode.com/@didicodes
+[3]: https://edidiongasikpo.com/open-source-contributions-a-catalyst-for-growth-b823fc5752b1
+[4]: https://summerofcode.withgoogle.com
+[5]: https://www.outreachy.org/
+[6]: https://developers.google.com/season-of-docs
+[7]: https://docs.opencollective.com/help/contributing/development/bounties
+[8]: https://www.firsttimersonly.com/
+[9]: https://opensourcedesign.net/
+[10]: https://github.com/Ruth-ikegah/opensource.guide
+[11]: https://twitter.com/IkegahRuth
+[12]: https://lctt.github.io/wiki/intro/lctt.html
diff --git a/published/202010/20180414 Go on very small hardware Part 2.md b/published/202010/20180414 Go on very small hardware Part 2.md
new file mode 100644
index 0000000000..fdce9ab34c
--- /dev/null
+++ b/published/202010/20180414 Go on very small hardware Part 2.md
@@ -0,0 +1,935 @@
+[#]: collector: (oska874)
+[#]: translator: (gxlct008)
+[#]: reviewer: (wxy)
+[#]: publisher: (wxy)
+[#]: url: (https://linux.cn/article-12747-1.html)
+[#]: subject: (Go on very small hardware Part 2)
+[#]: via: (https://ziutek.github.io/2018/04/14/go_on_very_small_hardware2.html)
+[#]: author: (Michał Derkacz https://ziutek.github.io/)
+
+Go 语言在极小硬件上的运用(二)
+============================================================
+
+
+
+在本文的 [第一部分][2] 的结尾,我承诺要写关于接口的内容。我不想在这里写有关接口或完整或简短的讲义。相反,我将展示一个简单的示例,来说明如何定义和使用接口,以及如何利用无处不在的 `io.Writer` 接口。还有一些关于反射和半主机的内容。
+
+
+
+接口是 Go 语言的重要组成部分。如果你想了解更多有关它们的信息,我建议你阅读《[高效的 Go 编程][3]》 和 [Russ Cox 的文章][4]。
+
+### 并发 Blinky – 回顾
+
+当你阅读前面示例的代码时,你可能会注意到一中打开或关闭 LED 的反直觉方式。 `Set` 方法用于关闭 LED,`Clear` 方法用于打开 LED。这是由于在 漏极开路配置 下驱动了 LED。我们可以做些什么来减少代码的混乱?让我们用 `On` 和 `Off` 方法来定义 `LED` 类型:
+
+```
+type LED struct {
+ pin gpio.Pin
+}
+
+func (led LED) On() {
+ led.pin.Clear()
+}
+
+func (led LED) Off() {
+ led.pin.Set()
+}
+```
+
+现在我们可以简单地调用 `led.On()` 和 `led.Off()`,这不会再引起任何疑惑了。
+
+在前面的所有示例中,我都尝试使用相同的 漏极开路配置来避免代码复杂化。但是在最后一个示例中,对于我来说,将第三个 LED 连接到 GND 和 PA3 引脚之间并将 PA3 配置为推挽模式会更容易。下一个示例将使用以此方式连接的 LED。
+
+但是我们的新 `LED` 类型不支持推挽配置,实际上,我们应该将其称为 `OpenDrainLED`,并定义另一个类型 `PushPullLED`:
+
+```
+type PushPullLED struct {
+ pin gpio.Pin
+}
+
+func (led PushPullLED) On() {
+ led.pin.Set()
+}
+
+func (led PushPullLED) Off() {
+ led.pin.Clear()
+}
+```
+
+请注意,这两种类型都具有相同的方法,它们的工作方式也相同。如果在 LED 上运行的代码可以同时使用这两种类型,而不必注意当前使用的是哪种类型,那就太好了。 接口类型可以提供帮助:
+
+```
+package main
+
+import (
+ "delay"
+
+ "stm32/hal/gpio"
+ "stm32/hal/system"
+ "stm32/hal/system/timer/systick"
+)
+
+type LED interface {
+ On()
+ Off()
+}
+
+type PushPullLED struct{ pin gpio.Pin }
+
+func (led PushPullLED) On() {
+ led.pin.Set()
+}
+
+func (led PushPullLED) Off() {
+ led.pin.Clear()
+}
+
+func MakePushPullLED(pin gpio.Pin) PushPullLED {
+ pin.Setup(&gpio.Config{Mode: gpio.Out, Driver: gpio.PushPull})
+ return PushPullLED{pin}
+}
+
+type OpenDrainLED struct{ pin gpio.Pin }
+
+func (led OpenDrainLED) On() {
+ led.pin.Clear()
+}
+
+func (led OpenDrainLED) Off() {
+ led.pin.Set()
+}
+
+func MakeOpenDrainLED(pin gpio.Pin) OpenDrainLED {
+ pin.Setup(&gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain})
+ return OpenDrainLED{pin}
+}
+
+var led1, led2 LED
+
+func init() {
+ system.SetupPLL(8, 1, 48/8)
+ systick.Setup(2e6)
+
+ gpio.A.EnableClock(false)
+ led1 = MakeOpenDrainLED(gpio.A.Pin(4))
+ led2 = MakePushPullLED(gpio.A.Pin(3))
+}
+
+func blinky(led LED, period int) {
+ for {
+ led.On()
+ delay.Millisec(100)
+ led.Off()
+ delay.Millisec(period - 100)
+ }
+}
+
+func main() {
+ go blinky(led1, 500)
+ blinky(led2, 1000)
+}
+
+```
+
+我们定义了 `LED` 接口,它有两个方法: `On` 和 `Off`。 `PushPullLED` 和 `OpenDrainLED` 类型代表两种驱动 LED 的方式。我们还定义了两个用作构造函数的 `Make*LED` 函数。这两种类型都实现了 `LED` 接口,因此可以将这些类型的值赋给 `LED` 类型的变量:
+
+```
+led1 = MakeOpenDrainLED(gpio.A.Pin(4))
+led2 = MakePushPullLED(gpio.A.Pin(3))
+```
+
+在这种情况下,可赋值性在编译时检查。赋值后,`led1` 变量包含一个 `OpenDrainLED{gpio.A.Pin(4)}`,以及一个指向 `OpenDrainLED` 类型的方法集的指针。 `led1.On()` 调用大致对应于以下 C 代码:
+
+```
+led1.methods->On(led1.value)
+```
+
+如你所见,如果仅考虑函数调用的开销,这是相当廉价的抽象。
+
+但是,对接口的任何赋值都会导致包含有关已赋值类型的大量信息。对于由许多其他类型组成的复杂类型,可能会有很多信息:
+
+```
+$ egc
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 10356 196 212 10764 2a0c cortexm0.elf
+```
+
+如果我们不使用 [反射][5],可以通过避免包含类型和结构字段的名称来节省一些字节:
+
+```
+$ egc -nf -nt
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 10312 196 212 10720 29e0 cortexm0.elf
+```
+
+生成的二进制文件仍然包含一些有关类型的必要信息和关于所有导出方法(带有名称)的完整信息。在运行时,主要是当你将存储在接口变量中的一个值赋值给任何其他变量时,需要此信息来检查可赋值性。
+
+我们还可以通过重新编译所导入的包来删除它们的类型和字段名称:
+
+```
+$ cd $HOME/emgo
+$ ./clean.sh
+$ cd $HOME/firstemgo
+$ egc -nf -nt
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 10272 196 212 10680 29b8 cortexm0.elf
+```
+
+让我们加载这个程序,看看它是否按预期工作。这一次我们将使用 [st-flash][6] 命令:
+
+```
+$ arm-none-eabi-objcopy -O binary cortexm0.elf cortexm0.bin
+$ st-flash write cortexm0.bin 0x8000000
+st-flash 1.4.0-33-gd76e3c7
+2018-04-10T22:04:34 INFO usb.c: -- exit_dfu_mode
+2018-04-10T22:04:34 INFO common.c: Loading device parameters....
+2018-04-10T22:04:34 INFO common.c: Device connected is: F0 small device, id 0x10006444
+2018-04-10T22:04:34 INFO common.c: SRAM size: 0x1000 bytes (4 KiB), Flash: 0x4000 bytes (16 KiB) in pages of 1024 bytes
+2018-04-10T22:04:34 INFO common.c: Attempting to write 10468 (0x28e4) bytes to stm32 address: 134217728 (0x8000000)
+Flash page at addr: 0x08002800 erased
+2018-04-10T22:04:34 INFO common.c: Finished erasing 11 pages of 1024 (0x400) bytes
+2018-04-10T22:04:34 INFO common.c: Starting Flash write for VL/F0/F3/F1_XL core id
+2018-04-10T22:04:34 INFO flash_loader.c: Successfully loaded flash loader in sram
+ 11/11 pages written
+2018-04-10T22:04:35 INFO common.c: Starting verification of write complete
+2018-04-10T22:04:35 INFO common.c: Flash written and verified! jolly good!
+
+```
+
+我没有将 NRST 信号连接到编程器,因此无法使用 `-reset` 选项,必须按下复位按钮才能运行程序。
+
+
+
+看来,`st-flash` 与此板配合使用有点不可靠(通常需要复位 ST-LINK 加密狗)。此外,当前版本不会通过 SWD 发出复位命令(仅使用 NRST 信号)。软件复位是不现实的,但是它通常是有效的,缺少它会将会带来不便。对于板卡程序员 来说 OpenOCD 工作得更好。
+
+### UART
+
+UART(通用异步收发传输器)仍然是当今微控制器最重要的外设之一。它的优点是以下属性的独特组合:
+
+* 相对较高的速度,
+* 仅两条信号线(在 半双工 通信的情况下甚至一条),
+* 角色对称,
+* 关于新数据的 同步带内信令(起始位),
+* 在传输 字 内的精确计时。
+
+这使得最初用于传输由 7-9 位的字组成的异步消息的 UART,也被用于有效地实现各种其他物理协议,例如被 [WS28xx LEDs][7] 或 [1-wire][8] 设备使用的协议。
+
+但是,我们将以其通常的角色使用 UART:从程序中打印文本消息。
+
+```
+package main
+
+import (
+ "io"
+ "rtos"
+
+ "stm32/hal/dma"
+ "stm32/hal/gpio"
+ "stm32/hal/irq"
+ "stm32/hal/system"
+ "stm32/hal/system/timer/systick"
+ "stm32/hal/usart"
+)
+
+var tts *usart.Driver
+
+func init() {
+ system.SetupPLL(8, 1, 48/8)
+ systick.Setup(2e6)
+
+ gpio.A.EnableClock(true)
+ tx := gpio.A.Pin(9)
+
+ tx.Setup(&gpio.Config{Mode: gpio.Alt})
+ tx.SetAltFunc(gpio.USART1_AF1)
+ d := dma.DMA1
+ d.EnableClock(true)
+ tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil)
+ tts.Periph().EnableClock(true)
+ tts.Periph().SetBaudRate(115200)
+ tts.Periph().Enable()
+ tts.EnableTx()
+
+ rtos.IRQ(irq.USART1).Enable()
+ rtos.IRQ(irq.DMA1_Channel2_3).Enable()
+}
+
+func main() {
+ io.WriteString(tts, "Hello, World!\r\n")
+}
+
+func ttsISR() {
+ tts.ISR()
+}
+
+func ttsDMAISR() {
+ tts.TxDMAISR()
+}
+
+//c:__attribute__((section(".ISRs")))
+var ISRs = [...]func(){
+ irq.USART1: ttsISR,
+ irq.DMA1_Channel2_3: ttsDMAISR,
+}
+
+```
+
+你会发现此代码可能有些复杂,但目前 STM32 HAL 中没有更简单的 UART 驱动程序(在某些情况下,简单的轮询驱动程序可能会很有用)。 `usart.Driver` 是使用 DMA 和中断来减轻 CPU 负担的高效驱动程序。
+
+STM32 USART 外设提供传统的 UART 及其同步版本。要将其用作输出,我们必须将其 Tx 信号连接到正确的 GPIO 引脚:
+
+```
+tx.Setup(&gpio.Config{Mode: gpio.Alt})
+tx.SetAltFunc(gpio.USART1_AF1)
+```
+
+在 Tx-only 模式下配置 `usart.Driver` (rxdma 和 rxbuf 设置为 nil):
+
+```
+tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil)
+```
+
+我们使用它的 `WriteString` 方法来打印这句名言。让我们清理所有内容并编译该程序:
+
+```
+$ cd $HOME/emgo
+$ ./clean.sh
+$ cd $HOME/firstemgo
+$ egc
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 12728 236 176 13140 3354 cortexm0.elf
+```
+
+要查看某些内容,你需要在 PC 中使用 UART 外设。
+
+**请勿使用 RS232 端口或 USB 转 RS232 转换器!**
+
+STM32 系列使用 3.3V 逻辑,但是 RS232 可以产生 -15 V ~ +15 V 的电压,这可能会损坏你的 MCU。你需要使用 3.3V 逻辑的 USB 转 UART 转换器。流行的转换器基于 FT232 或 CP2102 芯片。
+
+
+
+你还需要一些终端仿真程序(我更喜欢 [picocom][9])。刷新新图像,运行终端仿真器,然后按几次复位按钮:
+
+```
+$ 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: 0x080016f4 msp: 0x20000a20
+adapter speed: 4000 kHz
+** Programming Started **
+auto erase enabled
+target halted due to breakpoint, current mode: Thread
+xPSR: 0x61000000 pc: 0x2000003a msp: 0x20000a20
+wrote 13312 bytes from file cortexm0.elf in 1.020185s (12.743 KiB/s)
+** Programming Finished **
+adapter speed: 950 kHz
+$
+$ picocom -b 115200 /dev/ttyUSB0
+picocom v3.1
+
+port is : /dev/ttyUSB0
+flowcontrol : none
+baudrate is : 115200
+parity is : none
+databits are : 8
+stopbits are : 1
+escape is : C-a
+local echo is : no
+noinit is : no
+noreset is : no
+hangup is : no
+nolock is : no
+send_cmd is : sz -vv
+receive_cmd is : rz -vv -E
+imap is :
+omap is :
+emap is : crcrlf,delbs,
+logfile is : none
+initstring : none
+exit_after is : not set
+exit is : no
+
+Type [C-a] [C-h] to see available commands
+Terminal ready
+Hello, World!
+Hello, World!
+Hello, World!
+```
+
+每次按下复位按钮都会产生新的 “Hello,World!”行。一切都在按预期进行。
+
+要查看此 MCU 的 双向 UART 代码,请查看 [此示例][10]。
+
+### io.Writer 接口
+
+`io.Writer` 接口可能是 Go 中第二种最常用的接口类型,仅次于 `error` 接口。其定义如下所示:
+
+```
+type Writer interface {
+ Write(p []byte) (n int, err error)
+}
+```
+
+`usart.Driver` 实现了 `io.Writer`,因此我们可以替换:
+
+```
+tts.WriteString("Hello, World!\r\n")
+```
+
+为
+
+```
+io.WriteString(tts, "Hello, World!\r\n")
+```
+
+此外,你需要将 `io` 包添加到 `import` 部分。
+
+`io.WriteString` 函数的声明如下所示:
+
+```
+func WriteString(w Writer, s string) (n int, err error)
+```
+
+如你所见,`io.WriteString` 允许使用实现了 `io.Writer` 接口的任何类型来编写字符串。在内部,它检查基础类型是否具有 `WriteString` 方法,并使用该方法代替 `Write`(如果可用)。
+
+让我们编译修改后的程序:
+
+```
+$ egc
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 15456 320 248 16024 3e98 cortexm0.elf
+```
+
+如你所见,`io.WriteString` 导致二进制文件的大小显着增加:15776-12964 = 2812 字节。 Flash 上没有太多空间了。是什么引起了这么大规模的增长?
+
+使用这个命令:
+
+```
+arm-none-eabi-nm --print-size --size-sort --radix=d cortexm0.elf
+```
+
+我们可以打印两种情况下按其大小排序的所有符号。通过过滤和分析获得的数据(`awk`,`diff`),我们可以找到大约 80 个新符号。最大的十个如下所示:
+
+```
+> 00000062 T stm32$hal$usart$Driver$DisableRx
+> 00000072 T stm32$hal$usart$Driver$RxDMAISR
+> 00000076 T internal$Type$Implements
+> 00000080 T stm32$hal$usart$Driver$EnableRx
+> 00000084 t errors$New
+> 00000096 R $8$stm32$hal$usart$Driver$$
+> 00000100 T stm32$hal$usart$Error$Error
+> 00000360 T io$WriteString
+> 00000660 T stm32$hal$usart$Driver$Read
+```
+
+因此,即使我们不使用 `usart.Driver.Read` 方法,但它被编译进来了,与 `DisableRx`、`RxDMAISR`、`EnableRx` 以及上面未提及的其他方法一样。不幸的是,如果你为接口赋值了一些内容,就需要它的完整方法集(包含所有依赖项)。对于使用大多数方法的大型程序来说,这不是问题。但是对于我们这种极简的情况而言,这是一个巨大的负担。
+
+我们已经接近 MCU 的极限,但让我们尝试打印一些数字(你需要在 `import` 部分中用 `strconv` 替换 `io` 包):
+
+```
+func main() {
+ a := 12
+ b := -123
+
+ tts.WriteString("a = ")
+ strconv.WriteInt(tts, a, 10, 0, 0)
+ tts.WriteString("\r\n")
+ tts.WriteString("b = ")
+ strconv.WriteInt(tts, b, 10, 0, 0)
+ tts.WriteString("\r\n")
+
+ tts.WriteString("hex(a) = ")
+ strconv.WriteInt(tts, a, 16, 0, 0)
+ tts.WriteString("\r\n")
+ tts.WriteString("hex(b) = ")
+ strconv.WriteInt(tts, b, 16, 0, 0)
+ tts.WriteString("\r\n")
+}
+```
+
+与使用 `io.WriteString` 函数的情况一样,`strconv.WriteInt` 的第一个参数的类型为 `io.Writer`。
+
+```
+$ egc
+/usr/local/arm/bin/arm-none-eabi-ld: /home/michal/firstemgo/cortexm0.elf section `.rodata' will not fit in region `Flash'
+/usr/local/arm/bin/arm-none-eabi-ld: region `Flash' overflowed by 692 bytes
+exit status 1
+```
+
+这一次我们的空间超出的不多。让我们试着精简一下有关类型的信息:
+
+```
+$ cd $HOME/emgo
+$ ./clean.sh
+$ cd $HOME/firstemgo
+$ egc -nf -nt
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 15876 316 320 16512 4080 cortexm0.elf
+```
+
+很接近,但很合适。让我们加载并运行此代码:
+
+```
+a = 12
+b = -123
+hex(a) = c
+hex(b) = -7b
+```
+
+Emgo 中的 `strconv` 包与 Go 中的原型有很大的不同。它旨在直接用于写入格式化的数字,并且在许多情况下可以替换沉重的 `fmt` 包。 这就是为什么函数名称以 `Write` 而不是 `Format` 开头,并具有额外的两个参数的原因。 以下是其用法示例:
+
+```
+func main() {
+ b := -123
+ strconv.WriteInt(tts, b, 10, 0, 0)
+ tts.WriteString("\r\n")
+ strconv.WriteInt(tts, b, 10, 6, ' ')
+ tts.WriteString("\r\n")
+ strconv.WriteInt(tts, b, 10, 6, '0')
+ tts.WriteString("\r\n")
+ strconv.WriteInt(tts, b, 10, 6, '.')
+ tts.WriteString("\r\n")
+ strconv.WriteInt(tts, b, 10, -6, ' ')
+ tts.WriteString("\r\n")
+ strconv.WriteInt(tts, b, 10, -6, '0')
+ tts.WriteString("\r\n")
+ strconv.WriteInt(tts, b, 10, -6, '.')
+ tts.WriteString("\r\n")
+}
+```
+
+下面是它的输出:
+
+```
+-123
+ -123
+-00123
+..-123
+-123
+-123
+-123..
+```
+
+### Unix 流 和 莫尔斯电码
+
+由于大多数写入的函数都使用 `io.Writer` 而不是具体类型(例如 C 中的 `FILE` ),因此我们获得了类似于 Unix 流 的功能。在 Unix 中,我们可以轻松地组合简单的命令来执行更大的任务。例如,我们可以通过以下方式将文本写入文件:
+
+```
+echo "Hello, World!" > file.txt
+```
+
+`>` 操作符将前面命令的输出流写入文件。还有 `|` 操作符,用于连接相邻命令的输出流和输入流。
+
+多亏了流,我们可以轻松地转换/过滤任何命令的输出。例如,要将所有字母转换为大写,我们可以通过 `tr` 命令过滤 `echo` 的输出:
+
+```
+echo "Hello, World!" | tr a-z A-Z > file.txt
+```
+
+为了显示 `io.Writer` 和 Unix 流之间的类比,让我们编写以下代码:
+
+```
+io.WriteString(tts, "Hello, World!\r\n")
+```
+
+采用以下伪 unix 形式:
+
+```
+io.WriteString "Hello, World!" | usart.Driver usart.USART1
+```
+
+下一个示例将显示如何执行此操作:
+
+```
+io.WriteString "Hello, World!" | MorseWriter | usart.Driver usart.USART1
+```
+
+让我们来创建一个简单的编码器,它使用莫尔斯电码对写入的文本进行编码:
+
+```
+type MorseWriter struct {
+ W io.Writer
+}
+
+func (w *MorseWriter) Write(s []byte) (int, error) {
+ var buf [8]byte
+ for n, c := range s {
+ switch {
+ case c == '\n':
+ c = ' ' // Replace new lines with spaces.
+ case 'a' <= c && c <= 'z':
+ c -= 'a' - 'A' // Convert to upper case.
+ }
+ if c < ' ' || 'Z' < c {
+ continue // c is outside ASCII [' ', 'Z']
+ }
+ var symbol morseSymbol
+ if c == ' ' {
+ symbol.length = 1
+ buf[0] = ' '
+ } else {
+ symbol = morseSymbols[c-'!']
+ for i := uint(0); i < uint(symbol.length); i++ {
+ if (symbol.code>>i)&1 != 0 {
+ buf[i] = '-'
+ } else {
+ buf[i] = '.'
+ }
+ }
+ }
+ buf[symbol.length] = ' '
+ if _, err := w.W.Write(buf[:symbol.length+1]); err != nil {
+ return n, err
+ }
+ }
+ return len(s), nil
+}
+
+type morseSymbol struct {
+ code, length byte
+}
+
+//emgo:const
+var morseSymbols = [...]morseSymbol{
+ {1<<0 | 1<<1 | 1<<2, 4}, // ! ---.
+ {1<<1 | 1<<4, 6}, // " .-..-.
+ {}, // #
+ {1<<3 | 1<<6, 7}, // $ ...-..-
+
+ // Some code omitted...
+
+ {1<<0 | 1<<3, 4}, // X -..-
+ {1<<0 | 1<<2 | 1<<3, 4}, // Y -.--
+ {1<<0 | 1<<1, 4}, // Z --..
+}
+```
+
+你可以在 [这里][11] 找到完整的 `morseSymbols` 数组。 `//emgo:const` 指令确保 `morseSymbols` 数组不会被复制到 RAM 中。
+
+现在我们可以通过两种方式打印句子:
+
+```
+func main() {
+ s := "Hello, World!\r\n"
+ mw := &MorseWriter{tts}
+
+ io.WriteString(tts, s)
+ io.WriteString(mw, s)
+}
+```
+
+我们使用指向 `MorseWriter` `&MorseWriter{tts}` 的指针而不是简单的 `MorseWriter{tts}` 值,因为 `MorseWriter` 太大,不适合接口变量。
+
+与 Go 不同,Emgo 不会为存储在接口变量中的值动态分配内存。接口类型的大小受限制,相当于三个指针(适合 `slice` )或两个 `float64`(适合 `complex128`)的大小,以较大者为准。它可以直接存储所有基本类型和小型 “结构体/数组” 的值,但是对于较大的值,你必须使用指针。
+
+让我们编译此代码并查看其输出:
+
+```
+$ egc
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 15152 324 248 15724 3d6c cortexm0.elf
+```
+
+```
+Hello, World!
+.... . .-.. .-.. --- --..-- .-- --- .-. .-.. -.. ---.
+```
+
+### 终极闪烁
+
+Blinky 是等效于 “Hello,World!” 程序的硬件。一旦有了摩尔斯编码器,我们就可以轻松地将两者结合起来以获得终极闪烁程序:
+
+```
+package main
+
+import (
+ "delay"
+ "io"
+
+ "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, Speed: gpio.Low}
+ led.Setup(&cfg)
+}
+
+type Telegraph struct {
+ Pin gpio.Pin
+ Dotms int // Dot length [ms]
+}
+
+func (t Telegraph) Write(s []byte) (int, error) {
+ for _, c := range s {
+ switch c {
+ case '.':
+ t.Pin.Clear()
+ delay.Millisec(t.Dotms)
+ t.Pin.Set()
+ delay.Millisec(t.Dotms)
+ case '-':
+ t.Pin.Clear()
+ delay.Millisec(3 * t.Dotms)
+ t.Pin.Set()
+ delay.Millisec(t.Dotms)
+ case ' ':
+ delay.Millisec(3 * t.Dotms)
+ }
+ }
+ return len(s), nil
+}
+
+func main() {
+ telegraph := &MorseWriter{Telegraph{led, 100}}
+ for {
+ io.WriteString(telegraph, "Hello, World! ")
+ }
+}
+
+// Some code omitted...
+
+```
+
+在上面的示例中,我省略了 `MorseWriter` 类型的定义,因为它已在前面展示过。完整版可通过 [这里][12] 获取。让我们编译它并运行:
+
+```
+$ egc
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 11772 244 244 12260 2fe4 cortexm0.elf
+```
+
+
+
+### 反射
+
+是的,Emgo 支持 [反射][13]。`reflect` 包尚未完成,但是已完成的部分足以实现 `fmt.Print` 函数族了。来看看我们可以在小型 MCU 上做什么。
+
+为了减少内存使用,我们将使用 [半主机][14] 作为标准输出。为了方便起见,我们还编写了简单的 `println` 函数,它在某种程度上类似于 `fmt.Println`。
+
+```
+package main
+
+import (
+ "debug/semihosting"
+ "reflect"
+ "strconv"
+
+ "stm32/hal/system"
+ "stm32/hal/system/timer/systick"
+)
+
+var stdout semihosting.File
+
+func init() {
+ system.SetupPLL(8, 1, 48/8)
+ systick.Setup(2e6)
+
+ var err error
+ stdout, err = semihosting.OpenFile(":tt", semihosting.W)
+ for err != nil {
+ }
+}
+
+type stringer interface {
+ String() string
+}
+
+func println(args ...interface{}) {
+ for i, a := range args {
+ if i > 0 {
+ stdout.WriteString(" ")
+ }
+ switch v := a.(type) {
+ case string:
+ stdout.WriteString(v)
+ case int:
+ strconv.WriteInt(stdout, v, 10, 0, 0)
+ case bool:
+ strconv.WriteBool(stdout, v, 't', 0, 0)
+ case stringer:
+ stdout.WriteString(v.String())
+ default:
+ stdout.WriteString("%unknown")
+ }
+ }
+ stdout.WriteString("\r\n")
+}
+
+type S struct {
+ A int
+ B bool
+}
+
+func main() {
+ p := &S{-123, true}
+
+ v := reflect.ValueOf(p)
+
+ println("kind(p) =", v.Kind())
+ println("kind(*p) =", v.Elem().Kind())
+ println("type(*p) =", v.Elem().Type())
+
+ v = v.Elem()
+
+ println("*p = {")
+ for i := 0; i < v.NumField(); i++ {
+ ft := v.Type().Field(i)
+ fv := v.Field(i)
+ println(" ", ft.Name(), ":", fv.Interface())
+ }
+ println("}")
+}
+
+```
+
+`semihosting.OpenFile` 函数允许在主机端打开/创建文件。特殊路径 `:tt` 对应于主机的标准输出。
+
+`println` 函数接受任意数量的参数,每个参数的类型都是任意的:
+
+```
+func println(args ...interface{})
+```
+
+可能是因为任何类型都实现了空接口 `interface{}`。 `println` 使用 [类型开关][15] 打印字符串,整数和布尔值:
+
+```
+switch v := a.(type) {
+case string:
+ stdout.WriteString(v)
+case int:
+ strconv.WriteInt(stdout, v, 10, 0, 0)
+case bool:
+ strconv.WriteBool(stdout, v, 't', 0, 0)
+case stringer:
+ stdout.WriteString(v.String())
+default:
+ stdout.WriteString("%unknown")
+}
+```
+
+此外,它还支持任何实现了 `stringer` 接口的类型,即任何具有 `String()` 方法的类型。在任何 `case` 子句中,`v` 变量具有正确的类型,与 `case` 关键字后列出的类型相同。
+
+`reflect.ValueOf(p)` 函数通过允许以编程的方式分析其类型和内容的形式返回 `p`。如你所见,我们甚至可以使用 `v.Elem()` 取消引用指针,并打印所有结构体及其名称。
+
+让我们尝试编译这段代码。现在让我们看看如果编译时没有类型和字段名,会有什么结果:
+
+```
+$ egc -nt -nf
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 16028 216 312 16556 40ac cortexm0.elf
+```
+
+闪存上只剩下 140 个可用字节。让我们使用启用了半主机的 OpenOCD 加载它:
+
+```
+$ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; arm semihosting enable; reset run'
+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: 0x08002338 msp: 0x20000a20
+adapter speed: 4000 kHz
+** Programming Started **
+auto erase enabled
+target halted due to breakpoint, current mode: Thread
+xPSR: 0x61000000 pc: 0x2000003a msp: 0x20000a20
+wrote 16384 bytes from file cortexm0.elf in 0.700133s (22.853 KiB/s)
+** Programming Finished **
+semihosting is enabled
+adapter speed: 950 kHz
+kind(p) = ptr
+kind(*p) = struct
+type(*p) =
+*p = {
+ X. : -123
+ X. : true
+}
+```
+
+如果你实际运行此代码,则会注意到半主机运行缓慢,尤其是在逐字节写入时(缓冲很有用)。
+
+如你所见,`*p` 没有类型名称,并且所有结构字段都具有相同的 `X.` 名称。让我们再次编译该程序,这次不带 `-nt -nf` 选项:
+
+```
+$ egc
+$ arm-none-eabi-size cortexm0.elf
+ text data bss dec hex filename
+ 16052 216 312 16580 40c4 cortexm0.elf
+```
+
+现在已经包括了类型和字段名称,但仅在 ~~_main.go_ 文件中~~ `main` 包中定义了它们。该程序的输出如下所示:
+
+```
+kind(p) = ptr
+kind(*p) = struct
+type(*p) = S
+*p = {
+ A : -123
+ B : true
+}
+```
+
+反射是任何易于使用的序列化库的关键部分,而像 [JSON][16] 这样的序列化 ~~算法~~ 在物联网时代也越来越重要。
+
+这些就是我完成的本文的第二部分。我认为有机会进行第三部分,更具娱乐性的部分,在那里我们将各种有趣的设备连接到这块板上。如果这块板装不下,我们就换一块大一点的。
+
+--------------------------------------------------------------------------------
+
+via: https://ziutek.github.io/2018/04/14/go_on_very_small_hardware2.html
+
+作者:[Michał Derkacz][a]
+译者:[gxlct008](https://github.com/gxlct008)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]:https://ziutek.github.io/
+[1]:https://ziutek.github.io/2018/04/14/go_on_very_small_hardware2.html
+[2]:https://linux.cn/article-11383-1.html
+[3]:https://golang.org/doc/effective_go.html#interfaces
+[4]:https://research.swtch.com/interfaces
+[5]:https://blog.golang.org/laws-of-reflection
+[6]:https://github.com/texane/stlink
+[7]:http://www.world-semi.com/solution/list-4-1.html
+[8]:https://en.wikipedia.org/wiki/1-Wire
+[9]:https://github.com/npat-efault/picocom
+[10]:https://github.com/ziutek/emgo/blob/master/egpath/src/stm32/examples/f030-demo-board/usart/main.go
+[11]:https://github.com/ziutek/emgo/blob/master/egpath/src/stm32/examples/f030-demo-board/morseuart/main.go
+[12]:https://github.com/ziutek/emgo/blob/master/egpath/src/stm32/examples/f030-demo-board/morseled/main.go
+[13]:https://blog.golang.org/laws-of-reflection
+[14]:http://infocenter.arm.com/help/topic/com.arm.doc.dui0471g/Bgbjjgij.html
+[15]:https://golang.org/doc/effective_go.html#type_switch
+[16]:https://en.wikipedia.org/wiki/JSON
diff --git a/published/20180710 Building a Messenger App- Messages.md b/published/202010/20180710 Building a Messenger App- Messages.md
similarity index 100%
rename from published/20180710 Building a Messenger App- Messages.md
rename to published/202010/20180710 Building a Messenger App- Messages.md
diff --git a/published/20180710 Building a Messenger App- Realtime Messages.md b/published/202010/20180710 Building a Messenger App- Realtime Messages.md
similarity index 100%
rename from published/20180710 Building a Messenger App- Realtime Messages.md
rename to published/202010/20180710 Building a Messenger App- Realtime Messages.md
diff --git a/published/20180712 Building a Messenger App- Development Login.md b/published/202010/20180712 Building a Messenger App- Development Login.md
similarity index 100%
rename from published/20180712 Building a Messenger App- Development Login.md
rename to published/202010/20180712 Building a Messenger App- Development Login.md
diff --git a/published/20180716 Building a Messenger App- Access Page.md b/published/202010/20180716 Building a Messenger App- Access Page.md
similarity index 100%
rename from published/20180716 Building a Messenger App- Access Page.md
rename to published/202010/20180716 Building a Messenger App- Access Page.md
diff --git a/translated/tech/20180719 Building a Messenger App- Home Page.md b/published/202010/20180719 Building a Messenger App- Home Page.md
similarity index 86%
rename from translated/tech/20180719 Building a Messenger App- Home Page.md
rename to published/202010/20180719 Building a Messenger App- Home Page.md
index ad3cd30125..741206a4a7 100644
--- a/translated/tech/20180719 Building a Messenger App- Home Page.md
+++ b/published/202010/20180719 Building a Messenger App- Home Page.md
@@ -1,8 +1,8 @@
[#]: collector: (lujun9972)
[#]: translator: (gxlct008)
-[#]: reviewer: ( )
-[#]: publisher: ( )
-[#]: url: ( )
+[#]: reviewer: (wxy)
+[#]: publisher: (wxy)
+[#]: url: (https://linux.cn/article-12722-1.html)
[#]: subject: (Building a Messenger App: Home Page)
[#]: via: (https://nicolasparada.netlify.com/posts/go-messenger-home-page/)
[#]: author: (Nicolás Parada https://nicolasparada.netlify.com/)
@@ -10,6 +10,8 @@
构建一个即时消息应用(八):Home 页面
======
+
+
本文是该系列的第八篇。
* [第一篇: 模式][1]
@@ -20,8 +22,7 @@
* [第六篇: 仅用于开发的登录][6]
* [第七篇: Access 页面][7]
-
-继续前端部分,让我们在本文中完成 Home 页面的开发。 我们将添加一个开始对话的表单和一个包含最新对话的列表。
+继续前端部分,让我们在本文中完成 `home` 页面的开发。 我们将添加一个开始对话的表单和一个包含最新对话的列表。
### 对话表单
@@ -35,7 +36,7 @@
```
-将该表单添加到我们显示 auth user 和 logout 按钮部分的下方。
+将该表单添加到我们显示 “auth user” 和 “logout” 按钮部分的下方。
```js
page.getElementById('conversation-form').onsubmit = onConversationSubmit
@@ -78,7 +79,7 @@ function createConversation(username) {
}
```
-在提交时,我们使用用户名对 `/api/conversations` 进行 POST 请求,并重定向到 conversation 页面 (用于下一篇文章)。
+在提交时,我们使用用户名对 `/api/conversations` 进行 POST 请求,并重定向到 `conversation` 页面(用于下一篇文章)。
### 对话列表
@@ -166,8 +167,7 @@ export function escapeHTML(str) {
```
-这将非常烦人,因为该脚本将被执行😅。
-所以,永远记住要转义来自不可信来源的内容。
+这将非常烦人,因为该脚本将被执行😅。所以,永远记住要转义来自不可信来源的内容。
### 消息订阅
@@ -227,7 +227,7 @@ function getConversation(id) {
以上这些涵盖了主页的所有内容 😊。
在下一篇文章中,我们将对 conversation 页面进行编码。
-[Souce Code][10]
+- [源代码][10]
--------------------------------------------------------------------------------
@@ -235,20 +235,20 @@ via: https://nicolasparada.netlify.com/posts/go-messenger-home-page/
作者:[Nicolás Parada][a]
选题:[lujun9972][b]
-译者:[译者ID](https://github.com/gxlct008)
-校对:[校对者ID](https://github.com/校对者ID)
+译者:[gxlct008](https://github.com/gxlct008)
+校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://nicolasparada.netlify.com/
[b]: https://github.com/lujun9972
-[1]: https://nicolasparada.netlify.com/posts/go-messenger-schema/
-[2]: https://nicolasparada.netlify.com/posts/go-messenger-oauth/
-[3]: https://nicolasparada.netlify.com/posts/go-messenger-conversations/
-[4]: https://nicolasparada.netlify.com/posts/go-messenger-messages/
-[5]: https://nicolasparada.netlify.com/posts/go-messenger-realtime-messages/
-[6]: https://nicolasparada.netlify.com/posts/go-messenger-dev-login/
-[7]: https://nicolasparada.netlify.com/posts/go-messenger-access-page/
+[1]: https://linux.cn/article-11396-1.html
+[2]: https://linux.cn/article-11510-1.html
+[3]: https://linux.cn/article-12056-1.html
+[4]: https://linux.cn/article-12680-1.html
+[5]: https://linux.cn/article-12685-1.html
+[6]: https://linux.cn/article-12692-1.html
+[7]: https://linux.cn/article-12704-1.html
[8]: https://nicolasparada.netlify.com/img/go-messenger-home-page/conversation-form.png
[9]: https://nicolasparada.netlify.com/img/go-messenger-home-page/conversation-list.png
[10]: https://github.com/nicolasparada/go-messenger-demo
diff --git a/translated/tech/20180720 Building a Messenger App- Conversation Page.md b/published/202010/20180720 Building a Messenger App- Conversation Page.md
similarity index 86%
rename from translated/tech/20180720 Building a Messenger App- Conversation Page.md
rename to published/202010/20180720 Building a Messenger App- Conversation Page.md
index b249c2dcfb..95f91cabfa 100644
--- a/translated/tech/20180720 Building a Messenger App- Conversation Page.md
+++ b/published/202010/20180720 Building a Messenger App- Conversation Page.md
@@ -1,8 +1,8 @@
[#]: collector: (lujun9972)
[#]: translator: (gxlct008)
-[#]: reviewer: ( )
-[#]: publisher: ( )
-[#]: url: ( )
+[#]: reviewer: (wxy)
+[#]: publisher: (wxy)
+[#]: url: (https://linux.cn/article-12723-1.html)
[#]: subject: (Building a Messenger App: Conversation Page)
[#]: via: (https://nicolasparada.netlify.com/posts/go-messenger-conversation-page/)
[#]: author: (Nicolás Parada https://nicolasparada.netlify.com/)
@@ -10,6 +10,8 @@
构建一个即时消息应用(九):Conversation 页面
======
+
+
本文是该系列的第九篇,也是最后一篇。
* [第一篇: 模式][1]
@@ -21,8 +23,6 @@
* [第七篇: Access 页面][7]
* [第八篇: Home 页面][8]
-
-
在这篇文章中,我们将对对话页面进行编码。此页面是两个用户之间的聊天室。在顶部我们将显示其他参与者的信息,下面接着的是最新消息列表,以及底部的消息表单。
### 聊天标题
@@ -230,16 +230,15 @@ function readMessages(conversationID) {
}
```
-在这里我们仍然使用 partial application 来获取会话 ID。
-当新消息到达时,我们首先检查它是否来自此对话。如果是,我们会将消息条目预先添加到列表中,并向`/api/conversations/{conversationID}/read_messages`发起 POST 一个请求,以更新参与者上次阅读消息的时间。
+在这里我们仍然使用这个应用的部分来获取会话 ID。
+当新消息到达时,我们首先检查它是否来自此对话。如果是,我们会将消息条目预先添加到列表中,并向 `/api/conversations/{conversationID}/read_messages` 发起 POST 一个请求,以更新参与者上次阅读消息的时间。
* * *
-本系列到此结束。 Messenger app 现在可以运行了。
+本系列到此结束。 消息应用现在可以运行了。
-~~我将在对话和消息列表中添加分页功能,并在共享源代码之前添加用户搜索。我会在准备好的时候和托管的演示👨💻一起更新它~~
-
-[Souce Code][13] • [Demo][14]
+- [源代码][13]
+- [演示][14]
--------------------------------------------------------------------------------
@@ -248,20 +247,20 @@ via: https://nicolasparada.netlify.com/posts/go-messenger-conversation-page/
作者:[Nicolás Parada][a]
选题:[lujun9972][b]
译者:[gxlct008](https://github.com/gxlct008)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://nicolasparada.netlify.com/
[b]: https://github.com/lujun9972
-[1]: https://nicolasparada.netlify.com/posts/go-messenger-schema/
-[2]: https://nicolasparada.netlify.com/posts/go-messenger-oauth/
-[3]: https://nicolasparada.netlify.com/posts/go-messenger-conversations/
-[4]: https://nicolasparada.netlify.com/posts/go-messenger-messages/
-[5]: https://nicolasparada.netlify.com/posts/go-messenger-realtime-messages/
-[6]: https://nicolasparada.netlify.com/posts/go-messenger-dev-login/
-[7]: https://nicolasparada.netlify.com/posts/go-messenger-access-page/
-[8]: https://nicolasparada.netlify.com/posts/go-messenger-home-page/
+[1]: https://linux.cn/article-11396-1.html
+[2]: https://linux.cn/article-11510-1.html
+[3]: https://linux.cn/article-12056-1.html
+[4]: https://linux.cn/article-12680-1.html
+[5]: https://linux.cn/article-12685-1.html
+[6]: https://linux.cn/article-12692-1.html
+[7]: https://linux.cn/article-12704-1.html
+[8]: https://linux.cn/article-12722-1.html
[9]: https://nicolasparada.netlify.com/img/go-messenger-conversation-page/heading.png
[10]: https://nicolasparada.netlify.com/img/go-messenger-conversation-page/list.png
[11]: https://nicolasparada.netlify.com/img/go-messenger-conversation-page/form.png
diff --git a/published/202010/20190102 Using Yarn on Ubuntu and Other Linux Distributions.md b/published/202010/20190102 Using Yarn on Ubuntu and Other Linux Distributions.md
new file mode 100644
index 0000000000..0222a78d37
--- /dev/null
+++ b/published/202010/20190102 Using Yarn on Ubuntu and Other Linux Distributions.md
@@ -0,0 +1,273 @@
+[#]: collector: (lujun9972)
+[#]: translator: (gxlct008)
+[#]: reviewer: (wxy)
+[#]: publisher: (wxy)
+[#]: url: (https://linux.cn/article-12737-1.html)
+[#]: subject: (Using Yarn on Ubuntu and Other Linux Distributions)
+[#]: via: (https://itsfoss.com/install-yarn-ubuntu)
+[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
+
+在 Ubuntu 和其他 Linux 发行版上使用 Yarn
+======
+
+> 本速成教程向你展示了在 Ubuntu 和 Debian Linux 上安装 Yarn 包管理器的官方方法。你还将学习到一些基本的 Yarn 命令以及彻底删除 Yarn 的步骤。
+
+[Yarn][1] 是 Facebook 开发的开源 JavaScript 包管理器。它是流行的 npm 包管理器的一个替代品,或者应该说是改进。 [Facebook 开发团队][2] 创建 Yarn 是为了克服 [npm][3] 的缺点。 Facebook 声称 Yarn 比 npm 更快、更可靠、更安全。
+
+与 npm 一样,Yarn 为你提供一种自动安装、更新、配置和删除从全局注册库中检索到的程序包的方法。
+
+Yarn 的优点是它更快,因为它可以缓存已下载的每个包,所以无需再次下载。它还将操作并行化,以最大化资源利用率。在执行每个已安装的包代码之前,Yarn 还使用 [校验和来验证完整性][4]。 Yarn 还保证可以在一个系统上运行的安装,在任何其他系统上都会以完全相同地方式工作。
+
+如果你正 [在 Ubuntu 上使用 node.js][5],那么你的系统上可能已经安装了 npm。在这种情况下,你可以使用 npm 通过以下方式全局安装 Yarn:
+
+```
+sudo npm install yarn -g
+```
+
+不过,我推荐使用官方方式在 Ubuntu/Debian 上安装 Yarn。
+
+### 在 Ubuntu 和 Debian 上安装 Yarn [官方方式]
+
+![Yarn JS][6]
+
+这里提到的说明应该适用于所有版本的 Ubuntu,例如 Ubuntu 18.04、16.04 等。同样的一组说明也适用于 Debian 和其他基于 Debian 的发行版。
+
+由于本教程使用 `curl` 来添加 Yarn 项目的 GPG 密钥,所以最好验证一下你是否已经安装了 `curl`。
+
+```
+sudo apt install curl
+```
+
+如果 `curl` 尚未安装,则上面的命令将安装它。既然有了 `curl`,你就可以使用它以如下方式添加 Yarn 项目的 GPG 密钥:
+
+```
+curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+```
+
+在此之后,将存储库添加到源列表中,以便将来可以轻松地升级 Yarn 包,并进行其余系统更新:
+
+```
+sudo sh -c 'echo "deb https://dl.yarnpkg.com/debian/ stable main" >> /etc/apt/sources.list.d/yarn.list'
+```
+
+你现在可以继续了。[更新 Ubuntu][7] 或 Debian 系统,以刷新可用软件包列表,然后安装 Yarn:
+
+```
+sudo apt update
+sudo apt install yarn
+```
+
+这将一起安装 Yarn 和 node.js。该过程完成后,请验证是否已成功安装 Yarn。 你可以通过检查 Yarn 版本来做到这一点。
+
+```
+yarn --version
+```
+
+对我来说,它显示了这样的输出:
+
+```
+yarn --version
+1.12.3
+```
+
+这意味着我的系统上安装了 Yarn 版本 1.12.3。
+
+### 使用 Yarn
+
+我假设你对 JavaScript 编程以及依赖项的工作原理有一些基本的了解。我在这里不做详细介绍。我将向你展示一些基本的 Yarn 命令,这些命令将帮助你入门。
+
+#### 使用 Yarn 创建一个新项目
+
+与 `npm` 一样,Yarn 也可以使用 `package.json` 文件。在这里添加依赖项。所有依赖包都缓存在项目根目录下的 `node_modules` 目录中。
+
+在项目的根目录中,运行以下命令以生成新的 `package.json` 文件:
+
+它会问你一些问题。你可以按回车键跳过或使用默认值。
+
+```
+yarn init
+yarn init v1.12.3
+question name (test_yarn): test_yarn_proect
+question version (1.0.0): 0.1
+question description: Test Yarn
+question entry point (index.js):
+question repository url:
+question author: abhishek
+question license (MIT):
+question private:
+success Saved package.json
+Done in 82.42s.
+```
+
+这样,你就得到了一个如下的 `package.json` 文件:
+
+```
+{
+ "name": "test_yarn_proect",
+ "version": "0.1",
+ "description": "Test Yarn",
+ "main": "index.js",
+ "author": "abhishek",
+ "license": "MIT"
+}
+```
+
+现在你有了 `package.json`,你可以手动编辑它以添加或删除包依赖项,也可以使用 Yarn 命令(首选)。
+
+#### 使用 Yarn 添加依赖项
+
+你可以通过以下方式添加对特定包的依赖关系:
+
+```
+yarn add <包名>
+```
+
+例如,如果你想在项目中使用 [Lodash][8],则可以使用 Yarn 添加它,如下所示:
+
+```
+yarn add lodash
+yarn add v1.12.3
+info No lockfile found.
+[1/4] Resolving packages…
+[2/4] Fetching packages…
+[3/4] Linking dependencies…
+[4/4] Building fresh packages…
+success Saved lockfile.
+success Saved 1 new dependency.
+info Direct dependencies
+└─ [email protected]
+info All dependencies
+└─ [email protected]
+Done in 2.67s.
+```
+
+你可以看到,此依赖项已自动添加到 `package.json` 文件中:
+
+```
+{
+ "name": "test_yarn_proect",
+ "version": "0.1",
+ "description": "Test Yarn",
+ "main": "index.js",
+ "author": "abhishek",
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.11"
+ }
+}
+```
+
+默认情况下,Yarn 将在依赖项中添加最新版本的包。如果要使用特定版本,可以在添加时指定。
+
+```
+yarn add package@version-or-tag
+```
+
+像往常一样,你也可以手动更新 `package.json` 文件。
+
+#### 使用 Yarn 升级依赖项
+
+你可以使用以下命令将特定依赖项升级到其最新版本:
+
+```
+yarn upgrade <包名>
+```
+
+它将查看所涉及的包是否具有较新的版本,并且会相应地对其进行更新。
+
+你还可以通过以下方式更改已添加的依赖项的版本:
+
+```
+yarn upgrade package_name@version_or_tag
+```
+
+你还可以使用一个命令将项目的所有依赖项升级到它们的最新版本:
+
+```
+yarn upgrade
+```
+
+它将检查所有依赖项的版本,如果有任何较新的版本,则会更新它们。
+
+#### 使用 Yarn 删除依赖项
+
+你可以通过以下方式从项目的依赖项中删除包:
+
+```
+yarn remove <包名>
+```
+
+#### 安装所有项目依赖项
+
+如果对你 `project.json` 文件进行了任何更改,则应该运行:
+
+```
+yarn
+```
+
+或者,
+
+```
+yarn install
+```
+
+一次安装所有依赖项。
+
+### 如何从 Ubuntu 或 Debian 中删除 Yarn
+
+我将通过介绍从系统中删除 Yarn 的步骤来完成本教程,如果你使用上述步骤安装 Yarn 的话。如果你意识到不再需要 Yarn 了,则可以将它删除。
+
+使用以下命令删除 Yarn 及其依赖项。
+
+```
+sudo apt purge yarn
+```
+
+你也应该从源列表中把存储库信息一并删除掉:
+
+```
+sudo rm /etc/apt/sources.list.d/yarn.list
+```
+
+下一步删除已添加到受信任密钥的 GPG 密钥是可选的。但要做到这一点,你需要知道密钥。你可以使用 `apt-key` 命令获得它:
+
+```
+Warning: apt-key output should not be parsed (stdout is not a terminal) pub rsa4096 2016-10-05 [SC] 72EC F46A 56B4 AD39 C907 BBB7 1646 B01B 86E5 0310 uid [ unknown] Yarn Packaging yarn@dan.cx sub rsa4096 2016-10-05 [E] sub rsa4096 2019-01-02 [S] [expires: 2020-02-02]
+```
+
+这里的密钥是以 `pub` 开始的行中 GPG 密钥指纹的最后 8 个字符。
+
+因此,对于我来说,密钥是 `86E50310`,我将使用以下命令将其删除:
+
+```
+sudo apt-key del 86E50310
+```
+
+你会在输出中看到 `OK`,并且 Yarn 包的 GPG 密钥将从系统信任的 GPG 密钥列表中删除。
+
+我希望本教程可以帮助你在 Ubuntu、Debian、Linux Mint、 elementary OS 等操作系统上安装 Yarn。 我提供了一些基本的 Yarn 命令,以帮助你入门,并完成了从系统中删除 Yarn 的完整步骤。
+
+希望你喜欢本教程,如果有任何疑问或建议,请随时在下面留言。
+
+
+--------------------------------------------------------------------------------
+
+via: https://itsfoss.com/install-yarn-ubuntu
+
+作者:[Abhishek Prakash][a]
+选题:[lujun9972][b]
+译者:[gxlct008](https://github.com/gxlct008)
+校对:[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://yarnpkg.com/lang/en/
+[2]: https://code.fb.com/
+[3]: https://www.npmjs.com/
+[4]: https://itsfoss.com/checksum-tools-guide-linux/
+[5]: https://itsfoss.com/install-nodejs-ubuntu/
+[6]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/01/yarn-js-ubuntu-debian.jpeg?resize=800%2C450&ssl=1
+[7]: https://itsfoss.com/update-ubuntu/
+[8]: https://lodash.com/
diff --git a/published/20190521 How to Disable IPv6 on Ubuntu Linux.md b/published/202010/20190521 How to Disable IPv6 on Ubuntu Linux.md
similarity index 100%
rename from published/20190521 How to Disable IPv6 on Ubuntu Linux.md
rename to published/202010/20190521 How to Disable IPv6 on Ubuntu Linux.md
diff --git a/published/20190822 Things You Didn-t Know About GNU Readline.md b/published/202010/20190822 Things You Didn-t Know About GNU Readline.md
similarity index 100%
rename from published/20190822 Things You Didn-t Know About GNU Readline.md
rename to published/202010/20190822 Things You Didn-t Know About GNU Readline.md
diff --git a/published/202010/20191105 My first contribution to open source- Making a decision.md b/published/202010/20191105 My first contribution to open source- Making a decision.md
new file mode 100644
index 0000000000..a8863ea7ff
--- /dev/null
+++ b/published/202010/20191105 My first contribution to open source- Making a decision.md
@@ -0,0 +1,59 @@
+[#]: collector: (lujun9972)
+[#]: translator: (chenmu-kk)
+[#]: reviewer: (wxy)
+[#]: publisher: (wxy)
+[#]: url: (https://linux.cn/article-12768-1.html)
+[#]: subject: (My first contribution to open source: Making a decision)
+[#]: via: (https://opensource.com/article/19/11/my-first-open-source-contribution-mistake-decisions)
+[#]: author: (Galen Corey https://opensource.com/users/galenemco)
+
+我的第一次开源贡献:做出决定
+======
+
+> 一位新的开源贡献者告诉你如何加入到开源项目中。
+
+
+
+先前,我把我的第一次开源贡献的拖延归咎于[冒牌综合症][2]。但还有一个我无法忽视的因素:我做出决定太艰难了。在[成千上百万][3]的开源项目中选择时,选择一个要做贡献的项目是难以抉择的。如此重负,以至于我常常不得不关掉我的笔记本去思考:“或许我改天再做吧”。
+
+错误之二是让我对做出决定的恐惧妨碍了我做出第一次贡献。在理想世界里,也许开始我的开源之旅时,心中就已经有了一个真正关心和想去做的具体项目,但我有的只是总得为开源项目做出贡献的模糊目标。对于那些处于同一处境的人来说,这儿有一些帮助我挑选出合适的项目(或者至少是一个好的项目)来做贡献的策略。
+
+### 经常使用的工具
+
+一开始,我不认为有必要将自己局限于已经熟悉的工具或项目。有一些项目我之前从未使用过,但由于它们的社区很活跃,或者它们解决的问题很有趣,因此看起来很有吸引力。
+
+但是,考虑我投入到这个项目中的时间有限,我决定继续投入到我了解的工具上去。要了解工具需求,你需要熟悉它的工作方式。如果你想为自己不熟悉的项目做贡献,则需要完成一个额外的步骤来了解代码的功能和目标。这个额外的工作量可能是有趣且值得的,但也会使你的工作时间加倍。因为我的目标主要是贡献,投入到我了解的工具上是缩小范围的很好方式。回馈一个你认为有用的项目也是有意义的。
+
+### 活跃而友好的社区
+
+在选择项目的时候,我希望在那里有人会审查我写的代码才会觉得有信心。当然,我也希望审核我代码的人是个和善的人。毕竟,把你的作品放在那里接受公众监督是很可怕的。虽然我对建设性的反馈持开放态度,但开发者社区中的一些有毒角落是我希望避免的。
+
+为了评估我将要加入的社区,我查看了我正在考虑加入的仓库的议题部分。我要查看核心团队中是否有人定期回复。更重要的是,我试着确保没有人在评论中互相诋毁(这在议题讨论中是很常见的)。我还留意了那些有行为准则的项目,概述了什么是适当的和不适当的在线互动行为。
+
+### 明确的贡献准则
+
+因为这是我第一次为开源项目做出贡献,在此过程中我有很多问题。一些项目社区在流程的文档记录方面做的很好,可以用来指导挑选其中的议题并发起拉取请求。 [Gatsby][4] 是这种做法的典范,尽管那时我没有选择它们,因为在此之前我从未使用过该产品。
+
+这种清晰的文档帮助我们缓解了一些不知如何去做的不安全感。它也给了我希望:项目对新的贡献者是开放的,并且会花时间来查看我的工作。除了贡献准则外,我还查看了议题部分,看看这个项目是否使用了“第一个好议题”标志。这是该项目对初学者开放的另一个迹象(并可以帮助你学会要做什么)。
+
+### 总结
+
+如果你还没有计划好选择一个项目,那么选择合适的领域进行你的第一个开源贡献更加可行。列出一系列标准可以帮助自己缩减选择范围,并为自己的第一个拉取请求找到一个好的项目。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/article/19/11/my-first-open-source-contribution-mistake-decisions
+
+作者:[Galen Corey][a]
+选题:[lujun9972][b]
+译者:[chenmu-kk](https://github.com/chenmu-kk)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/galenemco
+[b]: https://github.com/lujun9972
+[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/lightbulb-idea-think-yearbook-lead.png?itok=5ZpCm0Jh (Lightbulb)
+[2]: https://opensource.com/article/19/10/my-first-open-source-contribution-mistakes
+[3]: https://github.blog/2018-02-08-open-source-project-trends-for-2018/
+[4]: https://www.gatsbyjs.org/contributing/
diff --git a/published/20200512 Scan your Linux security with Lynis.md b/published/202010/20200512 Scan your Linux security with Lynis.md
similarity index 100%
rename from published/20200512 Scan your Linux security with Lynis.md
rename to published/202010/20200512 Scan your Linux security with Lynis.md
diff --git a/published/202010/20200521 Use the internet from the command line with curl.md b/published/202010/20200521 Use the internet from the command line with curl.md
new file mode 100644
index 0000000000..9880cead4e
--- /dev/null
+++ b/published/202010/20200521 Use the internet from the command line with curl.md
@@ -0,0 +1,171 @@
+[#]: collector: (lujun9972)
+[#]: translator: (MjSeven)
+[#]: reviewer: (wxy)
+[#]: publisher: (wxy)
+[#]: url: (https://linux.cn/article-12772-1.html)
+[#]: subject: (Use the internet from the command line with curl)
+[#]: via: (https://opensource.com/article/20/5/curl-cheat-sheet)
+[#]: author: (Seth Kenlon https://opensource.com/users/seth)
+
+使用 curl 从命令行访问互联网
+======
+
+> 下载我们整理的 curl 备忘录。要在不使用图形界面的情况下从互联网上获取所需的信息,curl 是一种快速有效的方法。
+
+
+
+`curl` 通常被视作一款非交互式 Web 浏览器,这意味着它能够从互联网上获取信息,并在你的终端中显示,或将其保存到文件中。从表面看,这是 Web 浏览器,类似 Firefox 或 Chromium 所做的工作,只是它们默认情况下会*渲染*信息,而 `curl` 会下载并显示原始信息。实际上,`curl` 命令可以做更多的事情,并且能够使用多种协议与服务器进行双向传输数据,这些协议包括 HTTP、FTP、SFTP、IMAP、POP3、LDAP、SMB、SMTP 等。对于普通终端用户来说,这是一个有用的工具;而对于系统管理员,这非常便捷;对于微服务和云开发人员来说,它是一个质量保证工具。
+
+`curl` 被设计为在没有用户交互的情况下工作,因此与 Firefox 不同,你必须从头到尾考虑与在线数据的交互。例如,如果想要在 Firefox 中查看网页,你需要启动 Firefox 窗口。打开 Firefox 后,在地址栏或搜索引擎中输入要访问的网站。然后,导航到网站,然后单击要查看的页面。
+
+对于 `curl` 来说也是如此,不同之处在于你需要一次执行所有操作:在启动 `curl` 的同时提供需要访问的互联网地址,并告诉它是否要将数据保存在终端或文件中。当你必须与需要身份验证的网站或 API 进行交互时,会变得有点复杂,但是一旦你学习了 `curl` 命令语法,它就会变得自然而然。为了帮助你掌握它,我们在一个方便的[备忘录][2]中收集了相关的语法信息。
+
+### 使用 curl 下载文件
+
+你可以通过提供指向特定 URL 的链接来使用 `curl` 命令下载文件。如果你提供的 URL 默认为 `index.html`,那么将下载此页面,并将下载的文件显示在终端屏幕上。你可以将数据通过管道传递到 `less`、`tail` 或任何其它命令:
+
+```
+$ curl "http://example.com" | tail -n 4
+ Example Domain
+ This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.
+ More information...
+