Go 语言在极小硬件上的运用（二）

Summary: 我将展示一个简单的示例，来说明如何定义和使用接口，以及如何利用无处不在的 io.Writer 接口。

![](/data/attachment/album/202010/24/090026to9c9sweyrw9ww37.png)

在本文的 [第一部分](/article-11383-1.html) 的结尾，我承诺要写关于接口的内容。我不想在这里写有关接口或完整或简短的讲义。相反，我将展示一个简单的示例，来说明如何定义和使用接口，以及如何利用无处不在的 `io.Writer` 接口。还有一些关于<ruby> 反射 <rt>  reflection </rt></ruby>和<ruby> 半主机 <rt>  semihosting </rt></ruby>的内容。

![STM32F030F4P6](/data/attachment/album/202010/24/090147a1kqxkhkqzxv8mzj.jpg)]

接口是 Go 语言的重要组成部分。如果你想了解更多有关它们的信息，我建议你阅读《[高效的 Go 编程](https://golang.org/doc/effective_go.html#interfaces)》 和 [Russ Cox 的文章](https://research.swtch.com/interfaces)。

### 并发 Blinky – 回顾

当你阅读前面示例的代码时，你可能会注意到一中打开或关闭 LED 的反直觉方式。 `Set` 方法用于关闭 LED，`Clear` 方法用于打开 LED。这是由于在 <ruby> 漏极开路配置 <rt>  open-drain configuration </rt></ruby> 下驱动了 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()`，这不会再引起任何疑惑了。

在前面的所有示例中，我都尝试使用相同的 <ruby> 漏极开路配置 <rt>  open-drain configuration </rt></ruby>来避免代码复杂化。但是在最后一个示例中，对于我来说，将第三个 LED 连接到 GND 和 PA3 引脚之间并将 PA3 配置为<ruby> 推挽模式 <rt>  push-pull mode </rt></ruby>会更容易。下一个示例将使用以此方式连接的 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))

```

在这种情况下，<ruby> 可赋值性 <rt>  assignability </rt></ruby>在编译时检查。赋值后，`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

```

如果我们不使用 [反射](https://blog.golang.org/laws-of-reflection)，可以通过避免包含类型和结构字段的名称来节省一些字节：

```
$ 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](https://github.com/texane/stlink) 命令：

```
$ 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` 选项，必须按下复位按钮才能运行程序。

![Interfaces](/data/attachment/album/202010/24/090341dhi6yarzti080yh3.png)

看来，`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 位的字组成的异步消息的 UART，也被用于有效地实现各种其他物理协议，例如被 [WS28xx LEDs](http://www.world-semi.com/solution/list-4-1.html) 或 [1-wire](https://en.wikipedia.org/wiki/1-Wire) 设备使用的协议。

但是，我们将以其通常的角色使用 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 芯片。

![UART](/data/attachment/album/202010/24/090348naad2gau0aus2d2i.jpg)

你还需要一些终端仿真程序（我更喜欢 [picocom](https://github.com/npat-efault/picocom)）。刷新新图像，运行终端仿真器，然后按几次复位按钮：

```
$ 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 的 <ruby> 双向 <rt>  bi-directional </rt></ruby> UART 代码，请查看 [此示例](https://github.com/ziutek/emgo/blob/master/egpath/src/stm32/examples/f030-demo-board/usart/main.go)。

### 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 流 和 <ruby> 莫尔斯电码 <rt>  Morse code </rt></ruby>

由于大多数写入的函数都使用 `io.Writer` 而不是具体类型（例如 C 中的 `FILE` ），因此我们获得了类似于 Unix <ruby> 流 <rt>  stream </rt></ruby> 的功能。在 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 --..
}

```

你可以在 [这里](https://github.com/ziutek/emgo/blob/master/egpath/src/stm32/examples/f030-demo-board/morseuart/main.go) 找到完整的 `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` 类型的定义，因为它已在前面展示过。完整版可通过 [这里](https://github.com/ziutek/emgo/blob/master/egpath/src/stm32/examples/f030-demo-board/morseled/main.go) 获取。让我们编译它并运行：

```
$ egc
$ arm-none-eabi-size cortexm0.elf
   text    data     bss     dec     hex filename
  11772     244     244   12260    2fe4 cortexm0.elf

```

![Ultimate Blinky](/data/attachment/album/202010/24/090600wtuujx9zeweuxxid.png)

### 反射

是的，Emgo 支持 [反射](https://blog.golang.org/laws-of-reflection)。`reflect` 包尚未完成，但是已完成的部分足以实现 `fmt.Print` 函数族了。来看看我们可以在小型 MCU 上做什么。

为了减少内存使用，我们将使用 <ruby> <a href="http://infocenter.arm.com/help/topic/com.arm.doc.dui0471g/Bgbjjgij.html">  半主机 </a> <rt>  semihosting </rt></ruby> 作为标准输出。为了方便起见，我们还编写了简单的 `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` 使用 [类型开关](https://golang.org/doc/effective_go.html#type_switch) 打印字符串，整数和布尔值：

```
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](https://en.wikipedia.org/wiki/JSON) 这样的序列化 ~~算法~~ 在<ruby> 物联网 <rt>  IoT </rt></ruby>时代也越来越重要。

这些就是我完成的本文的第二部分。我认为有机会进行第三部分，更具娱乐性的部分，在那里我们将各种有趣的设备连接到这块板上。如果这块板装不下，我们就换一块大一点的。

---

via: <https://ziutek.github.io/2018/04/14/go_on_very_small_hardware2.html>

作者：[Michał Derkacz](https://ziutek.github.io/) 译者：[gxlct008](https://github.com/gxlct008) 校对：[wxy](https://github.com/wxy)

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


Linux 黑话解释：什么是 FOSS（自由和开源软件）？什么是开源？

Summary: 现在是时候解释一下什么是 Linux 和软件世界中的 FOSS 了。

现在是时候解释一下什么是 Linux 和软件世界中的 FOSS 了。

![](/data/attachment/album/202010/24/114238kqhwal39v3zag9hb.png)

什么是 FOSS？

在过去，我曾多次被问到这个问题，现在是时候解释一下什么是 Linux 和软件世界中的 FOSS 了。

这个区别很重要，因为 FOSS 是一个通用的词汇，它可以根据上下文的不同而有不同的含义。在这里，我讨论的是软件中的 FOSS 原则。

### 什么是 FOSS？

FOSS 是指<ruby> 自由和开放源码软件 <rt>  Free and Open Source Software </rt></ruby>。这并不意味着软件是免费的。它意味着软件的源代码是开放的，任何人都可以自由使用、研究和修改代码。这个原则允许人们像一个社区一样为软件的开发和改进做出贡献。

#### FOSS 的起源

在上世纪 60、70 年代，计算机以硬件为主，硬件价格昂贵。它们主要由大学的学者或实验室的研究人员使用。以前有限的软件都是免费的，或者是带有它们的源代码，用户可以根据自己的需要修改源代码。

在上世纪 70 年代末和 80 年代初，制造商为了不让自己的软件在竞争对手的计算机上运行，停止了分发源代码。

这种限制性的许可导致了那些习惯和喜欢修改软件的人的不便和不喜。上世纪 80 年代中期，Richard Stallman 发起了<ruby> 自由软件运动 <rt>  Free Software Movement </rt></ruby>。

[Stallman 指明了一个软件要成为 FOSS 的四个基本自由](https://www.gnu.org/philosophy/free-sw.html)。

![自由软件自由](/data/attachment/album/202010/24/114241zqiuu5yqz6ubbab5.jpg)

为了便于理解，我将它们重新表述：

* 任何用户应能为任何目的运行软件。
* 用户应能自由查看软件的源代码，如有需要，应允许用户修改代码。
* 用户应能自由地将软件的副本分发给他人。
* 如果用户修改了代码，她/他应该可以自由地将修改后的代码发布给他人。修改后的代码必须开放源代码。

如果有兴趣，我建议阅读这篇关于 [FOSS 的历史](https://itsfoss.com/history-of-foss/)的文章。

### FOSS 中的 “Free” 并不意味着免费

![](/data/attachment/album/202010/24/114247uq7e5jfmf7yeimve.jpg)

你可能已经注意到了，自由和开源软件中的 “Free” 并不意味着它是免费的，它意味着运行、修改和分发软件的“自由”。

人们经常错误地认为，FOSS 或开源软件不能有价格标签。这是不正确的。

大多数 FOSS 都是免费提供的，原因有很多：

* 源代码已经向公众开放，所以一些开发者认为没有必要在下载软件时贴上价格标签。
* 有些项目是由一些志愿者免费提供的。因此，主要的开发者认为对这么多人免费贡献的东西收费是不道德的。
* 有些项目是由较大的企业或非营利组织支持和/或开发的，这些组织会雇佣开发人员在他们的开源项目上工作。
* 有些开发者创建开源项目是出于兴趣，或者出于他们对用代码为世界做贡献的热情。对他们来说，下载量、贡献和感谢的话比金钱更重要。

为了避免强调 “免费”，有些人使用了 FLOSS 这个词（LCTT 译注：有时候也写作 F/LOSS）。FLOSS 是<ruby> 自由和开源软件 <rt>  Free/Libre Open Source Software </rt></ruby>的缩写。单词 Libre（意为自由）与 gartuit/gratis（免费）不同。

>
> “Free” 是言论自由的自由，而不是免费啤酒的免费。
>
>
>

### FOSS 项目如何赚钱？

开源项目不赚钱是一个神话。红帽是第一个达到 10 亿美元大关的开源公司。[IBM 以 340 亿美元收购了红帽](https://itsfoss.com/ibm-red-hat-acquisition/)。这样的例子有很多。

许多开源项目，特别是企业领域的项目，都会提供收费的支持和面向企业的功能。这是红帽、SUSE Linux 和更多此类项目的主要商业模式。

一些开源项目，如 Discourse、WordPress 等，则提供其软件的托管实例，并收取一定的费用。

许多开源项目，特别是桌面应用程序，依靠捐赠。VLC、GIMP、Inkscape 等这类开源软件就属于这一类。有[资助开源项目的方法](https://itsfoss.com/open-source-funding-platforms/)，但通常，你会在项目网站上找到捐赠链接。

利用开源软件赚钱可能很难，但也不是完全不可能。

### 但我不是程序员，我为什么要关心一个软件是否开源？

这是一个合理的问题。你不是一个软件开发者，只是一个普通的计算机用户。即使软件的源代码是可用的，你也不会理解程序的工作原理。

这很好。你不会明白，但有必要技能的人就会明白，这才是最重要的。

你可以这样想。也许你不会理解一份复杂的法律文件。但如果你有看文件的自由，并保留一份副本，你就可以咨询某个人，他可以检查文件中的法律陷阱。

换句话说，开源软件具有透明度。

### FOSS 与开源之间的区别是什么？

你会经常遇到 FOSS 和<ruby> 开源 <rt>  Open Source </rt></ruby>的术语。它们经常被互换使用。

它们是同一件事吗？这很难用“是”和“不是”来回答。

你看，FOSS 中的“Free”一词让很多人感到困惑，因为人们错误地认为它是免费的。企业高管、高层和决策者往往会关注自由和开源中的“免费”。由于他们是商业人士，专注于为他们的公司赚钱，“自由”一词在采用 FOSS 原则时起到了威慑作用。

这就是为什么在上世纪 90 年代中期创立了一个名为<ruby> <a href="https://opensource.org/">  开源促进会 </a> <rt>  Open Source Initiative </rt></ruby>的新组织。他们从自由和开放源码软件中去掉了“自由”一词，并创建了自己的[开放源码的定义](https://opensource.org/osd)，以及自己的一套许可证。

“<ruby> 开源 <rt>  Open Source </rt></ruby>”一词在软件行业特别流行。高管们对开源更加适应。开源软件的采用迅速增长，我相信 “免费”一词的删除确实起到了作用。

### 有问题吗？

这正如我在[什么是 Linux 发行版](https://itsfoss.com/what-is-linux-distribution/)一文中所解释的那样，FOSS/开源的概念在 Linux 的发展和普及中起到了很大的作用。

我试图在这篇黑话解释文章中用更简单的语言解释 FOSS 和开源的概念，而试图避免在细节或技术精度上做过多的阐述。

我希望你现在对这个话题有了更好的理解。如果你有问题或建议，欢迎留言并继续讨论。

---

via: <https://itsfoss.com/what-is-foss/>

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


如何从 LVM 的卷组中删除物理卷？

Summary: 如果你的 LVM 不再需要使用某个设备，你可以使用 vgreduce 命令从卷组中删除物理卷。

![](/data/attachment/album/202010/25/101355kav72t5iy5yj55tg.jpg)

如果你的 LVM 不再需要使用某个设备，你可以使用 `vgreduce` 命令从卷组中删除物理卷。

`vgreduce` 命令可以通过删除物理卷来缩小卷组的容量。但要确保该物理卷没有被任何逻辑卷使用，请使用 `pvdisplay` 命令查看。如果物理卷仍在使用，你必须使用 `pvmove` 命令将数据转移到另一个物理卷。

数据转移后，它就可以从卷组中删除。

最后使用 `pvremove` 命令删除空物理卷上的 LVM 标签和 LVM 元数据。

* **第一部分：[如何在 Linux 中创建/配置 LVM（逻辑卷管理）](/article-12670-1.html)**
* **第二部分：[如何在 Linux 中扩展/增加 LVM 大小（逻辑卷调整）](/article-12673-1.html)**
* **第三部分：[如何在 Linux 中减少/缩小 LVM 大小（逻辑卷调整）](/article-12740-1.html)**

![](/data/attachment/album/202010/25/101424vgcio5fooi9uo5gh.jpeg)

### 将扩展块移动到现有物理卷上

使用 `pvs` 命令检查是否使用了所需的物理卷（我们计划删除 LVM 中的 `/dev/sdc` 磁盘）。

```
# pvs -o+pv_used

PV VG Fmt Attr PSize PFree Used
/dev/sda myvg lvm2 a- 75.00G 14.00G 61.00G
/dev/sdb myvg lvm2 a- 50.00G 45.00G 5.00G
/dev/sdc myvg lvm2 a- 17.15G 12.15G 5.00G

```

如果使用了，请检查卷组中的其他物理卷是否有足够的空闲<ruby> 扩展块 <rt>  extent </rt></ruby>。

如果有的话，你可以在需要删除的设备上运行 `pvmove` 命令。扩展块将被分配到其他设备上。

```
# pvmove /dev/sdc

/dev/sdc: Moved: 2.0%
…
/dev/sdc: Moved: 79.2%
…
/dev/sdc: Moved: 100.0%

```

当 `pvmove` 命令完成后。再次使用 `pvs` 命令检查物理卷是否有空闲。

```
# pvs -o+pv_used

PV VG Fmt Attr PSize PFree Used
/dev/sda myvg lvm2 a- 75.00G  1.85G 73.15G
/dev/sdb myvg lvm2 a- 50.00G 45.00G 5.00G
/dev/sdc myvg lvm2 a- 17.15G 17.15G 0

```

如果它是空闲的，使用 `vgreduce` 命令从卷组中删除物理卷 `/dev/sdc`。

```
# vgreduce myvg /dev/sdc
Removed "/dev/sdc" from volume group "vg01"

```

最后，运行 `pvremove` 命令从 LVM 配置中删除磁盘。现在，磁盘已经完全从 LVM 中移除，可以用于其他用途。

```
# pvremove /dev/sdc
Labels on physical volume "/dev/sdc" successfully wiped.

```

### 移动扩展块到新磁盘

如果你在卷组中的其他物理卷上没有足够的可用扩展。使用以下步骤添加新的物理卷。

向存储组申请新的 LUN。分配完毕后，运行以下命令来[在 Linux 中发现新添加的 LUN 或磁盘](https://www.2daygeek.com/scan-detect-luns-scsi-disks-on-redhat-centos-oracle-linux/)。

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

```

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

```

```
# fdisk -l

```

操作系统中检测到磁盘后，使用 `pvcreate` 命令创建物理卷。

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

```

使用以下命令将新的物理卷 `/dev/sdd` 添加到现有卷组 `vg01` 中。

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

```

现在，使用 `pvs` 命令查看你添加的新磁盘 `/dev/sdd`。

```
# pvs -o+pv_used

PV VG Fmt Attr PSize PFree Used
/dev/sda myvg lvm2 a- 75.00G 14.00G 61.00G
/dev/sdb myvg lvm2 a- 50.00G 45.00G 5.00G
/dev/sdc myvg lvm2 a- 17.15G 12.15G 5.00G
/dev/sdd myvg lvm2 a- 60.00G 60.00G 0

```

使用 `pvmove` 命令将数据从 `/dev/sdc` 移动到 `/dev/sdd`。

```
# pvmove /dev/sdc /dev/sdd

/dev/sdc: Moved: 10.0%
…
/dev/sdc: Moved: 79.7%
…
/dev/sdc: Moved: 100.0%

```

数据移动到新磁盘后。再次使用 `pvs` 命令检查物理卷是否空闲。

```
# pvs -o+pv_used

PV VG Fmt Attr PSize PFree Used
/dev/sda myvg lvm2 a- 75.00G 14.00G 61.00G
/dev/sdb myvg lvm2 a- 50.00G 45.00G 5.00G
/dev/sdc myvg lvm2 a- 17.15G 17.15G 0
/dev/sdd myvg lvm2 a- 60.00G 47.85G 12.15G

```

如果空闲，使用 `vgreduce` 命令从卷组中删除物理卷 `/dev/sdc`。

```
# vgreduce myvg /dev/sdc
Removed "/dev/sdc" from volume group "vg01"

```

最后，运行 `pvremove` 命令从 LVM 配置中删除磁盘。现在，磁盘已经完全从 LVM 中移除，可以用于其他用途。

```
# pvremove /dev/sdc
Labels on physical volume "/dev/sdc" successfully wiped.

```

---

via: <https://www.2daygeek.com/linux-remove-delete-physical-volume-pv-from-volume-group-vg-in-lvm/>

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

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


2 种从 Linux 终端下载文件的方法

Summary: 在这篇终端技巧中，你将学习两种在 Linux 中使用命令行下载文件的方法。

在这篇终端技巧中，你将学习两种在 Linux 中使用命令行下载文件的方法。

如果你被困在 Linux 终端，比如说在服务器上，如何从终端下载文件？

在 Linux 中是没有 `download` 命令的，但是有几个 Linux 命令可用于下载文件。

在这篇终端技巧中，你将学习两种在 Linux 中使用命令行下载文件的方法。

我在这里使用的是 Ubuntu，但除了安装，其余的命令同样适用于所有其他 Linux 发行版。

### 使用 wget 命令从 Linux 终端下载文件

![](/data/attachment/album/202010/25/112653xu2w2cc7ssp5yq6x.png)

[wget](https://www.gnu.org/software/wget/) 也许是 Linux 和类 UNIX 系统中使用最多的命令行下载管理器。你可以使用 `wget` 下载一个文件、多个文件、整个目录甚至整个网站。

`wget` 是非交互式的，可以轻松地在后台工作。这意味着你可以很容易地在脚本中使用它，甚至构建像 [uGet 下载管理器](https://itsfoss.com/install-latest-uget-ubuntu-linux-mint/)这样的工具。

让我们看看如何使用 `wget` 从终端下载文件。

#### 安装 wget

大多数 Linux 发行版都预装了 `wget`。它也可以在大多数发行版的仓库中找到，你可以使用发行版的包管理器轻松安装它。

在基于 Ubuntu 和 Debian 的发行版上，你可以使用 [apt 包管理器](https://itsfoss.com/apt-command-guide/)命令：

```
sudo apt install wget

```

#### 使用 wget 下载文件或网页

你只需要提供文件或网页的 URL。它将在你所在的目录下以原始名下载该文件。

```
wget URL

```

![](/data/attachment/album/202010/25/112659fxda0lthgcchug4p.png)

要下载多个文件，你必须将它们的 URL 保存在一个文本文件中，并将该文件作为输入提供给 `wget`，就像这样：

```
wget -i download_files.txt

```

#### 用 wget 下载不同名字的文件

你会注意到，网页在 `wget` 中几乎总是以 `index.html` 的形式保存。为下载的文件提供自定义名称是个好主意。

你可以在下载时使用 `-O` （大写字母 `O`） 选项来提供输出文件名：

```
wget -O filename URL

```

![](/data/attachment/album/202010/25/112414rvk25kjjk5cjy5by.png)

#### 用 wget 下载一个文件夹

假设你正在浏览一个 FTP 服务器，你需要下载整个目录，你可以使用递归选项 `-r`：

```
wget -r ftp://server-address.com/directory

```

#### 使用 wget 下载整个网站

是的，你完全可以做到这一点。你可以用 `wget` 镜像整个网站。我说的下载整个网站是指整个面向公众的网站结构。

虽然你可以直接使用镜像选项 `-m`，但最好加上：

* `–convert-links`：链接将被转换，使内部链接指向下载的资源，而不是网站。
* `–page-requisites`：下载额外的东西，如样式表，使页面在离线状态下看起来更好。

```
wget -m --convert-links --page-requisites website_address

```

![](/data/attachment/album/202010/25/112416h9jgg43t7hul7mt5.png)

#### 额外提示：恢复未完成的下载

如果你因为某些原因按 `CTRL-C` 键中止了下载，你可以用选项 `-c` 恢复之前的下载：

```
wget -c

```

### 使用 curl 在 Linux 命令行中下载文件

和 `wget` 一样，[curl](https://curl.haxx.se/) 也是 Linux 终端中最常用的下载文件的命令之一。[使用 curl](https://linuxhandbook.com/curl-command-examples/) 的方法有很多，但我在这里只关注简单的下载。

#### 安装 curl

虽然 `curl` 并不是预装的，但在大多数发行版的官方仓库中都有。你可以使用你的发行版的包管理器来安装它。

要[在 Ubuntu](https://itsfoss.com/install-curl-ubuntu/) 和其他基于 Debian 的发行版上安装 `curl`，请使用以下命令：

```
sudo apt install curl

```

#### 使用 curl 下载文件或网页

如果你在使用 `curl` 命令时没有在 URL 中带任何选项，它就会读取文件并打印在终端上。

要在 Linux 终端中使用 `curl` 命令下载文件，你必须使用 `-O`（大写字母 `O`）选项：

```
curl -O URL

```

![](/data/attachment/album/202010/25/112701arpm5ii5rpm3obrd.png)

在 Linux 中，用 `curl` 下载多个文件是比较简单的。你只需要指定多个 URL 即可：

```
curl -O URL1 URL2 URL3

```

请记住，`curl` 不像 `wget` 那么简单。`wget` 可以将网页保存为 `index.html`，`curl` 却会抱怨远程文件没有网页的名字。你必须按照下一节的描述用一个自定义的名字来保存它。

#### 用不同的名字下载文件

这可能会让人感到困惑，但如果要为下载的文件提供一个自定义的名称（而不是原始名称），你必须使用 `-o`（小写 `O`）选项：

```
curl -o filename URL

```

![](/data/attachment/album/202010/25/112422gsj0sws9scwsjr2v.png)

有些时候，`curl` 并不能像你期望的那样下载文件，你必须使用选项 `-L`（代表位置）来正确下载。这是因为有些时候，链接会重定向到其他链接，而使用选项 `-L`，它就会跟随最终的链接。

#### 用 curl 暂停和恢复下载

和 `wget` 一样，你也可以用 `curl` 的 `-c` 选项恢复暂停的下载：

```
curl -c URL

```

### 总结

和以往一样，在 Linux 中做同一件事有多种方法。从终端下载文件也不例外。

`wget` 和 `curl` 只是 Linux 中最流行的两个下载文件的命令。还有更多这样的命令行工具。基于终端的网络浏览器，如 [elinks](http://elinks.or.cz/)、[w3m](http://w3m.sourceforge.net/) 等也可以用于在命令行下载文件。

就个人而言，对于一个简单的下载，我更喜欢使用 `wget` 而不是 `curl`。它更简单，也不会让你感到困惑，因为你可能很难理解为什么 `curl` 不能以预期的格式下载文件。

欢迎你的反馈和建议。

---

via: <https://itsfoss.com/download-files-from-linux-terminal/>

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

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


在 Linux 上使用 yum 设置 ZFS

Summary: 在 Fedora 上使用 yum 仓库来获取最新的 ZFS 特性。

在 Fedora 上使用 yum 仓库来获取最新的 ZFS 特性。

>
> 在 Fedora 上使用 yum 仓库来获取最新的 ZFS 特性。
>
>
>

![](/data/attachment/album/202010/26/103028g9q3v3e391c9b62v.jpg)

我是一名 Fedora Linux 用户，我每天都会运行 `yum upgrade`。虽然这个习惯使我能够运行所有最新的软件（[Fedora 的四个基础](https://docs.fedoraproject.org/en-US/project/#_what_is_fedora_all_about)之一的 “First” ，它也做到了），但它也会提醒 [ZFS](https://zfsonlinux.org/) 存储平台和新内核之间的不兼容性。

作为一名开发者，有时我需要最新的 ZFS 分支的新特性。例如，ZFS 2.0.0 包含了一个令人兴奋的新功能，它大大[提高了 ZVOL 同步性能](https://www.phoronix.com/scan.php?page=news_item&px=OpenZFS-3x-Boost-Sync-ZVOL)，这对我这个 KVM 用户来说至关重要。但这意味着，如果我想使用 2.0.0 分支，我必须自己构建 ZFS。

起初，我只是在每次内核更新后从它的 Git 仓库中手动编译 ZFS。如果我忘记了，ZFS 就会在下次启动时无法被识别。幸运的是，我很快就学会了如何为 ZFS 设置动态内核模块支持 （[DKMS](https://www.linuxjournal.com/article/6896)）。然而，这个解决方案并不完美。首先，它没有利用强大的 [yum](https://en.wikipedia.org/wiki/Yum_%28software%29) 系统，而这个系统可以帮助解决依赖关系和升级。此外，使用 `yum` 在你自己的包和上游包之间进行切换是非常容易的。

在本文中，我将演示如何设置 `yum` 仓库来打包 ZFS。这个方案有两个步骤：

1. 从 ZFS 的 Git 仓库中创建 RPM 包。
2. 建立一个 `yum` 仓库来托管这些包。

### 创建 RPM 包

要创建 RPM 包，你需要安装 RPM 工具链。`yum` 提供了一个组来捆绑安装这些工具：

```
sudo dnf group install 'C Development Tools and Libraries' 'RPM Development Tools'

```

安装完这些之后，你必须从 ZFS Git 仓库中安装构建 ZFS 所需的所有包。这些包属于三个组：

1. [Autotools](https://opensource.com/article/19/7/introduction-gnu-autotools)，用于从平台配置中生成构建文件。
2. 用于构建 ZFS 内核和用户态工具的库。
3. 构建 RPM 包的库。

```
sudo dnf install libtool autoconf automake gettext createrepo \
    libuuid-devel libblkid-devel openssl-devel libtirpc-devel \
    lz4-devel libzstd-devel zlib-devel \
    kernel-devel elfutils-libelf-devel \
    libaio-devel libattr-devel libudev-devel \
    python3-devel libffi-devel

```

现在你已经准备好创建你自己的包了。

### 构建 OpenZFS

[OpenZFS](https://openzfs.org/wiki/Main_Page) 提供了优秀的基础设施。要构建它:

1. 用 `git` 克隆仓库，并切换到你希望使用的分支/标签。
2. 运行 Autotools 生成一个 makefile。
3. 运行 `make rpm`，如果一切正常，RPM 文件将被放置在 `build` 文件夹中。

```
$ git clone --branch=zfs-2.0.0-rc3 <https://github.com/openzfs/zfs.git> zfs
$ cd zfs
$ ./autogen.sh
$ ./configure
$ make rpm

```

### 建立一个 yum 仓库

在 `yum` 中，仓库是一个服务器或本地路径，包括元数据和 RPM 文件。用户设置一个 INI 配置文件，`yum` 命令会自动解析元数据并下载相应的软件包。

Fedora 提供了 `createrepo` 工具来设置 `yum` 仓库。首先，创建仓库，并将 ZFS 文件夹中的所有 RPM 文件复制到仓库中。然后运行 `createrepo --update` 将所有的包加入到元数据中。

```
$ sudo mkdir -p /var/lib/zfs.repo
$ sudo createrepo /var/lib/zfs.repo
$ sudo cp *.rpm /var/lib/zfs.repo/
$ sudo createrepo --update /var/lib/zfs.repo

```

在 `/etc/yum.repos.d` 中创建一个新的配置文件来包含仓库路径：

```
$ echo \
"[zfs-local]\\nname=ZFS Local\\nbaseurl=file:///var/lib/zfs.repo\\nenabled=1\\ngpgcheck=0" |\
sudo tee /etc/yum.repos.d/zfs-local.repo

$ sudo dnf --repo=zfs-local list available --refresh

```

终于完成了！你已经有了一个可以使用的 `yum` 仓库和 ZFS 包。现在你只需要安装它们。

```
$ sudo dnf install zfs
$ sudo /sbin/modprobe zfs

```

运行 `sudo zfs version` 来查看你的用户态和内核工具的版本。恭喜！你拥有了 [Fedora 中的 ZFS](https://openzfs.github.io/openzfs-docs/Getting%20Started/Fedora.html)。

---

via: <https://opensource.com/article/20/10/zfs-dnf>

作者：[Sheng Mao](https://opensource.com/users/ivzhh) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


软硬兼施：如何用 IoT 技术撬动产业互联网

>
> 一段时间以来，IoT 屡屡被各大云厂商提及，不出意外的，作为中国科创板云计算第一股的 UCloud，也在 IoT 领域发力已久，而如今，更是有了新的突破。
>
>
>

![](/data/attachment/album/202010/26/114542ykbkbnxik6yyy0oi.jpg)

上周 UCloud 在上海举办了 2020 年的 ThinkInCloud 2020 大会，很荣幸，我代表 Linux 中国被邀请前往参会。在这次参会过程中，我发现 UCloud 新发布的物联网产品有了一些不同的进展，期间得以和 UCloud 物联网产品负责人钱波先生坐下来畅聊了一番 IoT 及 UCloud 在这方面的新成绩。

钱波，具有多年互联网产品研发经验，当我们聊起 UCloud 此次发布的 IoT 产品矩阵，他饶有兴致地谈起了UCloud在物联网领域所致力的产品与技术方向。作为 UCloud 的产品研发总监、IoT 产品线负责人，长久以来的相关从业经验让他言谈之间，就平添了一番信任感。

### 宝剑锋从磨砺出

2018 年伊始， UCloud 决定将物联网 IoT 作为一个新的增长点，开始投入人力和物力研发新产品；到 2019 年，UCloud 第一个物联网产品 UIoT Core 发布；再到如今的 UCloud 物联网产品家族正式结束公测，对外开放服务。其中经历的种种，让钱波激情澎湃。对于钱波来说，UCloud 的各种物联网产品，就如同他的孩子一样，从最早的只有基础的设备联网能力功能，到如今云边端的一揽子产品解决方案，UCloud 的物联网产品在一天天长大，一天天变得更加的适用。

>
> 钱波说：“物联网想要做好，云、边、端都要做好，一个做的不好，这个物联网就是瘸腿的。”
>
>
>

谈及对物联网的认识，钱波说：“物联网想要做好，云、边、端都要做好，一个做的不好，这个物联网就是瘸腿的。”他补充到，其实一直以来，产业互联网就有自己固有的问题：

* 行业内做上层业务的居多，缺乏广连接高并发的技术经验，导致设备连接多，并发一高平台就挂，结果设备的数据就丢失了；
* 行业内的设备类型很多，导致协议繁杂，无法统一，解决方案实现成本很高；
* 同时，物联网开发涉及到云、硬件、软件等，整个研发链路长，耦合深，企业无法很好的解决项目管理中遇见的种种问题，如架构代码复用；
* 此外，还有一个老大难，就是设备分散严重，老旧设备的改造成本极高。

这些问题，都是产业互联网一直以来的痛点。为了解决这些个痛点，在他的带领下，UCloud IoT 产品团队推出了一系列产品，来解决产业互联网的痛点。

>
> 让产业互联网上云不再困难。
>
>
>

为了提供能够承载更高压力的云服务，钱波团队基于 UCloud 的各项公有云能力，为产业互联网提供了 UIoT Core，背靠 UCloud 丰富的云计算资源，为产业互联网提供功能强大、负载能力强的云端物联网底座。

为了解决设备协议繁杂的问题，他们在推出的 UIoT Edge 中提供了业界使用最为广泛的 Modbus 协议的支持，让主流的设备都可以轻松地完成设备的接入，对于一些分散的行业协议，还提供了协议驱动的开源框架，帮助设备快速实现协议并接入。另外，UCloud还将陆续推出DLT645、OPCUA等设备驱动，覆盖更多的产业。

对于研发链路长的问题，UCloud 将整个物联网涉及到的所有资源都基于 UCloud 本身的各项能力来实现。你可以在 UIoT Core 中轻松的调用来自 UCloud 公有云的各项能力，从而让整个流程中涉及到的各项资源更具备一致性。开发者可以轻松的调试各个环节，减少中间服务商的存在。

至于老旧设备的改造问题，UCloud 创新性的提供了一套 UIoT Edge Runtime ，借助设备驱动，帮助产业互联网无侵入地接入设备和实现强大的功能。

为了让开发者们可以更好的基于 UIoT Core 开发产品，他们还调研了国内的开发者在嵌入式操作系统方面的投入，和国内的 RT-Thread 达成了合作，共同推出了 UIoT SDK For RTT，让RT-Thread 开发者可以在 10 分钟内，就完成产品的上云和接入。

借助这些能力，钱波和他的团队让产业互联网上云不再困难。

### 梅花香自苦寒来

时间的磨砺带来的是 UCloud IoT 产品线的不断壮大。和传统的 IoT 云服务提供商不同，UCloud 提供了很多极具特色又能够帮助到产业互联网企业的重要功能，比如可以解决老旧设备接入的开放协议驱动、可以解决 IoT 设备状态留存的设备影子、可以自由流转数据的规则引擎等等，云端serverless风格的数据解析，一系列的功能都表现出了其产品研发团队强大的实力。而在一些其他物联网服务提供的通用能力中，UCloud也是做得有声有色，如设备生命周期管理、物模型、日志监控等，应有尽有。

![](/data/attachment/album/202010/26/110717s76k6kmknn64n7mk.png)

整个 UCloud IoT 产品中，令我最为惊艳的是其 UIoT Edge 产品。UIoT Edge 产品所具备的特性，让 UCloud IoT 产品和使用了这些 IoT 产品的场景更具备想象的空间。

![](/data/attachment/album/202010/26/110737z325l8gi0gi5ilyk.png)

在我看来，UIoT Edge 提供的 Modbus 和开放协议，让存量的物联网设备可以更好的接入到 UCloud IoT 体系中，这让产业互联网能以一个较低的成本接入到其中，而不至于完全重新开发。这让产业互联网的接入成为了一个可能，毕竟产业互联网的各种设备其实造价很高，如果要求产业互联网必须完全开发才能使用的话，成本未免太高。开放协议驱动的支持，则让任何产品都可以根据自己的实际需求来完成接入，如果设备不支持 Modbus，就可以直接通过自行编写驱动来完成接入，成本总体来说低于重新打造一批设备。

![](/data/attachment/album/202010/26/110753js4lhiss3dnanksz.png)

>
> AIoT，这使得 UIoT Edge 拥有了更大的想象力。
>
>
>

而另一个更好的地方是 UIoT Edge 提供的函数计算环境，这个计算环境为 IoT 设备提供了一个新的可能： AIoT。有了函数计算环境，你可以用来做一些小计算量的边缘数据处理，但同样也可以用来执行一些机器学习的模型计算，从而实现在边缘设备上的计算，从而落地 AIoT，这使得 UIoT Edge 拥有了更大的想象力。

而提到 UIoT Edge 的各项能力，钱波也给我分享了几个案例：

UCloud 的合作伙伴一应科技，拥有多年的物业楼宇系统提供商经验，借助于 UCloud 的 UIoT Core 和 UIoT Edge 产品，将过去的数家厂商设备和云服务整合到了一起。将其底层的边缘设备，比如电气火灾、消防水压、电力监控、人脸识别、视频等，都整合到了一套基于 UIoT物联网中台，并借助于 UCloud 强大的公有云能力，与其应用层的楼宇应用、工单调度、通行应用等整合在了一起。大大的提升了管理的效率，减少了整个环节过程中对不同厂商云的依赖，提升稳定性。而这些，都得益于 UCloud 对于 Modbus、DLT645等协议的支持。

UCloud的合作伙伴晟能科技，长期从事电力能量源领域的解决方案研发，借助于UIoT产品，通过装有UIoT Edge的白盒ARM网关，实现了智能电力表计数据的实时采集与数据上云，扩宽了其项目覆盖能力。

而 AIoT 方面，UCloud 也有客户基于 UIoT和UAI实现了在边缘设备运行视觉检测程序，通过神经网络来判断药片是否符合标准，问题药片的识别和准确性都远超传统方案。

这些案例的分享，也证实了我对于 UIoT Edge 这个产品的看法 —— 前途无量。

另外，在采访中钱波也透露，近期UCloud将推出几款装有UIoT Edge软件的EdgeBox，同时加大行业设备协议接入的研发投入，使聚焦应用侧的开发客户真正做到数据采集的开箱即用；UCloud还将基于公有云的UIoT产品，推出可私有部署的IoT产品，覆盖更多领域，让客户有更多选择。

### 总结

UCloud 的 IoT 产品矩阵组合拳打得很好，UIoT Core 解决基础底座能力，让设备可以有一个稳定的基础做更多的事情； UIoT Edge 则为产业互联网提供了更多的可能，让 UCloud 的产品可以服务更多的企业，产生更大的价值。对于苦于产业割裂的企业来说， UCloud 的产品，会是一个不错的选择。


当 DMCA 被滥用，工具又该何去何从？

Summary: 近日，GitHub 收到了来自美国唱片业协会（RIAA）提供的数字千年版权法（DMCA）删除请求，移除知名项目：youtube-dl。

![](/data/attachment/album/202010/26/224628k9uivigzik2t44xf.jpg)

### 事件发展

正如我们[之前报道](/article-12749-1.html)的，近日，GitHub 收到了来自美国唱片业协会（RIAA）提供的[数字千年版权法（DMCA）](https://zh.wikipedia.org/wiki/%E6%95%B8%E5%AD%97%E5%8D%83%E5%B9%B4%E7%89%88%E6%AC%8A%E6%B3%95)[删除请求](https://github.com/github/dmca/blob/master/2020/10/2020-10-23-RIAA.md)，移除知名项目：youtube-dl。

![youtube-dl 项目的主页已经被 DMCA 撤下](/data/attachment/album/202010/26/221921kl3s33l7x33zllid.png)

在删除申请中，RIAA 提到该项目被删除主要有两个原因：

1. 规避 Youtube 提供的数字版权服务：

> this source code is to (i) circumvent the technological protection measures used by authorized streaming services such as YouTube
2. 在显著位置指引用户下载 Youtube 上的授权视频：

> the source code prominently includes as sample uses of the source code the downloading of copies of our members’ copyrighted sound recordings and music videos

youtube-dl 项目创建于 2008 年，早期被用来下载 Youtube 视频，以解决在网络较差的环境下查看较大的 Youtube 视频的用户（在当时，用户使用的主流蜂窝网络还是 3G）。后续，随着 youtube-dl 支持的网站越来越多，以及能够被模块化调用，一些企业也会使用 youtube-dl 来完成 Youtube 视频导入的功能。

比较有趣的是，在 youtube-dl 被删除后，有开发者将 youtube-dl 项目以 PR 的方式，将所有的[提交](https://github.com/github/dmca/pull/8142/commits)推到了 DMCA 库当中。

### 小白评论

在这个事件中，我们要看到的是 youtube-dl 本身作为一个开源项目，在过去的十数年里的蓬勃发展和用户量的不断攀升，以及作为技术人不断的去钻研的决心。这样的精神和努力是值得鼓励的，他们都是一些非常棒的开源人。

但我们也需要注意到的是，youtube-dl 项目本身从设计之处，就是违反了版权的设计理念。在没有授权的情况下， 任何人都不应该去下载和分发相应的版权文件。我们固然可以从技术的角度突破这个限制，但也需要明白，这样的行为和设计想法是不符合版权规则的。

当然，youtube-dl 的存在方便了很多网络条件不佳的人和一些生产用途的企业，但无法否定的是，其本身作为侵犯版权而设计出来的工具这一个基础事实。对于开发者来说，我们也需要在进行研发的时候注意规避相应的问题。不要授人以把柄。

回归到本次事件核心 youtube-dl，对于它来说，版权法的存在使得 youtube-dl 作为工具的存在被抹杀，但我们可以换一种方式让其存在，让 youtube-dl 的核心算法以论文和技术文章的方式进行发表，授人以鱼不如授人以渔，以技术文章的方式让技术本身流传下去， 也是一个不错的选择。

不过，事件开始变得有意思了，因为有一些愤怒的开发者开始基于 GitHub 的漏洞开始对 GitHub 的 DMCA 库进行攻击，比如 [Twitter 用户 lrvick](https://twitter.com/lrvick/status/1319729448166580227) 将 youtube-dl 提交到了 DMCA 库，对 GitHub 以及 RIAA 提出了挑战，让 GitHub 封禁自己的 DMCA库。如果封禁，则 DMCA 库本身也不可用。如果不封禁，则说明 RIAA 和 Github 针对 youtube-dl 的行为就是一场双标的表演。

这个事情很有意思，从法理的角度，我支持 youtube-dl 的封禁，但作为一个 Geek， 我对 Irvick 的行为更感兴趣。


MellowPlayer：一款用于各种流媒体音乐服务的桌面应用

>
> MellowPlayer 是一个自由开源的桌面应用，可以让你在 Linux 和 Windows 上整合基于网络的音乐流媒体服务。
>
>
>

![](/data/attachment/album/202010/26/233644pb0grbknk4kntgfp.png)

毋庸置疑，很多用户都喜欢使用流媒体服务来收听自己喜欢的音乐，而不是从商店购买单首音乐或者下载收藏。

当然，流媒体服务可以让你探索新的音乐，帮助艺术家轻松传播到更多的听众。但是，由于有许多的音乐流媒体服务（[Soundcloud](https://soundcloud.com)、[Spotify](https://www.spotify.com)、[YouTube Music](https://music.youtube.com)、[Amazon Music](https://music.amazon.com/home) 等），因此当在使用电脑时，要有效地使用它们往往会变得很麻烦。

你可以[在 Linux 上安装 Spotify](https://itsfoss.com/install-spotify-ubuntu-linux/)，但没有 Amazon Music 的桌面应用，所以，有可能你无法从单一门户管理流媒体服务。

如果一个桌面应用可以让你同时在 Windows 和 Linux 上免费整合流媒体服务呢？在本文中，我将介绍这样一款应用：[MellowPlayer](https://colinduquesnoy.gitlab.io/MellowPlayer/)。

### MellowPlayer: 集成各种流媒体音乐服务的开源应用

![](/data/attachment/album/202010/26/233715k0xhc0zm3tmcmf2t.jpg)

MellowPlayer 是一款自由开源的跨平台桌面应用，它可以让你整合多个流媒体服务，并在一个界面上管理它们。

你可以整合多个支持的流媒体服务。你还可以从每个服务中获得一定程度的控制权，来调整你的体验。例如，你可以设置在 YouTube 上自动跳过或静音广告。

对 Windows 和 Linux 的跨平台支持绝对是一个加分项。

除了能够管理流媒体服务外，它还将播放器与系统托盘整合在一起来轻松控制音乐。这意味着你可以使用键盘上的媒体键来控制音乐播放器。

另外值得一提的是，你只要在应用内自己创建一个插件，就可以添加一个官方不支持的新服务。为了让你对它有更多的了解，下面就让我重点介绍一下所有的主要特点。

### MellowPlayer 的特点

* 跨平台 （Windows 和 Linux）
* 自由且开源
* 基于插件的应用，让你可以通过创建一个插件来添加新的服务
* 将服务作为本地桌面应用与系统托盘整合
* 支持热键
* 支持通知
* 收听历史

### 在 Linux 上安装 MellowPlayer

![](/data/attachment/album/202010/26/233535y5cvuycz6c922az3.jpg)

MellowPlayer 是以 [Flatpak 包](https://flathub.org/apps/details/com.gitlab.ColinDuquesnoy.MellowPlayer)的形式提供的。我知道这让一些人很失望，但它在 Linux 中只有 Flaptak，Windows 中只有一个可执行文件。如果你不知道，请按照我们的[在 Linux 上使用 Flatpak](https://itsfoss.com/flatpak-guide/) 指南来开始使用。

* [下载 MellowPlayer](https://colinduquesnoy.gitlab.io/MellowPlayer/#features)

### 总结

MellowPlayer 是一款方便的桌面应用，适合经常涉猎多种流媒体音乐服务的用户使用。尽管根据我的测试，它在 SoundCloud、YouTube 和 Spotify 上都能正常工作，但我确实注意到，当我试图重新调整窗口大小时，应用会崩溃，只是在此提醒一下。你可以在它的 [GitLab 页面](https://gitlab.com/ColinDuquesnoy/MellowPlayer)上了解更多关于它的内容。

还有两个类似的应用，可以让你播放多个流媒体音乐服务。[Nuvola](https://itsfoss.com/nuvola-music-player/) 和 [Nuclear Music Player](https://itsfoss.com/nuclear-music-player-linux/)。你可能会想看看它们。

你试过 MellowPlayer 吗？欢迎在下方评论中分享你的想法。

---

via: <https://itsfoss.com/mellow-player/>

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

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


Linux 5.10 终于抛弃了造成了安全隐患的老函数

Summary: 多年来，set_fs() 一直是众所周知的麻烦，现在它终于消失了。

多年来，set_fs() 一直是众所周知的麻烦，现在它终于消失了。

>
> 多年来，set\_fs() 一直是众所周知的麻烦，现在它终于消失了。
>
>
>

![](/data/attachment/album/202010/27/215001zpsdddd3gs72s227.jpg)

Linus Torvalds 开启了 Linux 内核的又一个开发周期，宣布 5.10-rc1 发布了，而且这一次还有一个历史性的转折：新内核标志着一个几十年前的函数的落幕，这个函数在被发现会导致安全漏洞后早已成为多余的函数。

随着长达两周的，每一次新的 Linux 内核迭代发布前的合并窗口的关闭，Torvalds 在 Linux 内核邮件列表上分享了他的感想，他认为“事情似乎相当顺利”。

“合并窗口”是 Linux 新内核发布过程中的一个关键部分，在此窗口期间，每天有多达 1000 个由开发者社区提交的补丁被合并到 Torvalds 管理的主线仓库中。而审查过程会确保每个补丁都能实现理想的变化。

这一次，Torvalds 提请大家注意移除了一个叫 `set_fs()` 的寻址函数，这个函数可以追溯到 Linux 的最初版本。“对我来说，这是最有趣的变化，`set_fs()` 被移除了，”他写道，“这不是一个巨大的变化，但很有趣，因为 `set_fs()` 指定用户空间副本是否真正进入用户空间或内核空间的这个模式，几乎可以追溯到 Linux 的最初版本。”

正如 Torvalds 所解释的那样，`set_fs()` 函数可以通过取消用户空间和内核空间之间的分界来覆盖地址空间。该函数在管理英特尔早期 x86 处理器时被广泛使用，以控制非特权代码可以访问的虚拟地址范围。

然而，2010 年，“[常见漏洞和暴露](Common%20Vulnerabilities and Exposures)”（CVE）详细披露了 `set_fs()` 带来的安全问题。通过绕过某些访问限制，证实该函数能够“覆盖任意内核内存位置”和“获得特权” —— 在某些情况下，这可以让用户空间覆盖内核数据。

鉴于该工具的安全缺陷，包括 x86、PowerPC、s390 和 RISC-V 在内的一些架构已经取消了地址空间覆盖功能。但是，正如 Torvalds 写道：“我们仍然留着 `set_fs()`，因为并不是每个架构都已经转换为新的世界秩序。”

除了这个久违的历史补救之外，5.10-rc1 版本和大多数内核版本一样，还伴随着无数的变化。Torvalds 统计了近 1700 人的近 1.4 万次提交，改动范围从支持自动驾驶汽车和机器人的 Nvidia SOC 芯片到支持任天堂 Switch 控制器。

统计结果表明。大约新增了 70.4 万行代码和删除了 41.9 万行代码，这使得 5.10-rc1 的大小与 Linux 有史以来最大的内核 5.8 相当。“这看起来是一个比我预期的更大的版本，虽然合并窗口比 5.8 的窗口小，但也小不了多少，”Torvalds 说，“而 5.8 是我们有史以来最大的版本。”

按照 Linux 典型的时间表，5.10-rc1 之后将有几个星期时间来提交修复问题的补丁，在预计 12 月发布稳定内核之前，将发布几个候选版本。

---

via: <https://www.zdnet.com/article/linux-5-10-finally-ditches-decades-old-tool-that-caused-security-bugs/>

作者：[Daphne Leprince-ringuet](https://www.zdnet.com/meet-the-team/uk/daphne+leprince-ringuet/) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


Fedora 33 正式发布了

Summary: 本文译自 Fedora 官方发布公告。

Fedora is BEST

本文译自 Fedora 官方发布公告。

![](/data/attachment/album/202010/28/155023rw98712r4b84nn2n.jpg)

>
> 本文译自 Fedora 官方发布公告。
>
>
>

今天，我很兴奋地与大家分享数千名 Fedora 项目贡献者辛勤工作的成果：我们的最新版本 Fedora 33 来了! 这是一个有着很多变化的大版本，但我相信所有这些工作也会让您感到舒适，实现我们的目标：即为您带来最新的稳定、强大、健壮的自由和开源软件，并提供许多易于使用的产品。

如果您不想耽误时间，直接体验，现在就去 <https://getfedora.org/> 下载吧，欲了解详情请继续阅读！

### 找到适合您的 Fedora 风味！

Fedora Edition 是针对桌面、服务器和云环境中各种“体现”特定用途的目标产品，现在它也适用于物联网。

Fedora Workstation 专注于桌面，尤其是面向那些想要“只管去用”的 Linux 操作系统体验的软件开发者。这个版本的特点是 [GNOME 3.38](https://www.gnome.org/news/2020/09/gnome-3-38-released/)，它一如既往地有很多很棒的改进。新增的 Tour 应用可以帮助新用户学习它们的操作方式。和我们所有其他面向桌面的变体一样，Fedora Workstation 现在使用 [BTRFS 作为默认文件系统](https://fedoramagazine.org/btrfs-coming-to-fedora-33/)。这些发布的版本中带来了很多伟大的增强功能，这个先进的文件系统为之奠定了基础。为了您的视觉享受，Fedora 33 Workstation 现在默认提供了一个动画背景（它会基于一天中的时间变化）。

Fedora CoreOS 是一个新兴的 Fedora 版本。它是一个自动更新的、最小化的操作系统，用于安全地、大规模地运行容器化工作负载。它提供了几个[更新流](https://docs.fedoraproject.org/en-US/fedora-coreos/update-streams/)，可以遵循大致每两周一次的自动更新。目前 **next** 流是基于 Fedora 33 的，**testing** 和 **stable** 流后继也会跟进。您可以从[下载页面](https://getfedora.org/en/coreos/download?stream=next)中找到关于跟随 **next** 流发布的工件的信息，并在 [Fedora CoreOS 文档](https://docs.fedoraproject.org/en-US/fedora-coreos/getting-started/)中找到关于如何使用这些工件的信息。

新晋升为 Edition 状态的 [Fedora IoT](https://getfedora.org/iot)，为物联网生态系统和边缘计算用例提供了坚实的基础。在许多功能之外，Fedora 33 IoT 还引入了<ruby> 平台抽象安全 <rt>  Platform AbstRaction for SECurity </rt></ruby>（PARSEC），这是一个开源倡议，以平台无关的方式为硬件安全和加密服务提供了通用 API。

当然，我们制作的不仅仅是“官方版本”，还有 [Fedora Spin](https://spins.fedoraproject.org/)和 [Lab](https://labs.fedoraproject.org/)。[Fedora Spin](https://spins.fedoraproject.org/) 和 [Lab](https://labs.fedoraproject.org/) 针对不同的受众和用例，包括 [Fedora CompNeuro](https://labs.fedoraproject.org/en/comp-neuro/)，它为神经科学带来了大量的开源计算建模工具，以及 [KDE Plasma](https://spins.fedoraproject.org/en/kde/) 和 [Xfce](https://spins.fedoraproject.org/en/xfce/)等桌面环境。

此外，别忘了我们还有备用架构：[ARM AArch64、Power 和 S390x](https://alt.fedoraproject.org/alt/)。在 Fedora 33 中提供的新功能，AArch64 用户可以使用 .NET Core 语言进行跨平台开发。我们改进了对 Pine64 设备、NVidia Jetson 64 位平台以及 Rockchip 片上系统（SoC）设备的支持，包括 Rock960、RockPro64 和 Rock64。（不过，有个最新的说明：在其中一些设备上可能会出现启动问题。从现有的 Fedora 32 升级是没问题的。更多信息将在[常见错误](https://fedoraproject.org/wiki/Common_F33_bugs)页面上公布。）

我们也很高兴地宣布，Fedora 云镜像和 Fedora CoreOS 将首次与 Fedora 33 一起在亚马逊的 [AWS 市场](https://aws.amazon.com/marketplace) 中提供。Fedora 云镜像在亚马逊云中已经存在了十多年，您可以通过 AMI ID 或[点击一下](https://getfedora.org/en/coreos/download?tab=cloud_launchable&stream=stable)来启动我们的官方镜像。该市场提供了获得同样东西的另一种方式，显著扩大了 Fedora 的知名度。这也将使我们的云镜像可以更快地在新的 AWS 区域中可用。特别感谢 David Duncan 让这一切成为现实！

### 常规改进

无论您使用的是哪种版本的 Fedora，您都会得到开源世界提供的最新版本。遵循我们的 [First](https://docs.fedoraproject.org/en-US/project/#_first) 原则，我们更新了关键的编程语言和系统库包，包括 Python 3.9、Ruby on Rails 6.0 和 Perl 5.32。在 Fedora KDE 中，我们沿用了 Fedora 32 Workstation 中的工作，默认启用了 EarlyOOM 服务，以改善低内存情况下的用户体验。

为了让 Fedora 的默认体验更好，我们将 nano 设置为默认编辑器。nano 是一个对新用户友好的编辑器。当然，那些想要像 vi 这样强大的编辑器的用户可以自己设置默认编辑器。

我们很高兴您能试用新版本! 前往 <https://getfedora.org/> 并立即下载它。或者如果您已经在运行 Fedora 操作系统，请按照这个简单的[升级说明](https://docs.fedoraproject.org/en-US/quick-docs/upgrading/)进行升级。关于 Fedora 33 新特性的更多信息，请参见[发布说明](https://docs.fedoraproject.org/en-US/fedora/f33/release-notes/)。

### 关于安全启动的说明

<ruby> 安全启动 <rt>  Secure Boot </rt></ruby>是一种安全标准，它确保只有官方签署的操作系统软件才能加载到您的计算机上。这对于防止持久恶意软件非常重要，因为这些恶意软件可能会隐藏在您的计算机固件中，甚至在重新安装操作系统时也能存活。然而，在 [Boot Hole](https://access.redhat.com/security/vulnerabilities/grub2bootloader) 漏洞发生后，用于签署 Fedora <ruby> 引导加载器 <rt>  Bootloader </rt></ruby>软件的加密证书将被撤销，并被新的证书取代。由于这将产生大范围的影响，撤销应该要到 2021 年第二季度或更晚才会广泛推行。

然而，一些用户可能已经从其他操作系统或固件更新中收到了这种撤销。在这种情况下，Fedora 将不能在启用了安全启动时进行安装。要说明的是，这不会影响大多数用户。如果它确实影响到了您，您可以暂时禁用安全启动。我们会在大范围的证书撤销之前发布一个用新证书签署的更新，在所有支持的版本上都可以使用，到那时，安全启动应该可以重新启用。

### 万一出现问题时……

如果您遇到问题，请查看 [Fedora 33 常见错误](https://fedoraproject.org/wiki/Common_F33_bugs)页面；如果您有疑问，请访问我们的 [Ask Fedora](http://ask.fedoraproject.org) 用户支持平台。

### 谢谢大家

感谢在这个发布周期中为 Fedora 项目做出贡献的成千上万的人，尤其是那些在疫情大流行期间为使这个版本准时发布而付出额外努力的人。Fedora 是一个社区，很高兴看到我们如此互相支持。

---

via: <https://fedoramagazine.org/announcing-fedora-33/>

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


Kubernetes 复杂吗？可以不复杂

![](/data/attachment/album/202010/28/200700hbfeyxzil4en8n43.jpg)

>
> 作为云原生的核心产品，Kubernetes 提供的编排和管理功能，能轻松完成大规模容器部署，但 Kubernetes 自身的复杂性也导致众多企业一直徘徊于容器服务之外。据调查，只有不足 10% 的用户表示使用 Kubernetes 时没有遇到阻碍。
>
>
>

在 2019 年的一个关于容器技术的一个调查数据中，有 40% 的受访者表示计划采用容器。然而，据 UCloud 产品经理张鹏波透露说，在他们接触的用户当中，曾经在两年前表示有兴趣迁移到容器的用户，两年过去了，这些用户很多依旧“计划采用”容器服务。

![](/data/attachment/album/202010/28/200936vksziwc3rc424rp5.png)

这让我好奇其中发生了什么。让我们一起来探寻一下其中的症结，以及是否可以将复杂的 Kubernetes变得不复杂。

### Kubernetes 复杂吗？

自从 2017 年，Kubernetes 在容器编排三强争霸赛中胜出后，就成为了容器编排领域的事实标准，极大的助推了容器技术在业界的发展。作为企业级的容器服务编排系统，Kubernetes 从技术上是现在、乃至未来的主流。

容器拥有很多优点，包括不可变环境、轻量级、快速启动等优势；而容器编排系统又在此基础上更进一步，提供了更加强大的功能，包括自动部署、快速扩容、故障自愈等等。

![](/data/attachment/album/202010/28/201037chni9mlhqs9frqt9.jpg)

然而，在这一系列令人神往的优势和功能背后，Kubernetes 也相应的引入了更多的复杂性。这些复杂性体现在多个方面，比如说：

* 首先是 Kubernetes 架构，本质上，Kubernetes 是用来管理分布式系统的平台。而一说到分布式，复杂性是不可避免的，管理分布式系统的平台自然也不例外。
* 另外是容器支持的网络，Kubernetes 支持的网络类型很多。很多用户对于 Kubernetes 的网络非常疑惑或者说很纠结，当业务要迁移到 Kubernetes 时，选择什么类型的网络是一个难以抉择的问题。特别是在业务迁移上去之后又遇到一些问题的话，排查起来会非常麻烦，这给用户带来很大负担。
* 最后还有就是各种各样 Kubernetes 组件的配置，这些都是用户将业务迁移到 Kubernetes 的障碍。

### 有什么解决方案吗？

如上所述，并不是每个用户都能尝到桃子的美味的，还有很多渴望迁移到容器环境的用户被拒之门外。

在这种情况下，全球各家云服务商，都纷纷推出自己的容器编排服务，在 Kubernetes上构建了自己的 Kubernetes发行版或定制产品。

而作为中国第一家公有云科创板上市公司，UCloud 利用在 UK8S 产品的技术沉淀，推出新的容器管理服务产品 Cube：两步即可部署容器化应用；采用无服务器形态，不需要再维护底层基础设施；按秒后付费，无需预留资源；降低企业部署云原生产品的学习和技术门槛。

#### 为什么要推出 Cube？

在容器和 Kubernetes 如火如荼发展的同时，UCloud 发现，在他们的 UK8S 上线大概两年多的时间里，之前曾经想把业务迁到 Kubernetes 的用户。在两年后的今天，他们还是说这个话，说我很想上 Kubernetes，还需要你们来做技术交流。

UCloud 产品经理张鹏波说：

>
> 这让我们一度很困惑，我们一开始也做了一些尝试，比如说做了很多线下的培训、线上的交流，尝试去推动用户把他们的业务迁移到 UK8S 里面来。但是发现这个过程非常缓慢，我们没有办法控制和推动用户的迁移流程。很多公司的业务在快速发展，运维和研发是没有办法做架构上的调整的。
>
>
> 发现沟通、培训这条路实际上是走不通之后的。后来我们想，能不能换一条路，能不能基于 Kubernetes提供这样一个产品：这个产品只具有 Kubernetes 好处，掩盖了 Kubernetes 的复杂性，只提供类似于自动部署、快速扩容这样的特性，而用户不需要关心底层的 Kubernetes 架构，不需要再操心 Kubernetes 的网络了，也不需要去学习 Kubernetes 的各种 API 了。
>
>
>

#### Cube 的设计思路

沿着这个思路，UCloud 形成了自己的 Cube 产品。它的设计是从以下几个方面考虑的：

首先，Kubernetes 里面哪些功能是会经常会用到的，比如 Deployment 控制多副本容器组的管理， Job 控制任务型容器组，Service 做服务发现，PVC可以用来抽象使用块存储以及文件存储。这是 Kubernetes 最核心的一些功能。UCloud 在设计 Cube 的时候，希望将这些功能全部保留。这意味着整个 Cube 是基于 Kubernetes 来实现的。把这些复杂性全部屏蔽掉，用户不需要维护 Kubernetes 集群，不需要操心 Kubernetes 网络方案，而是由 Cube 提供一个最优网络方案，把容器的网络和虚拟机的网络扁平化。

![](/data/attachment/album/202010/28/201227q0jiqn60jt60ld5q.jpg)

其次，把业务迁移到 Kubernetes 的时候，把单体业务变成分布式业务、微服务的时候，用户一定需要考虑容器日志的统一收集、统一管理的问题。在 Cube 里面自动完成了日志的采集工作，集成了日志管理工作。另外，对容器环境的监控也是同样的道理，统一在 Cube 中完成。

![](/data/attachment/album/202010/28/201251m3n10nauu16z0nq6.jpg)

当把这些产品全部集成到 Cube 里以后，Cube 是一个什么样的产品形态呢？

首先保证 Kubernetes 最核心的功能，用户能够在 Cube 里面创建 Kubernetes 常用的对象；另外 Cube 的产品形态应该是无服务器的形式；最后，Cube 引入了轻量级的虚拟化技术，实现了容器组与容器组之间虚拟机级别的隔离，这样好处是什么呢？

我们都知道容器的运行是共享使用宿主机的内核的，存在一定的安全风险，Cube 为每一个容器组实现了一层虚拟机的封装，可以使用户安全的运行容器；同时 UCloud 容器团队针对虚拟机的启动进行了深入优化，虚拟机启动速度最快只需要 125 毫秒。

### Cube 的功能亮点

#### 快速迁移

让我们横向对比一下，用户使用 Kubernetes 和 Cube 的流程上会有哪些区别。

![](/data/attachment/album/202010/28/201356n6vo6ry6g2a5fvao.jpg)

左边是 Kubernetes，用户要把业务迁移到 Kubernetes，大概要经过这几个步骤：

* 第一个步骤学习 Kubernetes，不仅仅是一个人，也可能不仅仅是一个团队，这个过程可能需要三个月到一年。
* 搭建集群，考虑集群的参数配置、集群的维护工作。
* 然后是做业务镜像。
* 之后还要考虑了解 Kubernetes的 API 以及 Kubernetes 应用。
* 最后才部署应用。

从 UCloud 观察到的情况来看，如果是对 Kubernetes非常熟悉的用户，这个过程可能要一两个月；但是如果对 Kubernetes 不熟悉，需要半年乃至一年。

使用 Cube 的话，就不再需要学习复杂的 Kubernetes知识了，和创建虚拟机一样在 Cube 里面创建一个应用，全部都是图形化的方式。所以，Cube 整个流程只有两步：

* 制作镜像。
* 在 Cube 的界面上直接部署应用就好了。

UCloud 产品经理张鹏波说，“我们有几个用户知道了 Cube 公测，公测以后第一天进行了解，第二天就把自己部署的业务迁移到 Cube 上来了，一天的时间就可以完成业务迁移的工作。”

#### 成本降低

另外在成本方面，我们知道 Kubernetes 是一个大型的分布式集群，除了工作节点以外，还有管理节点。管理节点只是用来管理 Kubernetes 支持的应用，这部分开销实际上从企业角度来看是浪费的。对业务没有起到正向的作用，所以 Kubernetes 成本会比较高。

而对于 Cube，因为只需要为容器实例来付费，容器用了多少资源就付多少钱，不再考虑管理节点的开销、资源预留的问题。

#### 更多的便利性

此外，还有一些其他针对 Kubernetes 自身的一些的改进。在 Cube 整个研发过程中，引入了一些亮点。

第一个是镜像预热，我们知道容器的启动速度其实很快的，基本一秒钟就能拉起来容器实例。但是这是热启动的情况，就是说工作节点上有这个镜像时，拉起来速度是很快的。而在冷启动的情况下，如果虚拟机上没有对应的镜像时，并且镜像非常大时，这个过程就非常缓慢了。我们遇到过最大的镜像有 20G 以上，容器的启动的时间就要花费几分钟。这样，容器本来说快速启动的优势就没有了，比虚拟机还慢了。所以，UCloud 在研发 Cube 的时候，使用了镜像预热的技术，把容器镜像变成 MBD 设备，在容器启动的时候，把它纳入到启动容器的节点上去，省去了镜像拉起的时间，让容器冷启动的时间从以前需要十几分钟变成现在只要几秒钟就拉起来了。

另外，因为 Kubernetes 是由谷歌开源的一项方案，很多理念和大部分企业更加超前一点。所以，在这种设计理念下，Kubernetes 每一个容器在重启以后，容器的 IP 就会变化。而我们知道很多传统的应用设计上是依赖于固定 IP 的，IP 一旦变化整个应用就会出现一些问题。很多用户都希望让容器重启后 IP 保持不变。这对于特别是有状态的服务尤为重要。所以，在 Cube 里面使用了 UCloud 的 EIP 功能，能够让用户容器重启时其 IP 保持不变。

最后，Cube 要兼容原有的运维习惯。传统上，虚拟机和 IDC 里面的物理机在使用体验上是没有什么差别的。有些用户之前业务部署在虚拟机，经常需要在出现问题的时候，直接登录到虚拟机里面去排错，查看一些日志。所以，为了兼容用户以前使用虚拟机的习惯，在 Cube 里面的容器也提供了登录功能，让用户在业务出现问题的时候，能够登录到容器里去快速排查问题。

### 结语

说实话，关于 Kubernetes 和各种发行版，我们也看过和研究过不少产品了，但是更多的是叶公好龙，没有将自己的应用迁移到 Kubernetes上的想法。一方面对着 Kubernetes 的各种先进特性馋涎欲滴，另外一方面却担心没有足够的精力面对全新的技术变化带来的挑战。

不过，今天看到 UCloud 的 Cube 产品，我决定要去亲自试试这个桃子的味道了。


使用 Ansible 的 Kubernetes 模块实现容器编排自动化

>
> 将 Kubernetes 与 Ansible 结合实现云端自动化。此外，还可以参照我们的 Ansible 的 k8s 模块速查表。
>
>
>

![](/data/attachment/album/202010/28/211747jhlttlf3f81xrffi.jpg)

[Ansible](https://opensource.com/resources/what-ansible) 是实现自动化工作的优秀工具，而 [Kubernetes](https://opensource.com/resources/what-is-kubernetes) 则是容器编排方面的利器，要是把两者结合起来，会有怎样的效果呢？正如你所猜测的，Ansible + Kubernetes 的确可以实现容器编排自动化。

### Ansible 模块

实际上，Ansible 本身只是一个用于解释 YAML 文件的框架。它真正强大之处在于它[丰富的模块](https://docs.ansible.com/ansible/latest/modules/modules_by_category.html)，所谓<ruby> 模块 <rt>  module </rt></ruby>，就是在 Ansible <ruby> 剧本 <rt>  playbook </rt></ruby> 中让你得以通过简单配置就能调用外部应用程序的一些工具。

Ansible 中有模块可以直接操作 Kubernetes，也有对一些相关组件（例如 [Docker](https://opensource.com/resources/what-docker) 和 [Podman](http://podman.io)）实现操作的模块。学习使用一个新模块的过程和学习新的终端命令、API 一样，可以先从文档中了解这个模块在调用的时候需要接受哪些参数，以及这些参数在外部应用程序中产生的具体作用。

### 访问 Kubernetes 集群

在使用 Ansible Kubernetes 模块之前，先要有能够访问 Kubernetes 集群的权限。在没有权限的情况下，可以尝试使用一个短期在线试用账号，但我们更推荐的是按照 Kubernetes 官网上的指引，或是参考 Braynt Son 《[入门 Kubernetes](https://opensource.com/article/18/10/getting-started-minikube)》的教程安装 [Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube)。Minikube 提供了一个单节点 Kubernetes 实例的安装过程，你可以像使用一个完整集群一样对其进行配置和交互。

* 下载 [Ansible k8s 速记表](https://opensource.com/downloads/ansible-k8s-cheat-sheet)（需注册）

在安装 Minikube 之前，你需要确保你的环境支持虚拟化并安装 `libvirt`，然后对 `libvirt` 用户组授权：

```
$ sudo dnf install libvirt
$ sudo systemctl start libvirtd
$ sudo usermod --append --groups libvirt `whoami`
$ newgrp libvirt

```

#### 安装 Python 模块

为了能够在 Ansible 中使用 Kubernetes 相关的模块，你需要安装以下这些 Python 模块：

```
$ pip3.6 install kubernetes --user
$ pip3.6 install openshift --user

```

#### 启动 Kubernetes

如果你使用的是 Minikube 而不是完整的 Kubernetes 集群，请使用 `minikube` 命令在本地创建一个最精简化的 Kubernetes 实例：

```
$ minikube start --driver=kvm2 --kvm-network default

```

然后等待 Minikube 完成初始化，这个过程所需的时间会因实际情况而异。

### 获取集群信息

集群启动以后，通过 `cluster-info` 选项就可以获取到集群相关信息了：

```
$ kubectl cluster-info
Kubernetes master is running at https://192.168.39.190:8443
KubeDNS is running at https://192.168.39.190:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

```

### 使用 k8s 模块

Ansible 使用 `k8s` 这个模块来实现对 Kubernetes 的操作，在剧本中使用 `k8s` 模块就可以对 Kuvernetes 对象进行管理。这个模块描述了 `kubectl` 命令的最终状态，例如对于以下这个使用 `kubectl` 创建新的[命名空间](https://opensource.com/article/19/10/namespaces-and-containers-linux)的操作：

```
$ kubectl create namespace my-namespace

```

这是一个很简单的操作，而对这个操作的最终状态用 YAML 文件来描述是这样的：

```
- hosts: localhost
  tasks:
    - name: create namespace
      k8s:
        name: my-namespace
        api_version: v1
        kind: Namespace
        state: present

```

如果你使用的是 Minikube，那么主机名（`hosts`）应该定义为 `localhost`。需要注意的是，所使用的模块也定义了可用参数的语法（例如 `api_version` 和 `kind` 参数）。

在运行这个剧本之前，先通过 `yamllint` 命令验证是否有错误：

```
$ yamllint example.yaml

```

确保没有错误之后，运行剧本：

```
$ ansible-playbook ./example.yaml

```

可以验证新的命名空间是否已经被创建出来：

```
$ kubectl get namespaces
NAME              STATUS   AGE
default           Active   37h
kube-node-lease   Active   37h
kube-public       Active   37h
kube-system       Active   37h
demo              Active   11h
my-namespace      Active   3s

```

### 使用 Podman 拉取容器镜像

容器是个 Linux 系统，几乎是最小化的，可以由 Kubernetes 管理。[LXC 项目](https://www.redhat.com/sysadmin/exploring-containers-lxc)和 Docker 定义了大部分的容器规范。最近加入容器工具集的是 Podman，它不需要守护进程就可以运行，为此受到了很多用户的欢迎。

通过 Podman 可以从 Docker Hub 或者 [Quay.io](http://Quay.io) 等存储库拉取容器镜像。这一操作对应的 Ansible 语法也很简单，只需要将存储库网站提供的镜像路径写在剧本中的相应位置就可以了：

```
   - name: pull an image
      podman_image:
        name: quay.io/jitesoft/nginx

```

使用 `yamllint` 验证：

```
$ yamllint example.yaml

```

运行剧本：

```
$ ansible-playbook ./example.yaml
[WARNING]: provided hosts list is empty, only localhost is available.
Note that the implicit localhost does not match 'all'

PLAY [localhost] ************************

TASK [Gathering Facts] ************************
ok: [localhost]

TASK [create k8s namespace] ************************
ok: [localhost]

TASK [pull an image] ************************
changed: [localhost]

PLAY RECAP ************************
localhost: ok=3 changed=1 unreachable=0 failed=0
           skipped=0 rescued=0 ignored=0

```

### 使用 Ansible 实现部署

Ansible 除了可以执行小型维护任务以外，还可以通过剧本实现其它由 `kubectl` 实现的功能，因为两者的 YAML 文件之间只有少量的差异。在 Kubernetes 中使用的 YAML 文件只需要稍加改动，就可以在 Ansible 剧本中使用。例如下面这个用于使用 `kubectl` 命令部署 Web 服务器的 YAML 文件：

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-webserver
spec:
  selector:
    matchLabels:
      run: my-webserver
  replicas: 1
  template:
    metadata:
      labels:
        run: my-webserver
    spec:
      containers:
      - name: my-webserver
        image: nginx
        ports:
        - containerPort: 80

```

如果你对其中的参数比较熟悉，你只要把 YAML 文件中的大部分内容放到剧本中的 `definition` 部分，就可以在 Ansible 中使用了：

```
   - name: deploy a web server
      k8s:
        api_version: v1
        namespace: my-namespace
        definition:
          kind: Deployment
          metadata:
            labels:
              app: nginx
            name: nginx-deploy
          spec:
            replicas: 1
            selector:
              matchLabels:
                app: nginx
            template:
              metadata:
                labels:
                  app: nginx
              spec:
                containers:
                  - name: my-webserver
                    image: quay.io/jitesoft/nginx
                    ports:
                      - containerPort: 80
                        protocol: TCP

```

执行完成后，使用 `kubectl` 命令可以看到预期中的的<ruby> 部署 <rt>  deployment </rt></ruby>：

```
$ kubectl -n my-namespace get pods
NAME                      READY  STATUS
nginx-deploy-7fdc9-t9wc2  1/1    Running

```

### 在云上使用模块

随着现在越来越多的开发和部署工作往云上转移的趋势，我们必须了解如何在云上实现自动化。其中 `k8s` 和 `podman_image` 这两个模块只是云开发中的其中一小部分。你可以在你的工作流程中寻找一些需要自动化的任务，并学习如何使用 Ansible 让你在这些任务上事半功倍。

---

via: <https://opensource.com/article/20/9/ansible-modules-kubernetes>

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

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


我的第一次开源贡献：做出决定

Summary: 一位新的开源贡献者告诉你如何加入到开源项目中。

一位新的开源贡献者告诉你如何加入到开源项目中。

>
> 一位新的开源贡献者告诉你如何加入到开源项目中。
>
>
>

![](/data/attachment/album/202010/30/112350rh0xwp1x1y6awehn.jpg)

先前，我把我的第一次开源贡献的拖延归咎于[冒牌综合症](https://opensource.com/article/19/10/my-first-open-source-contribution-mistakes)。但还有一个我无法忽视的因素：我做出决定太艰难了。在[成千上百万](https://github.blog/2018-02-08-open-source-project-trends-for-2018/)的开源项目中选择时，选择一个要做贡献的项目是难以抉择的。如此重负，以至于我常常不得不关掉我的笔记本去思考：“或许我改天再做吧”。

错误之二是让我对做出决定的恐惧妨碍了我做出第一次贡献。在理想世界里，也许开始我的开源之旅时，心中就已经有了一个真正关心和想去做的具体项目，但我有的只是总得为开源项目做出贡献的模糊目标。对于那些处于同一处境的人来说，这儿有一些帮助我挑选出合适的项目（或者至少是一个好的项目）来做贡献的策略。

### 经常使用的工具

一开始，我不认为有必要将自己局限于已经熟悉的工具或项目。有一些项目我之前从未使用过，但由于它们的社区很活跃，或者它们解决的问题很有趣，因此看起来很有吸引力。

但是，考虑我投入到这个项目中的时间有限，我决定继续投入到我了解的工具上去。要了解工具需求，你需要熟悉它的工作方式。如果你想为自己不熟悉的项目做贡献，则需要完成一个额外的步骤来了解代码的功能和目标。这个额外的工作量可能是有趣且值得的，但也会使你的工作时间加倍。因为我的目标主要是贡献，投入到我了解的工具上是缩小范围的很好方式。回馈一个你认为有用的项目也是有意义的。

### 活跃而友好的社区

在选择项目的时候，我希望在那里有人会审查我写的代码才会觉得有信心。当然，我也希望审核我代码的人是个和善的人。毕竟，把你的作品放在那里接受公众监督是很可怕的。虽然我对建设性的反馈持开放态度，但开发者社区中的一些有毒角落是我希望避免的。

为了评估我将要加入的社区，我查看了我正在考虑加入的仓库的<ruby> 议题 <rt>  issue </rt></ruby>部分。我要查看核心团队中是否有人定期回复。更重要的是，我试着确保没有人在评论中互相诋毁（这在议题讨论中是很常见的）。我还留意了那些有行为准则的项目，概述了什么是适当的和不适当的在线互动行为。

### 明确的贡献准则

因为这是我第一次为开源项目做出贡献，在此过程中我有很多问题。一些项目社区在流程的文档记录方面做的很好，可以用来指导挑选其中的议题并发起拉取请求。 [Gatsby](https://www.gatsbyjs.org/contributing/) 是这种做法的典范，尽管那时我没有选择它们，因为在此之前我从未使用过该产品。

这种清晰的文档帮助我们缓解了一些不知如何去做的不安全感。它也给了我希望：项目对新的贡献者是开放的，并且会花时间来查看我的工作。除了贡献准则外，我还查看了议题部分，看看这个项目是否使用了“<ruby> 第一个好议题 <rt>  good first issue </rt></ruby>”标志。这是该项目对初学者开放的另一个迹象（并可以帮助你学会要做什么）。

### 总结

如果你还没有计划好选择一个项目，那么选择合适的领域进行你的第一个开源贡献更加可行。列出一系列标准可以帮助自己缩减选择范围，并为自己的第一个拉取请求找到一个好的项目。

---

via: <https://opensource.com/article/19/11/my-first-open-source-contribution-mistake-decisions>

作者：[Galen Corey](https://opensource.com/users/galenemco) 选题：[lujun9972](https://github.com/lujun9972) 译者：[chenmu-kk](https://github.com/chenmu-kk) 校对：[wxy](https://github.com/wxy)

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


用这个创建可引导 USB 的工具在电脑上尝试 Linux

>
> Fedora Media Writer 是创建临场版 USB 以尝试 Linux 的方便方法。
>
>
>

![](/data/attachment/album/202010/30/122142c1cmcv7cxgjmw5vw.jpg)

[Fedora Media Writer](https://github.com/FedoraQt/MediaWriter) 是一个小巧、轻量、全面的工具，它简化了 Linux 的入门体验。它可以下载 Fedora 的 Workstation 或 Server 版本并写入到一个可以在任何系统上引导的 USB 驱动器上，使你无需将其安装到硬盘上就可以试用 Fedora Linux。

Media Writer 工具可以用来创建一个<ruby> 临场体验的 <rt>  Live </rt></ruby>、可引导的 USB 驱动器。在你的平台上安装 Fedora Media Writer 应用后，你可以下载并烧录最新的 Fedora Workstation 或 Server 稳定版，也可以选择烧录你下载的任何其他镜像。而且它并不局限于英特尔 64 位设备。它还可以用于 ARM 设备，如[树莓派](https://fedoraproject.org/wiki/Architectures/ARM/Raspberry_Pi)，这些设备日益变得更加强大和有用。

![Fedora Media Writer main screen](/data/attachment/album/202010/30/122203l571227urtagh9e4.png "Fedora Media Writer main screen")

### 安装 Fedora Media Writer

[安装 Fedora Media Writer](https://docs.fedoraproject.org/en-US/fedora/f32/install-guide/install/Preparing_for_Installation/#_fedora_media_writer) 有几种方式。你可以通过 GitHub [从源码编译](https://github.com/FedoraQt/MediaWriter#building)、下载 MacOS 或 Windows 版本、使用 `dnf` 或 `yum` 安装 RPM 包，或者以 Flatpak 的形式获得。

在 Fedora 上：

```
$ sudo dnf install mediawriter

```

最新版本请参见 GitHub 仓库的[发布](https://github.com/FedoraQt/MediaWriter/releases)部分。

### 准备好你的媒体介质

首先，你需要一个 USB 驱动器来安装你的 Linux 操作系统。这就是 Fedora Media Writer 要烧录的设备。这个驱动器必须是空白或可擦除的，因为 **该 USB 驱动器上的所有数据都会被删除**。如果有任何数据，哪怕只是一个文件，如果你不想丢失，那么你必须在继续之前备份它！

确认你的 USB 驱动器是可擦除的之后，将它插入你的电脑并启动 Fedora Media Writer。

### 使用 Fedora Media Writer

当你启动 Fedora Media Writer 时，你会看到一个页面，提示你从互联网上获取一个可引导的镜像，或者从你的硬盘上加载一个自定义镜像。第一个选择是 Fedora 的最新版本。Workstation 版本适用于台式机和笔记本电脑，Server 版本适用于虚拟化、机架服务器或任何你想作为服务器运行的情况。

如果你选择了 Fedora 镜像，Media Writer 会下载一个光盘镜像（通常称为 “iso”，文件扩展名为 .iso），并将其保存到你的下载文件夹中，这样你就可以重复使用它来烧录另一个驱动器。

![Select your image](/data/attachment/album/202010/30/122205stzrs5lrroe3u3yx.png)

另外还有 Fedora Spins，这是来自 Fedora 社区的镜像，它旨在满足小众的兴趣。例如，如果你是 [MATE 桌面](https://opensource.com/article/19/12/mate-linux-desktop)的粉丝，那么你会很高兴地发现 Media Writer 提供了 MATE spin。它有很多，所以请滚动查看所有的选择。如果你是 Linux 的新手，不要被吓到或感到困惑：这些额外的选项是为老用户准备的，这些用户除了默认的选项外，还发展出了自己的偏好，所以对你来说，只使用 Workstation 或 Server 选项就行，这取决于你是想把 Fedora 作为一个桌面还是作为一个服务器操作系统来运行。

如果你需要一个与你当前使用的 CPU 不同架构的镜像，从窗口右上角的下拉菜单中选择 CPU 架构。

如果你已经将镜像保存在硬盘上，请选择“Custom Image”选项，并选择你要烧录到 USB 的发行版的 .iso 文件。

### 写入 USB 驱动器

当你下载或选择了一个镜像，你必须确认你要将该镜像写入驱动器。

驱动器选择下拉菜单只显示了外部驱动器，所以你不会意外地覆盖自己的硬盘驱动器。这是 Fedora Media Writer 的一个重要功能，它比你在网上看到的许多手动说明要安全得多。然而，如果你的计算机上连接了多个外部驱动器，除了你想覆盖的那个，你应该将它们全部移除，以增加安全性。

选择你要安装镜像的驱动器，然后单击“Write to Disk”按钮。

![Media write](/data/attachment/album/202010/30/122205j65b671p61rtde4a.png "Media write")

稍等一会儿，镜像就会被写入到你的驱动器，然后可以看看 Don Watkins 对[如何从 USB 驱动器启动到 Linux](https://opensource.com/article/20/4/first-linux-computer)的出色介绍。

### 开始使用 Linux

Fedora Media Writer 是一种将 Fedora Workstation 或 Server，或任何 Linux 发行版烧录到 USB 驱动器的方法，因此你可以在安装它到设备上之前试用它。试试吧，并在评论中分享你的经验和问题。

---

via: <https://opensource.com/article/20/10/fedora-media-writer>

作者：[Sumantro Mukherjee](https://opensource.com/users/sumantro) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


《代码英雄》第二季（9）：特别篇 - 开发者推广大使圆桌会议

Summary: 开发者推广大使在开源社区中起到的作用是举足轻重的。我们邀请了几位这样的推广大使来到本期节目中，以向我们揭示他们的工作方式，并阐述这份工作背后的意义。

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

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

![](/data/attachment/album/202010/30/143829f4q93sjesd4quvis.jpg)

本文是《[代码英雄](https://www.redhat.com/en/command-line-heroes)》系列播客[第二季（9）：特别篇 - 开发人员拥护者圆桌会议](https://www.redhat.com/en/command-line-heroes/season-2/developer-advocacy-roundtable)的[音频](https://dts.podtrac.com/redirect.mp3/audio.simplecast.com/7a9b329b.mp3)脚本。

>
> 导语：<ruby> 开发者推广大使 <rt>  developer advocate </rt></ruby>在开源社区中起到的作用是举足轻重的。我们邀请了几位这样的推广大使来到本期节目中，以向我们揭示他们的工作方式，并阐述这份工作背后的意义。
>
>
> 来自 Mozilla 的 Sandra Persing、Twilio 的 Ricky Robinett 与 红帽的 Robyn Bergerron 将在此接受 Saron 的采访，分享他们的工作内容、他们支持社区的方式，以及他们对 2019 年的展望。
>
>
>

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

大家好，我是 Saron Yitbarek，这里是红帽的原创播客节目《<ruby> 代码英雄 <rt>  Command Line Heroes </rt></ruby>》的特别篇。我们的节目在第 2 季去了很多地方。我们探索了[编程语言](/article-12595-1.html)、[数据大爆炸](/article-12649-1.html)、[安全危机](/article-12641-1.html)以及[无服务器](/article-12717-1.html)时代的到来，我们甚至去了[火星](/article-12744-1.html)。

**00:00:28**：

但是在结束了第 2 季之后，我们还有一个地方要去。我们要走进推广大使和思想领袖们的认识当中，他们帮着塑造了开发人员所做的全部工作。有时，他们被称为<ruby> 开发者推广大使 <rt>  developer advocate </rt></ruby>，或者说他们担任开发者关系的职位，或开发者布道师。

**00:00:50**：

从开发人员的角度来看，无论他们的具体头衔是什么，他们似乎都做着许多相同的事情。你在会议上见过他们发表主题演讲。你在播客上听过他们接受采访，就像在本期节目中一样。你可能还阅读过他们的博文。但是他们是谁呢？他们到底在用自己的声音做了些什么？

**00:01:10**：

为了迎接 2019 年的到来，我们为大家召集了一个优秀人物的圆桌会议。尽管他们的头衔各不相同，但他们的目的是一致的。他们来到这里，是为了帮助开发人员，并确保其声音和需求能被大众听到。这些人都是典型的代码英雄。

**00:01:29**：

来自<ruby> 湾区 <rt>  Bay Area </rt></ruby>的 Sandra Persing 是 Mozilla 的<ruby> 全球策略师 <rt>  Global Strategist </rt></ruby>，也是 DevRel 峰会的创始人。

**00:01:38 - Sandra Persing**：

嗨， Saron。

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

你好。还有同样来自旧金山的 Ricky Robinett。他是 Twilio 的开发人员网络总监。

**00:01:47 - Ricky Robinett**：

你好。

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

从凤凰城外和我一起来的是 Robyn Bergeron，Red Hat 的社区架构师。

**00:01:55 - Robyn Bergeron**：

嗨！你好吗？

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

我很好。

**00:01:57 - Robyn Bergeron**：

看到你真高兴。

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

在不同的地方，这份工作的践行者被冠以不同的头衔。有<ruby> 开发者推广大使 <rt>  Developer Advocate </rt></ruby>、<ruby> 开发者布道师 <rt>  Developer Evangelist </rt></ruby>、<ruby> 开发者关系 <rt>  Developer Relations </rt></ruby>；对于新事物来说，基本定义非常重要。因此，我认为一个好的起点是来定义这些东西是什么，尤其是定义你可以在这个领域做什么。所以，你能告诉我你的头衔以及这个头衔实质上的含义吗？ Ricky，让我们从你开始吧。

**00:02:22 - Ricky Robinett**：

好的，听起来很棒。我的头衔是<ruby> 开发者网络总监 <rt>  Director of the Developer Network </rt></ruby>。我有幸供职于一个开发者关系专家团队。我们有一个布道师团队、一个教育团队和一个社区团队。因此，这绝对是一个大杂烩，有你听说过的各种不同的头衔，我们将其汇集在这个团队之中。

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

厉害啊。Sandra，你呢？

**00:02:45 - Sandra Persing**：

我在 Mozilla 担任活动和赞助的<ruby> 全球策略师 <rt>  Global Strategist </rt></ruby>，并与 Mozilla 新兴技术小组的开发者拓展团队合作。我通常会将我的日常活动解释为大量的研究、交流和探索，而这都将最终影响我们评估与决策应当如何投资我们的资源：我们的时间、金钱、纪念品、演说者等等，以回馈开发者社区，同时也从开发者社区得到反馈。这份工作既有布道的一面，也有倡导的一面。

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

很棒。 Robyn，该你了。

**00:03:26 - Robyn Bergeron**：

你好。好吧，我的正式头衔是<ruby> 社区架构师 <rt>  Community Architect </rt></ruby>。人们就我这个头衔有过很多问题。我曾被称为<ruby> 社区经理 <rt>  Community Manager </rt></ruby>，也曾被称为<ruby> 开发者推广大使 <rt>  Developer Advocate </rt></ruby>，甚至还在过去的一份工作中做过<ruby> 运维推广大使 <rt>  Operations Advocate </rt></ruby>。但是我想我所做的是……嗯，你知道，像是“社区经理”之类头衔所暗示的“你在管理着社区中发自内心为项目做出贡献的人”的这种想法，其实相当愚蠢，因此，我喜欢把我所做的事情看作是构建一个框架，人们可以真正顺利地参与其中，确保过程中没有障碍，并确保他们可以完成所有他们想要做的事情。

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

Ricky，由于你基本上是负责整个开发者网络的总监，你不仅仅是置身于推广大使团队、布道团队，也是社区团队之中，这有点儿像是你在运行着整个项目。你是怎样理解的这个的，是向开发者推广还是推广开发者？

**00:04:20 - Ricky Robinett**：

是的，这是个很棒的问题。 Phil Nash 是我们团队中的一名布道师，他对此有一个很好的认识，而我要将它剽窃过来。我们可以从很多不同的角度来解释这份工作，但说到底，我们的工作其实大体上就是帮助他人。

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

我喜欢这一点。是的。

**00:04:35 - Ricky Robinett**：

我们帮助开发人员，有时候这些帮助看起来就像是在 Stack Overflow 上回答问题，有时看起来像是构建一款新工具，有时看起来像组织一个活动，有时看起来又像是发起一次产品的内部改动。因此，我认为这就是我所听说过的最好的认识 —— 我们的责任是帮助。

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

的确。Sandra，你的职位让我感兴趣的一点是，你不光是策略师，还是全球策略师。让世界各地的开发人员组织起来，并且帮助他们，这是你在 Mozilla 的工作的一部分。在全球范围内的推广倡导会是什么样的呢？在不同的国家、不同的大洲之间，这份工作的含义会有所不同吗？

**00:05:20 - Sandra Persing**：

对，确实存在一些不同。我们今年刚刚在新加坡举办了第三届 DevRel 年度峰会。在过去的两年间，我们在西雅图主办了这项活动，而到了新加坡，我们看到了不同的视角。当我们走出湾区时，就连某些最基本的组织方式都需要做出改变。比如，如何确保一切都能在线下运转，因为连接性是一个大问题；再比如，我们该怎样使一名工作于印尼的开发人员能在峰会过程中与社区充分互动，无论他是在线上参加聚会，还是来到现场。

**00:05:59 - Sandra Persing**：

我们总会发现，所谓的“基础”，一些对我们来说如此寻常的事，比如在湾区举办一次简单的聚会——这再常见不过了，对吧？你高呼一声“我要参会”，就会发现到处都有聚会可以参加。然而对于，比如，越南的开发者来说，此事可以是具有特殊意义，且对他们而言相当重要的。这种聚会很宝贵，丰富了他们的开发者生活。

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

我注意到的一件事是，大家都以某种方式提到了社区。而我自己也运营着一个社区，Code Newbie，而且我经常被一些公司聘用为他们的布道师或社区经理。有一件事我一直都有点儿担心，也许有点畏惧；我在想，在过去的三四年里，我一直在尽我所能，尽心尽力地负责着这个社区。

**00:06:55**：

但如果我为一家公司工作，我就必须要牺牲这些吗？我是不是必须要将公司置于社区的需求之上？我该如何平衡这种关系呢？

**00:07:06**：

所以我在想，Robyn，或许我们可以问问你这个<ruby> 社区架构师 <rt>  Community Architect </rt></ruby>，你是如何区分这两者的呢？或者说，你是如何平衡这两者的？

**00:07:16 - Robyn Bergeron**：

好吧，这当然是一个有趣的平衡。我的意思是，我以前的一个工作实际上是担任 Fedora 项目负责人。而你知道，Fedora 是 Red Enterprise Linux 的上游，在这里，你角色的一部分确实是某种找寻平衡的行为，对吧？两者之间的平衡是让社区里的人们开心，让公司对社区所做的事情高兴，同时要确保每个人都是快乐大家庭的一员。

**00:07:41**：

而且，你知道，我想当你在这个职位上做得最好的时候，你肯定会时不时地激怒公司决策层的某些人。但是你知道的，最终还是要用结果来证明，对吧？

**00:07:59**：

人们总是问我，你是如何平衡 Ansible 和红帽之间的关系的，你知道，当 Ansible 被红帽收购时，就像是 —— 哦我的天哪，红帽会接管它，然后对它做些糟糕的事情，并摆脱 Ubuntu 的支持吗？

**00:08:12**：

而这就像是，拥有整个项目的全部目的就是为了破坏它，就像是为了不要吸引 4000 名贡献者而故意变得糟糕一样。

**00:08:22**：

确保你的管理层信任你，并始终与人们保持清晰的沟通以了解实际发生的事情，并确保沟通通道的两侧都不会出现意外，这是成功与否的部分原因，也许并不总是会成功，但肯定会让人感到惊讶。

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

嗯嗯。确实。Ricky，你呢？当你在做这么多不同事情的时候，你是如何看待这种关系中的平衡的？

**00:08:49 - Ricky Robinett**：

我认为你必须相信公司和技术。你必须相信，你所带给开发人员的东西将会对他们的生活、职业生涯以及公司产生影响。

**00:09:03**：

另一方面，你必须让高管们相信这种方法。因此，我们非常幸运的一点是我们的 CEO 是一名开发人员，而且在很多方面，他是我们和开发者社区打交道的原动力。我们的布道师们的使命是激励并装备开发人员。因此，有时候我们会说这能激励并装备他们吗？因为如果不能的话，我们就不应该这么做，因为这超出了我们的职权限范围。

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

嗯嗯。Sandra，我觉得你有点儿优势，因为 Mozilla 是一个非营利性组织，对吧？所以我觉得或许 ——

**00:09:44 - Sandra Persing**：

我刚想说。

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

跟我讲讲这个吧。

**00:09:47 - Sandra Persing**：

Mozilla 的历史就是我们是一家叛逆的公司。我们一直在反抗企业家，对吗？出走 Netscape ，并与我们的创始人之一 Mitchell Baker 一起走过的整段历史，确保了 Web ——

**00:10:01**：

—— 乃至互联网是面向所有人的开放而自由的资源。我的意思是，我们仍然，我们每个人，每一个 Mozilla 人，都信奉这一口号，我们对此深信不疑。因此，这绝对是一家令人惊叹的、100% 拥抱了社区的公司。

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

确实。所以，Ricky，我还清楚地记得那件红色的运动夹克，我还记得你亲自出马做的那些很棒的演示，感觉那种联系并帮助开发人员的方式非常新颖。这个想法是怎么在 Twilio 上产生的？

**00:10:41 - Ricky Robinett**：

嗯，你这么说真是太好了。我们确实相信我们是站在巨人的肩膀之上。你会想到 Apple 公司的 Guy Kawasaki，有人在我们之前就采用了这种营销方式。我认为我们很幸运能在正确的时间把它带给开发人员。

**00:11:03**：

而且有这么多的人进来，带着我们如何能够做到这一点以及如何能够不断提高方法水平的想法。但是我实际上不知道是谁发明了红色运动夹克，因此现在我需要去探寻历史 ——

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

你一定得找到答案。

**00:11:19 - Ricky Robinett**：

—— 有关这是何时开始的历史。

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

这是件很棒的夹克。

**00:11:21 - Ricky Robinett**：

知道了，我今天下午就去找。

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

而我想知道的是，布道和推广的想法在 Twilio 是如何随着时间而发生变化的。你知道，曾经你们只是一家小小的初创公司，一家叛逆的初创公司，然后现如今成了一家大公司。随着公司本身的变化，布道的形式又是如何变化的呢？

**00:11:46 - Ricky Robinett**：

是的，开始时，我觉得我本可以一年 365 天都在<ruby> 黑客马拉松 <rt>  hackathon </rt></ruby>上渡过，而在纽约，每个周末你都要从五到六个黑客马拉松之中抉择一个。我们在布道方面所做的许多事情都是黑客马拉松场景，而现在，场景不同了。确实，最大的变化在于公司外部而不是内部。

**00:12:11**：

因此，我谈到了布道师的激励和装备。所以令人高兴的是，这些年来这个任务并没有发生任何变化，他们激励和装备的方式一直在变化，但任务本身并没有什么改变。

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

那么 Robyn，随着 DevOps 和 DevSecOps 的兴起，推广大使在你和社区架构师看来如何呢？会有运维推广大使吗？

**00:12:39 - Robyn Bergeron**：

嗯，实际上那是 …… 我的意思是说，孩子们，那是我辍学后的第一份工作。我不建议这样做，不要在家做这个事情。

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

留在学校里继续上学。

**00:12:48 - Robyn Bergeron**：

听着。你看，小姑娘，我的第一份工作实际上是在 Motorola 担任<ruby> 系统管理员 <rt>  SysAdmin </rt></ruby>多年，而当我从担任 Fedora 负责人跳槽时，我在 Elastic Search 工作了一段时间，你知道，那个头衔是开发者推广大使，因此我花了几个学期在大学里攻读 C。但是我的心一直都在运维这边。我开始对自己真的是一名开发者推广大使感到疑惑？我觉得我只是在推广运维人员而已。我开始自称是一名运维推广大使，每个人都为之注目。

**00:13:22**：

每个人都说，嗯，这真的是一个很酷的头衔。我的意思是，你知道，我基本上只是在公司内部倡导其他人在做什么。

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

所以，Sandra，我们谈到了推广大使和布道师在世界不同地区看起来如何不同，但是我想知道，随着我们变得越来越全球化，联系越来越紧密，更大范围和形式的布道对你来说是不是也在随着时间流逝而变化？

**00:13:52 - Sandra Persing**：

你知道什么是开发者关系吗？我们是在推销我们的产品吗？你知道，而我注意到的是，即使是大公司也正在远离这种策略。要明白的是，做一个真实的人，真正留心倾听并回应开发人员的需求是最为重要的，而不是去推销产品。

**00:14:17**：

我总是回过头来和我在 Mozilla 的团队分享一个理念，开发者们实际上是我们所合作过的最聪明、最具有创造力的客户群体之一。他们可以大老远就闻到商业销售的气味。因此，我们必须对于如何共享信息保持精明，就像它必须是一群有着才华和智慧的人聚集在一起，成为我们与开发者社区进行沟通的创新方式。

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

嗯，真的，我很喜欢这样的想法，需要将不同的技能，我想，还有不同的背景，集合在一起，才能很好地服务于开发者，也能帮助开发者自助。

**00:15:01**：

当我想到开发者推广大使的兴起时，在我看来，它与开源的兴起息息相关。这感觉几乎就像是开源贡献者越多，开源就变得越重要，重视它的大公司也越多，他们几乎必须与这些开源贡献者、这些开发人员建立起更好的关系，并且我感觉这二者真的是紧密相关的。

**00:15:27**：

因此我很好奇你的想法。那么 Robyn，让我们从你开始吧。是这样的吗？推广大使的这个想法与开源的兴起有所联系吗？

**00:15:37 - Robyn Bergeron**：

如果你是一家销售软件或是销售许可证和长期支持的公司，你知道，无论你的开源公司的业务模型是什么，如果你没有这种反馈回路，或者你没有真正关注人们在说什么，那你最终会犯下一个众所周知的错误。我认为，真正能在全世界范围内每天都做到这一点，就是成功和失败之间的区别，没有人愿意花时间去做错误的事情。那是个坏主意。

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

是的，这通常是个坏主意。很好。因此，我想知道你们每个人各自都在关注什么，都真正在思考什么。因此，Ricky，让我们从你开始吧。在你的 Twilio 角色中，你试图为开发者文化带来什么样的改进呢？

**00:16:27 - Ricky Robinett**：

如果我要说的我与开发者交谈时听到过最多的一个短语，那就是“我不是开发者，但是 …… ”，而这可能是一直萦绕在我脑海中的最大的事情之一 —— 开发者定义的扩大化。

**00:16:45**:

对我们很多人来说，“冒牌综合症”是一个非常真实的现象。令人惊讶的是，即使是你所认识的一些最好的开发者也在为此努力。而对我来说，我们所有人能在我们的文化中做的最重要的事情之一，就是让人们说，“你知道吗？我是一名开发者。我用代码解决问题。”

**00:17:08**：

因此，我最喜欢的故事之一是，我们社区有一个名为 Doug McKenziethat 的成员，他是个魔术师。他自学了 PHP，以便在魔术中使用技术。

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

噢，很棒。

**00:17:23 - Ricky Robinett**：

Doug 之所以如此之酷，是因为他超级谦虚，就像是：“哦，我不是一名开发者。”而突然之间你发现，他正在编写比许多人所见过的更为复杂的代码，可以完成令人震惊的事情。因此，我感觉世界上有许多 Doug McKenzies 这样的人，他们都在用代码做着伟大的事情，我们有机会让他们成为社区的一份子，并且在这项工作中拥有自己的身份。

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

我喜欢这个故事，因为它让我想起了我曾经为 Code Newbie 播客采访过的一个人，她是一名作家而且有很多写作任务，必须学习 Git 才能撰写有关 Git 的文章，她写了很多这方面的文章，又逐渐转向了其他的编程主题，最终，你知道，几年过去之后，她实际上已经成为了一名开发者却不自知。

**00:18:13**：

在我采访她的时候，我说：“嘿，你知道自己是一名开发者吗？”她说：“不，我是个作家。”而我说：“你可以二者兼备。它们并不互相排斥。”但是，是的，这种说法换个角度说，“嘿，我实际上是在编程和创造，因此现在我可以自称为一名码农了”，这种认识对人们来说很难，要做到这样需要一段时间。

**00:18:32 - Ricky Robinett**：

是的，当然是这样。

**00:18:33 - Saron Yitbarek**：

是的。因此，Robyn，对你来说，最近几年来你一直在努力推动的最积极的变化是什么？

**00:18:41 - Robyn Bergeron**：

只是确保在我们的成长过程中，我们不会失去对大局观的把握。我们的主要目标是使人们易于使用、易于贡献、易于实际完成生活中的事情，确保我们不会偏离这个目标，或者在实际项目的某些结构层面获得更多的工程帮助，并确保我们保持所有这些东西都井井有条。我们在这方面做得很好，我不知道是不是，但我认为这很重要。我不知道自己会不会因此而获得诺贝尔和平奖，但我知道这对许多贡献者都非常重要。

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

这对我来说很有用。Sandra，你呢？作为全球策略师，你近些年来所推动的最积极的变化是什么？

**00:19:26 - Sandra Persing**：

在我脑海中真正突出的两件事是扩展开发者角色这个定义，这一点非常重要。我们想要发展我们的社区，对吗？那么为什么要限制开发者的定义和描述呢？

**00:19:43**：

当我们在 Sundance 与 Reggie Watts 和 Chris Milk 等著名人物进行座谈时，我们给电影制作人、制片人、决策者一个机会，让他们说：“哦，我们也能做到。我们不需要受限于作为创意电影制作人能做些什么。”

**00:20:00**：

我们可以采纳技术，也可以成为开发者，而这相当令人耳目一新。我们在 Mozilla 经历的另一个时刻是让一位芝加哥的灯光艺术家 Ian Brill 与我们合作进行一个项目，我们把这个项目标记为 Arch，我们为今年两个重要的 JavaScript 开发者活动带来了这种巨型的塑料灯，LED 灯，带着七个可编程的树莓派结构。这是为了邀请更多的程序员（无论他们是否自称为开发者）来尝试 Mozilla 今年大力倡导的两种语言： WebAssembly 和 Rust。

**00:20:49**：

因此，我们创建了两个简单的模板，说，“尝试一下吧”，但这不是我们所想要推动的编程。这不是编程，而是“是的，有几行代码。它是编程语言，但你要做的是创建几行代码，现在可以将它们转化为艺术”，而这让许许多多的新人进入到了我们的群体之中，也就是把他们带到我们的桌子旁边，然后他们会编写他们的代码行，在 Arch 之下走过，去查看目前正在结构中循环着的灯光表现。这真是太神奇了。

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

哇。这听上去很美，确实美。因此，我想知道的是，当我们谈论到为开发者做推广大使时，我们谈论了很多有关社区的话题，也谈论了那个最终的想法，无论我们的职业头衔是什么，我们真的只是想要帮助人们，开发者需要什么方面的帮助？ Ricky，让我们从你开始吧。开发者们说他们需要从你这里得到什么呢？

**00:21:44 - Ricky Robinett**：

哇。是的，这是个好问题。我认为，我们所发现的其中一件事是，科技变化如此之快，我们被问到的很多问题都是，“我从哪里开始？我首先要做什么？我怎么知道我走的路是正确的呢？”对我们来说，这可能是我们投入最多的领域之一，我们称之为帮助人们发现他们用代码改变世界的能力。

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

噢，这很美妙。我赞成。

**00:22:18 - Ricky Robinett**：

嗯，谢谢你。是的，这令人兴奋。因此我们构建了一款名为 TwilioQuest 的工具，以帮助人们发现这种力量，帮助他们知道从何处开始。但我只是感觉，你知道，人们一直在寻找自己的身份或获得允许使用该身份的方式，而对每个人而言这就已经是写代码，是用代码或软件解决问题。有许多人想这么做，然而却不知道从哪里开始。所以这是我们经常思考的一件事。

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

是的。因此，Robyn，对于你们红帽来说，红帽的开发者所寻求的是什么呢？

**00:23:00 - Robyn Bergeron**：

很多时候，是有人来找你，他们遇到了一些障碍，也许是“不知怎么的，我的公关人员被机器人遗漏了”，但是很多时候，也有人会说，“嘿，我有一个很酷的想法。或许它不太适合这里，但是我认为它可能会改善社区的运行方式，或者可能会是其它我们正在研究的东西的不错的辅助工具。我该怎么办呢？”这就像是，“好吧，我该如何帮你入门呢？”你知道吗？“我能怎么做呢？比如说，你只是需要有人同意吗？因为我在这儿整天都基本上是对任何事情点头，让人们知道，这行得通，你当然有权力这样做”，所以这是我所认为最好的事情是，你可以做。至少在我看来，就是确保人们在自己的前进道路上没有阻碍，或者如果阻碍他们前进的一件事是正等着某人说可以，我一直在重申，你不需要别人的许可，但是如果有人需要，那么看在上帝的份上，请给他们许可。

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

所以，最后一个问题，我们需要总结一下，我将向你们逐一询问。你在 2019 年将要倡导的最重要的事情是什么呢？如果你有魔杖，那么你想要改变的下一件大事是什么呢？ Sandra，让我们从你开始吧。

**00:24:15 - Sandra Persing**：

嘿，我就知道你会这么做。好吧，我们将在 2019 年进行的最大挑战和最为激动人心的项目是真正兑现我们的承诺，使 Web 成为最棒、最大、最易于访问的平台之一。我们总是告诉开发者这是你应当为之构建并部署的地方，但是我们知道 Web 本身非常复杂，而且有着多个浏览器供应商，有时候这并不是一个正确的说法，这对我们而言是一个长期的挑战，尤其是在 Mozilla，在这里我们想要保持 Web 开放、自由，并且能被所有人访问。我们想要继续确保能够兑现对开发人员的承诺，确保网络确实是开放的、可访问的，对所有人都开放。

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

嗯，爱了。Ricky，你呢？

**00:25:13 - Ricky Robinett**：

只需要确保我们为开发人员提供服务，使得他们能够在全世界范围内进行线上或是线下的聚会。更容易专注于你所看到的内容，并且无需记得开发人员是在何处，即使你没有看到他们。因此，我会挥舞我的魔杖，并在全世界更多的地方，找出我们如何能够帮助那里的开发人员。

**00:25:36 - Sandra Persing**：

我只想说，我喜欢 Jarod 有关<ruby> 暗物质开发者 <rt>  dark matter developer </rt></ruby>的演讲。实在是太棒了。

**00:25:42 - Ricky Robinett**：

当你第一次听到它的时候，感觉像是一个了不起的概念，就像是，“哇，真的是一回事儿。”

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

那就给我们讲讲吧，什么是暗物质开发者呢？

**00:25:52 - Sandra Persing**：

本质上，有一些开发者，他们不参与你的聚会，不参与 GitHub 和在线社区，不为 Stack Overflow 做贡献。这些开发者仍在努力工作并作出贡献，但我们不知道。我们知道他们就在那里，但我们看不到他们。我们无法认出他们，而这些人实际上是开发者社区中非常重要的一部分，我们往往会忽略他们，但不能这样。忽略那些不愿发表意见的社区将损害我们与开发者的关系，我们需要更加积极主动地寻找宇宙中那些暗物质开发者。

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

哦，我喜欢。这很酷。是的，Jarod 实际上在 Twilio 工作，对吗？

**00:26:38 - Sandra Persing**：

是的。是的，他负责运营亚太区的 DevRel。

**00:26:42 - Robyn Bergeron**：

我从前在红帽的同事之一 Chris Grams，现在在一家名为 Tidelift 的公司里工作，他曾经有一个名为<ruby> 暗物质很重要 <rt>  Dark Matter Matters </rt></ruby>的博客，因为它有点儿像是 ——

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

嗯嗯。

**00:26:54 - Robyn Bergeron**：

你知道，你所看不到的东西实际上仍然很重要，所以 ——

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

确实是这样。 Robyn，你呢？你会用你的魔杖做什么呢？

**00:27:03 - Robyn Bergeron**：

哦，用我的魔杖，有很多事情，但是根据这次谈话，我想我会更好地管理彼此之间的依赖关系，而且也许并不令人惊讶，尤其是当我们之中有许多人在 OpenStack、 OPNFV、 Ansible 以及所有这些建立在彼此之上的东西工作时。只是确保我们项目之间的关系比你筋疲力尽时所能看到的关系更加明显。因此，我非常期待来年的事情，因为我们在事物上越来越受关注。现在这非常令人兴奋。

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

吸引力总是那么好。它是如此令人兴奋。好吧，我想感谢你们所有人，非常感谢你们今天加入我们并分享你们的思维、想法和故事。非常感谢大家。那么现在说再见？

**00:27:51 - Robyn Bergeron**：

再见，各位。

**00:27:54 - Sandra Persing**：

很好。非常感谢你， Saron。能上这期节目很荣幸。

**00:27:58 - Ricky Robinett**：

它已经开始了，游戏工作室对于开源的态度已经开始转变。

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

确实。今天的圆桌会议包括红帽的社区架构师 Robyn Bergeron、 Mozilla 的全球策略师 Sandra Persing 以及 Twilio 的开发者网络总监 Ricky Robinet。我认为自己很幸运，能有这样的平台让我分享对于我们社区将何去何从的愿景，无论是这档播客节目还是在其他地方，但我想要指出的是，你无需拥有自己的播客就能够成为推广大使。成为推广大使只是意味着睁大眼睛并代表他人大声疾呼。这确实可以是每个人的工作。因此，我希望 Robyn、 Sandra 和 Ricky 给了你一点儿有关倡导你觉得重要的事情的启发。

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

同时，《代码英雄》第 3 季已经在制作中了。当新剧集在今年春天推出时，你可以成为最早了解新剧集的人之一。如果你尚未注册，请在 Apple Podcasts、 Google Podcasts 或任何你获得播客节目的地方进行订阅。只需点击一下，100% 免费。我是 Saron Yitbarek。非常感谢你的收听，在第 3 季到来之前，继续编程。

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

---

via: <https://www.redhat.com/en/command-line-heroes/season-2/developer-advocacy-roundtable>

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

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


使用 curl 从命令行访问互联网

>
> 下载我们整理的 curl 备忘录。要在不使用图形界面的情况下从互联网上获取所需的信息，curl 是一种快速有效的方法。
>
>
>

![](/data/attachment/album/202010/31/000543n032ud499yy4d94v.jpg)

`curl` 通常被视作一款非交互式 Web 浏览器，这意味着它能够从互联网上获取信息，并在你的终端中显示，或将其保存到文件中。从表面看，这是 Web 浏览器，类似 Firefox 或 Chromium 所做的工作，只是它们默认情况下会*渲染*信息，而 `curl` 会下载并显示原始信息。实际上，`curl` 命令可以做更多的事情，并且能够使用多种协议与服务器进行双向传输数据，这些协议包括 HTTP、FTP、SFTP、IMAP、POP3、LDAP、SMB、SMTP 等。对于普通终端用户来说，这是一个有用的工具；而对于系统管理员，这非常便捷；对于微服务和云开发人员来说，它是一个质量保证工具。

`curl` 被设计为在没有用户交互的情况下工作，因此与 Firefox 不同，你必须从头到尾考虑与在线数据的交互。例如，如果想要在 Firefox 中查看网页，你需要启动 Firefox 窗口。打开 Firefox 后，在地址栏或搜索引擎中输入要访问的网站。然后，导航到网站，然后单击要查看的页面。

对于 `curl` 来说也是如此，不同之处在于你需要一次执行所有操作：在启动 `curl` 的同时提供需要访问的互联网地址，并告诉它是否要将数据保存在终端或文件中。当你必须与需要身份验证的网站或 API 进行交互时，会变得有点复杂，但是一旦你学习了 `curl` 命令语法，它就会变得自然而然。为了帮助你掌握它，我们在一个方便的[备忘录](https://opensource.com/downloads/curl-command-cheat-sheet)中收集了相关的语法信息。

### 使用 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 则表示服务器在处理请求时出现了错误。

要查看协商过程中发生了什么错误，添加 `--show-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` 选项非常有用。短网址对于有字符限制的社交网络（当然，如果你使用[现代和开源的社交网络](https://opensource.com/article/17/4/guide-to-mastodon)的话，这可能不是问题），或者对于用户不能复制粘贴长地址的印刷媒体来说是有用处的。但是，它们也可能存在风险，因为其目的地址本质上是隐藏的。通过结合使用 `--head` 选项仅查看 HTTP 头，`--location` 选项可以查看一个 URL 的最终地址，你可以查看一个短网址而无需加载其完整的资源：

```
$ curl --head --location "<https://bit.ly/2yDyS4T>"

```

### 下载我们的 curl 备忘录

一旦你开始考虑了将探索 web 由一条命令来完成，那么 `curl` 就成为一种快速有效的方式，可以从互联网上获取所需的信息，而无需麻烦图形界面。为了帮助你适应到工作流中，我们创建了一个 [curl 备忘录](https://opensource.com/downloads/curl-command-cheat-sheet)，它包含常见的 `curl` 用法和语法，包括使用它查询 API 的概述。

---

via: <https://opensource.com/article/20/5/curl-cheat-sheet>

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


Linux 黑话解释：什么是显示管理器？

>
> 在这篇 Linux 黑话解释中，你将了解 Linux 中的显示管理器。它是桌面环境的一部分吗？它的作用是什么？
>
>
>

### 什么是 Linux 中的显示管理器？

简单来说，<ruby> 显示管理器 <rt>  display manager </rt></ruby>（DM）是一个为你的 Linux 发行版提供图形登录功能的程序。它控制用户会话并管理用户认证。显示管理器会在你输入用户名和密码后，立即启动[显示服务器](https://itsfoss.com/display-server/)并加载[桌面环境](https://itsfoss.com/what-is-desktop-environment/)。

显示管理器通常是登录界面的代名词。毕竟它是可见的部分。然而，可见的登录屏幕，也叫<ruby> 欢迎页 <rt>  greeter </rt></ruby>，只是显示管理器的一部分。

![登录屏幕是显示管理器的可见部分](/data/attachment/album/202010/31/125316ikekhtu3kqkclqkk.jpg)

像[各种桌面环境](https://itsfoss.com/best-linux-desktop-environments/)和显示服务器一样，也有各种显示管理器。我们来看看它们。

### 不同的显示管理器

有些人认为显示管理器是桌面环境的一部分，但事实并非如此。它是一个独立的程序。

桌面环境可能会推荐某个显示管理器，但这并不意味着它不能与其它一些显示管理器一起工作。如果你曾经在同一个系统中安装过不止一个桌面环境，你会记得，登录界面（即显示管理器）允许你切换桌面环境。

![显示管理器可以与各种桌面环境配合使用](/data/attachment/album/202010/31/125150tulluvlvvzl19c21.jpg)

虽然显示管理器不是桌面环境本身的一部分，但它往往与桌面环境由同一个开发团队开发。它也成为桌面环境的代表。

例如，GNOME 桌面环境开发了 GDM（GNOME Display Manager），光看登录界面就会想到 GNOME 桌面环境。

![GNOME登录屏幕与GDM](/data/attachment/album/202010/31/125240v16ucum3edmi29fi.png)

一些流行的显示管理器有：

* [GDM](https://wiki.gnome.org/Projects/GDM)（<ruby> GNOME 显示管理器 <rt>  GNOME Display Manager </rt></ruby>）：GNOME 的首选。
* [SDDM](https://github.com/sddm)（<ruby> 简单桌面显示管理器 <rt>  Simple Desktop Display Manager </rt></ruby>)：KDE 首选。
* LightDM：由 Ubuntu 为 Unity 桌面开发。

### 显示管理器可以定制

有这么多的桌面环境可供选择，它们都有自己的显示管理器吗？不，不是这样的。

正如我之前提到的，可见的登录屏幕被称为欢迎页。这个欢迎页可以进行自定义来改变登录屏幕的外观。

事实上，许多发行版和/或桌面环境都制作了自己的欢迎页，以给用户提供一个类似于他们品牌的登录屏幕。

例如，Mint 的 Cinnamon 桌面使用了 LightDM，但有自己的欢迎页来给它更多的 Mint 式（或者我应该说是 Cinnamon）的外观。

![基于 LightDM 的 Linux Mint 登录屏幕](/data/attachment/album/202010/31/125244z2a8maaj40bc2tpr.jpg)

来看看 Kali Linux 的登录界面：

![Kali Linux 登录界面](/data/attachment/album/202010/31/125322hcc9pt76h7fkva7v.jpg)

如果你喜欢编码和调整，你可以根据自己的喜好修改或编码自己的欢迎页。

### 改变显示管理器

如果你愿意，可以[更改显示管理器](https://itsfoss.com/switch-gdm-and-lightdm-in-ubuntu-14-04/)。你需要先安装显示管理器。安装时你会看到切换显示管理器的选项。

![](/data/attachment/album/202010/31/125251sycruaiiiczbai1j.jpg)

如果当时没有做切换，那么以后可以通过手动配置来改变显示管理器。不同的发行版重新配置显示管理器的方法略有不同，这不在本文讨论范围内。

### 结语

希望大家对 Linux 中的显示管理器这个术语有一点了解。本黑话解释系列的目的是用非技术性的语言解释常见的 Linux 俗语和技术术语，而不涉及太多的细节。

欢迎大家提出意见和建议。

---

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

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


文档基金会希望 Apache 放弃陷入困境的 OpenOffice，转而支持 LibreOffice

Summary: 你认为应该怎么做？

LibreOffice……现在还是一言难尽啊

公式编辑器太丑了，我宁愿markdown

对于 Linux 用户来说，当我们想到[微软 Office 的开源替代品](https://itsfoss.com/best-free-open-source-alternatives-microsoft-office/)时，Apache OpenOffice 仍然是一个相关的推荐，这是不言而喻的。然而，在过去的几年里，OpenOffice 的发展几乎是停滞的。

当然，考虑到 Abhishek 早在 2016 年就写过 [Apache OpenOffice 关闭的可能性](https://itsfoss.com/openoffice-shutdown/)，这并不令人震惊。

现在，在一封[文档基金会的公开信](https://blog.documentfoundation.org/blog/2020/10/12/open-letter-to-apache-openoffice/)中，他们呼吁 Apache OpenOffice 推荐用户开始使用更好的替代品，比如 LibreOffice。在本文中，我将提到文档基金会的博文中的一些重点，以及它对 Apache OpenOffice 的意义。

![](/data/attachment/album/202011/01/100708mfi6x8jcklvi5lzu.png)

### Apache OpenOffice 是历史，LibreOffice 是未来？

尽管我当年没有使用过 OpenOffice，但可以肯定地说，它绝对不是微软 Office 的现代开源替代品。至少现在不是了。

是的，Apache OpenOffice 对于传统用户来说仍然是很重要的东西，在几年前是一个很好的替代品。

以下是 OpenOffice 和 LibreOffice 的主要发布时间线：

![](/data/attachment/album/202011/01/100711xajhhajhejnskh48.jpg)

现在 OpenOffice 已经没有重大的开发了，Apache OpenOffice 的未来是什么？一个相当活跃的项目，这个最大的开源基金会会不发布重大版本？

这听起来并不乐观，而这正是文档基金会在他们的公开信中所强调的：

>
> OpenOffice（.org），是 LibreOffice 的”父项目“，它是一个伟大的办公套件，它改变了世界。它有着引人入胜的历史，但**自从 2014 年以来，Apache OpenOffice （它现在的家）还没有一个重大的版本**。没错，六年多来，没有重大的新功能或重大更新到来，也很少发布次要版本，而且在及时的安全更新方面也存在问题。
>
>
>

对于一个普通用户来说，如果他们不知道 [LibreOffice](https://itsfoss.com/libreoffice-tips/)，我肯定希望他们知道。但是，Apache 基金会是否应该建议 OpenOffice 用户尝试使用 LibreOffice 来体验更好或更高级的办公套件呢？

我不知道，也许是，或者不是？

>
> ...很多用户不知道 LibreOffice 的存在。OpenOffice 的品牌仍然如此强大，尽管该软件已经有六年多没有重大的版本发布，而且几乎没有人开发或支持它。
>
>
>

正如在公开信中提到的，文档基金会强调了 LibreOffice 相对于 OpenOffice的 优势/改进，并呼吁 Apache OpenOffice 开始推荐他们的用户尝试更好的东西（即 LibreOffice）：

>
> 我们呼吁 Apache OpenOffice 做正确的事情。我们的目标应该是**把强大的、最新的、维护良好的生产力工具送到尽可能多的人手中**。让我们一起努力吧！
>
>
>

### Apache OpenOffice 应该做什么？

如果 OpenOffice 能完成工作，用户可能不需要努力寻找替代品。那么，因为他们的缓慢开发而呼唤另一个项目，建议用户采用未来工具并推荐它是一个好主意么？

在争论中，有人可能会说，如果你已经完成了你的目标，并且对改进 OpenOffice 没有兴趣，那么推广你的竞争对手才是公平的。而且，这并没有错，开源社区应该一直合作，以确保新用户得到最好的选择。

从另一个侧面来看，我们可以说，文档基金会对于 OpenOffice 到了 2020 年即使没有任何重大改进却仍然有重要意义感到沮丧。

我不会去评判，但当我看了这封公开信时，这些矛盾的想法就浮现在我的脑海里。

### 你认为是时候把 OpenOffice 搁置起来，依靠 LibreOffice 了吗？

即使 LibreOffice 似乎是一个更优越的选择，并且绝对值得关注，你认为应该怎么做？Apache 是否应该停止 OpenOffice，并将用户重定向到 LibreOffice？

欢迎你的意见。

---

via: <https://itsfoss.com/libreoffice-letter-openoffice/>

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

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


开源是如何支撑区块链技术发展的

Summary: 创造出区块链安全性和可靠性的原因：是开放，而非监管。

创造出区块链安全性和可靠性的原因：是开放，而非监管。

>
> 创造出区块链安全性和可靠性的原因：是开放，而非监管。
>
>
>

![](/data/attachment/album/202011/01/155124l5c81g86mfwgjxfx.jpg)

当人们发现以安全性而闻名的区块链技术居然是建立在开源软件代码之上时，通常会感到非常惊讶。事实上，正是这种开放性才赋予了区块链技术的安全性和可靠性。

以开源方式构建的任何事物，其核心价值之一就是为了提高效率。建立起一个有着不同观点和技能的开发人员社区，这些开发人员工作在同一个代码库的时候，可以成倍增加构建出来的应用程序数量以及复杂性。

### 开源比人们想象中的要更加普遍

开源的 Linux，就是一种比较流行的操作系统。Linux 为服务器提供了许多服务，这些服务让我们可以轻松地共享个人信息。其中包括 Google、Facebook 和数千个主要网站。当我们使用这些服务时，就是在和这些在网络上运行着 Linux 系统的计算机进行交互。Chromebook 也使用 Linux，Android 手机使用的操作系统也是基于 Linux 的。

Linux 不属于任何一家公司，人们可以免费使用并且可以共同协作来完善创造它。自 2005 年推出以来，已经有来自 1,700 多家公司的 20,000 多名开发人员 [为其中的代码做出了贡献](https://www.linuxfoundation.org/wp-content/uploads/2020/08/2020_kernel_history_report_082720.pdf) 。

这就是开源软件的运作方式。大量的人为此贡献，并不断添加、修改或构建开源代码库来创建新的应用程序和平台。区块链和加密货币的大部分代码都是使用开源软件开发的。开源软件是由充满热情的用户构建的，这些用户对错误、故障或缺陷时刻保持警惕。当发现问题时，开源社区中的开发人员将一起努力来解决问题。

### 区块链和开源

整个开源区块链开发者社区都在不断地添加和完善代码库。

以下是区块链的基本表现方式：

* 区块链平台具有一个交易数据库，该交易数据库允许对等方在任何时候彼此进行交易。
* 附有用户识别标签，以方便交易。
* 平台一定有一种安全的方式来在交易批准前对交易进行验证。
* 无法被验证的交易不会进行。

开源软件允许开发者在 [去中心化应用程序（Dapp）](https://www.freecodecamp.org/news/what-is-a-dapp-a-guide-to-ethereum-dapps/)中创建这些平台，这是区块链中交易的安全、保障和可变性的关键。

这种去中心化的方式意味着没有中央权威机构来调解交易，没有人能控制发生的事情。直接的点对点的交易可以更快速、安全的进行。由于交易被记录在分类账簿中，它们也会分发到整个生态系统中。

区块链使用密码学来保证安全。每一笔交易都携带着与前一笔交易相关联的信息，以验证其真实性。这可以防止威胁者篡改数据，因为一旦数据被添加到公共分类账中，其他用户就不能更改。

### 区块链是开源的吗？

虽然区块链本身在技术上可以是不开源的，但区块链系统通常是使用开源软件实现的，因为没有政府机构对其进行监管，所以这些开源软件使用的概念体现了一种开放文化。私人公司开发的用于处理金融交易的专有软件很可能受到 [政府机构](https://www.investopedia.com/ask/answers/063015/what-are-some-major-regulatory-agencies-responsible-overseeing-financial-institutions-us.asp) 的监管。在美国，这可能包括美国证券交易委员会（SEC）、联邦储备委员会和联邦存款保险公司（FDIC）。区块链技术在开放环境下使用不需要政府监管，实际上，用来验证交易的是用户社区。

你可以称它为一种极端的众包形式，既用于开发构建区块链平台的开源软件，也用于验证交易。这就是区块链得到如此多关注的原因之一：它有可能颠覆整个行业，因为它可以作为处理和验证交易的权威中介。

### 比特币，以太坊和其他加密货币

截至 2020 年 6 月，超过 [5000 万人拥有区块链钱包](https://www.statista.com/statistics/647374/worldwide-blockchain-wallet-users/) 。他们大多数用于金融交易，例如交易比特币、以太坊和其他加密货币。对许多人来说，像交易员观察股票价格一样，[查看加密货币价格](https://www.okex.com/markets) 已成为主流。

加密货币平台也使用开源软件。[以太坊项目](https://ethereum.org/en/) 开发出了任何人都可以免费使用的开源软件，社区中大量的开发者都为此贡献了代码。比特币客户端的参考实现版是由 450 多个开发人员和工程师进行开发的，他们已经贡献了超过 150,000 个贡献。

加密货币区块链是一个持续增长的记录。每个被称作为块的记录按顺序链接在一起，它们互相链接形成一条链。每个块都有其自己的唯一标记，这个标记称为 [哈希](https://opensource.com/article/18/7/bitcoin-blockchain-and-open-source) 。一个块包含自身的哈希值和前一个块的加密计算出的哈希值。从本质上讲，每个块都链接到前一个块，形成了无法中断的长链，每个块都包含其它区块的信息，用于验证交易。

在金融或是加密货币的区块链中没有中央银行。这些分布在整个互联网中的区块，建立了一个性能强大的审计跟踪系统。任何人都能够通过区块链来验证交易，但却不能更改上面的记录。

### 牢不可破的区块链

尽管区块链不受任何政府或机构的监管，但分布式的网络保证了它们的安全。随着链的增长，每一笔交易都会增加伪造的难度。区块分布在世界各地的网络中，它们使用的信任标记不可被改变，这条链条几乎变得牢不可破。

这种去中心化的网络，其背后的代码是开源的，这也是用户在交易中不必使用诸如银行或经纪人之类的中介就可以相互信任的原因之一。支撑加密货币平台的软件是由相互独立的开发者组建的联盟创建的，并且任何人都可以免费使用。这创造了世界上最大的制衡体系之一。

---

via: <https://opensource.com/article/20/10/open-source-blockchain>

作者：[Matt Shealy](https://opensource.com/users/mshealy) 选题：[lujun9972](https://github.com/lujun9972) 译者：[xiao-song-123](https://github.com/xiao-song-123) 校对：[wxy](https://github.com/wxy)

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


命令行生存指南：监视命令输出

Summary: 有时候需要不断的执行某个命令并追踪其输出产生的变化情况。

有时候需要不断的执行某个命令并追踪其输出产生的变化情况。

![](/data/attachment/album/202011/01/171721wt4s8s498cd389s4.jpg)

>
> 作为一个工程师，我们经常与命令行打交道，但除了工作用的命令，你是否尝试过让命令来替代你生活中方方面面？
>
>
> 为了让更多的爱好者可以用命令行作为自己的有效工具，Linux 中国特别邀请了三位作者，撰写了《命令行生存指南》，希望可以通过这个系列的文章，让你换一个视角，来看待命令行的存在。如果你想体验一下不一样的命令行，那就不妨来看看今天的《命令行生存指南》。
>
>
> 本次的内容为试读内容，也欢迎你针对今天的内容提出自己的意见和建议。
>
>
>

### 操作概述

有时候需要不断的执行某个命令并追踪其输出产生的变化情况。一种常见的方法是通过写一段死循环的 shell 脚本来实现，不过 Linux 本身已经提供了一个工具来帮你定期执行指定的程序并将结果全屏输出，本节将会介绍该工具。

### 概念和术语

在 shell 上执行一个命令行时通常会自动打开三个标准文件，即标准输入文件（`stdin`），通常对应终端的键盘；标准输出文件（`stdout`）和标准错误输出文件（`stderr`），这两个文件都对应终端的屏幕。进程将从标准输入文件中得到输入数据，将正常输出数据输出到标准输出文件，而将错误信息送到标准错误文件中。

### 操作实战

#### 场景一：使用 watch 定期执行命令并监视输出

| 难度 | ★ |
| --- | --- |
| 演示用发行版 | Ubuntu |
| 涉及命令 | `watch` |

##### watch 的基本用法

`watch` 的使用方法很简单，只需要：

```
watch 命令

```

就行了，这样 `watch` 命令会每隔两秒执行一次 `命令`，并全屏输出执行结果。

下图是执行 `watch date` 的一个界面：

```
Every 2.0s: date                         orangepipc2: Sat Oct  3 09:55:14 2020

Sat 03 Oct 2020 09:55:14 AM CST

```

图中第一行中的 `Every 2.0s:` 表示 `watch` 每隔 2 秒执行一次命令。后面的 `date` 为要执行的命令。再后面的 `orangepipc2:` 是执行命令的主机名，`Sat Oct 3 09:55:14 2020` 为执行命令的时间。

从第三行开始就是命令执行的输出结果。

##### 指定重复执行命令的间隔时间

通过 `-n 间隔` 你也可以设置重复执行命令的间隔时间，比如我可以调整为每 5 秒中执行一次 `date` 命令

```
watch -n 5 date
```

##### 发现输出中的变化

如果只是单纯的重复执行命令的话，那么可以很容易地通过 shell 循环来实现，然而 `watch` 的能力不仅如此而已。

通过 `-d` 选项，`watch` 还能高亮显示两次输出中不同的部分，这个功能相当实用：

```
watch -d -n 10 date
```

![](/data/attachment/album/202011/01/171038ul3mypkrl43y1n81.png)

除了高亮显示输出中改变的部分外，你也可以设置让 `watch` 发现结果有改变时退出循环执行，方法是使用 `-g/--chgexit` 选项。例如我们可以通过下面命令来发现 USB 变动情况。

```
watch -g 'dmesg |grep -i usb |tail'
```

##### 检测命令执行的返回值

默认情况下，`watch` 并不会关心命令的执行结果是否成功，但你可以设置让 `watch` 检测命令的返回值，当命令运行返回非 0 时发出蜂鸣（`-b`/`--beep`）或者直接退出（`-e`/`--errexit`）。

```
watch -e wrong_commands
```

![](/data/attachment/album/202011/01/171058xrrgrwqmm6lwm8rw.png)

### 小练习

* 尝试使用 `watch` 监控工程目录，并结合其他工具实现自动编译。