Merge remote-tracking branch 'LCTT/master'

This commit is contained in:
Xingyu Wang 2020-10-25 09:23:58 +08:00
commit 253f2baf20
6 changed files with 289 additions and 328 deletions

View File

@ -1,8 +1,8 @@
[#]: collector: (oska874)
[#]: translator: (gxlct008)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: 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/)
@ -10,17 +10,17 @@
Go 语言在极小硬件上的运用(二)
============================================================
![](https://img.linux.net.cn/data/attachment/album/201909/24/210256yihkuy8kcigugr2h.png)
[![STM32F030F4P6](https://ziutek.github.io/images/mcu/f030-demo-board/board.jpg)][1]
在本文的 [第一部分][2] 的结尾,我承诺要写关于接口的内容。我不想在这里写有关接口或完整或简短的讲义。相反,我将展示一个简单的示例,来说明如何定义和使用接口,以及如何利用无处不在的 `io.Writer` 接口。还有一些关于<ruby>反射<rt>reflection</rt></ruby><ruby>半主机<rt>semihosting</rt></ruby>的内容。
![STM32F030F4P6](https://ziutek.github.io/images/mcu/f030-demo-board/board.jpg)
在本文的 [第一部分][2] 的结尾,我承诺要写关于 _interfaces_ 的内容。我不想在这里写有关接口的完整甚至简短的讲义。相反,我将展示一个简单的示例,来说明如何定义和使用接口,以及如何利用无处不在的 _io.Writer_ 接口。还有一些关于 _reflection__semihosting_ 的内容。
接口是 Go 语言的重要组成部分。如果您想了解更多有关它们的信息,我建议您阅读 [Effective Go][3] 和 [Russ Cox 的文章][4]。
接口是 Go 语言的重要组成部分。如果你想了解更多有关它们的信息,我建议你阅读《[高效的 Go 编程][3]》 和 [Russ Cox 的文章][4]。
### 并发 Blinky 回顾
您阅读前面示例的代码时,您可能会注意到一个违反直觉的方式来打开或关闭 LED。 _Set_ 方法用于关闭 LED_Clear_ 方法用于打开 LED。这是由于在 <ruby>漏极开路配置<rt>open-drain configuration</rt></ruby> 下驱动了 LED。我们可以做些什么来减少代码的混乱 让我们用 _On__Off_ 方法来定义 _LED_ 类型:
你阅读前面示例的代码时,你可能会注意到一中打开或关闭 LED 的反直觉方式。 `Set` 方法用于关闭 LED`Clear` 方法用于打开 LED。这是由于在 <ruby>漏极开路配置<rt>open-drain configuration</rt></ruby> 下驱动了 LED。我们可以做些什么来减少代码的混乱让我们用 `On``Off` 方法来定义 `LED` 类型:
```
type LED struct {
@ -34,15 +34,13 @@ func (led LED) On() {
func (led LED) Off() {
led.pin.Set()
}
```
现在我们可以简单地调用 `led.On()``led.Off()`,这不会再引起任何疑惑了。
在前面的所有示例中,我都尝试使用相同的 <ruby>漏极开路配置<rt>open-drain configuration</rt></ruby>来避免代码复杂化。但是在最后一个示例中,对于我来说,将第三个 LED 连接到 GND 和 PA3 引脚之间并将 PA3 配置为<ruby>推挽模式<rt>push-pull mode</rt></ruby>会更容易。下一个示例将使用以此方式连接的 LED。
在前面的所有示例中,我都尝试使用相同的 <ruby>漏极开路配置<rt>open-drain configuration</rt></ruby>来 避免代码复杂化。但是在最后一个示例中,对于我来说,将第三个 LED 连接到 GND 和 PA3 引脚之间并将 PA3 配置为<ruby>推挽模式<rt>push-pull mode</rt></ruby>会更容易。下一个示例将使用以此方式连接的 LED。
但是我们的新 _LED_ 类型不支持推挽配置。实际上,我们应该将其称为 _OpenDrainLED_,并定义另一个类型 _PushPullLED_
但是我们的新 `LED` 类型不支持推挽配置,实际上,我们应该将其称为 `OpenDrainLED`,并定义另一个类型 `PushPullLED`
```
type PushPullLED struct {
@ -56,10 +54,9 @@ func (led PushPullLED) On() {
func (led PushPullLED) Off() {
led.pin.Clear()
}
```
请注意,这两种类型都具有相同的方法,它们的工作方式也相同。如果在 LED 上运行的代码可以同时使用这两种类型,而不必注意当前使用的是哪种类型,那就太好了。 _interface type_ 可以提供帮助:
请注意,这两种类型都具有相同的方法,它们的工作方式也相同。如果在 LED 上运行的代码可以同时使用这两种类型,而不必注意当前使用的是哪种类型,那就太好了。 接口类型可以提供帮助:
```
package main
@ -134,21 +131,20 @@ func main() {
```
我们定义了 _LED_ 接口,它有两个方法: _On__Off__PushPullLED__OpenDrainLED_ 类型代表两种驱动 LED 的方式。我们还定义了两个用作构造函数的 _Make_ _*LED_ 函数。这两种类型都实现了 _LED_ 接口,因此可以将这些类型的值赋给 _LED_ 类型的变量:
我们定义了 `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)}`,以及一个指向 _OpenDainLED_ 类型的方法集的指针。 `led1.On()` 调用大致对应于以下 C 代码:
在这种情况下,<ruby>可赋值性<rt>assignability</rt></ruby>在编译时检查。赋值后,`led1` 变量包含一个 `OpenDrainLED{gpio.A.Pin(4)}`,以及一个指向 `OpenDrainLED` 类型的方法集的指针。 `led1.On()` 调用大致对应于以下 C 代码:
```
led1.methods->On(led1.value)
```
如您所见,如果仅考虑函数调用的开销,这是相当便宜的抽象。
如你所见,如果仅考虑函数调用的开销,这是相当廉价的抽象。
但是,对接口的任何赋值都会导致包含有关已赋值类型的大量信息。对于由许多其他类型组成的复杂类型,可能会有很多信息:
@ -168,7 +164,7 @@ $ arm-none-eabi-size cortexm0.elf
10312 196 212 10720 29e0 cortexm0.elf
```
生成的二进制文件仍然包含一些有关类型的必要信息和关于所有导出方法(带有名称)的完整信息。在运行时,主要是当将存储在接口变量中的一个值赋值给任何其他变量时,需要此信息来检查可赋值性。
生成的二进制文件仍然包含一些有关类型的必要信息和关于所有导出方法(带有名称)的完整信息。在运行时,主要是当将存储在接口变量中的一个值赋值给任何其他变量时,需要此信息来检查可赋值性。
我们还可以通过重新编译所导入的包来删除它们的类型和字段名称:
@ -203,28 +199,23 @@ Flash page at addr: 0x08002800 erased
```
我没有将 NRST 信号连接到编程器,因此无法使用 _-reset_ 选项,必须按下 reset 按钮才能运行程序。
我没有将 NRST 信号连接到编程器,因此无法使用 `-reset` 选项,必须按下复位按钮才能运行程序。
![Interfaces](https://ziutek.github.io/images/mcu/f030-demo-board/interfaces.png)
看来,_st-flash_ 与此板配合使用有点不可靠 (通常需要重置 ST-LINK 加密狗)。此外,当前版本不会通过 SWD 发出 reset 命令 (仅使用 NRST 信号)。 软件重置是不现实的,但是它通常是有效的,缺少它会将会带来不便。对于<ruby>电路板-程序员<rt>board-programmer</rt></ruby> 组合 _OpenOCD_ 工作得更好。
看来,`st-flash` 与此板配合使用有点不可靠(通常需要复位 ST-LINK 加密狗)。此外,当前版本不会通过 SWD 发出复位命令(仅使用 NRST 信号)。软件复位是不现实的,但是它通常是有效的,缺少它会将会带来不便。对于<ruby>板卡程序员<rt>board-programmer</rt></ruby> 来说 OpenOCD 工作得更好。
### UART
UART<ruby>通用异步收发传输器<rt>Universal Aynchronous Receiver-Transmitter</rt></ruby>)仍然是当今微控制器最重要的外设之一。它的优点是以下属性的独特组合:
* 相对较高的速度,
* 仅两条信号线(在 <ruby>半双工<rt>half-duplex</rt></ruby> 通信的情况下甚至一条),
* 角色对称,
* 关于新数据的 <ruby>同步带内信令<rt>synchronous in-band signaling</rt></ruby>(起始位),
* 在传输 <ruby><rt>words</rt></ruby> 内的精确计时。
这使得最初用于传输由 7-9 位 words 组成的异步消息的 UART也被用于有效地实现各种其他物理协议例如被 [WS28xx LEDs][7] 或 [1-wire][8] 设备使用的协议。
这使得最初用于传输由 7-9 位的字组成的异步消息的 UART也被用于有效地实现各种其他物理协议例如被 [WS28xx LEDs][7] 或 [1-wire][8] 设备使用的协议。
但是,我们将以其通常的角色使用 UART从程序中打印文本消息。
@ -286,7 +277,7 @@ var ISRs = [...]func(){
```
会发现此代码可能有些复杂,但目前 STM32 HAL 中没有更简单的 UART 驱动程序(在某些情况下,简单的轮询驱动程序可能会很有用)。 _usart.Driver_ 是使用 DMA 和中断来卸载 CPU 的高效驱动程序。
会发现此代码可能有些复杂,但目前 STM32 HAL 中没有更简单的 UART 驱动程序(在某些情况下,简单的轮询驱动程序可能会很有用)。 `usart.Driver` 是使用 DMA 和中断来减轻 CPU 负担的高效驱动程序。
STM32 USART 外设提供传统的 UART 及其同步版本。要将其用作输出,我们必须将其 Tx 信号连接到正确的 GPIO 引脚:
@ -295,13 +286,13 @@ tx.Setup(&gpio.Config{Mode: gpio.Alt})
tx.SetAltFunc(gpio.USART1_AF1)
```
在 Tx-only 模式下配置 _usart.Driver_ rxdma 和 rxbuf 设置为 nil
在 Tx-only 模式下配置 `usart.Driver` rxdma 和 rxbuf 设置为 nil
```
tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil)
```
我们使用它的 _WriteString_ 方法来打印这句名句。让我们清理所有内容并编译该程序:
我们使用它的 `WriteString` 方法来打印这句名言。让我们清理所有内容并编译该程序:
```
$ cd $HOME/emgo
@ -313,15 +304,15 @@ $ arm-none-eabi-size cortexm0.elf
12728 236 176 13140 3354 cortexm0.elf
```
要查看某些内容,需要在 PC 中使用 UART 外设。
要查看某些内容,需要在 PC 中使用 UART 外设。
**请勿使用 RS232 端口或 USB 转 RS232 转换器!**
STM32 系列使用 3.3V 逻辑,但是 RS232 可以产生 -15 V ~ +15 V 的电压,这可能会损坏您的 MCU。您需要使用 3.3 V 逻辑的 USB 转 UART 转换器。流行的转换器基于 FT232 或 CP2102 芯片。
STM32 系列使用 3.3V 逻辑,但是 RS232 可以产生 -15 V ~ +15 V 的电压,这可能会损坏你的 MCU。你需要使用 3.3V 逻辑的 USB 转 UART 转换器。流行的转换器基于 FT232 或 CP2102 芯片。
![UART](https://ziutek.github.io/images/mcu/f030-demo-board/uart.jpg)
您还需要一些终端仿真程序 (我更喜欢 [picocom][9])。刷新新图像,运行终端仿真器,然后按几次 reset 按钮:
你还需要一些终端仿真程序(我更喜欢 [picocom][9])。刷新新图像,运行终端仿真器,然后按几次复位按钮:
```
$ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; reset run; exit'
@ -377,13 +368,13 @@ Hello, World!
Hello, World!
```
每次按下 reset 按钮都会产生新的 “HelloWorld”行。一切都在按预期进行。
每次按下复位按钮都会产生新的 “HelloWorld”行。一切都在按预期进行。
要查看此 MCU 的 <ruby>双向<rt>bi-directional</rt></ruby> UART 代码,请查看 [此示例][10]。
### io.Writer 接口
_io.Writer_ 接口可能是 Go 中第二种最常用的接口类型,紧接在 _error_ 接口之后。其定义如下所示:
`io.Writer` 接口可能是 Go 中第二种最常用的接口类型,仅次于 `error` 接口。其定义如下所示:
```
type Writer interface {
@ -391,7 +382,7 @@ type Writer interface {
}
```
_usart.Driver_ 实现了 _io.Writer_ ,因此我们可以替换:
`usart.Driver` 实现了 `io.Writer`,因此我们可以替换:
```
tts.WriteString("Hello, World!\r\n")
@ -403,15 +394,15 @@ tts.WriteString("Hello, World!\r\n")
io.WriteString(tts, "Hello, World!\r\n")
```
此外,您需要将 _io_ 包添加到 _import_ 部分。
此外,你需要将 `io` 包添加到 `import` 部分。
_io.WriteString_ 函数的声明如下所示:
`io.WriteString` 函数的声明如下所示:
```
func WriteString(w Writer, s string) (n int, err error)
```
您所见_io.WriteString_ 允许使用实现了 _io.Writer_ 接口的任何类型来编写字符串。在内部,它检查基础类型是否具有 _WriteString_ 方法,并使用该方法代替 _Write_ (如果可用)
你所见,`io.WriteString` 允许使用实现了 `io.Writer` 接口的任何类型来编写字符串。在内部,它检查基础类型是否具有 `WriteString` 方法,并使用该方法代替 `Write`(如果可用)
让我们编译修改后的程序:
@ -422,7 +413,7 @@ $ arm-none-eabi-size cortexm0.elf
15456 320 248 16024 3e98 cortexm0.elf
```
您所见_io.WriteString_ 导致二进制文件的大小显着增加15776-12964 = 2812字节。 Flash 上没有太多空间了。是什么引起了这么大规模的增长?
你所见,`io.WriteString` 导致二进制文件的大小显着增加15776-12964 = 2812 字节。 Flash 上没有太多空间了。是什么引起了这么大规模的增长?
使用这个命令:
@ -430,7 +421,7 @@ $ arm-none-eabi-size cortexm0.elf
arm-none-eabi-nm --print-size --size-sort --radix=d cortexm0.elf
```
我们可以打印两种情况下按其大小排序的所有符号。通过过滤和分析获得的数据awkdiff我们可以找到大约 80 个新符号。最大的十个如下所示:
我们可以打印两种情况下按其大小排序的所有符号。通过过滤和分析获得的数据(`awk``diff`),我们可以找到大约 80 个新符号。最大的十个如下所示:
```
> 00000062 T stm32$hal$usart$Driver$DisableRx
@ -444,9 +435,9 @@ arm-none-eabi-nm --print-size --size-sort --radix=d cortexm0.elf
> 00000660 T stm32$hal$usart$Driver$Read
```
因此,即使我们不使用 _usart.Driver.Read_ 方法进行编译,也与 _DisableRx_、_RxDMAISR_、_EnableRx_ 以及上面未提及的其他方法相同。不幸的是,如果您为接口赋值了一些内容,那么它的完整方法集是必需的(包含所有依赖项)。对于使用大多数方法的大型程序来说,这不是问题。但是对于我们这种极简的情况而言,这是一个巨大的负担。
因此,即使我们不使用 `usart.Driver.Read` 方法,但它被编译进来了,与 `DisableRx`、`RxDMAISR`、`EnableRx` 以及上面未提及的其他方法一样。不幸的是,如果你为接口赋值了一些内容,就需要它的完整方法集(包含所有依赖项)。对于使用大多数方法的大型程序来说,这不是问题。但是对于我们这种极简的情况而言,这是一个巨大的负担。
我们已经接近 MCU 的极限,但让我们尝试打印一些数字(您需要在 _import_ 部分中用 _strconv_ 替换 _io_ 包):
我们已经接近 MCU 的极限,但让我们尝试打印一些数字(你需要在 `import` 部分中用 `strconv` 替换 `io` 包):
```
func main() {
@ -469,8 +460,7 @@ func main() {
}
```
与使用 _io.WriteString_ 函数的情况一样_strconv.WriteInt_ 的第一个参数的类型为 _io.Writer_
与使用 `io.WriteString` 函数的情况一样,`strconv.WriteInt` 的第一个参数的类型为 `io.Writer`
```
$ egc
@ -479,7 +469,7 @@ $ egc
exit status 1
```
这一次我们的空间用完了。让我们试着精简一下有关类型的信息:
这一次我们的空间超出的不多。让我们试着精简一下有关类型的信息:
```
$ cd $HOME/emgo
@ -500,7 +490,7 @@ hex(a) = c
hex(b) = -7b
```
Emgo 中的 _strconv_ 包与 Go 中的原型有很大的不同。 它旨在直接用于写入格式化的数字,并且在许多情况下可以替换繁重的 _fmt_ 包。 这就是为什么函数名称以 _Write_ 而不是 _Format_ 开头,并具有额外的两个参数的原因。 以下是其用法示例:
Emgo 中的 `strconv` 包与 Go 中的原型有很大的不同。它旨在直接用于写入格式化的数字,并且在许多情况下可以替换沉重的 `fmt` 包。 这就是为什么函数名称以 `Write` 而不是 `Format` 开头,并具有额外的两个参数的原因。 以下是其用法示例:
```
func main() {
@ -536,7 +526,7 @@ func main() {
### Unix 流 和 <ruby>莫尔斯电码<rt>Morse code</rt></ruby>
得益于事实上大多数写入功能的函数都使用 _io.Writer_ 而不是具体类型(例如 C 中的 _FILE_ ),因此我们获得了类似于 _Unix stream_ 的功能。在 Unix 中,我们可以轻松地组合简单的命令来执行更大的任务。例如,我们可以通过以下方式将文本写入文件:
由于大多数写入的函数都使用 `io.Writer` 而不是具体类型(例如 C 中的 `FILE` ),因此我们获得了类似于 Unix <ruby><rt>stream</rt></ruby> 的功能。在 Unix 中,我们可以轻松地组合简单的命令来执行更大的任务。例如,我们可以通过以下方式将文本写入文件:
```
echo "Hello, World!" > file.txt
@ -544,13 +534,13 @@ echo "Hello, World!" > file.txt
`>` 操作符将前面命令的输出流写入文件。还有 `|` 操作符,用于连接相邻命令的输出流和输入流。
多亏了流,我们可以轻松地转换/过滤任何命令的输出。例如,要将所有字母转换为大写,我们可以通过 `tr` 命令过滤 `echo` 的输出:
多亏了流,我们可以轻松地转换/过滤任何命令的输出。例如,要将所有字母转换为大写,我们可以通过 _tr_ 命令过滤 echo 的输出:
```
echo "Hello, World!" | tr a-z A-Z > file.txt
```
为了显示 _io.Writer_ 和 Unix 流之间的类比,让我们编写以下代码:
为了显示 `io.Writer` 和 Unix 流之间的类比,让我们编写以下代码:
```
io.WriteString(tts, "Hello, World!\r\n")
@ -628,7 +618,7 @@ var morseSymbols = [...]morseSymbol{
}
```
您可以在 [这里][11] 找到完整的 _morseSymbols_ 数组。 `//emgo:const` 指令确保 _morseSymbols_ 数组不会被复制到 RAM 中。
你可以在 [这里][11] 找到完整的 `morseSymbols` 数组。 `//emgo:const` 指令确保 `morseSymbols` 数组不会被复制到 RAM 中。
现在我们可以通过两种方式打印句子:
@ -642,10 +632,9 @@ func main() {
}
```
我们使用指向 _MorseWriter_ `&MorseWriter{tts}` 的指针而不是简单的 `MorseWriter{tts}` 值,因为 _MorseWriter_ 太大,不适合接口变量。
我们使用指向 `MorseWriter` `&MorseWriter{tts}` 的指针而不是简单的 `MorseWriter{tts}` 值,因为 `MorseWriter` 太大,不适合接口变量。
与 Go 不同Emgo 不会为存储在接口变量中的值动态分配内存。接口类型的大小受限制,等于三个指针(适合 _slice_ )或两个 _float64_(适合 _complex128_ )的大小,以较大者为准。它可以直接存储所有基本类型和小型 “结构体/数组” 的值,但是对于较大的值,您必须使用指针。
与 Go 不同Emgo 不会为存储在接口变量中的值动态分配内存。接口类型的大小受限制,相当于三个指针(适合 `slice` )或两个 `float64`(适合 `complex128`)的大小,以较大者为准。它可以直接存储所有基本类型和小型 “结构体/数组” 的值,但是对于较大的值,你必须使用指针。
让我们编译此代码并查看其输出:
@ -661,9 +650,9 @@ Hello, World!
.... . .-.. .-.. --- --..-- .-- --- .-. .-.. -.. ---.
```
### 终极 Blinky
### 终极闪烁
_Blinky_ 等效于 _HelloWorld_ 程序的硬件。一旦有了 Morse 编码器,我们就可以轻松地将两者结合起来以获得 _Ultimate Blinky_ 程序:
Blinky 是等效于 “HelloWorld” 程序的硬件。一旦有了摩尔斯编码器,我们就可以轻松地将两者结合起来以获得终极闪烁程序:
```
package main
@ -726,7 +715,7 @@ func main() {
```
在上面的示例中,我省略了 _MorseWriter_ 类型的定义,因为它已在前面展示过。完整版可通过 [这里][12] 获取。让我们编译它并运行:
在上面的示例中,我省略了 `MorseWriter` 类型的定义,因为它已在前面展示过。完整版可通过 [这里][12] 获取。让我们编译它并运行:
```
$ egc
@ -739,9 +728,9 @@ $ arm-none-eabi-size cortexm0.elf
### 反射
是的Emgo 支持 [反射][13]。 _reflect_ 包尚未完成,但是已完成的部分足以实现 _fmt.Print_ 函数族了。来看看我们可以在小型 MCU 上做什么。
是的Emgo 支持 [反射][13]。`reflect` 包尚未完成,但是已完成的部分足以实现 `fmt.Print` 函数族了。来看看我们可以在小型 MCU 上做什么。
为了减少内存使用,我们将使用 [semihosting][14] 作为标准输出。为了方便起见,我们还编写了简单的 _println_ 函数,它在某种程度上类似于 _fmt.Println_
为了减少内存使用,我们将使用 <ruby>[半主机][14]<rt>semihosting</rt></ruby> 作为标准输出。为了方便起见,我们还编写了简单的 `println` 函数,它在某种程度上类似于 `fmt.Println`
```
package main
@ -819,15 +808,15 @@ func main() {
```
_semihosting.OpenFile_ 函数允许在主机端 打开/创建 文件。特殊路径 _:tt_ 对应于主机的标准输出。
`semihosting.OpenFile` 函数允许在主机端打开/创建文件。特殊路径 `:tt` 对应于主机的标准输出。
_println_ 函数接受任意数量的参数,每个参数的类型都是任意的:
`println` 函数接受任意数量的参数,每个参数的类型都是任意的:
```
func println(args ...interface{})
```
可能是因为任何类型都实现了空接口 _interface{}__println_ 使用 [类型开关][15] 打印字符串,整数和布尔值:
可能是因为任何类型都实现了空接口 `interface{}``println` 使用 [类型开关][15] 打印字符串,整数和布尔值:
```
switch v := a.(type) {
@ -844,12 +833,11 @@ default:
}
```
此外,它还支持任何实现了 _stringer_ 接口的类型,即任何具有 _String()_ 方法的类型。在任何 _case_ 子句中_v_ 变量具有正确的类型,与 _case_ 关键字后列出的类型相同。
此外,它还支持任何实现了 `stringer` 接口的类型,即任何具有 `String()` 方法的类型。在任何 `case` 子句中,`v` 变量具有正确的类型,与 `case` 关键字后列出的类型相同。
`reflect.ValueOf(p)` 函数通过允许以编程的方式分析其类型和内容的形式返回 `p`。如你所见,我们甚至可以使用 `v.Elem()` 取消引用指针,并打印所有结构体及其名称。
reflect.ValueOf(p) 函数以允许以编程方式分析其类型和内容的形式返回 _p_。如您所见,我们甚至可以使用 `v.Elem()` 取消引用指针,并打印所有结构体及其名称。
让我们尝试编译这段代码。现在,让我们看看如果不使用类型和字段名进行编译会产生什么结果:
让我们尝试编译这段代码。现在让我们看看如果编译时没有类型和字段名,会有什么结果:
```
$ egc -nt -nf
@ -858,7 +846,7 @@ $ arm-none-eabi-size cortexm0.elf
16028 216 312 16556 40ac cortexm0.elf
```
闪存上只剩下 140 个可用字节。让我们使用启用了 semihosting 的 OpenOCD 加载它:
闪存上只剩下 140 个可用字节。让我们使用启用了半主机的 OpenOCD 加载它:
```
$ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; arm semihosting enable; reset run'
@ -891,9 +879,9 @@ type(*p) =
}
```
如果您实际运行过此代码,则会注意到 semihosting 运行缓慢,尤其是在逐字节写入时(缓冲很有用)。
如果你实际运行此代码,则会注意到半主机运行缓慢,尤其是在逐字节写入时(缓冲很有用)。
您所见,`*p` 没有类型名称,并且所有结构字段都具有相同的 _X._ 名称。让我们再次编译该程序,这次不带 _-nt -nf_ 选项:
你所见,`*p` 没有类型名称,并且所有结构字段都具有相同的 `X.` 名称。让我们再次编译该程序,这次不带 `-nt -nf` 选项:
```
$ egc
@ -902,7 +890,7 @@ $ arm-none-eabi-size cortexm0.elf
16052 216 312 16580 40c4 cortexm0.elf
```
现在已经包括了类型和字段名称,但仅在 ~~_main.go_ 文件中~~ _main_ 包中定义了它们。该程序的输出如下所示:
现在已经包括了类型和字段名称,但仅在 ~~_main.go_ 文件中~~ `main` 包中定义了它们。该程序的输出如下所示:
```
kind(p) = ptr
@ -922,15 +910,15 @@ type(*p) = S
via: https://ziutek.github.io/2018/04/14/go_on_very_small_hardware2.html
作者:[Michał Derkacz ][a]
作者:[Michał Derkacz][a]
译者:[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://ziutek.github.io/
[1]:https://ziutek.github.io/2018/04/14/go_on_very_small_hardware2.html
[2]:https://ziutek.github.io/2018/03/30/go_on_very_small_hardware.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

View File

@ -1,8 +1,8 @@
[#]: collector: (lujun9972)
[#]: translator: (wxy)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: reviewer: (wxy)
[#]: publisher: (wxy)
[#]: url: (https://linux.cn/article-12748-1.html)
[#]: subject: (Linux Jargon Buster: What is FOSS \(Free and Open Source Software\)? What is Open Source?)
[#]: via: (https://itsfoss.com/what-is-foss/)
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
@ -47,26 +47,26 @@ FOSS 是指<ruby>自由和开放源码软件<rt>Free and Open Source Software</r
![][4]
你可能已经注意到了,自由和开源软件中的 “Free” 并不意味着它是免费的,它意味着运行、修改和分发软件的自由。
你可能已经注意到了,自由和开源软件中的 “Free” 并不意味着它是免费的,它意味着运行、修改和分发软件的自由
人们经常错误地认为FOSS 或开源软件不能有价格标签。这是不正确的。
大多数 FOSS 都是免费提供的,原因有很多:
* 源代码已经向公众开放,所以一些开发者认为没有必要在下载贴上价格标签。
* 源代码已经向公众开放,所以一些开发者认为没有必要在下载软件时贴上价格标签。
* 有些项目是由一些志愿者免费提供的。因此,主要的开发者认为对这么多人免费贡献的东西收费是不道德的。
* 有些项目是由较大的企业或非营利组织支持和/或开发的,这些组织会雇佣开发人员在他们的开源项目上工作。
* 有些开发者创建开源项目是出于兴趣,或者出于他们对用代码为世界做贡献的热情。对他们来说,下载量、贡献和感谢的话比金钱更重要。
为了避免强调 “免费”,有些人使用了 FLOSS 这个词。FLOSS 是<ruby>自由和开源软件<rt>Free/Libre Open Source Software</rt></ruby>的缩写。单词 Libre意为自由与 gartuit/gratis免费不同。
为了避免强调 “免费”,有些人使用了 FLOSS 这个词LCTT 译注:有时候也写作 F/LOSS。FLOSS 是<ruby>自由和开源软件<rt>Free/Libre Open Source Software</rt></ruby>的缩写。单词 Libre意为自由与 gartuit/gratis免费不同。
> “Free”是言论自由的自由而不是免费啤酒的免费。
> “Free” 是言论自由的自由,而不是免费啤酒的免费。
### FOSS 项目如何赚钱?
开源项目不赚钱是一个神话。红帽是第一个达到 10 亿美元大关的开源公司。[IBM 以 340 亿美元收购了红帽][5]。这样的例子有很多。
许多开源项目,特别是企业领域的项目,都会提供收费的支持和面向企业的功能。这是R红帽、SUSE Linux 和更多此类项目的主要商业模式。
许多开源项目特别是企业领域的项目都会提供收费的支持和面向企业的功能。这是红帽、SUSE Linux 和更多此类项目的主要商业模式。
一些开源项目,如 Discourse、WordPress 等,则提供其软件的托管实例,并收取一定的费用。
@ -86,13 +86,13 @@ FOSS 是指<ruby>自由和开放源码软件<rt>Free and Open Source Software</r
### FOSS 与开源之间的区别是什么?
你会经常遇到 FOSS 和开源的术语。它们经常被互换使用。
你会经常遇到 FOSS 和<ruby>开源<rt>Open Source</rt></ruby>的术语。它们经常被互换使用。
它们是同一件事吗?这很难用“是”和“不是”来回答。
你看FOSS 中的“Free”一词让很多人感到困惑因为人们错误地认为它是免费的。企业高管、高层和决策者往往会关注自由和开源中的“免费”。由于他们是商业人士专注于为他们的公司赚钱“自由”一词在采用 FOSS 原则时起到了威慑作用。
这就是为什么在上世纪 90 年代中期创建出了一个名为<ruby>[开源促进会][8]<rt>Open Source Initiative</rt></ruby>的新组织。他们从自由和开放源码软件中去掉了“自由”一词,并创建了自己的[开放源码的定义][9],以及自己的一套许可证。
这就是为什么在上世纪 90 年代中期创了一个名为<ruby>[开源促进会][8]<rt>Open Source Initiative</rt></ruby>的新组织。他们从自由和开放源码软件中去掉了“自由”一词,并创建了自己的[开放源码的定义][9],以及自己的一套许可证。
<ruby>开源<rt>Open Source</rt></ruby>”一词在软件行业特别流行。高管们对开源更加适应。开源软件的采用迅速增长,我相信 “免费”一词的删除确实起到了作用。
@ -111,7 +111,7 @@ via: https://itsfoss.com/what-is-foss/
作者:[Abhishek Prakash][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/) 荣誉推出

View File

@ -1,58 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: ( chenmu-kk )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: 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)
My first contribution to open source: Making a decision
======
A new open source contributor documents a series of five mistakes she
made starting out in open source.
![Lightbulb][1]
Previously, I put a lot of [blame on impostor syndrome][2] for delaying my first open source contribution. But there was another factor that I cant ignore: I cant make a decision to save my life. And with [millions][3] of open source projects to choose from, choosing one to contribute to is overwhelming. So overwhelming that I would often end up closing my laptop, thinking, "Maybe Ill just do this another day."
Mistake number two was letting my fear of making a decision get in the way of making my first contribution. In an ideal world, perhaps I would have come into my open source journey with a specific project in mind that I genuinely cared about and wanted to work on, but all I had was a vague goal of contributing to open source somehow. For those of you in the same position, here are strategies that helped me pick out the right project (or at least a good one) for my contribution.
### Tools that I used frequently
At first, I did not think it would be necessary to limit myself to tools or projects with which I was already familiar. There were projects that I had never used before but seemed like appealing candidates because of their active community, or the interesting problems that they solved.
However, given that I had a limited amount of time to devote to this project, I decided to stick with a tool that I already knew. To understand what a tool needs, you need to be familiar with how it is supposed to work. If you want to contribute to a project that you are unfamiliar with, you need to complete an additional step of getting to know the functionality and goals of the code. This extra load can be fun and rewarding, but it can also double your work time. Since my goal was primarily to contribute, sticking to what I knew was a helpful way to narrow things down. It is also rewarding to give back to a project that you have found useful.
### An active and friendly community
When choosing my project, I wanted to feel confident that someone would be there to review the code that I wrote. And, of course, I wanted the person who reviewed my code to be a nice person. Putting your work out there for public scrutiny is scary, after all. While I was open to constructive feedback, there were toxic corners of the developer community that I hoped to avoid.
To evaluate the community that I would be joining, I checked out the _issues_ sections of the repos that I was considering. I looked to see if someone from the core team responded regularly. More importantly, I tried to make sure that no one was talking down to each other in the comments (which is surprisingly common in issues discussions). I also looked out for projects that had a code of conduct, outlining what was appropriate vs. inappropriate behavior for online interaction.
### Clear contribution guidelines
Because this was my first time contributing to open source, I had a lot of questions around the process. Some project communities are excellent about documenting the procedures for choosing an issue and making a pull request. Although I did not select them at the time because I had never worked with the product before, [Gatsby][4] is an exemplar of this practice.
This type of clear documentation helped ease some of my insecurity about not knowing what to do. It also gave me hope that the project was open to new contributors and would take the time to look at my work. In addition to contribution guidelines, I looked in the issues section to see if the project was making use of the "good first issue" flag. This is another indication that the project is open to beginners (and helps you discover what to work on).
### Conclusion
If you dont already have a project in mind, choosing the right place to make your first open source contribution can be overwhelming. Coming up with a list of standards helped me narrow down my choices and find a great project for my first pull request.
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/11/my-first-open-source-contribution-mistake-decisions
作者:[Galen Corey][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [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/

View File

@ -1,186 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (MjSeven)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: 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)
Use the internet from the command line with curl
======
Download our new curl cheat sheet. Curl is a fast and efficient way to
pull the information you need from the internet without using a
graphical interface.
![Cheat Sheet cover image][1]
Curl is commonly considered a non-interactive web browser. That means it's able to pull information from the internet and display it in your terminal or save it to a file. This is literally what web browsers, such as Firefox or Chromium, do except they _render_ the information by default, while curl downloads and displays raw information. In reality, the curl command does much more and has the ability to transfer data to or from a server using one of many supported protocols, including HTTP, FTP, SFTP, IMAP, POP3, LDAP, SMB, SMTP, and many more. It's a useful tool for the average terminal user, a vital convenience for the sysadmin, and a quality assurance tool for microservices and cloud developers.
Curl is designed to work without user interaction, so unlike Firefox, you must think about your interaction with online data from start to finish. For instance, if you want to view a web page in Firefox, you launch a Firefox window. After Firefox is open, you type the website you want to visit into the URL field or a search engine. Then you navigate to the site and click on the page you want to see.
The same concepts apply to curl, except you do it all at once: you launch curl at the same time you feed it the internet location you want and tell it whether you want to the data to be saved in your terminal or to a file. The complexity increases when you have to interact with a site that requires authentication or with an API, but once you learn the **curl** command syntax, it becomes second nature. To help you get the hang of it, we collected the pertinent syntax information in a handy [cheat sheet][2].
### Download a file with curl
You can download a file with the **curl** command by providing a link to a specific URL. If you provide a URL that defaults to **index.html**, then the index page is downloaded, and the file you downloaded is displayed on your terminal screen. You can pipe the output to less or tail or any other command:
```
$ curl "<http://example.com>" | tail -n 4
    &lt;h1&gt;Example Domain&lt;/h1&gt;
    &lt;p&gt;This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.&lt;/p&gt;
    &lt;p&gt;&lt;a href="[https://www.iana.org/domains/example"\&gt;More][3] information...&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;
```
Because some URLs contain special characters that your shell normally interprets, it's safest to surround your URL in quotation marks.
Some files don't translate well to being displayed in a terminal. You can use the **\--remote-name** option to cause the file to be saved according to what it's called on the server:
```
$ curl --remote-name "<https://example.com/linux-distro.iso>"
$ ls
linux-distro.iso
```
Alternatively, you can use the **\--output** option to name your download whatever you want:
```
`curl "http://example.com/foo.html" --output bar.html`
```
### List contents of a remote directory with curl
Because curl is non-interactive, it's difficult to browse a page for downloadable elements. Provided that the remote server you're connecting to allows it, you can use **curl** to list the contents of a directory:
```
`$ curl --list-only "https://example.com/foo/"`
```
### Continue a partial download
If you're downloading a very large file, you might find that you have to interrupt the download. Curl is intelligent enough to determine where you left off and continue the download. That means the next time you're downloading a 4GB Linux distribution ISO and something goes wrong, you never have to go back to the start. The syntax for **\--continue-at** is a little unusual: if you know the byte count where your download was interrupted, you can provide it; otherwise, you can use a lone dash (**-**) to tell curl to detect it automatically:
```
`$ curl --remote-name --continue-at - "https://example.com/linux-distro.iso"`
```
### Download a sequence of files
If you need to download several files—rather than just one big file—curl can help with that. Assuming you know the location and file-name pattern of the files you want to download, you can use curl's sequencing notation: the start and end point between a range of integers, in brackets. For the output filename, use **#1** to indicate the first variable:
```
`$ curl "https://example.com/file_[1-4].webp" --output "file_#1.webp"`
```
If you need to use another variable to represent another sequence, denote each variable in the order it appears in the command. For example, in this command, **#1** refers to the directories **images_000** through **images_009**, while **#2** refers to the files **file_1.webp** through **file_4.webp**:
```
$ curl "<https://example.com/images\_00\[0-9\]/file\_\[1-4\].webp>" \
\--output "file_#1-#2.webp"
```
### Download all PNG files from a site
You can do some rudimentary web scraping to find what you want to download, too, using only **curl** and **grep**. For instance, say you need to download all images associated with a web page you're archiving. First, download the page referencing the images. Pipe the page to grep with a search for the image type you're targeting (PNG in this example). Finally, create a **while** loop to construct a download URL and to save the files to your computer:
```
$ curl <https://example.com> |\
grep --only-matching 'src="[^"]*.[png]"' |\
cut -d\" -f2 |\
while read i; do \
curl <https://example.com/"${i}>" -o "${i##*/}"; \
done
```
This is just an example, but it demonstrates how flexible curl can be when combined with a Unix pipe and some clever, but basic, parsing.
### Fetch HTML headers
Protocols used for data exchange have a lot of metadata embedded in the packets that computers send to communicate. HTTP headers are components of the initial portion of data. It can be helpful to view these headers (especially the response code) when troubleshooting your connection to a site:
```
curl --head "<https://example.com>"
HTTP/2 200
accept-ranges: bytes
age: 485487
cache-control: max-age=604800
content-type: text/html; charset=UTF-8
date: Sun, 26 Apr 2020 09:02:09 GMT
etag: "3147526947"
expires: Sun, 03 May 2020 09:02:09 GMT
last-modified: Thu, 17 Oct 2019 07:18:26 GMT
server: ECS (sjc/4E76)
x-cache: HIT
content-length: 1256
```
### Fail quickly
A 200 response is the usual HTTP indicator of success, so it's what you usually expect when you contact a server. The famous 404 response indicates that a page can't be found, and 500 means there was a server error.
To see what errors are happening during negotiation, add the **\--show-error** flag:
```
`$ curl --head --show-error "http://opensource.ga"`
```
These can be difficult for you to fix unless you have access to the server you're contacting, but curl generally tries its best to resolve the location you point it to. Sometimes when testing things over a network, seemingly endless retries just waste time, so you can force curl to exit upon failure quickly with the **\--fail-early** option:
```
`curl --fail-early "http://opensource.ga"`
```
### Redirect query as specified by a 3xx response
The 300 series of responses, however, are more flexible. Specifically, the 301 response means that a URL has been moved permanently to a different location. It's a common way for a website admin to relocate content while leaving a "trail" so people visiting the old location can still find it. Curl doesn't follow a 301 redirect by default, but you can make it continue on to a 301 destination by using the **\--location** option:
```
$ curl "<https://iana.org>" | grep title
&lt;title&gt;301 Moved Permanently&lt;/title&gt;
$ curl --location "<https://iana.org>"
&lt;title&gt;Internet Assigned Numbers Authority&lt;/title&gt;
```
### Expand a shortened URL
The **\--location** option is useful when you want to look at shortened URLs before visiting them. Shortened URLs can be useful for social networks with character limits (of course, this may not be an issue if you use a [modern and open source social network][4]) or for print media in which users can't just copy and paste a long URL. However, they can also be a little dangerous because their destination is, by nature, concealed. By combining the **\--head** option to view just the HTTP headers and the **\--location** option to unravel the final destination of a URL, you can peek into a shortened URL without loading the full resource:
```
$ curl --head --location \
"<https://bit.ly/2yDyS4T>"
```
### [Download our curl cheat sheet][2]
Once you practice thinking about the process of exploring the web as a single command, curl becomes a fast and efficient way to pull the information you need from the internet without bothering with a graphical interface. To help you build it into your usual workflow, we've created a [curl cheat sheet][2] with common curl uses and syntax, including an overview of using it to query an API.
--------------------------------------------------------------------------------
via: https://opensource.com/article/20/5/curl-cheat-sheet
作者:[Seth Kenlon][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [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/coverimage_cheat_sheet.png?itok=lYkNKieP (Cheat Sheet cover image)
[2]: https://opensource.com/downloads/curl-command-cheat-sheet
[3]: https://www.iana.org/domains/example"\>More
[4]: https://opensource.com/article/17/4/guide-to-mastodon

View File

@ -0,0 +1,56 @@
[#]: collector: (lujun9972)
[#]: translator: ( chenmu-kk )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: 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)
我的第一次开源贡献:做出决定
======
一位新的开源贡献者记录了她加入到开源项目后开始犯得五个错误。
![Lightbulb][1]
先前,我将大量的责任归咎于[冒充综合症][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)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [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/

View File

@ -0,0 +1,161 @@
[#]: collector: (lujun9972)
[#]: translator: (MjSeven)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: 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 是一种快速有效的方法。
![Cheat Sheet cover image][1]
Curl 通常被认为是非交互式 Web 浏览器,这意味着它能够从互联网上获取信息并在你的终端中显示或将其保存到文件中。从表面看,这是 Web 浏览器,例如 Firefox 或 Chromium 所做的工作,只是它们默认情况下会 _渲染_ 信息,而 curl 会下载并显示原始信息。实际上curl 命令可以做更多的事情,并且能够使用多种协议与服务器进行双向传输数据,这些协议包括 HTTP、FTP、SFTP、IMAP、POP3、LDAP、SMB、SMTP 等。对于普通终端用户来说,这是一个有用的工具;而对于系统管理员,这非常便捷;对于微服务和云开发人员来说,它是质量保证的工具。
Curl 被设计为在没有用户交互的情况下工作,因此与 Firefox 不同,你必须从头到尾考虑与在线数据的交互。例如,如果想要在 Firefox 中查看网页,你需要启动 Firefox 窗口。打开 Firefox 后,在地址栏或搜索引擎中输入要访问的网站。然后,导航到网站,然后单击要查看的页面。
对于 curl 来说也是如此,不同之处在于你需要一次执行所有操作:在启动 curl 的同时提供需要访问的 Internet 位置,并告诉它是否要将数据保存在终端或文件中。当你必须与需要身份验证的网站或 API 进行交互时,会变得有点复杂,但是一旦你学习了 **curl** 命令语法,它就会成为你的第二天性。为了帮助你掌握它,我们在一个方便的[备忘录][2]中收集了相关的语法信息。
### 使用 curl 下载文件
你可以通过提供指向特定 URL 的链接来使用 **curl** 命令下载文件。如果你提供的 URL 默认为 **index.html**,那么将下载此页面,并将下载的文件显示在终端屏幕上。你可以将数据通过管道传递到 less、tail 或任何其它命令:
```
$ curl "http://example.com" | tail -n 4
<h1>Example Domain</h1>
<p>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</p>
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div></body></html>
```
由于某些 URL 包含特殊字符shell 通常会将其解释,因此用引号将 URL 包起来是最安全的。
某些文件在终端中无法很好的转换显示。你可以使用 **\--remote-name** 选项使文件根据服务器上的命名进行保存:
```
$ curl --remote-name "<https://example.com/linux-distro.iso>"
$ ls
linux-distro.iso
```
或者,你可以使用 **\--output** 选项来命名你想要下载的内容:
```
curl "http://example.com/foo.html" --output bar.html
```
### 使用 curl 列出带有远程目录的内容
因为 curl 不是交互式的,所以很难浏览页面上的可下载元素。如果你要连接的远程服务器允许,可以使用 **curl** 来列出目录的内容:
```
$ curl --list-only "https://example.com/foo/"
```
### 继续中断下载
如果你正在下载一个非常大的文件你可能会发现有时候必须中断下载。Curl 非常智能,可以确定下载从何处中断并继续下载。这意味着,下一次当你下载一个 4GB 的 Linux 发行版 ISO 出现问题时,就不必重新开始了。**\--continue-at** 的语法有点不寻常:如果你知道下载中断时的字节数,你可以提供给 curl否则你可以使用单独的一个破折号**-**)指示 curl 自动检测:
```
$ curl --remote-name --continue-at - "https://example.com/linux-distro.iso"
```
### 下载文件序列
如果你需要下载多个文件而不是一个大文件,那么 curl 可以帮助你解决这个问题。假设你知道要下载的文件的位置和文件名模式,则可以使用 curl 的排序标记:中括号中整数范围内的起点和终点。对于输出文件名,使用 **#1** 表示第一个变量:
```
$ curl "https://example.com/file_[1-4].webp" --output "file_#1.webp"
```
如果你需要使用其它变量来表示另一个序列,按照每个变量在命令中出现的顺序表示它们。例如,在这个命令中,**#1** 指目录 **images_000****images_009**,而 **#2** 指目录 **file_1.webp****file_4.webp**
```
$ curl "<https://example.com/images\_00\[0-9\]/file\_\[1-4\].webp>" \
\--output "file_#1-#2.webp"
```
### 从站点下载所有 PNG 文件
你也可以仅使用 **curl****grep** 进行一些基本的 Web 抓取操作,以找到想要下载的内容。例如,假设你需要下载与正在归档网页关联的所有图像,首先,下载引用图像的页面。将页面内通过管道传输到 grep搜索所需的图片类型在此示例中为 PNG。最后创建一个 **while** 循环来构造下载 URL并将文件保存到你的计算机
```
$ curl https://example.com |\
grep --only-matching 'src="[^"]*.[png]"' |\
cut -d\" -f2 |\
while read i; do \
curl https://example.com/"${i}" -o "${i##*/}"; \
done
```
这只是一个示例,但它展示了 curl 与 Unix 管道和一些基本巧妙的解析结合使用时是多么的灵活。
### 获取 HTML 头
用于数据交换的协议在计算机发送通信的数据包中嵌入了大量元数据。HTTP 头是数据初始部分的组件。在连接一个网站出现问题时,查看这些报文头(尤其是响应码)会有所帮助:
```
curl --head "<https://example.com>"
HTTP/2 200
accept-ranges: bytes
age: 485487
cache-control: max-age=604800
content-type: text/html; charset=UTF-8
date: Sun, 26 Apr 2020 09:02:09 GMT
etag: "3147526947"
expires: Sun, 03 May 2020 09:02:09 GMT
last-modified: Thu, 17 Oct 2019 07:18:26 GMT
server: ECS (sjc/4E76)
x-cache: HIT
content-length: 1256
```
### 快速失败
响应 200 通常是 HTTP 成功指示符,这是你与服务器连接时通常期望的结果。著名的 404 响应表示找不到页面,而 500 则表示服务器在处理请求时出现了错误。
要查看协商过程中发生了什么错误,添加 **\--shou-error** 选项:
```
$ curl --head --show-error "http://opensource.ga"
```
除非你可以访问要连接的服务器,否则这些问题将很难解决,但是 curl 通常会尽力连接你指定的地址。有时在网络上进行测试时,无休止的重试似乎只会浪费时间,因此你可以使用 **\--fail-early** 选项来强制 curl 在失败时迅速退出:
```
curl --fail-early "http://opensource.ga"
```
### 由 3xx 响应指定的重定向查询
300 这个系列的响应更加灵活。具体来说301 响应意味着一个 URL 已被永久移动到其它位置。对于网站管理员来说重新定位内容并留下“痕迹”是一种常见的方式这样访问老地址的人们仍然可以找到它。默认情况下Curl 不会进行 301 重定向,但你可以使用 **\--localtion** 选项使其继续进入 301 响应指向的目标:
```
$ curl "https://iana.org" | grep title
<title>301 Moved Permanently</title>
$ curl --location "https://iana.org"
<title>Internet Assigned Numbers Authority</title>
```
### 展开短网址
如果你想要在访问短网址之前先查看它们,那么 **\--location** 选项非常有用。短网址对于有字符限制的社交网络(当然,如果你使用[现代和开源的社交网络][4]的话,这可能不是问题),或者对于用户不能复制粘贴长地址的打印媒体来说是有用处的。但是,它们也可能存在风险,因为其目的地址本质上是隐藏的。通过结合使用 **\--head** 选项仅查看 HTTP 头,**\--location** 选项可以查看一个 URL 的最终地址,你可以查看一个短网址而无需加载其完整的资源:
```
$ curl --head --location \
"<https://bit.ly/2yDyS4T>"
```
### [下载我们的 curl 备忘录][2]
一旦你开始考虑了将探索 web 由一条命令来完成,那么 curl 就成为一种快速有效的方式,可以从 Internet 上获取所需的信息,而无需麻烦图形界面。为了帮助你适应到工作流中,我们创建了一个 [curl 备忘录][2],它包含常见的 curl 用法和语法,包括使用它查询 API 的概述。
--------------------------------------------------------------------------------
via: https://opensource.com/article/20/5/curl-cheat-sheet
作者:[Seth Kenlon][a]
选题:[lujun9972][b]
译者:[MjSeven](https://github.com/MjSeven)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [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/coverimage_cheat_sheet.png?itok=lYkNKieP (Cheat Sheet cover image)
[2]: https://opensource.com/downloads/curl-command-cheat-sheet
[3]: https://www.iana.org/domains/example"\>More
[4]: https://opensource.com/article/17/4/guide-to-mastodon