Go 通道是糟糕的，你应该也觉得很糟糕

Summary: 如果你在大街上问一个有名的程序员，Go 有什么特别之处？ 她很可能会告诉你 Go 最出名的是通道和 goroutine。

golang:看我理你吧

自己用不习惯就来黑golang，不喜欢不要用啊，

# Go 通道是糟糕的，你应该也觉得很糟糕

更新：如果你是从一篇题为 《[糟糕的 Go 语言](https://github.com/ksimka/go-is-not-good)》 的汇编文章看到这篇博文的话，那么我想表明的是，我很惭愧被列在这样的名单上。Go 绝对是我使用过的最不糟糕的的编程语言。在我写作本文时，我是想遏制我所看到的一种趋势，那就是过度使用 Go 的一些较复杂的部分。我仍然认为 <ruby> 通道 <rt>  Channel </rt></ruby>可以更好，但是总体而言，Go 很棒。这就像你最喜欢的工具箱中有 [这个工具](https://blog.codinghorror.com/content/images/uploads/2012/06/6a0120a85dcdae970b017742d249d5970d-800wi.jpg)；它可以有用途（甚至还可能有更多的用途），它仍然可以成为你最喜欢的工具箱！

更新 2：如果我没有指出这项对真实问题的优秀调查，那我将是失职的：《[理解 Go 中的实际并发错误](https://songlh.github.io/paper/go-study.pdf)》。这项调查的一个重要发现是...Go 通道会导致很多错误。

从 2010 年中后期开始，我就断断续续地在使用 Google 的 [Go 编程语言](https://golang.org/)，自 2012 年 1 月开始（在 Go 1.0 之前！），我就用 Go 为 [Space Monkey](http://www.spacemonkey.com/) 编写了合规的产品代码。我对 Go 的最初体验可以追溯到我在研究 Hoare 的 [通信顺序进程](https://en.wikipedia.org/wiki/Communicating_sequential_processes) 并发模型和 [Matt Might](http://matt.might.net) 的 [UCombinator 研究组](http://www.ucombinator.org/) 下的 [π-演算](https://en.wikipedia.org/wiki/%CE%A0-calculus) 时，作为我（[现在已重定向](https://www.jtolio.com/writing/2015/11/research-log-cell-states-and-microarrays/)）博士工作的一部分，以更好地支持多核开发。Go 就是在那时发布的（多么巧合啊！），我当即就开始学习尝试了。

它很快就成为了 Space Monkey 开发的核心部分。目前，我们在 Space Monkey 的生产系统有超过 42.5 万行的纯 Go 代码（*不* 包括我们所有的 vendored 库中的代码量，这将使它接近 150 万行），所以也并不是你见过的最多的 Go 代码，但是对于相对年轻的语言，我们是重度用户。我们之前 [写了我们的 Go 使用情况](https://www.jtolio.com/writing/2014/04/go-space-monkey/)。也开源了一些使用率很高的库；许多人似乎是我们的 [OpenSSL 绑定](https://godoc.org/github.com/spacemonkeygo/openssl)（比 [crypto/tls](https://golang.org/pkg/crypto/tls/) 更快，但请保持 openssl 本身是最新的！）、我们的 [错误处理库](https://godoc.org/github.com/spacemonkeygo/errors)、[日志库](https://godoc.org/github.com/spacemonkeygo/spacelog) 和 [度量标准收集库/zipkin 客户端](https://godoc.org/gopkg.in/spacemonkeygo/monitor.v1) 的粉丝。我们使用 Go、我们热爱 Go、我们认为它是目前为止我们使用过的最不糟糕的、符合我们需求的编程语言。

尽管我也不认为我能说服自己不要提及我的广泛避免使用 [goroutine-local-storage 库](https://github.com/jtolds/gls) （尽管它是一个你不应该使用的魔改技巧，但它是一个漂亮的魔改），希望我的其他经历足以证明我在解释我故意煽动性的帖子标题之前知道我在说什么。

### 等等，什么？

如果你在大街上问一个有名的程序员，Go 有什么特别之处？ 她很可能会告诉你 Go 最出名的是<ruby> 通道 <rt>  Channels </rt></ruby> 和 goroutine。 Go 的理论基础很大程度上是建立在 Hoare 的 CSP（<ruby> 通信顺序进程 <rt>  Communicating Sequential Processes </rt></ruby>）模型上的，该模型本身令人着迷且有趣，我坚信，到目前为止，它产生的收益远远超过了我们的预期。

CSP（和 π-演算）都使用通信作为核心同步原语，因此 Go 会有通道是有道理的。Rob Pike 对 CSP 着迷（有充分的理由）[相当深](https://en.wikipedia.org/wiki/Newsqueak) 已经有一段时间了。（[当时](https://en.wikipedia.org/wiki/Alef_%28programming_language%29) 和 [现在](https://en.wikipedia.org/wiki/Limbo_%28programming_language%29)）。

但是从务实的角度来看（也是 Go 引以为豪的），Go 把通道搞错了。在这一点上，通道的实现在我的书中几乎是一个坚实的反模式。为什么这么说呢？亲爱的读者，让我细数其中的方法。

#### 你可能最终不会只使用通道

Hoare 的 “通信顺序进程” 是一种计算模型，实际上，唯一的同步原语是在通道上发送或接收的。一旦使用 <ruby> 互斥量 <rt>  mutex </rt></ruby>、<ruby> 信号量 <rt>  semaphore </rt></ruby> 或 <ruby> 条件变量 <rt>  condition variable </rt></ruby>、bam，你就不再处于纯 CSP 领域。 Go 程序员经常通过高呼 “[通过交流共享内存](https://blog.golang.org/share-memory-by-communicating)” 的 [缓存的思想](https://lesswrong.com/lw/k5/cached_thoughts/) 来宣扬这种模式和哲学。

那么，让我们尝试在 Go 中仅使用 CSP 编写一个小程序！让我们成为高分接收者。我们要做的就是跟踪我们看到的最大的高分值。如此而已。

首先，我们将创建一个 `Game` 结构体。

```
type Game struct {
  bestScore int
  scores    chan int
}

```

`bestScore` 不会受到<ruby> 互斥量 <rt>  mutex </rt></ruby>的保护！这很好，因为我们只需要一个 goroutine 来管理其状态并通过通道来接收新的分值即可。

```
func (g *Game) run() {
  for score := range g.scores {
    if g.bestScore < score {
      g.bestScore = score
    }
  }
}

```

好的，现在我们将创建一个有用的构造函数来开始 `Game`。

```
func NewGame() (g *Game) {
  g = &Game{
    bestScore: 0,
    scores:    make(chan int),
  }
  go g.run()
  return g
}

```

接下来，假设有人给了我们一个可以返回分数的 `Player`。它也可能会返回错误，因为可能传入的 TCP 流可能会死掉或发生某些故障，或者玩家退出。

```
type Player interface {
  NextScore() (score int, err error)
}

```

为了处理 `Player`，我们假设所有错误都是致命的，并将获得的比分向下传递到通道。

```
func (g *Game) HandlePlayer(p Player) error {
  for {
    score, err := p.NextScore()
    if err != nil {
      return err
    }
    g.scores <- score
  }
}

```

好极了！现在我们有了一个 `Game` 类型，可以以线程安全的方式跟踪 `Player` 获得的最高分数。

你圆满完成了自己的开发工作，并开始拥有客户。你将这个游戏服务器公开，就取得了令人难以置信的成功！你的游戏服务器上也许正在创建许多游戏。

很快，你发现人们有时会离开你的游戏。许多游戏不再有任何玩家在玩，但没有任何东西可以阻止游戏运行的循环。死掉的 `(*Game).run` goroutines 让你不知所措。

**挑战：** 在无需互斥量或 panics 的情况下修复上面的 goroutine 泄漏。实际上，可以滚动到上面的代码，并想出一个仅使用通道来解决此问题的方案。

我等着。

就其价值而言，它完全可以只通过通道来完成，但是请观察以下解决方案的简单性，它甚至没有这个问题：

```
type Game struct {
  mtx sync.Mutex
  bestScore int
}

func NewGame() *Game {
  return &Game{}
}

func (g *Game) HandlePlayer(p Player) error {
  for {
    score, err := p.NextScore()
    if err != nil {
      return err
    }
    g.mtx.Lock()
    if g.bestScore < score {
      g.bestScore = score
    }
    g.mtx.Unlock()
  }
}

```

你想选择哪一个？不要被欺骗了，以为通道的解决方案可以使它在更复杂的情况下更具可读性和可理解性。<ruby> 拆解 <rt>  Teardown </rt></ruby>是非常困难的。这种拆解若用<ruby> 互斥量 <rt>  mutex </rt></ruby>来做那只是小菜一碟，但最困难的是只使用 Go 专用通道来解决。另外，如果有人回复说发送通道的通道更容易推理，我马上就是感到头疼。

重要的是，这个特殊的情况可能真的 **很容易** 解决，而通道有一些运行时的帮助，而 Go 没有提供！不幸的是，就目前的情况来看，与 Go 的 CSP 版本相比，使用传统的<ruby> 同步原语 <rt>  synchronization primitives </rt></ruby>可以更好地解决很多问题，这是令人惊讶的。稍后，我们将讨论 Go 可以做些什么来简化此案例。

**练习：** 还在怀疑？ 试着让上面两种解决方案（只使用通道与只使用互斥量channel-only vs mutex-only）在一旦 `bestScore` 大于或等于 100 时，就停止向 `Players` 索要分数。继续打开你的文本编辑器。这是一个很小的玩具问题。

这里的总结是，如果你想做任何实际的事情，除了通道之外，你还会使用传统的同步原语。

#### 通道比你自己实现要慢一些

Go 如此重视 CSP 理论，我认为其中一点就是，运行时应该可以通过通道做一些杀手级的调度优化。也许通道并不总是最直接的原语，但肯定是高效且快速的，对吧？

正如 [Dustin Hiatt](https://twitter.com/HiattDustin) 在 [Tyler Treat’s post about Go](http://bravenewgeek.com/go-is-unapologetically-flawed-heres-why-we-use-it/) 上指出的那样，

>
> 在幕后，通道使用锁来序列化访问并提供线程安全性。 因此，通过使用通道同步对内存的访问，你实际上就是在使用锁。 被包装在线程安全队列中的锁。 那么，与仅仅使用标准库 `sync` 包中的互斥量相比，Go 的花式锁又如何呢？ 以下数字是通过使用 Go 的内置基准测试功能，对它们的单个集合连续调用 Put 得出的。
>
>
>

```
> BenchmarkSimpleSet-8 3000000 391 ns/op
> BenchmarkSimpleChannelSet-8 1000000 1699 ns/o
>

```

无缓冲通道的情况与此类似，甚至是在争用而不是串行运行的情况下执行相同的测试。

也许 Go 调度器会有所改进，但与此同时，良好的旧互斥量和条件变量是非常好、高效且快速。如果你想要提高性能，请使用久经考验的方法。

#### 通道与其他并发原语组合不佳

好的，希望我已经说服了你，有时候，你至少还会与除了通道之外的原语进行交互。标准库似乎显然更喜欢传统的同步原语而不是通道。

你猜怎么着，正确地将通道与互斥量和条件变量一起使用，其实是有一定的挑战性的。

关于通道的一个有趣的事情是，通道发送是同步的，这在 CSP 中是有很大意义的。通道发送和通道接收的目的是为了成为同步屏蔽，发送和接收应该发生在同一个虚拟时间。如果你是在执行良好的 CSP 领域，那就太好了。

实事求是地说，Go 通道也有多种缓冲方式。你可以分配一个固定的空间来考虑可能的缓冲，以便发送和接收是不同的事件，但缓冲区大小是有上限的。Go 并没有提供一种方法来让你拥有任意大小的缓冲区 —— 你必须提前分配缓冲区大小。 *这很好*，我在邮件列表上看到有人在争论，*因为无论如何内存都是有限的*。

What。

这是个糟糕的答案。有各种各样的理由来使用一个任意缓冲的通道。如果我们事先知道所有的事情，为什么还要使用 `malloc` 呢？

没有任意缓冲的通道意味着在 *任何* 通道上的幼稚发送可能会随时阻塞。你想在一个通道上发送，并在互斥下更新其他一些记账吗？小心！你的通道发送可能被阻塞！

```
// ...
s.mtx.Lock()
// ...
s.ch <- val // might block!
s.mtx.Unlock()
// ...

```

这是哲学家晚餐大战的秘诀。如果你使用了锁，则应该迅速更新状态并释放它，并且尽可能不要在锁下做任何阻塞。

有一种方法可以在 Go 中的通道上进行非阻塞发送，但这不是默认行为。假设我们有一个通道 `ch := make(chan int)`，我们希望在其上无阻塞地发送值 `1`。以下是在不阻塞的情况下你必须要做的最小量的输入：

```
select {
case ch <- 1: // it sent
default:      // it didn't
}

```

对于刚入门的 Go程序员来说，这并不是自然而然就能想到的事情。

综上所述，因为通道上的很多操作都会阻塞，所以需要对哲学家及其就餐仔细推理，才能在互斥量的保护下，成功地将通道操作与之并列使用，而不会造成死锁。

#### 严格来说，回调更强大，不需要不必要的 goroutines

每当 API 使用通道时，或者每当我指出通道使某些事情变得困难时，总会有人会指出我应该启动一个 goroutine 来读取该通道，并在读取该通道时进行所需的任何转换或修复。

呃，不。如果我的代码位于热路径中怎么办？需要通道的实例很少，如果你的 API 可以设计为使用<ruby> 互斥量 <rt>  mutexes </rt></ruby>、<ruby> 信号量 <rt>  semaphores </rt></ruby>和<ruby> 回调 <rt>  callbacks </rt></ruby>，而不使用额外的 goroutine （因为所有事件边缘都是由 API 事件触发的），那么使用通道会迫使我在资源使用中添加另一个内存分配堆栈。是的，goroutine 比线程轻得多，但更轻量并不意味着是最轻量。

正如我以前 [在一篇关于使用通道的文章的评论中争论过的](http://www.informit.com/articles/article.aspx?p=2359758#comment-2061767464)（呵呵，互联网），如果你使用回调而不是通道，你的 API *总是* 可以更通用，*总是* 更灵活，而且占用的资源也会大大减少。“总是” 是一个可怕的词，但我在这里是认真的。有证据级的东西在进行。

如果有人向你提供了一个基于回调的 API，而你需要一个通道，你可以提供一个回调，在通道上发送，开销不大，灵活性十足。

另一方面，如果有人提供了一个基于通道的 API 给你，而你需要一个回调，你必须启动一个 goroutine 来读取通道，*并且* 你必须希望当你完成读取时，没有人试图在通道上发送更多的东西，这样你就会导致阻塞的 goroutine 泄漏。

对于一个超级简单的实际例子，请查看 [context 接口](https://godoc.org/golang.org/x/net/context)（顺便说一下，它是一个非常有用的包，你应该用它来代替 [goroutine 本地存储](https://github.com/jtolds/gls)）。

```
type Context interface {
  ...
  // Done returns a channel that closes when this work unit should be canceled.
  // Done 返回一个通道，该通道在应该取消该工作单元时关闭。
  Done() <-chan struct{}

  // Err returns a non-nil error when the Done channel is closed
  // 当 Done 通道关闭时，Err 返回一个非 nil 错误
  Err() error
  ...
}

```

想象一下，你要做的只是在 `Done()` 通道触发时记录相应的错误。你该怎么办？如果你没有在通道中选择的好地方，则必须启动 goroutine 进行处理：

```
go func() {
  <-ctx.Done()
  logger.Errorf("canceled: %v", ctx.Err())
}()

```

如果 `ctx` 在不关闭返回 `Done()` 通道的情况下被垃圾回收怎么办？哎呀！这正是一个 goroutine 泄露！

现在假设我们更改了 `Done` 的签名：

```
// Done calls cb when this work unit should be canceled.
Done(cb func())

```

首先，现在日志记录非常容易。看看：`ctx.Done(func() { log.Errorf ("canceled:%v", ctx.Err()) })`。但是假设你确实需要某些选择行为。你可以这样调用它：

```
ch := make(chan struct{})
ctx.Done(func() { close(ch) })

```

瞧！通过使用回调，不会失去表现力。 `ch` 的工作方式类似于用于返回的通道 `Done()`，在日志记录的情况下，我们不需要启动整个新堆栈。我必须保留堆栈跟踪信息（如果我们的日志包倾向于使用它们）；我必须避免将其他堆栈分配和另一个 goroutine 分配给调度程序。

下次你使用通道时，问问你自己，如果你用互斥量和条件变量代替，是否可以消除一些 goroutine ？ 如果答案是肯定的，那么修改这些代码将更加有效。而且，如果你试图使用通道只是为了在集合中使用 `range` 关键字，那么我将不得不请你放下键盘，或者只是回去编写 Python 书籍。

#### 通道 API 不一致，只是 cray-cray

在通道已关闭的情况下，执行关闭或发送消息将会引发 panics！为什么呢？ 如果想要关闭通道，你需要在外部同步它的关闭状态（使用互斥量等，这些互斥量的组合不是很好！），这样其他写入者才不会写入或关闭已关闭的通道，或者只是向前冲，关闭或写入已关闭的通道，并期望你必须恢复所有引发的 panics。

这是多么怪异的行为。 Go 中几乎所有其他操作都有避免 panic 的方法（例如，类型断言具有 `, ok =` 模式），但是对于通道，你只能自己动手处理它。

好吧，所以当发送失败时，通道会出现 panic。我想这是有一定道理的。但是，与几乎所有其他带有 nil 值的东西不同，发送到 nil 通道不会引发 panic。相反，它将永远阻塞！这很违反直觉。这可能是有用的行为，就像在你的除草器上附加一个开罐器，可能有用（在 Skymall 可以找到）一样，但这肯定是意想不到的。与 nil 映射（执行隐式指针解除引用），nil 接口（隐式指针解除引用），未经检查的类型断言以及其他所有类型交互不同，nil 通道表现出实际的通道行为，就好像为该操作实例化了一个全新的通道一样。

接收的情况稍微好一点。在已关闭的通道上执行接收会发生什么？好吧，那会是有效操作——你将得到一个零值。好吧，我想这是有道理的。奖励！接收允许你在收到值时进行 `, ok =` 样式的检查，以确定通道是否打开。谢天谢地，我们在这里得到了 `, ok =`。

但是，如果你从 nil 渠道接收会发生什么呢？ *也是永远阻塞！* 耶！不要试图利用这样一个事实：如果你关闭了通道，那么你的通道是 nil！

### 通道有什么好处？

当然，通道对于某些事情是有好处的（毕竟它们是一个通用容器），有些事情你只能用它们来做（比如 `select`）。

#### 它们是另一种特殊情况下的通用数据结构

Go 程序员已经习惯于对泛型的争论，以至于我一提起这个词就能感觉到 PTSD（创伤后应激障碍）的到来。我不是来谈论这件事的，所以擦擦额头上的汗，让我们继续前进吧。

无论你对泛型的看法是什么，Go 的映射、切片和通道都是支持泛型元素类型的数据结构，因为它们已经被特殊封装到语言中了。

在一种不允许你编写自己的泛型容器的语言中，任何允许你更好地管理事物集合的东西都是有价值的。在这里，通道是一个支持任意值类型的线程安全数据结构。

所以这很有用！我想这可以省去一些陈词滥调。

我很难把这算作是通道的胜利。

#### Select

使用通道可以做的主要事情是 `select` 语句。在这里，你可以等待固定数量的事件输入。它有点像 epoll，但你必须预先知道要等待多少个套接字。

这是真正有用的语言功能。如果不是 `select`，通道将被彻底清洗。但是我的天呐，让我告诉你，第一次决定可能需要在多个事物中选择，但是你不知道有多少项，因此必须使用 `reflect.Select`。

### 通道如何才能更好？

很难说 Go 语言团队可以为 Go 2.0 做的最具战术意义的事情是什么（Go 1.0 兼容性保证很好，但是很费劲），但这并不能阻止我提出一些建议。

#### 在条件变量上的 Select ！

我们可以不需要通道！这是我提议我们摆脱一些“<ruby> 圣牛 <rt>  sacred cows </rt></ruby>”（LCTT 译注：神圣不可质疑的事物）的地方，但是让我问你，如果你可以选择任何自定义同步原语，那会有多棒？（答：太棒了。）如果有的话，我们根本就不需要通道了。

#### GC 可以帮助我们吗？

在第一个示例中，如果我们能够使用定向类型的通道垃圾回收（GC）来帮助我们进行清理，我们就可以轻松地解决通道的高分服务器清理问题。

如你所知，Go 具有定向类型的通道。 你可以使用仅支持读取的通道类型（`<-chan`）和仅支持写入的通道类型（`chan<-`）。 这太棒了！

Go 也有垃圾回收功能。 很明显，某些类型的记账方式太繁琐了，我们不应该让程序员去处理它们。 我们清理未使用的内存！ 垃圾回收非常有用且整洁。

那么，为什么不帮助清理未使用或死锁的通道读取呢？ 与其让 `make(chan Whatever)` 返回一个双向通道，不如让它返回两个单向通道（`chanReader, chanWriter:= make(chan Type)`）。

让我们重新考虑一下最初的示例：

```
type Game struct {
  bestScore int
  scores    chan<- int
}

func run(bestScore *int, scores <-chan int) {
  // 我们不会直接保留对游戏的引用，因为这样我们就会保留着通道的发送端。
  for score := range scores {
    if *bestScore < score {
      *bestScore = score
    }
  }
}

func NewGame() (g *Game) {
  // 这种 make(chan) 返回风格是一个建议
  scoreReader, scoreWriter := make(chan int)
  g = &Game{
    bestScore: 0,
    scores:    scoreWriter,
  }
  go run(&g.bestScore, scoreReader)
  return g
}

func (g *Game) HandlePlayer(p Player) error {
  for {
    score, err := p.NextScore()
    if err != nil {
      return err
    }
    g.scores <- score
  }
}

```

如果垃圾回收关闭了一个通道，而我们可以证明它永远不会有更多的值，那么这个解决方案是完全可行的。是的，是的，`run` 中的评论暗示着有一把相当大的枪瞄准了你的脚，但至少现在这个问题可以很容易地解决了，而以前确实不是这样。此外，一个聪明的编译器可能会做出适当的证明，以减少这种脚枪造成的损害。

#### 其他小问题

* **Dup 通道吗？** —— 如果我们可以在通道上使用等效于 `dup` 的系统调用，那么我们也可以很容易地解决多生产者问题。 每个生产者可以关闭自己的 `dup` 版通道，而不会破坏其他生产者。
* **修复通道 API！** —— 关闭不是幂等的吗？ 在已关闭的通道上发送信息引起的 panics 没有办法避免吗？ 啊！
* **任意缓冲的通道** —— 如果我们可以创建没有固定的缓冲区大小限制的缓冲通道，那么我们可以创建非阻塞的通道。

### 那我们该怎么向大家介绍 Go 呢？

如果你还没有，请看看我目前最喜欢的编程文章：《[你的函数是什么颜色](http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/)》。虽然不是专门针对 Go，但这篇博文比我更有说服力地阐述了为什么 goroutines 是 Go 最好的特性（这也是 Go 在某些应用程序中优于 Rust 的方式之一）。

如果你还在使用这样的一种编程语言写代码，它强迫你使用类似 `yield` 关键字来获得高性能、并发性或事件驱动的模型，那么你就是活在过去，不管你或其他人是否知道这一点。到目前为止，Go 是我所见过的实现 M：N 线程模型（非 1：1 ）的语言中最好的入门者之一，而且这种模型非常强大。

所以，跟大家说说 goroutines 吧。

如果非要我选择 Go 的另一个主要特性，那就是接口。静态类型的 <ruby>   鸭子模型  <rt>  duck typing </rt></ruby> 使得扩展、使用你自己或他人的项目变得如此有趣而令人惊奇，这也许值得我改天再写一组完全不同的文章来介绍它。

### 所以…

我一直看到人们争先恐后冲进 Go，渴望充分利用通道来发挥其全部潜力。这是我对你的建议。

**够了！**

当你在编写 API 和接口时，尽管“绝不”的建议可能很糟糕，但我非常肯定，通道从来没有什么时候好过，我用过的每一个使用通道的 Go API，最后都不得不与之抗争。我从来没有想过“哦 太好了，这里是一个通道；”它总是被一些变体取代，***这是什么新鲜的地狱？***

所以，*请在适当的地方，并且只在适当的地方使用通道。*

在我使用的所有 Go 代码中，我可以用一只手数出有多少次通道真的是最好的选择。有时候是这样的。那很好！那就用它们吧。但除此之外，就不要再使用了。

*特别感谢我的校对读者 Jeff Wendling、[Andrew Harding](https://github.com/azdagron)、[George Shank](https://twitter.com/taterbase) 和 [Tyler Treat](http://bravenewgeek.com) 提供的宝贵反馈。*

如果你想和我们一起用 Go 在 Space Monkey 项目工作，请[给我打个招呼](https://www.jtolio.com/contact/)！

---

via: <https://www.jtolio.com/2016/03/go-channels-are-bad-and-you-should-feel-bad>

作者：[jtolio.com](https://www.jtolio.com/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[gxlct008](https://github.com/gxlct008) 校对：[wxy](https://github.com/wxy)

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


编写一个简单的游戏来练习用 C++ 编程

Summary: C++ 语言很复杂，但它可以教会你很多关于数据类型、内存管理和代码链接的知识。

C++ 语言很复杂，但它可以教会你很多关于数据类型、内存管理和代码链接的知识。

# 编写一个简单的游戏来练习用 C++ 编程

>
> C++ 语言很复杂，但它可以教会你很多关于数据类型、内存管理和代码链接的知识。
>
>
>

<img src="content_image/album_202101_05_110821dwzztxchazhto8ko.jpg" alt="" title="">

学习一门编程语言有几种方法。如果你是编码新手，你通常会学习一些基本的计算机编码概念，并尝试应用它们。如果你已经知道如何用另一种语言进行编码，你可以重新学习编码概念在新语言中是如何表达的。

不管是哪种情况，学习这些新原理的便捷方法是创建一个简单的猜谜游戏。这会迫使你了解一门语言如何接收输入和发送输出，如何比较数据，如何控制程序的流程，以及如何利用条件来影响结果。它还确保你知道一门语言是如何组织其代码的；例如，Lua 或 [Bash](https://opensource.com/article/20/12/learn-bash) 可以很容易地作为脚本运行，而 [Java](https://opensource.com/article/20/12/learn-java-writing-guess-number-game) 则需要你创建一个类。

在本文中，我将演示如何用 [C++](https://www.cplusplus.com/) 在终端上实现猜谜游戏。

### 安装依赖关系

要跟上本文的步伐，你需要 C++ 和一个编译器。

在 Linux 上，你可以通过从你的发行版软件仓库中安装 Qt Creator IDE 来获得你所需要的一切。

在 Fedora、CentOS 或 RHEL 上：

```
$ sudo dnf install qt-creator

```

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

```
$ sudo apt install qtcreator

```

本文并没有使用 Qt Creator IDE，但它是一个安装你所需要的一切的简单方法，对于复杂的 C++ 项目（包括那些带有 GUI 的项目），它是一个必不可少的工具。在 macOS 或 Windows 上，按照 Qt 网站上的[安装说明](https://www.qt.io/product/development-tools)进行安装。

### 设置包含和命名空间

C++ 的核心语言是精简的。即使是一个简单的应用程序也需要使用额外的库。这个应用程序使用 [iostream](http://www.cplusplus.com/reference/iostream/) 来获得对 `cout` 和 `cin` 关键字的访问。

另外，确保程序使用 `std` 命名空间：

```
#include <iostream>

using namespace std;

```

这并不是绝对必要，但如果不将命名空间设置为 `std`，所有来自 `iostream` 库的关键字都需要一个命名空间前缀。例如，我不能写作 `cout`，而是要写作 `std::cout`。

C++ 中的语句以分号结束。

### 创建一个函数

每个 C++ 应用程序至少需要一个函数。一个 C++ 应用程序的主函数必须称为 `main`，它必须返回一个整数（`int`），这符合 [POSIX](https://opensource.com/article/19/7/what-posix-richard-stallman-explains) 的期望，即一个进程在成功时返回 0，而在失败时返回其他值。你可以通过为它提供返回类型和名称来创建一个新函数。

```
int main() {
 // code goes here
}

```

### 实现程序逻辑

游戏代码必须首先产生一个随机数供玩家猜测。在 C++ 中，你可以通过建立一个用于生成伪随机数的*种子*来实现。一个简单的种子就是当前的时间。一旦有了种子，你就可以得到一个在 1 和 100 之间的数字。通过调用 `rand` 函数，并设置上限值 100 来产生一个从 0 到 99 的随机数，所以无论选择了什么数字都要加 1，并将结果分配给一个名为 `number` 的变量。你还必须声明一个变量来保存玩家的猜测值。为了清楚起见，我称这个变量为 `guess`。

这个示例代码还包括一个调试语句，告诉你随机数到底是什么。这对于猜测游戏来说不是很好，但它使测试速度快了很多。以后，你可以删除这一行，或者直接在行前面用 `//` 注释出来：

```
 srand (time(NULL));
 int number = rand() % 100+1;
 int guess = 0;

 cout << number << endl; //debug

```

### 增加 do-while 和 if 语句

C++ 中的 `do-while` 语句以关键字 `do` 开头，并将你希望 C++ 做的所有事情用括号括起来。用 `while` 关键字结束语句，后面是必须满足的条件（括号内）：

```
do {
 // code here
} while ( number != guess );

```

游戏代码出现在 `if`、`else if` 和 `else` 语句之间，为玩家提供提示。

首先，用 `cout` 语句提示玩家猜测。`cout` 函数将输出打印到 `stdout` 上。因为 `cout` 语句没有连着 `endl`（endline）函数，所以没有换行。紧接着这个 `cout` 语句，通过使用 `cin` 函数告诉 C++ 等待输入。正如你所猜测的那样，`cin` 等待来自 `stdin` 的输入。

接下来，程序进入 `if` 控制语句。如果玩家的猜测大于 `number` 变量中包含的伪随机数，那么程序就会打印出一个提示，后面是一个换行符。这就中断了 `if` 语句，但 C++ 仍然被困在 `do-while` 循环中，因为它的条件（`number` 变量等于 `guess`）还没有满足。

如果玩家的猜测小于 `number` 变量中包含的伪随机数，那么程序就会打印出一个提示，后面是一个换行符。这再次中断了 `if` 语句，但程序仍然被困在 `do-while` 循环中。

当 `guess` 等于 `number` 时，最终满足关键条件，触发 `else` 语句，`do-while` 循环结束，程序结束：

```
do {
  cout << "Guess a number between 1 and 100: ";
  cin >> guess;

  if ( guess > number) { cout << "Too high.\n" << endl; }
  else if ( guess < number ) { cout << "Too low.\n" << endl; }
  else {
    cout << "That's right!\n" << endl;
    exit(0);
  } // fi
 } while ( number != guess );
return 0;
} // main

```

### 构建代码和玩游戏

你可以用 GCC 构建你的应用程序：

```
$ g++ -o guess.bin guess.cpp

```

运行二进制文件试试：

```
$ ./guess.bin
74
Guess a number between 1 and 100: 76
Too high.

Guess a number between 1 and 100: 1
Too low.

Guess a number between 1 and 100: 74
That's right!

```

成功了！

### 试试 C++ 吧

C++ 语言很复杂。为终端编写 C++ 应用程序可以让你学到很多关于数据类型、内存管理和代码链接的知识。试着用 C++ 编写一个有用的实用程序，看看你能发现什么！

---

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

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

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


Summary: 但如果你想在安装一个软件包之前或之后知晓这个软件包的依赖，那该怎么办呢？

但如果你想在安装一个软件包之前或之后知晓这个软件包的依赖，那该怎么办呢？

# 如何在基于 Ubuntu 或 Debian 的 Linux 发行版中查看一个软件包的依赖

在 Ubuntu 或 Debian 中通过命令行来安装应用是一件很简单的事，你只需要执行 `apt install package_name` 就可以了。

但如果你想在安装一个软件包之前或之后知晓这个软件包的依赖，那该怎么办呢？

在本教程中，我将向你展示多种方法来在 Ubuntu 或其他使用 [APT 包管理器](https://wiki.debian.org/Apt) 的 Debian 系 Linux 发行版中查看一个软件包的依赖。

### 什么是 Ubuntu 中的包依赖？

当你在 Linux 中安装一个软件包，有时这个软件包还需要其他的软件包来使它工作正常。这些额外的软件包就叫作这个包的依赖。假如这些软件包之前没有在系统中被安装，那么这些依赖在安装这个软件包的同时会被自动安装上。

举个例子，用来转换视频格式的 GUI 工具 [HandBrake](https://itsfoss.com/handbrake/) 需要 [FFmpeg](https://ffmpeg.org/)、[GStreamer](https://gstreamer.freedesktop.org/) 软件包。所以对于 HandBrake 来说， FFmpeg 和 GStreamer 就是它的包依赖。

假如在你的系统上这些软件包没有被安装，则当你 [在 Ubuntu 上安装 HandBrake](https://itsfoss.com/install-handbrake-ubuntu/) 时，就会自动安装上它们。

### 在 Ubuntu 和基于 Debian 的发行版中查看一个软件包的依赖

正如在 Linux 上经常发生的那样，有多种方法来达到相同的目标。下面让我们一起瞧瞧查看一个软件包依赖的多种方法。

#### 使用 apt show 来查看依赖

你可以使用 [apt show 命令](https://itsfoss.com/apt-search-command/) 来展示一个包的详细信息。其中依赖信息就是其中一部分，你可以在以 “Depends” 打头的那些行中看到它们。

例如，下面展示的是使用 `apt show` 展示 [ubuntu-restricted-extras](https://itsfoss.com/install-media-codecs-ubuntu/) 这个包的详细信息：

```
abhishek@itsfoss:~$ apt show ubuntu-restricted-extras
Package: ubuntu-restricted-extras
Version: 67
Priority: optional
Section: multiverse/metapackages
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 14.3 kB
Depends: ubuntu-restricted-addons
Recommends: libavcodec-extra, ttf-mscorefonts-installer, unrar
Download-Size: 3,200 B
APT-Manual-Installed: yes
APT-Sources: http://us.archive.ubuntu.com/ubuntu focal/multiverse amd64 Packages
Description: Commonly used media codecs and fonts for Ubuntu
 This collection of packages includes:
  - MP3 and other audio codec software to play various audio formats
    (GStreamer plugins)
  - software to install the Microsoft Web fonts
  - the Adobe Flash plugin
  - LAME, software to create compressed audio files.
 .
 This software does not include libdvdcss2, and will not let you play
 encrypted DVDs. For more information, see
 https://help.ubuntu.com/community/RestrictedFormats/PlayingDVDs
 .
 These software packages are from the Multiverse channel, restricted by
 copyright or legal issues in some countries. For more information, see
 http://www.ubuntu.com/ubuntu/licensing

```

如你所见，`ubuntu-restricted-extras` 包依赖于 `ubuntu-restricted-addons` 这个软件包。

但你得小心的是依赖包还可能依赖于其他包，这样一直循环往复直到尽头。但幸好 APT 包管理器可以为你处理这些复杂的依赖关系，自动地安装所有的依赖（大多数情况下）。

>
> **什么是推荐包？**
>
>
> 你注意到了上面结果输出中以 “Recommends” 开头的那些行了吗？
>
>
> 推荐包不是软件包的直接依赖，但它们可以开启软件包的一些额外功能。
>
>
> 正如你上面看到的那样， `ubuntu-restricted-extras` 包有 `ttf-mscorefonts-installer` 这个推荐包，用来在 Ubuntu 上安装 Microsoft 的字体。
>
>
> 这些推荐包也会默认被一同安装上，假如你想显式地禁止这些推荐包的安装，你可以像下面这样使用 `–-no-install-recommends` 选项。
>
>
>
> ```
> sudo apt install --no-install-recommends package_name
>
> ```
>
>

#### 使用 apt-cache 来直接获取依赖信息

上面通过 `apt show` 的方式会获取到大量信息，假如你想在脚本中获取到依赖信息，那么 `apt-cache` 命令将会给你一个更好且更简洁的输出结果。

```
apt-cache depends package_name

```

下面的输出看起来更加干净，不是吗？

#### 使用 dpkg 来查看一个 DEB 文件的依赖

`apt` 和 `apt-cache` 都作用于软件仓库中的软件包，但假如你下载了一个 DEB 文件，那么这两个命令就不起作用了。

在这种情形下，你可以使用 `dpkg` 命令的 `-I` 或 `--info` 选项。

```
dpkg -I path_to_deb_file

```

依赖信息就可以在以 “Depends” 开头的那些行中找到。

#### 使用 apt-rdepends 来查看依赖及依赖的依赖

假如你想查看更多关于依赖的信息，那么你可以使用 `apt-rdepends` 工具。这个工具可以创建完整的依赖树。这样你就可以得到一个软件包的依赖以及这些依赖的依赖。

它不是一个常规的 `apt` 命令，所以你需要从 universe 软件仓库中安装上它：

```
sudo apt install apt-rdepends

```

这个命令的输出通常很多，取决于依赖树的大小。

```
Reading package lists... Done
Building dependency tree
Reading state information... Done
shutter
  Depends: procps
  Depends: xdg-utils
imagemagick
  Depends: imagemagick-6.q16 (>= 8:6.9.2.10+dfsg-2~)
imagemagick-6.q16
  Depends: hicolor-icon-theme
  Depends: libc6 (>= 2.4)
  Depends: libmagickcore-6.q16-6 (>= 8:6.9.10.2)
  Depends: libmagickwand-6.q16-6 (>= 8:6.9.10.2)
hicolor-icon-theme
libc6
  Depends: libcrypt1 (>= 1:4.4.10-10ubuntu4)
  Depends: libgcc-s1
libcrypt1
  Depends: libc6 (>= 2.25)

```

`apt-rdepends` 工具的功能非常多样，它还可以用来计算反向依赖。这意味着你可以查看某个特定的包被哪些软件包依赖。

```
apt-rdepends -r package_name

```

输出可能会非常多，因为它将打印出反向依赖树。

```
abhishek@itsfoss:~$ apt-rdepends -r ffmpeg
Reading package lists... Done
Building dependency tree
Reading state information... Done
ffmpeg
  Reverse Depends: ardour-video-timeline (>= 1:5.12.0-3ubuntu4)
  Reverse Depends: deepin-screen-recorder (5.0.0-1build2)
  Reverse Depends: devede (4.15.0-2)
  Reverse Depends: dvd-slideshow (0.8.6.1-1)
  Reverse Depends: green-recorder (>= 3.2.3)

```

我希望这个快速的教程可以帮助你提高一点儿你的命令行知识。为了知晓更多类似小知识点，请保持关注。

---

via: <https://itsfoss.com/check-dependencies-package-ubuntu/>

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

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


用 Loki 和 fzf 进阶你的 Shell 历史记录

Summary: Loki 扩展了 Prometheus 用于度量监测和日志聚合的模型。

太重了

Loki 扩展了 Prometheus 用于度量监测和日志聚合的模型。

# 用 Loki 和 fzf 进阶你的 Shell 历史记录

>
> Loki 扩展了 Prometheus 用于度量监测和日志聚合的模型。
>
>
>

[Loki](https://github.com/grafana/loki) 是一个 Apache 2.0 许可的开源日志聚合框架，由 Grafana 实验室设计，并在不断发展的社区的巨大支持之下建立。它也是我每天为之努力的项目。在这篇文章中，我将不只是谈论 Loki 如何工作，而是提供一个实际操作的介绍，以解决实际问题。

### 问题：一个持久的集中式 Shell 历史记录

我喜欢我的 shell 历史，一直是 `CTRL+R` 的狂热用户。大约一年前，我的终端生活发生了翻天覆地的变化，我的同行 Dieter Plaetinck 向我介绍了命令行模糊查找器 [fzf](https://github.com/junegunn/fzf)。

突然间，在命令中搜索就从这样：

变成了这样：

虽然 `fzf` 极大地提高了我的生活质量，但围绕着我的 shell 历史记录，还是缺少了一些片段：

* 终端突然关闭、电脑崩溃、死机、整盘加密密钥被遗忘等情况下会丢失 shell 历史记录。
* 想从我的所有电脑上访问我的 shell 历史记录。

我认为我的 shell 历史记录是文件：它是一个重要的故事，我不想失去。将 Loki 与我的 shell 历史结合起来，有助于解决这些问题和更多问题。

### 关于 Loki

Loki 采用了开源 [Prometheus](https://prometheus.io/) 项目用于度量的直观的标签模型，并将其扩展到日志聚合的世界。这使得开发人员和运维人员能够使用相同的标签集在他们的度量和日志之间无缝切换。即使你没有使用 Prometheus，也有很多理由说明 Loki 可能很适合你的日志存储需求：

* **低开销：** Loki 不做全文日志索引；它只创建你放在日志上的标签的索引。保持小的索引大大降低了 Loki 的运维要求。我在 [树莓派](https://www.raspberrypi.org/) 上运行我的 loki-shell 项目，该项目使用 Loki 来存储 shell 历史记录，只使用了 50MB 多一点的内存。
* \*成本低：\*\*日志内容被压缩并存储在对象存储中，如 Amazon S3、Google 云存储、Azure Blob，甚至可以直接存储在文件系统中。我们的目标是使用价格低廉且持久的存储。
* **灵活性：** Loki 以单个二进制文件的形式提供，可以直接下载并运行，也可以作为 Docker 镜像在任何容器环境中运行。在 Kubernetes 中可以用一个 [Helm 海图](https://helm.sh/docs/topics/charts/) 快速上手。如果你对日志工具的要求很高，可以看看运行在 Grafana 实验室的 [生产环境](https://grafana.com/docs/loki/latest/installation/tanka/)。它使用开源的 [Jsonnet](https://jsonnet.org) 和 [Tanka](https://tanka.dev/) 部署了同样的 Loki 镜像作为离散的构件，以实现大规模的水平扩展、高可用性、复制、读写路径的分别扩展、高度可并行的查询等。

总而言之，Loki 的方法是保留一个关于你的日志元数据的小索引（标签），并将未索引的、压缩的日志内容存储在廉价的对象存储中，以使操作更容易和更便宜。该应用程序被构建为单进程运行，并很容易演变成一个高可用的分布式系统。你可以通过并行化和查询的分片，在较大的日志工作负载上获得较高的查询性能 —— 有点像为你的日志设计的 MapReduce。

此外，这个功能是任何人都可以免费使用的。与其 [Grafana](https://grafana.com/) 开放观测性平台一样，Grafana 实验室致力于将 Loki 打造成一个任何人都可以使用的全功能、全开放的日志聚合软件。

### 开始吧

我在树莓派上运行 Loki，并将我的 shell 历史记录异地存储在 S3 bucket 中。

当我按下 `CTRL+R`，Loki 的 [LogCLI](https://grafana.com/docs/loki/latest/getting-started/logcli/) 命令行界面会发起几个批处理请求，传输至 `fzf`。下面是一个例子，上半部分显示的是树莓派上的 Loki 服务器日志。

准备试试？下面的指南将帮助你设置和运行 Loki，与你的 shell 历史记录集成。为了让本教程保持简洁，此设置将 Loki 本地运行在你的计算机上，并在文件系统上存储所有文件。

在 [loki-shell 的 GitHub 版本库](https://github.com/slim-bean/loki-shell)，你可以找到所有这一切，以及如何设置一个更复杂的安装的信息。

请注意，本教程不会改变任何围绕你的历史记录的现有行为，所以 *你现有的 shell 历史记录命令和历史记录设置不会被触动*。相反，这将用 Bash 中的 `$PROMPT_COMMAND` 和 Zsh 中的 `precmd` 复制命令历史记录到 Loki。在 `CTRL+R` 方面，它重载了 `fzf` 用来访问 `CTRL+R` 命令的函数。因此试一试是安全的，如果你觉得不喜欢它，只需按照 GitHub 版本库中的 [卸载步骤](https://github.com/slim-bean/loki-shell/blob/master/uninstall) 来删除所有痕迹。你的 shell 历史记录不会被触及。

#### 第一步：安装 fzf

安装 `fzf` 有几种方法，但我更喜欢 [Git 方法](https://github.com/junegunn/fzf#using-git)：

```
git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
~/.fzf/install

```

对所有的问题提示说 `yes`。

如果你已经安装了 `fzf`，确保你已经启用了键绑定（即，确保当你输入 `CTRL+R` 时，`fzf` 会弹出）。如果有必要的话，你可以重新运行 `fzf` 安装过程来启用键绑定。

#### 第二步：安装 loki-shell

和 `fzf` 一样，loki-shell 也有一个 Git 版本库和安装脚本：

```
git clone --depth 1 https://github.com/slim-bean/loki-shell.git ~/.loki-shell
~/.loki-shell/install

```

首先，该脚本创建了 `~/.loki-shell` 目录，所有的文件都将保存在该目录下（包括 Loki 数据），接下来，它将下载 [Promtail](https://grafana.com/docs/loki/latest/clients/promtail/)、LogCLI 和 Loki 的二进制文件。

然后它会问：

```
Do you want to install Loki? ([y]/n)

```

如果你已经为 Loki-shell 运行了一个集中化的 Loki，你可以回答 `n`；然而，对于本教程，回答 `y` 或按回车键。

在本地运行 Loki 有两种方式：作为一个 Docker 镜像或作为一个单一的二进制文件（支持添加为 systemd 服务）。如果可以，我建议使用 Docker，因为我认为它稍微简化了操作，但这两者都可以工作。

##### 使用 Docker 运行

将 Loki 作为 Docker 镜像运行：

```
[y] to run Loki in Docker, [n] to run Loki as a binary ([y]/n) y
Error: No such object: loki-shell
Error response from daemon: No such container: loki-shell
Error: No such container: loki-shell
54843ff3392f198f5cac51a6a5071036f67842bbc23452de8c3efa392c0c2e1e

```

如果这是你第一次运行这个安装程序，你可以忽略错误信息。这个脚本将停止和替换运行的 Loki 容器，如果版本不匹配，你可以重新运行此脚本升级 Loki。

就是这样！Loki 现在作为一个 Docker 容器运行了。

Loki 的数据将存储在 `~/.loki-shell/data` 中。

由于带着 `-restart=unless-stopped` 标志运行该镜像，所以它会在系统重启时重启该服务，但如果你运行 `docker stop loki-shell` 则会保持停止。

（如果你使用的是 Docker，你可以跳到 “Shell 集成”一节。）

##### 以二进制文件运行

在 Linux 系统上运行二进制文件的方法有很多。这个脚本可以安装一个 systemd 服务。如果你没有 systemd，你也可以使用二进制安装：

```
[y] to run Loki in Docker, [n] to run Loki as a binary ([y]/n) n

Run Loki with systemd? ([y]/n) n

This is as far as this script can take you
You will need to setup an auto-start for Loki
It can be run with this command: /home/username/.loki-shell/bin/loki -config.file=/home/username/.loki-shell/config/loki-binary-config.yaml

```

脚本会输出你需要用来运行 Loki 的命令，你可以自己设置一个 init 脚本或其他方法来自动启动它。

如果你想的话，你可以直接运行该命令，从你当前的 shell 运行 Loki。

如果你有 systemd，你可以选择让脚本安装 systemd 服务或显示出你自己运行它的命令：

```
Run Loki with systemd? ([y]/n) y

Installing the systemd service requires root permissions.
[y] to run these commands with sudo [n] to print out the commands and you can run them yourself. ([y]/n) n
sudo cp /home/ed/.loki-shell/config/loki-shell.service /etc/systemd/system/loki-shell.service
sudo systemctl daemon-reload
sudo systemctl enable loki-shell
sudo systemctl start loki-shell
Copy these commands and run them when the script finishes. (press enter to continue)

```

##### Shell 集成

无论你如何安装 Loki，你现在应该看到一个提示：

```
Enter the URL for your Loki server or press enter for default (http://localhost:4100)

```

如果你已经设置了一个中心化的 Loki，你应在这里输入其 URL。然而，这个演示只是使用了默认的 URL，所以你可以按回车键。

它会输出很多文本来解释添加到你的 `~.bashrc` 或 `~.zshrc`（或两者）的所有条目。

好了！

```
Finished. Restart your shell or reload config file.
   source ~/.bashrc  # bash
   source ~/.zshrc   # zsh

```

#### 第三步：试试吧!

开始使用你的 shell，并使用 `CTRL+R` 查看你的命令。

打开多个终端窗口，在一个窗口中输入命令，在另一个窗口中输入 `CTRL+R`，你会看到你的命令立即可用。

另外，请注意，当你在终端之间切换并输入命令时，使用 `CTRL+R` 可以立即使用它们，但向上箭头的操作在终端之间不受影响。（如果你安装了 Oh My Zsh，情况可能就不一样了，因为它会自动将所有命令追加到历史记录中。）

多次按下 `CTRL+R` 可以在按时间排序和按相关性排序之间切换。

请注意，此配置将只显示当前主机的查询历史记录，即使你正在从多个主机向 Loki 发送 shell 数据。我认为默认情况下这是最合理的。如果你想改变这种行为，有很多地方可以调整；请参见 loki-shell 版本库了解更多。

它还安装了一个名为 `hist` 的别名。

```
alias hist="$HOME/.loki-shell/bin/logcli --addr=$LOKI_URL"

```

LogCLI 可以用来直接在 Loki 上查询和搜索你的历史，也允许你搜索其他主机。查看 LogCLI 的入门指南，了解更多关于查询的信息。

Loki 的日志查询语言（LogQL）提供了度量查询，可以让你做一些有趣的事情，例如，我可以看到在过去 30 天里我发出了多少次 `kc` 命令（我对 `kubectl` 的别名）。

### 额外增强

安装 Grafana，摆弄一下你的 shell 历史记录。

```
docker run -d -p 3000:3000 --name=grafana grafana/grafana

```

打开 Web 浏览器，访问 `http://localhost:3000`，使用默认的 `admin`/`admin` 用户名和密码登录。

在左边，导航到“<ruby> 配置 <rt>  Configuration </rt></ruby>-><ruby> 数据源 <rt>  Datasources </rt></ruby>”，点击“<ruby> 添加数据源 <rt>  Add Datasource </rt></ruby>”按钮，然后选择 “Loki”。

对于 URL，你应该可以使用 `http://localhost:4100`（然而，在我的 WSL2 机器上，我必须使用计算机的实际 IP 地址）。

单击“<ruby> 保存并测试 <rt>  Save and Test </rt></ruby>”。你应该看到连接了数据源并找到了标签。

点击左边的“<ruby> 管理器 <rt>  Explore </rt></ruby>”图标，确保选择 Loki 数据源，并尝试这个查询：

```
{job="shell"}

```

如果发送 shell 命令的主机较多，可以使用“<ruby> 主机 <rt>  Host </rt></ruby>”标签将结果限制在某个主机上：

```
{job="shell", hostname="myhost"}.

```

你也可以用过滤表达式寻找特定的命令：

```
{job="shell"} |= "docker"

```

或者你可以从日志中探索度量的世界，看看你使用 shell 的频率：

```
rate({job="shell"}[1m])

```

想从一个事件中重建一个时间线？你可以通过特定的命令进行过滤，查看它的运行时间：

要想知道你还能做什么，并了解更多关于 Loki 查询语言的信息，请查看 LogQL 指南。

### 总结

更多的想法、故障排除和更新，请关注该 GitHub 版本库。这仍然是一项正在进行中的工作，所以请在那里报告发现的任何问题。

要了解更多关于 Loki 的信息，请查看文档、博客文章和该 GitHub 版本库，或者在 Grafana Cloud 中试用。

---

特别感谢我的同事 Jack Baldry 为这个想法播下的种子。我有 Loki 的知识来实现这个想法，但如果不是他的建议，我想我永远也不会做到这一点。

---

via: <https://opensource.com/article/20/10/shell-history-loki-fzf>

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


使用 tqdm 在 Python 应用中显示进度

Summary: 如果你的程序需要一段时间才能显示结果，可通过显示它的进度来避免让用户感到沮丧。

如果你的程序需要一段时间才能显示结果，可通过显示它的进度来避免让用户感到沮丧。

# 使用 tqdm 在 Python 应用中显示进度

>
> 如果你的程序需要一段时间才能显示结果，可通过显示它的进度来避免让用户感到沮丧。
>
>
>

阿拉米语，希伯来语和阿拉伯语中的闪米特语根 *q-d-m* 通常与前进或进度有关。阿拉伯语 *taqaddum* （تقدّم）的意思是“进度”。进度是很重要的。正如每部感觉良好的电影都会告诉你，旅程和目的地同样重要。

大多数程序都有一个明确的目标，一个期望的最终状态。有时，计算这个最终状态可能需要很长的时间。虽然计算机没有感情不在乎，但人却在乎。人类并不乐意坐在原地等待，而看不到任何明显的进展迹象。疑问不断蔓延。程序崩溃了吗？磁盘性能是否抖动？操作系统是否把所有的计算资源都分配给了其他任务？

就像正义一样，进度必须被看到，而不仅仅是完成。Python 库 [tqdm](https://pypi.org/project/tqdm/) 有助于使进度变得明确。

`tqdm` 模块可在控制台下工作，但它也专门支持了我最喜欢的环境之一 Jupyter。要在 Jupyter 中使用 `tqdm`，你需要导入 `notebook` 子模块并安装 [ipywidgets](https://opensource.com/article/20/11/daily-journal-jupyter)。`notebook` 子模块与 `tqdm` 接口兼容。

这意味着你可以做一些导入时操作来导入正确的模块，同时保持 `tqdm` 的用法不变。诀窍是检查 `__main__` 模块是否具有全局变量 `get_ipython`。虽然这只是一个启发式的方法，但却是一个相当准确的方法：

```
import sys
if hasattr(sys.modules["__main__"], "get_ipython"):
    from tqdm import notebook as tqdm
else:
    import tqdm

```

最简单的情况是，某件事情需要运行一定的迭代次数（事先已知），而每一次迭代的时间都差不多。例如，有一个计算任何数字的平方根的算法，通过从 1 作为猜测值开始，然后计算出一个改进后的猜测值：

```
def improve_guess(rt, n):
    return (rt + n/rt) / 2

```

一点点的改进可以让你更加接近该平方根。例如，你可以计算 2 的平方根：

```
guess = 1
target = 2
for i in tqdm.trange(10):
    guess = improve_guess(guess, target)

```

精确了到小数点后 10 位！

```
round(2 - guess*guess, 10)

```

```
0.0

```

一个稍微复杂一点的例子是，当元素的数量是已知的，而处理每个元素需要类似的时间。例如，你可以计算一些数字的乘积。为此，你需要一些随机数：

```
import random
numbers = [random.uniform(0, 2.8) for i in range(100)]
numbers[:5]

```

```
[2.6575636572230916,
0.1286674965830302,
1.0634250104041332,
1.1760969844376505,
0.45192978568125486]

```

现在有了这些数字，可以将它们相乘了。使用 `tqdm` 最简单的方法是包装一个 Python 迭代函数。数值是一样的，但是 `tqdm` 会显示一个进度条：

```
result = 1
for num in tqdm.tqdm(numbers):
    result *= num
result

```

```
2.4081854901728303

```

然而，并不是所有的事情都可以预测。最不容易预测的事情之一就是网络速度。当你下载一个大文件时，衡量进度的唯一方法就是检查已经下载了多少：

```
url = "https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz"
import httpx
with httpx.stream("GET", url) as response:
    total = int(response.headers["Content-Length"])
    with tqdm.tqdm(total=total) as progress:
        for chunk in response.iter_bytes():
            progress.update(len(chunk))

```

有时，“嵌套”进度条是有意义的。例如，如果你要下载一个目录，你就需要一个进度条来跟踪文件，并为每个文件设置一个进度条。

下面是一个例子（但没有实际下载一个目录）：

```
files = [f"vid-{i}.mp4" for i in range(4)]
for fname in tqdm.tqdm(files, desc="files"):
    total = random.randrange(10**9, 2 * 10**9)
    with tqdm.tqdm(total=total, desc=fname) as progress:
        current = 0
        while current < total:
            chunk_size = min(random.randrange(10**3, 10**5), total - current)
            current += chunk_size
            if random.uniform(0, 1) < 0.01:
                time.sleep(0.1)
            progress.update(chunk_size)

```

所以，如果你的程序需要一段时间才能显示最终结果，为避免让你的用户感到沮丧。请显示它的进度！

---

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

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

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


学习使用 Sed 文本编辑器

Summary: Sed 缺少通常的文本框，而是按照用户的命令直接写入到文件上。

这可能正是用S语言实现的jed为什么不叫sed的原因

Sed 缺少通常的文本框，而是按照用户的命令直接写入到文件上。

# 学习使用 Sed 文本编辑器

>
> Sed 缺少通常的文本框，而是按照用户的命令直接写入到文件上。
>
>
>

<img src="content_image/album_202101_07_002353st8vgivu78yzp77v.jpg" alt="" title="">

`sed` 命令是为 AT&T 最初的 Unix 操作系统第 7 版创建的，此后，可能每一个 Unix 和 Linux 操作系统都包含了它。`sed` 应用程序是一个 *流编辑器*，与文本编辑器不同的是，它不会打开一个视觉缓冲区，将文件的数据加载到其中进行处理。相反，它根据在终端输入的命令或脚本中的一系列命令，逐行对文件进行操作。

### 安装

如果你使用的是 Linux、BSD 或 macOS，那么你已经安装了 GNU 或 BSD 版的 `sed`。这是两个不同的原始 `sed` 命令的重新实现，虽然它们很相似，但也有一些小的区别。GNU `sed` 通常被认为是功能最丰富的 `sed`，而且它在这些平台上都可以广泛使用。

如果你找不到 GNU `sed`（在非 Linux 系统上通常被称为 `gsed`），那么你可以[从 GNU 网站上下载它的源代码](http://www.gnu.org/software/sed/)。安装 GNU `sed` 的好处是，可以使用它的额外功能，但它也可以被限制为只符合 `sed` 的 [POSIX](https://opensource.com/article/19/7/what-posix-richard-stallman-explains) 规范，如果你需要移植性的话。

在 Windows 上，你可以用 [Chocolatey](https://opensource.com/article/20/3/chocolatey) 来[安装](https://chocolatey.org/packages/sed) GNU `sed`。

### Sed 如何工作

`sed` 应用程序一次只处理一行。因为它没有视觉显示，所以它在内存中创建了一个模式空间：一个包含输入流的当前行的空间（去掉任何尾部的换行符）。一旦填充了模式空间，你对 `sed` 的指令就会被执行。有时你的指令是有条件的，有时是无条件的，所以这些指令的结果取决于你如何使用 `sed`。

当命令结束时，`sed` 会将模式空间的内容打印到输出流中。默认的输出流是**标准输出**，但可以将其重定向到一个文件，甚至使用 `--in-place=.bak` 选项重定向到同一个文件中。

然后再从下一个输入行开始循环。

`sed`命令的语法是：

```
$ sed --options [optional SCRIPT] [INPUT FILE or STREAM]

```

#### 找到你要编辑的内容

在可视化编辑器中，你通常不需要考虑太多，就能在文本文件中找到你想要修改的内容。你的眼睛（或屏幕阅读器）会扫描文本，找到你想改变的单词或你想插入或删除文本的地方，然后你就可以开始输入了。而 `sed` 没有交互模式，所以你需要告诉它必须满足什么条件才能运行特定的命令。

在这些例子中，假设一个名为 `example.txt` 的文件包含了这样的文字：

```
hello
world
This is line three.
Here is the final line.

```

#### 行号

指定行号告诉 `sed` 只对文件中的那一行进行操作。

例如，下面这条命令选择文件的第 1 行并打印出来。因为 `sed` 在处理后的默认操作也是打印一行到**标准输出**，这样做的效果就是重复第一行：

```
$ sed '1p' example.txt
hello
hello
world
This is line three.
Here is the final line.

```

你也可以步进式指定行号。例如，`1~2` 表示每两行选择一行（“从第一行开始每两行选择一行”）。指令 `1~3` 表示从第一行开始，每三行选择一行：

```
$ sed '1p' example.txt
hello
hello
world
This is line three.
Here is the final line.
Here is the final line.

```

#### 行定位

你可以通过使用 `$` 作为选择器，只对文件的最后一行进行操作：

```
$ sed '$p' example.txt
hello
world
This is line three.
Here is the final line.
Here is the final line.

```

在 GNU `sed` 中，你可以选择多行（例如，`sed '1,$p'` 打印第一行和最后一行）。

#### 反转

任何数字或位置的选择，你都可以用感叹号（`!`）字符反转。下面这将选择除第一行以外的所有行：

```
$ sed '1!p' example.txt
hello
world
world
This is line three.
This is line three.
Here is the final line.
Here is the final line.

```

#### 模式匹配

你可以把模式匹配想象成文字处理器或浏览器中的**查找**操作。你提供一个词（一个 *模式*），然后选择了结果。模式匹配的语法是 `/pattern/`：

```
$ sed '/hello/p' example.txt
hello
hello
world
This is line three.
Here is the final line.
$ sed '/line/p' example.txt
hello
world
This is line three.
This is line three.
Here is the final line.
Here is the final line.

```

### 用 Sed 编辑

一旦你找到了你要编辑的内容，你就可以执行你想要的任何操作。你可以用 `sed` 中的命令来执行编辑。`sed` 中的命令不是 `sed` 命令本身。如果这样说有帮助的话，可以把它们看作是“动作”或“动词”或“指令”。

`sed` 中的命令是单个字母，例如前面例子中使用的**打印**命令的 `p`。它们一开始可能很难记忆，但和所有事情一样，你会随着练习而了解它们。

#### p 代表打印

`p` 指令打印当前模式空间中的任何内容。

#### d 用于删除

`d` 指令删除模式空间：

```
$ sed '$d' example.txt
hello
world
This is line three.
$ sed '1d' example.txt
world
This is line three.
Here is the final line.

```

#### s 用于搜索和替换

`s` 命令搜索一个模式并将其替换为其他东西。这可能是 `sed` 最流行和最随意的用法，而且它通常是用户学习的第一个（有时也是唯一的）`sed` 命令。几乎可以肯定它是文本编辑中最有用的命令：

```
$ sed 's/world/opensource.com/' example.txt
hello
opensource.com
This is line three.
Here is the final line.

```

在你的替换文本中，也可以使用一些特殊的功能。例如，`\L` 将替换文本转换为小写，`\l` 则只转换下一个字符。还有其他一些功能，列在 `sed` 文档中（你可以用 `info sed` 命令查看）。

替换子句中的特殊字符 `&` 指的是匹配到的模式：

```
$ sed 's/is/\U&/' example.txt
hello
world
ThIS is line three.
Here IS the final line.

```

你也可以通过特殊的标志来影响 `s` 如何处理它找到的内容。`g`（应该是指 *全局*）标志告诉 `s` 对行上找到的所有匹配项进行替换，而不仅仅是第一个匹配项：

```
$ sed 's/is/\U&/g' example.txt
hello
world
ThIS IS line three.
Here IS the final line.

```

其他重要的标志还包括用一个数字来表示要影响第几个出现的匹配模式：

```
$ sed 's/is/\U&/2' example.txt
hello
world
This IS line three.
Here is the final line.

```

`w` 标志，后面跟着一个文件名，\_只有\_在有变化的情况下，才会将匹配的行写入文件：

```
$ sed 's/is/\U&/w sed.log' example.txt
hello
world
ThIS is line three.
Here IS the final line.
$ cat sed.log
ThIS is line three.
Here IS the final line.

```

标志可以组合：

```
$ sed 's/is/\U&/2w sed.log' example.txt
hello
world
This IS line three.
Here is the final line.
$ cat sed.log
This IS line three.

```

### 脚本

有很多很棒的网站都有 `sed` “单行脚本”，它们给你提供了面向任务的 `sed` 命令来解决常见的问题。然而，自己学习 `sed` 可以让你写出自己的单行脚本，而且这些单行脚本可以根据你的具体需求来定制。

`sed` 的脚本可以在终端中写成一行，也可以保存到文件中，然后用 `sed` 本身执行。我倾向于把小脚本写成一个命令，因为我发现自己在现实生活中很少重复使用 `sed` 命令。当我写一个 `sed` 脚本时，通常都是针对一个文件的。例如，在写完这篇文章的初稿后，我用 `sed` 来规范 “sed” 的大小写，而这是我可能永远也不会再做的任务。

你可以向 `sed` 发出一系列不同的命令，用分号（`;`）分开。

```
$ sed '3t ; s/line/\U&/' example.txt
hello
world
This is LINE three.
This is the final line.

```

### 带括号的范围改变

你也可以用大括号（`{}`）限制哪些结果受到影响。当你将 `sed` 命令用大括号括起来时，它们只适用于特定的选择。例如，“line” 字出现在样本文本的两行中。你可以通过声明所需的匹配条件（`$` 表示最后一行），并将你希望执行的 `s` 命令放在紧随其后的括号中，强制 `sed` 只影响最后一行：

```
$ sed '$ {s/line/\U&/}' example.txt
hello
world
This is line three.
This is the final LINE.

```

### 学习 Sed

你可以用 `sed` 做的事情比本文所解释的多得多。我甚至还没有涉及到分支（`b`）、测试（`t`）、保留空格（`H`）和许多其他功能。就像 [ed](https://opensource.com/article/20/12/gnu-ed) 一样，`sed` 可能不是你要用来创建文档的文本编辑器，甚至不是你需要做的每一个脚本任务中使用的文本编辑器，但它是你作为 POSIX 用户的一个强大的选择。学习 `sed` 命令的结构以及如何编写简短的脚本可以快速修改大量的文本。阅读 GNU `sed` 的`info` 页面，或者 BSD `sed` 的手册页，看看 `sed` 能为你做什么。

---

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

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

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


学习在 Linux 上使用 JOE 文本编辑器

bisqwit直播写代码的时候用的就是JOE

# 学习在 Linux 上使用 JOE 文本编辑器

>
> Joe's Own Editor （JOE）是一款简单易学和使用的通用文本编辑器。
>
>
>

我很喜欢那些能让你轻松学会如何使用的文本编辑器。GNU nano 是我最喜欢的例子：你启动 nano，你会在整个会话的窗口底部看到一个最常用的命令列表。Joe's Own Editor（简称 `joe`）是另一个很好的例子。

`joe` 编辑器使用了一个基于终端的界面，提供了简单的文本编辑功能和一个易于访问的帮助屏幕。它用 C 语言编写，体积小，只对 libc 有硬性依赖（ncurses 是可选的），并且采用 GPL 许可证。

### 安装

在 Linux 上，你也许能在你的发行版软件仓库中找到 JOE。虽然它有点小众，并不是所有的发行版都打包了它。如果是这样的话，你可以从 [SourceForge](https://joe-editor.sourceforge.io/) 下载源码，然后自己编译。这是个简单的过程：

```
$ ./configure
$ make
$ sudo make install

```

### 使用 JOE

据其作者介绍，JOE 的灵感来自于一个叫 WordStar 的应用和 [GNU Emacs](https://opensource.com/article/20/12/emacs)。它的大部分基本编辑键都与 WordStar 的快捷键相同，编辑器本身也在努力向 WordStar 靠拢。JOE 也有 GNU Emacs 的一些键绑定和功能。这种两种灵感来源的混合有时会让人迷惑，但话又说回来，在任何情况下，让自己离开 Emacs（或你通常选择的文本编辑器）都会让人迷惑。重要的是，JOE 提供了帮助，而且很容易访问。

在 JOE 界面右上角，有一个持续的提示，你可以按 `Ctrl+K`，紧接着按 `H` 来查看帮助界面。这是一个切换键，所以一旦你激活它，帮助屏幕就会一直显示在你的编辑器窗口顶部，直到用同样的组合键（JOE 中的键盘符号为 `^KH`）解除。

### 键盘快捷键

尽管 JOE 的作者在模拟 WordStar 用户体验方面非常自豪，但我不得不承认，这对我来说是失落的。我从来没有听说过 WordStar，直到我在 JOE 的文档中读到了它，在我看来，它的键盘快捷键方案完全是任意的。有的用 `Ctrl+K` 作为转义序列的前缀，有的用 `Esc` 作为前缀，还有的完全不需要转义序列。我无法确定其中的逻辑。编辑操作和应用选项一样，都有可能需要或不需要转义序列，而且字母关联对我来说是没有什么意义（例如，`Ctrl+K D` 代表**另存为**）。

幸运的是，JOE 可以让你在键盘快捷键上作弊。安装后，JOE 会悄悄为 `joe` 可执行文件创建一些符号链接。包括：

* `jmacs` JOE 使用 Emacs 键绑定
* `jpico` JOE 使用 Pico （或 GNU nano） 键绑定
* `rjoe` JOE 的编辑只限于启动时传递给 JOE 的文件
* `jstar` JOE 使用 WordStar 键绑定

持久保留的帮助菜单仍然存在，所以如果你不清楚有哪些功能可以使用，可以通过屏幕右上方的帮助提示来参考。当然，在 Pico/nano 模式下，主要命令总是可见的。

### 尝试 JOE

JOE 并不是一个你永远不会离开的文本编辑器，它不会成为你的 IDE、电子邮件客户端、网络浏览器和文件管理器。事实上，它甚至可能不是你所有任务的主要文本编辑器。它专注于做好一件事，那就是成为一般的文本编辑。

JOE 拥有你所需要的所有基本功能，比如能够快速浏览你的文本、能够选择文本、复制和粘贴等等。试试 JOE 吧，使用你喜欢的符号链接。

---

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

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

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


Git 入门：术语基础

Summary: 想学习 Git？看看这个最重要的术语和命令的快速总结。

想学习 Git？看看这个最重要的术语和命令的快速总结。

# Git 入门：术语基础

>
> 想学习 Git？看看这个最重要的术语和命令的快速总结。
>
>
>

如今，对于任何希望跟踪他们的变化的人来说，版本控制是一个重要的工具。它对程序员、系统管理员和<ruby> 网站可靠性工程师 <rt>  site reliability engineers </rt></ruby>（SRE）都特别有用。确保可以从错误中恢复到已知的良好状态是一个巨大的胜利，比以前给复制的文件添加 `.old` 后缀的策略更友好。

但学习 Git 这件事往往被告诉大家“投身开源”的好心同行们过度简化了。在你还不明白之前，就有人要你给一个从<ruby> 上游 <rt>  upstream </rt></ruby> <ruby> 变基 <rt>  rebase </rt></ruby>的<ruby> 拉取请求 <rt>  pull request </rt></ruby>（PR）或<ruby> 合并请求 <rt>  merge request </rt></ruby>（MR），然后他们才能从你的<ruby> 远程版本库 <rt>  remote </rt></ruby>合并 —— 而且一定会删除<ruby> 合并提交 <rt>  merge commits </rt></ruby>。无论你想给开源项目做出什么好的贡献，当你看到这些你不认识的单词时，都会觉得难以融入。

* [下载](https://opensource.com/downloads/cheat-sheet-git) 我们的 Git 速查表。

如果你有一两个月的时间和足够的好奇心，[Git SCM](https://git-scm.com/about) 是你需要学习所有术语的权威来源。但如果你正在寻找来自实践的总结，请继续阅读。

### 提交就是提醒

对我来说，Git 最难理解的部分是 Git 最简单的概念：一个<ruby> 提交 <rt>  commit </rt></ruby>就是一个内容的集合，包括一个关于描述的信息，以及之前的提交。没有固有的代码发布策略，甚至没有内置的明确建议。这个内容甚至不一定是代码 —— 可以是*任何*你想添加到版本库的东西。<ruby> 提交消息 <rt>  commit message </rt></ruby>会对这些内容进行注释。

我喜欢把提交信息看作是给未来的自己的礼物：它可能会提到你编辑的文件，但更重要的是它提醒你修改这些文件的意图。添加更多关于你为什么编辑这些内容的信息，可以帮助任何使用你的版本库的人，即使那个人是你。

### origin/master 在哪里？

要知道自己在 Git 项目中的位置，首先把它想成一棵树。所有 Git 项目都有一个根目录，类似于文件系统的根目录。所有的提交都是这个根目录下的分支。这样一来，分支只是一个提交的指针。按照惯例，`master` 是根目录下默认的分支名称。（LCTT 译注：世界变得快，原文发表于 2019 年，而现在有些地方开始用 `main` 替代这个名字。）

由于 Git 是一个分布式的版本控制系统，同一个代码库分布在多个地方，所以人们经常用<ruby> 版本库 <rt>  repository </rt></ruby>这个词来表示同一个项目的所有副本。（LCTT 译注：“repository” 英文原意是仓库、存储库，在计算机环境中，常用于版本控制、软件分发等方面，有时候会统一译作“仓库”、“存储库”。但我们认为，应该根据不同语境采用更有指向性的译法。在 Git 等版本控制语境中，采用“版本库”；在软件分发方面，采用“软件库”；其它泛指或不确定的语境中，可采用“仓库”、“存储库”译法。）有<ruby> 本地版本库 <rt>  local repository </rt></ruby>，这是你编辑代码的地方（稍后会有更多的介绍），还有<ruby> 远程版本库 <rt>  remote repository </rt></ruby>，这是你完成后想把代码发送到的地方。远程版本库可以在任何地方，甚至在你的本地版本库所在的同一台计算机上，但它们通常托管在 GitLab 或 GitHub 等版本库服务上。

### 我在哪里？

虽然不是官方的卖点，但迷路也是 Git 仓库的“乐趣”之一。你可以通过这套可靠的命令来找到自己的方向：

* `git branch` —— 找到你所在的分支。
* `git log` —— 查看你正在进行的提交。
* `git status` —— 查看自上次提交以来你所做的编辑。
* `git remote` —— 查看你正在跟踪的远程仓库。

用这些命令来定位自己的方向，当你被卡住的时候，会让你有一种方向感。

### 我是否已将我的提交暂存或缓存起来？

你电脑上的代码俗称为你的<ruby> 工作空间 <rt>  workspace </rt></ruby>。但不是很明显的是，当你在 Git 仓库中时，你还有两个（是的，两个！）其他位置：<ruby> 索引 <rt>  index </rt></ruby>和<ruby> 暂存 <rt>  stash </rt></ruby>。当你写了一些内容，然后**添加**时，你是把它添加到索引中，也就是准备提交的缓存内容。有的时候，你的索引中的文件还没有准备好提交，但你想查看另一个分支。这时，暂存就派上用场了。你可以使用 `git stash` 将索引了但尚未提交的文件存储到暂存区中。当你准备好取回文件时，运行 `git stash pop` 将更改带回索引中。

下面是一些你需要使用暂存区和缓存区的命令：

* `git diff ...origin/master` —— 显示最近的本地提交和远程的 `origin` 版本库的 `master` 分支之间的差异。
* `git diff --cached` —— 显示最近的本地提交与添加到本地索引的内容之间的任何差异。
* `git stash` —— 将索引的（已添加但未提交的）文件放在暂存区堆栈中。
* `git stash list` —— 显示暂存区堆栈中的变化。
* `git stash pop` —— 将最近的变化从暂存库中删除。

### 无头骑士

Git 里面有各种比喻。当我想到 `HEAD` 是哪里的时候，我就会想到火车线路。如果你最终处于<ruby> 脱离的 HEAD <rt>  detached HEAD </rt></ruby>模式，就意味着你已经脱离了这个隐喻的轨道。

`HEAD` 是指向当前签出分支中最近一次提交的指针。默认的“签出checkout”是指当你创建一个 Git 仓库并进入到 `master` 分支的时候。每次创建或修改到另一个分支时，你都会切换到该分支行。如果你在当前分支的某处进行 `git checkout <commit>`，`HEAD` 就会移动到该提交。如果没有提交历史记录将你的当前提交连接到已签出的提交，那么你将处于脱离的 `HEAD` 状态。如果你找不到 `HEAD` 的位置，你可以随时用 `git reset --hard origin/master` 来删除修改，回到已知状态。*警告：这将删除你上次推送到 `master` 后的任何改动。*

### 你是上游还是下游？

你的项目的本地副本被认为是你的本地版本库，它可能有也可能没有远程版本库 —— 远程版本库的副本是用于协作或保存的。也可能还有一个<ruby> 上游 <rt>  upstream </rt></ruby>版本库，在那里，项目的第三个副本由不同的贡献者托管和维护。

例如，假设我想为 Kubernetes 做贡献。我会首先将 `kubernetes/kubernetes` 项目<ruby> 复刻 <rt>  fork </rt></ruby>到我的账户下 `mbbroberg/kubernetes`。然后我会将我的项目克隆到我的本地工作区。在这种情况下，我的本地克隆是我的本地仓库，`mbbroberg/kubernetes` 是我的远程仓库，`kubernetes/kubernetes` 是上游。

### 合并的隐喻

当你深入 Git 分支时，根系统的视觉效果就会和火车轨道的形象合二为一。分支通常被用作开发一个新功能的方式，最终你想把它<ruby> 合并 <rt>  merge </rt></ruby>到主分支中。当这样做时，Git 会按顺序保留共同的提交历史，然后将你的分支的新提交追加到历史中。这个过程有一大堆的细节：是否<ruby> 变基 <rt>  rebase </rt></ruby>，是否添加一个<ruby> 合并提交 <rt>  merge commit </rt></ruby>，[Brent Laster](https://opensource.com/users/bclaster) 在《[如何在 Git 中重置、恢复和返回之前的状态](https://opensource.com/article/18/6/git-reset-revert-rebase-commands)》中会有更详细的探讨。

### 我想现在就去 Git

要掌握 Git 命令的世界，有大量的术语和需要探索的地方。我希望这篇关于日常使用术语的第一人称探索能帮助你适应这一切。如果你觉得自己被卡住了或者遇到了挫折，欢迎在 Twitter [@mbbroberg](https://twitter.com/mbbroberg) 上联系我。

#### 回顾

* <ruby> 提交 <rt>  Commit </rt></ruby> —— 将当前索引的内容保存在一个新的提交中，并附上用户描述更改的日志信息。
* <ruby> 分支 <rt>  Branch </rt></ruby> —— 指向一个提交的指针。
* `master` —— 第一个分支的默认名称。
* `HEAD` —— 指向当前分支上最近一次提交的指针。
* <ruby> 合并 <rt>  Merge </rt></ruby> —— 合并两个或多个提交的历史。
* <ruby> 工作空间 <rt>  Workspace </rt></ruby> —— Git 仓库本地副本的通俗名称。
* <ruby> 工作树 <rt>  Working tree </rt></ruby> —— 工作区中的当前分支；任何时候你都可以在 `git status` 的输出中看到这个。
* <ruby> 缓存 <rt>  Cache </rt></ruby> —— 用于临时存储未提交的变更的空间。
* <ruby> 索引 <rt>  Index </rt></ruby> —— 变更提交前存储其变化的缓存。
* 跟踪和未跟踪的文件 —— 没有被索引缓存的文件或尚未加入其中的文件。
* <ruby> 暂存 <rt>  Stash </rt></ruby> —— 另一个缓存，作为一个堆栈，在这里可以存储更改而不需要提交它们。
* `origin` —— 远程版本库的默认名称。
* <ruby> 本地仓库 <rt>  Local repository </rt></ruby> —— 也就是你在工作站上保存 Git 仓库副本的地方。
* <ruby> 远程存储库 <rt>  Remote repository </rt></ruby> —— Git 存储库的第二副本，你可以在这里推送变更以便协作或备份。
* <ruby> 上游存储库 <rt>  Upstream repository </rt></ruby> —— 你跟踪的远程存储库的通俗说法。
* <ruby> 拉取请求 <rt>  Pull request </rt></ruby> —— 这是 GitHub 的专用术语，用于让其他人知道你推送到仓库分支的变化。
* <ruby> 合并请求 <rt>  Merge request </rt></ruby> —— 这是 GitLab 的专用术语，用于让其他人知道你推送到仓库分支的变化。
* `origin/master` —— 远程版本库及其主要分支的默认名称。

后记：双关语是 Git 最好的部分之一，愿你喜欢。

---

via: <https://opensource.com/article/19/2/git-terminology>

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


2021 的 8 个开源软件速查表

# 2021 的 8 个开源软件速查表

>
> 当那个讨厌的命令就在你的指间，但是就是想不起来，一个方便的速查表就可以拯救你。这份速查表清单涵盖了从编程语言到协作工具的各种内容。
>
>
>

<img src="content_image/album_202101_09_131938ktycoc99i2l6dlly.jpg" alt="" title="Cheat Sheet cover image">

有时候，你需要一个简短的提示。你知道自己想做什么，但就是想不起具体怎么做。这时候速查表就派上用场了。在这篇文章中，我给大家提供了 MariaDB 和 MySQL 的速查表。这个速查表对于偶尔使用数据库的人来说是非常有用的。接下来，我列出了两个树莓派速查表。第一张可以让你入门这台单板计算机。一旦你有了一定的基础知识，就可以把它变成一个家庭实验室，可以开始使用 Kubernetes 进行容器管理。一定要同时看看下一张速查表，因为它涵盖了 `kubectl` 工具。接下来，有一个很棒的 Emacs 的速查表，以及还有一个 C 编程语言的速查表。最后，这个列表以两个非常实用的速查表结束：协作工具和开源替代品。

下面是这份清单：

### MariaDB 和 MySQL 速查表

MariaDB 是一个企业级数据库。学习 MariaDB 是使用它来管理 Web 应用程序或编程语言库的重要一步。这个 [MariaDB 和 MySQL 速查表](https://opensource.com/downloads/mariadb-mysql-cheat-sheet)涵盖了连接到服务器、分析数据和其他关键活动的有用命令。本速查表上的命令对交互式提示符和 SQL 脚本有效，但很多内容推断可以用于编程库。

### 树莓派：如何入门

自 2012 年以来，树莓派一直是学习编程技能、构建硬件项目、进行家庭自动化和创建应用程序的必备工具。它实惠的价格使它对任何技能水平的开源爱好者都有吸引力。这本[可下载的指南](https://opensource.com/downloads/raspberry-pi-guide)将为你提供挑选树莓派、保持更新、使用它进行 Linux 游戏、为社区做贡献等方面的提示，让你获得成功。

### 在你的树莓派家庭实验室上运行 Kubernetes

由于树莓派单板计算机的经济性和多功能性，建立一个家庭实验室变得越来越容易。你可以用树莓派做成千上万的事情，包括实验 Kubernetes。在这本[电子书](https://opensource.com/downloads/kubernetes-raspberry-pi)中，作者 [Chris Collins](https://opensource.com/users/clcollins) 演示了如何开始在树莓派上运行 Kubernetes。本指南的每个部分都可以单独学习，也可以作为一个整体项目来学习。无论你的日常工作是什么，阅读这些教程并按照自己的节奏进行尝试，一定会提升你的云技术实力。

### kubectl 速查表

`kubectl` 是维护 Kubernetes 集群的强大命令行工具。这个 2 页的[速查表](https://opensource.com/downloads/kubectl-cheat-sheet)涵盖了常用的命令，让你超越一般的集群管理。它分为三个不同的部分，包括基本命令、故障排除命令和高级命令。掌握这九个关键的 `kubectl` 命令，可以帮助你排除故障和管理 Kubernetes 集群。

### Emacs 速查表

要记住每一个 Emacs 的快捷键几乎是不可能的。通过这个[速查表](https://opensource.com/downloads/emacs-cheat-sheet)，你可以熟悉最常见的组合，从而提高工作效率。当你有这个指南在身边时，你可以不用担心忘记 Emacs 键盘快捷键。通过学习这个速查表中演示的基本键盘快捷键，成为 Emacs 高手。

### C 语言编程速查表

C 是一种直接的编译编程语言。许多其他编程语言从 C 语言中借用了概念，如果你想学习 Lua、C++、Java 或 Go 等编程语言，C 语言是一个很好的起点。Jim Hall 的这份双面[速查表](https://opensource.com/downloads/c-programming-cheat-sheet)包含了所有的基础知识，因此你可以立即获得 C 语法的所有要领。通过下载这个速查表来学习 C 语言，并将语法要领随身携带。

### 5 个开源协作工具

[Kevin Sonney](https://opensource.com/users/ksonney) 的生产力指南通过[五个开源工具教程](https://opensource.com/downloads/collaboration-tools-ebook)让协作变得轻而易举。下载的速查表所涉及的工具包括开源邮件客户端、Google Docs 的替代方案等。让团队沟通不畅成为过去，迎接团队生产力的提升。

### 开放源码软件替代方案速查表

在我们广阔的开源世界里，有很多通用的专有软件工具的替代品。然而，有时要找到合适的开源替代品以满足你的特定需求是令人生畏的。这就是为什么我们整理了这一页的[速查表](https://opensource.com/downloads/open-source-software-alternatives-cheat-sheet)，涵盖了主要的软件工具类别，包括团队聊天、文件共享、文字处理等。我们并没有止步于办公工具。我们还包括游戏、社交媒体和个人理财的顶级开源应用。通过这个速查表，可以快速找到常见的专有软件工具的替代品。

### 总结

这个列表涵盖了很多主题。在我们的[下载页面](https://opensource.com/downloads/cheat-sheets)上可以查看更多的速查表和指南。请在下面的评论中告诉我们你最喜欢的是哪一个。不要忘了为你最喜欢的开源工具写和提交速查表。

---

via: <https://opensource.com/article/21/1/cheat-sheets>

作者：[Jim Hall](https://opensource.com/users/jim-hall) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


使用 Nextcloud 中的 Markdown 编辑器应用

# 使用 Nextcloud 中的 Markdown 编辑器应用

>
> Nextcloud 拥有流行的 Markdown 文件的最流畅的编辑器之一，它有很多方便和直观的功能。
>
>
>

纯文本的好处是，没有额外的针对计算机的信息会杂乱无章地出现在原本供人类阅读的文字中。计算机的好处是它们是可编程的，因此只要我们人类同意在写作时遵循非常特定的惯例，我们就可以对计算机进行编程，将人类可读的文本解释为秘密指令。例如，我们在一个词的周围打上两个星号，不仅可以给人类一个视觉上的提示，说明这个词很重要，我们还可给计算机编程让它用**粗体**显示这个词。

这正是 [Markdown](https://opensource.com/article/19/9/introduction-markdown) 背后的理论和实践，这种流行的纯文本格式向作者们承诺，只要\_他们\_使用特定的纯文本约定，那么他们的文本就会以特定的风格呈现。

传统中，这意味着作者用纯文本写作，直到文本被传给转换器应用（最初是 `markdown.pl`），才会看到漂亮的样式，但 Nextcloud 的 Markdown 编辑器应用改变了这一点。

通过 Nextcloud 的 Markdown 编辑器，你可以一边输入纯文本，一边看到它渲染后的样式。这对于那些要努力记住 Markdown 有时令人困惑的符号的作者们无异于是救星（方括号是在小括号中的超链接之前还是之后？）。而且更好的是，它运行在 Nextcloud 中，所以你可以在任何地方使用它。

### 安装

要使用 Nextcloud 的 Markdown 编辑器，你必须安装 Nextcloud。好消息是，Nextcloud 非常\_容易\_安装。我已经在树莓派、共享服务器、甚至作为一个本地应用安装了它（这是愚蠢的，不要这样做）。如果你不相信自己的能力，你甚至可以依靠 [Turnkey Linux](https://www.turnkeylinux.org/nextcloud) 来帮你完成这些难关，否则就直接从 [Nextcloud.com](http://nextcloud.com) 购买托管服务。在你安装 Nextcloud 后，添加应用就很简单了。点击 Nextcloud 界面右上角的用户图标，选择 **Apps**。找到 **Office and Text** 类别，点击安装并启用 **Markdown Editor**。

### 启动

激活后，Markdown 编辑器会与 Nextcloud 文件中任何以 .md 结尾的文件相关联，当你打开一个 Markdown 文件时，你就会启动 Markdown 编辑器。

### 使用 Markdown 编辑器

Markdown 编辑器包含了一个大的文本区域供你输入，以及一个沿着顶部的工具栏。

工具栏包含了文字处理器的基本功能：用粗体、斜体和删除线设计文本样式、创建标题和段落、列表等等。

如果你了解 Markdown，这些功能中的许多可以在你输入时自动调用。如果你不熟悉 Markdown，那么工具栏或常用的键盘快捷键（`Ctrl+B` 表示粗体，`Ctrl+I` 表示斜体等等）可以帮助你设计文本的样式。

Markdown 编辑器的工作方式的最好的一点是，它真正做到了使人人满意：如果你想以 Markdown 格式输入，那么它就会接受它并立即将其转化为视觉样式；如果你不想操心 Markdown 语法，那么当你使用键盘快捷键或工具栏按钮时，它就会为你生成样式。无论哪种方式，你永远不会看到 Markdown 语法格式，但你也永远不会失去它。这是一个完美的折中方案。

它也是一个非常聪明的编辑器。当你选择一个单词时，它会为你创建一个超链接，它能快速流畅地自动转换 Markdown，而且它知道一些不同“风格”的 Markdown 语法（主要是 Commonmark，但也有传统的 Markdown、Github Markdown，等等）。

### 尝试 Nextcloud

我用过几个 Markdown 预览应用，Nextcloud 的 Markdown 编辑器是最流畅的一个。它尊重用户，并为显示 Markdown 做了最基本的工作，所以它的转换速度很快，也很准确。因为它是 Nextcloud 中的一个应用，你还可以获得这样的好处：你的作品可以即时保存在自己的私有的开源云上，并进行版本控制。没有比这更好的文本编辑器了。

---

via: <https://opensource.com/article/20/12/nextcloud-markdown>

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

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


为 Linux 爱好者打造的极简 Mac 终端

Summary: 以下是我如何通过很多微妙复杂的方式来保持终端的简单和我的点文件的安全。

以下是我如何通过很多微妙复杂的方式来保持终端的简单和我的点文件的安全。

# 为 Linux 爱好者打造的极简 Mac 终端

>
> 以下是我如何通过很多微妙复杂的方式来保持终端的简单和我的点文件的安全。
>
>
>

<img src="content_image/album_202101_10_122050pyit2d9eelje9kfl.jpg" alt="" title="Coffee and laptop">

我要向大家坦白。我成为 Mac 用户已经有 10 多年了。起初，鉴于我强大的 Linux 背景，我觉得有点惭愧，但 Mac 给了我一个类似 Unix 的 Shell 和一个伟大的窗口管理器。因为这段历史，我有一个合二为一的功能，它运行在 macOS 上，但又为 Linux 用户所熟悉。没有理由它不能移植到 Linux 上（而且它已经移植了！）。

### 在 Mac 上使用 iTerm2

很长时间以来，我首选的终端是基本的内置 Term.app，但我最近改用了 [iTerm2](https://www.iterm2.com/)，因为它有更好的自定义和配置文件支持。对我来说，它的一个主要优点是可以轻松地在 Mac 之间移植配置。对于日常使用，我更喜欢 Solarized Dark 主题；但对于演示文稿，我有一个单独的配置文件，可以放大文本，并使用色彩更鲜艳的纯黑色背景。

要使 iTerm2 可用，我首先要做的是配置 `Ctrl+Left` 和 `Ctrl+Right` 箭头，使其尊重经典的终端行为，即跳转到单词边界的开始和结束。要做到这一点，请导航到 “Preferences > Profiles > Your Profile > Keys” 并输入以下内容：

* 键盘快捷键：`^←`
* 行动：发送 Escape 序列
* Esc+：`b`

然后是另一个：

* 键盘快捷键：`^→`
* 行动：发送 Escape 序列
* Esc+：`f`

了解更多关于 [iTerm2](https://www.iterm2.com/documentation.html) 的内容，享受自定义体验。

### 一个简单的命令提示符

我是那种无聊的终端提示符用户。我不想在其中包含 Git 目录或退出代码，我也只用单行提示符。我唯一使用的花哨组件是 [kubectx](https://github.com/ahmetb/kubectx) ，它包括当前的 Kubernetes 上下文。作为一位 [OpenShift Dedicated](https://www.openshift.com/products/dedicated/) 的站点可靠性工程师（SRE），我必须在运行命令时有相应的上下文，而 `kubectx` 让我在打字时很容易知道自己在哪里。所以，我的 Bash PS1 就是无聊的 `username@host cwd $`，免除了 Kubernetes 上下文前缀。（LCTT 译注：如果想将 Kubernetes 上下文和名字空间显示在 PS1，可以看看 [kube-ps1](https://github.com/jonmosco/kube-ps1)。）

毫无疑问，与我见过的一些花哨的终端相比，我是偏向于极简的。有些人喜欢透明背景，有些人则喜欢他们的提示符上有很多信息 —— 从时间到退出代码以及其他一切。我觉得这些在我的终端上会让人分心，所以我很喜欢远离的那些设置。

### 精美复杂的点文件

与我的极简主义终端相比，很容易看出我在哪里投入了最大的努力：部署我的[点文件](https://opensource.com/article/19/3/move-your-dotfiles-version-control)，包括我的 `.bash_profile` 和我的整体 Mac 设置。

我使用通过 GitHub 托管的 [一系列 Makefile](https://github.com/lisa/mac-setup) 来管理我的 Mac 设置。这将会拉取我的[点文件专用部署机制](https://github.com/lisa/dotrc)，它也在 GitHub 上。你要问为什么要围绕安全做这些工具？IT 专业人士和业余爱好者都需要一种强大的方式来将安全的数据片段放在新系统上。也许你想要你的 SSH 配置是隐藏的，或者你通过第三方系统部署凭证。我发现把我的安全数据和其他一切数据放在一起是很有用的，我用 [Ansible Vault](https://docs.ansible.com/ansible/latest/user_guide/vault.html) 解决了这个问题。我所有的机密信息都存储在 Git 中，用 Ansible Vault 加密。解密是用 Makefile 处理的。

无论是第一次安装还是更新现有的点文件，我（当然）都必须有 Ansible Vault，为了避免到处安装，我把它放在一个用 Docker 运行的容器里，这样我就到处都安装好了。我把解密口令放到一个文件中， `run make`，然后用 `make clean` 清理一切。你可以通过[探索这些点文件](https://github.com/lisa/dotrc)来了解更多）。

我想说的是，这种管理方案可能过于夸张，而有些人喜欢复杂的终端提示。所以，也许在权衡之下，一切都会变得平衡。

---

via: <https://opensource.com/article/20/6/iterm2-mac>

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


通过编写“猜数字”游戏学习 Lua

# 通过编写“猜数字”游戏学习 Lua

>
> 通过编写一个简单的游戏来认识 Lua，它是一种动态类型的、轻量级的、高效的、可嵌入的脚本语言。
>
>
>

<img src="content_image/album_202101_10_125405hgrqrigagbucpbqg.jpg" alt="" title="Puzzle pieces coming together to form a computer screen">

如果你是 Bash、Python 或 Ruby 等脚本语言的爱好者，你可能会发现 Lua 很有趣。Lua 是一种动态类型的、轻量级的、高效的、可嵌入的脚本语言，它有与 C 语言的 API 接口。它通过基于寄存器的虚拟机解释字节码来运行，它可以用于过程式编程、函数式编程和数据驱动编程等编程方式。它甚至可以通过巧妙地使用数组（即“<ruby> 表 <rt>  table </rt></ruby>”）来模拟类，以用于面向对象的编程。

感受一门语言的好方法是通过编写一个你已经熟悉的简单应用。最近，一些作者已经演示了如何使用他们最喜欢的语言来创建一个“猜数字”游戏。[Lua](https://www.lua.org/) 是我最喜欢的语言之一，所以这是我的 Lua 版猜数字游戏。

### 安装 Lua

如果你是在 Linux 上，你可以从你的发行版仓库中安装 Lua。在 macOS 上，你可以从 [MacPorts](https://opensource.com/article/20/11/macports) 或 [Homebrew](https://opensource.com/article/20/6/homebrew-mac) 安装 Lua。在 Windows 上，你可以从 [Chocolatey](https://opensource.com/article/20/3/chocolatey) 安装 Lua。

安装 Lua 后，打开你最喜欢的文本编辑器，可以准备编写了。

### Lua 代码

首先，你必须设置一个伪随机数生成器，这样你的玩家就有一些不可预知的东西来尝试猜测。这是一个两个步骤的过程：首先，你根据当前的时间生成一个随机种子，然后在 1 到 100 的范围内选择一个数字：

```
math.randomseed(os.time())
number = math.random(1,100)

```

接下来，创建一个 Lua 所谓的<ruby> 表 <rt>  table </rt></ruby>来表示你的玩家。表就像一个 [Bash 中的数组](https://opensource.com/article/20/6/associative-arrays-bash)或 Java 中的 `ArrayList`。你可以创建一个表，然后分配与该表相关的子变量。在这段代码中，`player` 是表，而 `player.guess` 是表中的一个条目：

```
player = {}
player.guess = 0

```

处于调试的需求，可以输出这个秘密数字。这对游戏并不合适，但对测试很有帮助。Lua 中的注释是在前面放双破折号：

```
print(number) --debug

```

接下来，设置一个 `while` 循环，当分配给 `player.guess` 的值不等于代码开始时建立的随机的 `number` 时，循环将永远运行。目前，`player.guess` 被设置为 0，所以它不等于 `number`。Lua 的不等式数学运算符是 `~=`，诚然这很独特，但过一段时间你就会习惯。

在这个无限循环的过程中，首先游戏会打印一个提示，让玩家明白游戏的内容。

接下来，Lua 会暂停，等待玩家输入猜测的数。Lua 使用 `io.read` 函数从文件和标准输入 （stdin） 中读取数据。你可以将 `io.read` 的结果分配到一个变量中，这个变量是在 `player` 表中动态创建的。处理玩家输入的问题是，即使它是一个数字，它也是作为一个字符串读取的。你可以使用 `tonumber()` 函数将这个输入转换为整数类型，将结果赋值回初始为 `0` 的 `player.guess` 变量：

```
while ( player.guess ~= number ) do
  print("Guess a number between 1 and 100")
  player.answer = io.read()
  player.guess = tonumber(player.answer)

```

现在 `player.guess` 包含了一个新的值，它将与 `if` 语句中的随机数进行比较。Lua 使用关键字 `if`、`elseif` 和 `else`，并用关键字 `end` 来结束该语句：

```
  if ( player.guess > number ) then
    print("Too high")
  elseif ( player.guess < number) then
    print("Too low")
  else
    print("That's right!")
    os.exit()
  end
end

```

最后，函数 `os.exit()` 在成功后关闭应用，关键字 `end` 使用了两次：一次是结束 `if` 语句，另一次是结束 `while` 循环。

### 运行应用

在终端上运行游戏：

```
$ lua ./guess.lua
96
Guess a number between 1 and 100
1
Too low
Guess a number between 1 and 100
99
Too high
Guess a number between 1 and 100
96
That's right!

```

就是这样！

### 直观且一致

从这段代码中可以看出，Lua 是非常一致且相当直观的。它的表机制是一种令人耳目一新的数据关联方式，它的语法也是简约而高效的。Lua 代码中几乎没有浪费的行，事实上，这个例子中至少有两行可以进一步优化，但我想把数据转换作为它的步骤来演示（也许你可以找到我所指的两行，并对它们进行重构）。

Lua 非常易于使用，它的[文档阅读起来很愉快](https://www.lua.org/docs.html)，主要是因为它的内容实在是不多。你会在短时间内学会核心语言，然后你就可以自由地探索 [LuaRocks](https://opensource.com/article/19/11/getting-started-luarocks)，发现别人贡献的各种很棒的库。“Lua” 在葡萄牙语中的意思是“月亮”，所以今晚可以尝试一下。

---

via: <https://opensource.com/article/20/12/lua-guess-number-game>

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

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


全屏文本编辑器 Focuswriter 提高关注力的六种方式

Summary: 这款全屏编辑器可以帮助你设定写作目标，并将干扰降到最低，从而实现你的目标。

竟然没有osx

这款全屏编辑器可以帮助你设定写作目标，并将干扰降到最低，从而实现你的目标。

# 全屏文本编辑器 Focuswriter 提高关注力的六种方式

>
> 这款全屏编辑器可以帮助你设定写作目标，并将干扰降到最低，从而实现你的目标。
>
>
>

计算机的好处是，它们真的很擅长一心多用。

计算机的坏处是，它们实在太擅长一心多用。

无论你认为自己作为一个人，在多任务处理方面是好是坏，有时你需要一点帮助来集中注意力，尤其是当你试图撰写清晰和简明的交流信息时。而这正是开发 Focuswriter 的原因。

### 安装

在 Linux 上，你可以从 [Flathub](https://flathub.org/apps/details/org.gottcode.FocusWriter) 以 Flatpak 包安装 Focuswriter。

在 Windows 或 Linux 上（如果你不使用 Flatpak），你可以[从它的网站安装 Focuswriter](https://gottcode.org/focuswriter/)。你也可以从源代码安装，它也可以从 Focuswriter 网站上获得。

### 使用 Focuswriter

不可否认，Focuswriter 实际上处于文本编辑器和文字处理器的重叠的地带。它的默认格式是 <ruby> 开放文档文本 <rt>  Open Document Text </rt></ruby>（.odt）格式，所以它允许你设置文本样式和对齐文本、标记页眉，以及开启和关闭智能引号。但它的文字处理器功能也就到此为止，因为 Focuswriter 确实主要专注于\_写作\_，而不是对你所写的东西进行样式设计。

Focuswriter 以几种不同的方式鼓励专注力：

#### 吸引力

Focuswriter 看起来并不像你平常的电脑应用程序，更不像你平常的文本编辑器。当它启动时，它会在你的文档后面加载一个主题。主题一般是良性的 —— 默认是写字台，但也有星空、远方的风景等等。当然，你并不是要全神贯注于此主题，但它确实可以帮助你避免被徘徊于你的编辑器后面的其他应用窗口或电脑桌面分散注意力。因为它很容易定制，所以你几乎不大可能找不到你喜欢的主题。

<img src="content_image/album_202101_11_002729rqql2ll68wr8i8vz.png" alt="木质写字台背景主题上的 Focuswriter 白色盒子与灰色字样" title="Focuswriter white box with gray wording on wooden writing desk background theme">

#### 音效

Focuswriter 使用起来其实很有趣。除了视觉主题外，它还有一个\_可选的\_打字机音频主题。启用后，每按一次键都会发出机械打字机按键的声音。当你按下**回车键**时，回车的声音令人满意，这是对你完成一段文字（或一行文字，如果你为了改善版本控制而每行写一句话）的奖励。

这个音频主题完全是可选的，默认情况下是不开启的。你可能会怀疑它的效果，但你不应该低估发出这些生产力声音而有具有现代文本编辑能力所带来的满足感。

#### 全屏显示

Focuswriter 不仅在默认情况下启动全屏，而且还从视图中隐藏了其菜单和其他部件。要访问主菜单栏，你要将光标放在屏幕顶部，直到它出现。右边隐藏了一个滚动条，左边隐藏了一个文档切换器小部件，底部则隐藏着一个状态栏。

#### 极简主义

Focuswriter 的编辑功能并不多。它没有高级搜索选项，也没有代码格式化或语法高亮的专门选项。它的设计是让你在文档中写入文本，而不是其他的什么。即使是上下文右键菜单也只提供基本的编辑功能（复制、剪切、粘贴等）或拼写检查选项。

#### 聚焦一行

Focuswriter 的另一个可选功能是它能够“灰化”除了你的当前文本行之外的所有文本行。这可以帮助你的眼睛懒洋洋地定位你当前的文字行，如果你不是一个盲打打字员，或者如果你正在抄写一些你手写在纸张上的东西，眼睛必须经常远离屏幕，这可能特别有用。

#### 目标追踪

你可以在 Focuswriter 的首选项中为自己设置一个每日字数目标或定时目标。当你工作时，你的进度会被跟踪，并显示在屏幕底部的状态栏中。除非你将光标悬停在它上面，否则它是隐藏在你的视野中的，所以你不会被迫沉迷于它，但它足够方便，可以帮助你保持进度。

### 即使你认为它很傻，也请尝试一下 Focuswriter

以前也听说过全屏文本编辑器，但我一直不觉得全屏文本编辑器对我来说有什么重要意义。毕竟，几乎所有的应用程序都可以全屏显示，而我个人也没有任何关注力问题。

不过在尝试过 Focuswriter 之后，我明白了一个好的全屏“关注力优先”文本编辑器的魅力所在。也许没有必要，但话又说回来，开源是如此的兴盛，我们有大量的冗余应用，这些应用功能略有差异，但实现上差异很大。Focuswriter 让写作变得有趣，它让工作变得愉快。下载它，设定一个目标，然后开始写作吧！

---

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

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

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


使用 Python 调整图像大小

Summary: 快速解释如何在 Python 中调整图像大小，同时保持相同的长宽比。

快速解释如何在 Python 中调整图像大小，同时保持相同的长宽比。

# 使用 Python 调整图像大小

>
> 快速解释如何在 Python 中调整图像大小，同时保持相同的长宽比。
>
>
>

<img src="content_image/album_202101_11_121120pa77t6gpagggttzp.jpg" alt="Python in a tree" title="Python in a tree">

我喜欢 [Python](http://python.org/ "Python Programming Language – Official Website")，而且我已经学了一段时间了。前段时间，我写了一个 Python 脚本，在这个脚本中，我需要调整一堆图片的大小，同时保持长宽比（比例）不变。于是我四处寻找，发现了 [Pillow](https://pypi.org/project/Pillow/ "Python Imaging Library")，这是一个 Python 图像库，也是一个叫做 PIL 的旧库的“友好复刻”。

要安装 Pillow，请使用 Python 的 `pip` 模块：

```
$ python3 -m pip install Pillow

```

### 按宽度缩放

这是一个使用 Pillow 模块来调整图片大小的基本脚本：

```
from PIL import Image

basewidth = 300
img = Image.open('fullsized_image.jpg')
wpercent = (basewidth / float(img.size[0]))
hsize = int((float(img.size[1]) * float(wpercent)))
img = img.resize((basewidth, hsize), Image.ANTIALIAS)
img.save('resized_image.jpg')

```

这几行 Python 代码使用 Pillow 将一张图片 （`fullsized_image.jpg`） 调整为 300 像素的宽度，宽度在变量 `basewidth` 中设置，高度则与新的宽度成比例。比例高度的计算方法是：确定 300 像素占原宽度 （`img.size[0]`） 的百分比，然后将原高度（`img.size[1]`） 乘以该百分比。所得的高度值保存在变量 `hsize` 中。

如果你需要不同的图片宽度，你可以将 `basewidth` 改为任何其他数字。另外，请注意，因为我想保留全尺寸的图片 （`fullsized_image.jpg`），因此我将调整后的图片以一个不同的名称 `resized_image.jpg` 保存。当然，你不必这么做。如果你想，你可以使用相同的文件名将调整后的图片覆盖全尺寸的图片。

### 按高度缩放

如果高度是固定的，而宽度是按比例变化的，那也基本差不多，你只需要把东西换一下：

```
from PIL import Image

baseheight = 560
img = Image.open('fullsized_image.jpg')
hpercent = (baseheight / float(img.size[1]))
wsize = int((float(img.size[0]) * float(hpercent)))
img = img.resize((wsize, baseheight), Image.ANTIALIAS)
img.save('resized_image.jpg')

```

注意 `basewidth` 现在换成了 `baseheight`，因为高度是固定的。在第三行中，我们在计算高度百分比，所以我们需要 `img.size[1]` 而不是 `img.size[0]`。`size` 属性是一个元组，包含宽度和高度，单位是像素，`size[0]` 指的是第一个元组元素，也就是宽度，`size[1]` 是第二个元素，也就是高度。第 4 行也有这样的切换，`size[0]` 代表宽度，`size[1]` 代表高度。

---

via: <https://opensource.com/life/15/2/resize-images-python>

作者：[Dayo Ntwari](https://opensource.com/users/dayontwari) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


光速！搜索、学习和实践 Linux 命令！！

Summary: 如果你是中高级 Linux 用户，你可以通过一行命令或脚本在几秒钟/分钟内轻松完成这一任务。但是，如果你是一个不懂得太多 Linux 命令的菜鸟你可能就需要一些帮助了。

# 光速！搜索、学习和实践 Linux 命令！！

这一标题可能看起来很粗略且吸睛。请允许我解释一下我在本教程中将要阐释的内容。假设你想下载一个压缩文件，将其解压缩，并从命令行中将文件从一个位置移动到另一个位置。根据上面的场景，我们可能需要至少三个 Linux 命令，一个用于下载文件，一个用于提取下载的文件，一个用于移动文件。如果你是中高级 Linux 用户，你可以通过[一行命令](https://ostechnix.com/random-one-liner-linux-commands-part-1/)或脚本在几秒钟/分钟内轻松完成这一任务。但是，如果你是一个不懂得太多 Linux 命令的菜鸟你可能就需要一些帮助了。

当然，谷歌的快速搜索可能会找到很多结果。或者，你可以使用 [手册页](https://www.ostechnix.com/learn-use-man-pages-efficiently/)。但是有些手册页实在很长，很全面，但缺少有用的示例。当你在特定的标志/选项上寻找特定的信息时，你可能需要向下检索相当长的时间。值得庆幸的是，有一些 [好的手册页替代品](https://www.ostechnix.com/3-good-alternatives-man-pages-every-linux-user-know/)，它们主要关注于实用的命令。一个很好的选择是 **TLDR 手册**。使用 TLDR 手册，我们可以通过实际示例快速轻松地学习一个 Linux 命令。要使用 TLDR 手册，我们需要 TLDR 客户端。有很多客户。今天我们就来了解一个这样的客户端，名为 **“Tldr++”**。

Tldr++ 是一个快速和交互式的 Tldr 客户端，用 **Go** 编程语言编写。与其他 Tldr 客户端不同，它是完全交互式的。这意味着，你可以选择一个命令，读取所有示例，并立即运行任何命令，而不必在终端中重新键入或复制/粘贴每个命令。还是不明白？没有问题。请继续阅读，以便动态学习和实践 Linux 命令。

### 安装 Tldr++

安装 Tldr++ 非常简单。从 [发布页面](https://github.com/isacikgoz/tldr/releases) 下载 Tldr++ 最新版本。解压它并将 Tldr++ 二进制文件移动到你的 `$PATH` 中。

```
$ wget https://github.com/isacikgoz/tldr/releases/download/v0.5.0/tldr_0.5.0_linux_amd64.tar.gz
$ tar xzf tldr_0.5.0_linux_amd64.tar.gz
$ sudo mv tldr /usr/local/bin
$ sudo chmod +x /usr/local/bin/tldr

```

现在，运行 `tldr` 二进制代码将 TLDR 手册部署到本地系统中。

```
$ tldr

```

示例输出：

```
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Compressing objects: 100% (6/6), done.
Total 18157 (delta 0), reused 3 (delta 0), pack-reused 18151
Successfully cloned into: /home/sk/.local/share/tldr

```

Tldr++ 可以在 AUR 中使用。如果你使用 Arch Linux 上，你可以使用任何 AUR 助手来安装它，例如 [YaY](https://www.ostechnix.com/yay-found-yet-another-reliable-aur-helper/)。确保你已经从系统中删除了任何现有的 TLDR 客户端，并运行以下命令安装 Tldr++。

```
$ yay -S tldr++

```

或者，你也可以像下面描述的那样从源代码进行编译。因为 Tldr++ 是用 Go 语言编写的，所以请确保你 Linux 系统中已经安装了 Go 语言。如果还没有安装，请参考下面的指南。

* [如何在 Linux 系统中安装 Go 语言](https://www.ostechnix.com/install-go-language-linux/)

在安装好 Go 语言之后, 运行以下的命令来安装 Tldr++。

```
$ go get -u github.com/isacikgoz/tldr

```

该命令在当前工作目录中下载 `tldr` 代码库中的内容并存储到 `go` 文件夹中。

现在，运行 `tldr` 二进制代码将 TLDR 手册部署到本地系统中。

```
$ go/bin/tldr

```

示例输出：

最后，将 `tldr` 二进制文件复制到你的路径中。

```
$ sudo mv tldr /usr/local/bin

```

现在是时候看一些例子了。

### Tldr++ 用法

输入不带任何选项的 `tldr` 命令，以字母顺序显示所有命令示例。

使用 **向上/向下箭头** 来浏览命令，键入任何字母来搜索或键入命令名称来查看相应命令的示例。 `?` 以浏览更多消息，按 `Ctrl+c` 返回/退出。

要显示特定命令的示例命令，例如 `apt`，可以这样做:

```
$ tldr apt

```

从列表中选择任意示例命令并按回车键。在选定的命令前会看到一个 `*` 符号。例如，我选择第一个命令即 `sudo apt update`。现在，它会问你是否继续。如果命令正确，只需键入 `y` 继续，并输入 `sudo` 密码运行所选命令。

看到了吗？你不需要在终端中复制/粘贴或键入实际的命令。只需从列表中选择它，并极速运行！

Tldr 手册中有数百个 Linux 命令示例。你可以每天选择一个或两个命令，并彻底学习它们。每天坚持这样的练习，尽可能多的掌握。

### 使用 Tldr++ 动态学习和实践 Linux 命令

现在回到我在第一段中提到的场景。你需要下载一个文件，将其解压缩并将其移动到不同的位置，并使其可执行。让我们看看如何使用 Tldr++ 客户端进行交互。

#### 第一步 – 从网上下载文件

要使用命令行下载文件，我们主要使用 `curl` 或 `wget` 命令。让我使用 `wget` 下载文件。要打开 `wget` 命令的 TLDR 页面，只需执行以下命令：

```
$ tldr wget

```

下面是 `wget` 命令的示例。

你可以使用 **向上/向下箭头** 来浏览命令列表。一旦你选择了你所选择的命令，按回车键。这里我选择了第一个命令。

现在，输入路径来下载文件。

然后将要求你确认该命令是否正确。如果命令正确，只需键入 `yes` 或 `y` 就可以开始下载文件。

我们已经下载了文件。让我们继续解压这个文件。

#### 第二步 – 解压已下载的文件

我们下载了 tar.gz 文件。所以我将打开 TLDR 手册的 `tar` 页面。

```
$ tldr tar

```

你将看到示例命令列表。浏览这些示例，找出哪个命令适合提取 tar.gz（gzip 格式）文件，按回车键。在我们的例子中，它是第三个命令。

现在，系统将提示你输入 tar.gz 文件的路径。只需输入路径并按回车键。Tldr++ 支持智能文件提示。这意味着它会在你键入时自动补全文件名。只需按 `TAB` 键自动完成。

如果将文件下载到其他位置，只需键入完整路径，例如 `/home/sk/Downloads/tldr_0.5.0_linux_amd64.tar.gz`。

输入要解压的文件的路径后，按回车键，然后输入 `y` 进行确认。

#### 第三步 – 将文件从一个目录移动到另一个目录

我们解压了文件。现在我们需要将文件移动到另一个位置。为了将文件从一个位置移动到另一个位置，我们使用 `mv` 命令。所以，让我们打开 TLDR 手册的 `mv` 命令。

```
$ tldr mv

```

选择正确的命令将文件从一个位置移动到另一个位置。在我们的例子中，第一个命令可以工作，所以让我们选中它。

输入要移动的文件路径，并输入目标路径并按回车键。

**附注：** 输入 `y!` 或 `yes!` 来以 `sudo` 权限运行命令。

正如你在上面的截图中看到的，我将名为 ``tldr`的文件移动到`/usr/local/bin/`。

要了解更多细节，请参考项目最后给出的 GitHub 页面。

### 总结

别误会，毫无疑问 **手册页** 是伟大的！但是，正如我已经说过的，许多手册页都很全面，缺少有用的示例。我不可能记住带有复杂标志的冗长的所有命令。有时，我花了很多时间在手册页上，却还是一窍不通。Tldr 手册帮助我在几分钟内找到了我需要的东西。而且，我们偶尔会使用一些命令，然后就会完全忘记它们。另一方面，Tldr 手册实际上在使用那些使用率很低的命令时很有帮助。Tldr++ 客户端通过智能的用户交互使这个任务变得更容易。试试吧，在下面的评论区告诉我们你对这个工具的看法。

（LCTT 译注：我们也[开发](/article-10528-1.html)了一个 TLDR 客户端，是基于[微信小程序](/article-11989-1.html)的，大家在微信中搜索“**Linux**”小程序即可找到。虽然微信小程序版的 TLDR 客户端不能像这个客户端一样实际运行 Linux，但是它是中文的，而且中文翻译是由使用者来贡献和修正的。顺便说一句，全部一千多个命令已经翻译完毕，感谢我们的贡献者们！ ）

以上，更多的好的分享将会陆续推出，请继续保持关注！

祝好!

---

via: <https://www.ostechnix.com/search-study-and-practice-linux-commands-on-the-fly/>

作者：[SK](https://www.ostechnix.com/author/sk/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[qfzy1233](https://github.com/qfzy1233) 校对：[wxy](https://github.com/wxy)

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


Kubernetes 资源配额使用指南

Summary: 资源配额控制应用的 CPU 或内存使用情况，防止资源被过量使用或被抢占。

资源配额控制应用的 CPU 或内存使用情况，防止资源被过量使用或被抢占。

# Kubernetes 资源配额使用指南

>
> 资源配额控制应用的 CPU 或内存使用情况，防止资源被过量使用或被抢占。
>
>
>

当 Kubernetes 集群运行过一段时间或者在被开发者大量使用后，[Kubernetes](https://kubernetes.io/) 资源（例如 CPU 和内存）的控制的问题就会显现出来。而在大多情况下只有集群出问题后，我们才会意识到资源控制的重要性。

Kubernetes 部署过程如果没有能充分考虑到将来的扩展性，资源类问题将会非常常见，此类问题与集群的管理和部署团队的经验有关。

如果不加以合理控制，一个暴力的应用或者开发者可能影响到共享该集群的所有业务，大家因此会相互埋怨、指责并保护性地抢占资源。这对于集群管理和开发人员都是非常难以处理的场景。

在 Kubernetes 环境中控制应用的计算资源使用有多种方式。大部分情况下，我们可以使用“资源控制”和“限制范围”。注意存储管理不在我们讨论范围之内，存储管理可以通过<ruby> 持久卷 <rt>  Persistent Volume </rt></ruby> 件，以实现针对不同的存储控制需求。

资源配额是一种控制 Kubernetes 计算资源的方法。本文告诉你如何使用该功能来管理开发人员行为并控制应用的资源使用。

### 什么是资源配额

简而言之，[资源配额](https://kubernetes.io/docs/concepts/policy/resource-quotas) 提供了限制每个命名空间资源消耗的约束条件，它们只能在命名空间级别上应用，这意味着它们可以应用于计算资源，并限制命名空间内的对象数量。

Kubernetes资源配额通过 `ResourceQuota` 对象来为每个命名空间设置资源配额，对以下对象类型的 CPU 和内存进行限制：

* <ruby> 吊舱 <rt>  Pod </rt></ruby>
* <ruby> 服务 <rt>  Service </rt></ruby>
* <ruby> 机密信息 <rt>  Secret </rt></ruby>
* <ruby> 持久卷断言 <rt>  Persistent Volume Claim </rt></ruby>（PVC）
* <ruby> 配置映射 <rt>  ConfigMap </rt></ruby>

Kubernetes 通过 `request` 和 `limit` 两个参数对 CPU 和内存进行限制（参考 [LimitRange](https://kubernetes.io/docs/concepts/policy/limit-range/) 文档）。前者表示容器最小被保证资源，后者表示容器最大可用资源。实际上最大可用资源还受限于其它容器的实际使用情况。

下一张图片解释了配额中 `request` 和 `limit` 的区别：

下面我们就通过一个例子来说明如何设置资源配额来创建约束，将应用程序限制在某些资源上，它还展示了实现资源配额以获得对 Kubernetes 的控制的有用性。

### 准备环境

首先你需要一个 Kubernetes 环境。以下是我使用 Kubernetes 环境：

* [Minikube](https://opensource.com/article/18/10/getting-started-minikube) v1.14.2
* Fedora 33 操作系统
* 互联网接入

如果你想在 Linux 机器上通过 Minikube 搭建 Kubernetes 测试环境，可以参考 Bryant Son 的《[Minikube 入门](https://opensource.com/article/18/10/getting-started-minikube)》 一文。Window 或者 macOS 用户可以参考[这篇文章](https://www.liquidweb.com/kb/how-to-install-minikube/)。

### 设置资源配额

这里我们仅展示 CPU 配额设置步骤，配置内存配额或两者的组合与之类似。

在生产环境中，CPU 是最需要被控制的资源，尤其是在多应用的场景下特别需要注意防止某些应用消耗太多 CPU 而影响到其它应用。

首先我们创建一个命名空间，在其中设置 CPU 配额：

```
$ kubectl create namespace quota-test
namespace/quota-test created
```

准备 `cpu-quota.yaml` 文件，内容如下：

```
apiVersion: v1
kind: ResourceQuota
metadata:
  name: test-cpu-quota
spec:
  hard:
    requests.cpu: "100m"
    limits.cpu: "200m"
```

应用 CPU 配额到 Kubernetes 集群：

```
$ kubectl apply -f cpu-qouta.yaml
resourcequota/test-cpu-quota created
```

使用 `kubectl describe` 检查配额配置情况：

```
$ kubectl describe resourcequota/test-cpu-quota --namespace quota-test
Name:         test-cpu-quota
Namespace:    quota-test
Resource      Used  Hard
--------      ----  ----
limits.cpu    0     200m
requests.cpu  0     100m
```

在 `Used resources` 列中显示了当前情况，该列值会随着<ruby> 吊舱 <rt>  Pod </rt></ruby>的部署而变化。

下面是我们来验证限额管理的场景。我们将在同一命名空间下部署三个不同的吊舱，为它们配置以不同的资源限制如下：

* PodA：第一个被实例化，使用 50% 可用 CPU 资源
* PodB：第二个被实例化，使用其余 50% 可用 CPU 资源
* PodC：没有可用 CPU 资源，因此不会被部署

#### 部署吊舱

PodA：

```
$ kubectl create -n quota-test -f - << EOF
apiVersion: v1
kind: Pod
metadata:
  name: poda
spec:
  containers:
  - name: quota-test
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Pod is Running ; sleep 5000']
    resources:
      requests:
        cpu: "50m"
      limits:
        cpu: "100m"
  restartPolicy: Never
EOF
```

部署 PodA 后，再次查看配额描述信息中的 `Used CPU` 信息：

```
$ kubectl describe resourcequota/test-cpu-quota --namespace quota-test
Name:         test-cpu-quota
Namespace:    quota-test
Resource      Used  Hard
--------      ----  ----
limits.cpu    100m  200m
requests.cpu  50m   100m
```

PodB：

```
$ kubectl create -n quota-test -f - << EOF
apiVersion: v1
kind: Pod
metadata:
  name: podb
spec:
  containers:
  - name: quota-test
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Pod is Running ; sleep 5000']
    resources:
      requests:
        cpu: "50m"
      limits:
        cpu: "100m"
  restartPolicy: Never
EOF
```

再次查看 CPU 资源使用，此时 PodB 启动后 CPU 限制已经达到上限：

```
$ kubectl describe resourcequota/test-cpu-quota --namespace quota-test
Name:         test-cpu-quota
Namespace:    quota-test
Resource      Used  Hard
--------      ----  ----
limits.cpu    200m  200m
requests.cpu  100m  100m
```

PodC：

试着创建 PodC，此时 CPU 配额已经被 PodA 和 PodB 用尽：

```
$ kubectl create -n quota-test -f - << EOF
apiVersion: v1
kind: Pod
metadata:
  name: podc
spec:
  containers:
  - name: quota-test
    image: busybox
    imagePullPolicy: IfNotPresent
    command: ['sh', '-c', 'echo Pod is Running ; sleep 5000']
    resources:
      requests:
        cpu: "5m"
      limits:
        cpu: "10m"
  restartPolicy: Never
EOF
```

正我们期望，第三个 Pod 无法被启动，配额限制了吊舱的创建：

```
Error from server (Forbidden): error when creating "STDIN": pods "podc" is forbidden: exceeded quota: test-cpu-quota, requested: limits.cpu=10m,requests.cpu=5m, used: limits.cpu=200m,requests.cpu=100m, limited: limits.cpu=200m,requests.cpu=100m
```

如我们的例子所示，定义合理的资源配额限制开发者行为对 Kubernetes 管理十分重要。

### 清理

删除刚才创建的命名空间 `quota-test`:

```
$ kubectl delete -n quota-test
```

### 规划资源配额

Kubernetes 中提供多种方式来控制资源的抢占和使用，合理的规划和配置配额、限制范围和其它原生参数对保持集群的稳定性十分必要。

你应该十分谨慎地控制计算资源的资源配额，特别是关键业务的生产应用环境。

在规划资源配额时，开发人员的参与很重要，需要他们预估并给出最合理的资源使用值。

---

via: <https://opensource.com/article/20/12/kubernetes-resource-quotas>

作者：[Mike Calizo](https://opensource.com/users/mcalizo) 选题：[lujun9972](https://github.com/lujun9972) 译者：[larryzju](https://github.com/larryzju) 校对：[wxy](https://github.com/wxy)

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


使用 Atom 文本编辑器的 5 个理由

Summary: Atom 是一个全面的环境，可以完成从基本到复杂的任务，适合从初学者到老用户。

Atom 是一个全面的环境，可以完成从基本到复杂的任务，适合从初学者到老用户。

# 使用 Atom 文本编辑器的 5 个理由

>
> Atom 是一个全面的环境，可以完成从基本到复杂的任务，适合从初学者到老用户。
>
>
>

现在漂亮的开源文本编辑器很常见，有 Adobe 的 [Brackets](https://opensource.com/article/20/12/brackets)、微软的 [VSCode](https://opensource.com/article/20/12/%C2%A0https://opensource.com/article/20/6/open-source-alternatives-vs-code) 和 GitHub 的 [Atom](https://opensource.com/article/17/5/atom-text-editor-packages-writers)。这些似乎都提供了类似的体验：现代化的界面、易于安装的插件，以及背后的一个大厂商支持。而且它们其实都很不错。那么 Atom 与其他超现代的文本编辑器有什么不同呢？与经典的老式编辑器如 [Vim](https://opensource.com/article/20/12/vi-text-editor) 或 [Emacs](https://opensource.com/article/20/12/emacs) 有何区别呢？

我用过很多文本编辑器，并且经过反思，我不得不承认，所有的文本编辑器基本上都差不多。在判断一个编辑器的功效时，只要它能做一件事：编辑文字，就能满足 80% 的要求。另外的 20% 则是额外的便利、额外的小玩意和花哨的功能。它们是很好的东西，但几乎不是必不可少的。

不过我经常回到 Atom，因为作为一个开源的用户，只要我愿意，我就可以奢侈地有很多应用使用。以下是我喜欢 Atom 的原因。

### 新手友好

我最喜欢 Atom 的一点是，它让人感觉很“正常”。我可以把 Atom 安装在任何人的电脑上，他们很快就可以开始输入了。没有新的键盘快捷键需要学习，没有严重偏离用户界面的惯例。如果我花几分钟时间向他们展示一些应用的强大功能，那么他们很快就会有能力安装新的插件，发现他们喜欢的有用功能。

它的独特之处在于让人感到与众不同，但又足够“安全”，足以让人相信（也确实如此）他们可以使用它。这是一条很难走的路线，但 Atom 做到了，我很欣赏它。

### 强大的扩展

当你启动应用就已经满足了大多数需求时，开源文本编辑器的一个主要“卖点”就是它的扩展。我习惯使用的编辑器是 [GNU Emacs](https://opensource.com/article/20/2/who-cares-about-emacs)，它的扩展功能多到令人瞠目结舌，可以提供从电子邮件客户端到视频游戏的所有功能。要做到这一点很难，老实说，我还没有看到其它能做到的编辑器。不过这说明了扩展是多么重要，而 Atom 有一套不错的插件。

有一些扩展可以为语言和格式添加语法高亮、添加动态格式检查、以及集成调试器、运行时环境、视频和音乐播放器控件等等。实际上，你可以让 Atom 成为你桌面的控制中心，你很少需要离开它。

### 语言和语法支持

我是 [Docbook](https://opensource.com/article/17/9/docbook) 的忠实粉丝。顺便说一下，也是它的简化前端 Asciidoc 的粉丝。当我评估一个编辑器时，Docbook 模式和 Asciidoc 支持是我的两个主要指标。虽然对 XML 的支持比较常见，但与特定模式的集成可能是一个高难度的任务，而且 Asciidoc 相对小众。Atom 的社区为我最喜欢的格式提供了很好的支持。

当然，我已经提到了 Atom 一般来说有很好的扩展，但无论你在输入什么语言，语法高亮都是一个重要的功能。再次感谢充满活力的社区，在 Atom 的软件包仓库中，丰富的语法高亮选项的是它的优点之一。

### 简单定制主题

Atom 让你制作自己的风格就像设计网站的风格一样简单，所以如果你会使用 CSS，你可以制作你自己的 Atom 主题。要创建你自己的主题，找到<ruby> 软件包 <rt>  Package </rt></ruby>菜单。如果你没有看到<ruby> 软件包 <rt>  Package </rt></ruby>菜单，先按 `Alt` 键，露出顶部菜单栏。在<ruby> 软件包 <rt>  Package </rt></ruby>菜单中，将鼠标悬停在<ruby> 软件包生成器 <rt>  Package Generator </rt></ruby> 上，然后选择<ruby> Generate Atom Syntax Theme <rt>  生成 Atom 语法主题 </rt></ruby>。这将打开一个新的项目，默认名为 “my-theme-syntax”。你可以把它命名为任何你想要的名字，但根据 Atom 惯例，它应该以 `-syntax` 结尾。

在你的新主题项目中，找到这些文件：`base.less`、`colors.less` 和 `syntax-variables.less`。这些文件定义了当语法激活时，特殊的关键字，甚至是背景和前景的颜色是如何显示的。因为它们都是从一个通用模板中继承的值，所以很容易修改。比如说：

```
// Syntax styles

.syntax--comment {
  color: @light-gray;
}

.syntax--keyword {
  color: @purple;

  &.syntax--control {
    color: @purple;
  }

  &.syntax--operator {
    color: @syntax-text-color;
  }

  &.syntax--other.syntax--special-method {
    color: @blue;
  }

  &.syntax--other.syntax--unit {
    color: @orange;
  }
}

```

以两个破折号结尾的值，如 `.syntax--keyword`，是语法高亮引擎识别的对象。当然，如果你想进一步开发你的自定义设置，你甚至可以创建你自己的语法定义（尽管这比 CSS 主题更费劲）。在 [flight-manual.atom.io](https://flight-manual.atom.io/) 上可以阅读所有关于修改 Atom 的方法。

### 灵活的工作流

Atom 有很多功能，默认情况下仅激活其中一部分。这意味着你可以决定你喜欢的工作方式，是否激活新的扩展并使用它们从根本上改变 Atom，还是只打开 Atom 的偏好并进行小的调整。你可以用 Atom 来写一本小说，也可以用它来写 Python 代码或技术文档或其他任何东西。

即使是它的 Git 集成也没有坚持让你使用可能会想到的显而易见的仓库（Github 赞助了 Atom）。它没有任何限制，而且它对每个人都同样有用，不管受众是谁。

### 安装

在 Linux、Windows 和 macOS 上，你可以 [从它的网站安装 Atom](https://atom.io)。

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

如果你想自己构建 Atom，你也可以 [从 Github 上的源码编译它](https://github.com/atom)。

### 尝试 Atom

Atom 可以成为你的下一个文本编辑器、记事本和 IDE。它易于使用、易于配置、易于扩展，并提供了良好的用户体验。今天就下载 Atom，试试吧。

---

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

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

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


三种 Linux 下的 SSH 图形界面工具

# 三种 Linux 下的 SSH 图形界面工具

在你作为 Linux 系统管理员的职业生涯中，你可以使用 Secure Shell（SSH）远程访问 Linux 服务器或桌面系统。很有可能，你已经使用过了。在某些情况下，你会通过 SSH 一次性登录多个 Linux 服务器。实际上，SSH 很可能是 Linux 工具箱中最常用的工具之一。因此，你会希望操作尽可能高效。对于许多系统管理员来说，没有什么比命令行更有效的了。但是，有些用户确实更喜欢 GUI 工具，尤其是从一台桌面台式机远程连接到服务器上工作时。

如果你碰巧喜欢 GUI 工具，那么你肯定想了解一下 Linux 上的一些出色的 SSH 图形界面工具。将其与独特的终端窗口相结合，可以从同一窗口远程访问多台计算机，你便拥有了高效工作所需的一切。让我们看以下这三个工具，找出其中一个（或多个）是否完全适合你的需求。

我将在 [Elementary OS](https://elementary.io/) 上演示这些工具，但是大多数流行的发行版都可以使用它们。

### PuTTY

只要是久经沙场的人都知道 [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html)。 实际上，PuTTY 是从 Windows 环境通过 SSH 连接到 Linux 服务器的事实标准工具。但是 PuTTY 不仅适用于 Windows。实际上，从其标准存储库中，PuTTY 也可以安装到 Linux 上。 PuTTY 的功能列表包括：

* 保存会话
* 通过 IP 地址或主机名链接
* 定义备用 SSH 端口
* 链接类型定义
* 日志记录
* 键盘、响铃、外观、连接等选项
* 本地和远程隧道配置
* 支持代理
* 支持X11 隧道

PuTTY GUI 主要是一种保存 SSH 会话的方式，因此，你可以更轻松地管理那些你需要不断远程登录、登出的各种 Linux 服务器和桌面台式机。从 PuTTY 连接到 Linux 服务器后，你将拥有一个可以运行的终端窗口。此时，你可能会问自己，为什么不只在终端窗口中工作？对于某些人来说，保存会话的便捷性确实使 PuTTY 值得使用。

在 Linux 上安装 PuTTY 很简单。例如，你可以在基于 Debian 的发行版上执行命令：

```
sudo apt-get install -y putty

```

安装后，你可以从桌面菜单运行 PuTTY GUI 或执行命令 `putty`。在“<ruby> PuTTY 配置 <rt>  PuTTY Configuration </rt></ruby>” 窗口（图 1）中，在 “<ruby> 主机名（或 IP 地址） <rt>  HostName (or IP address) </rt></ruby>”位置键入主机名或 IP 地址，配置<ruby> 端口 <rt>  Port </rt></ruby>”（如果不是默认值 22），从“<ruby> 连接类型 <rt>  Connection type </rt></ruby>”中选择 “SSH” ，然后单击“<ruby> 打开 <rt>  Open </rt></ruby>”。

*图 1：PuTTY 连接配置窗口。*

建立连接后，系统将提示你输入远程服务器上的用户凭据（图 2）。

*图 2：使用 PuTTY 登录到远程服务器。*

要保存会话（以便不必总是键入远程服务器信息），填写 IP 地址（或主机名），配置端口和连接类型，然后（在单击 “<ruby> 打开 <rt>  Open </rt></ruby>” 之前），在 “<ruby> 保存会话 <rt>  Saved Sessions </rt></ruby>” 顶部文本区域中键入链接的名称，然后单击 “<ruby> 保存 <rt>  Save </rt></ruby>”。 这样将保存此会话的配置。若要连接到已保存的会话，请从 “<ruby> 已保存的会话 <rt>  Saved Sessions </rt></ruby>” 窗口中选择它，单击 “<ruby> 加载 <rt>  Load </rt></ruby>”，然后单击 “<ruby> 打开 <rt>  Open </rt></ruby>”。 然后，系统将提示你输入远程服务器上的远程凭据登录远程服务器。

### EasySSH

尽管 [EasySSH](https://github.com/muriloventuroso/easyssh) 没有提供 PuTTY 中提供的大量配置选项，但是（顾名思义）它非常易于使用。 EasySSH 的最佳功能之一是提供标签式界面，因此你可以打开多个 SSH 连接并在它们之间快速切换。 EasySSH 的功能包括：

* 分组（你可以将选项卡分组以获得更高效的体验）。
* 保存用户名/密码。
* 外观选项。
* 支持本地和远程隧道。

在 Linux 桌面上安装 EasySSH 很简单，因为可以通过 flatpak 安装该应用程序（这意味着你必须在系统上安装 Flatpak ）。 安装 flatpak 后，使用以下命令添加 EasySSH ：

```
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo

sudo flatpak install flathub com.github.muriloventuroso.easyssh

```

使用以下命令运行 EasySSH ：

```
flatpak run com.github.muriloventuroso.easyssh

```

你可以在其中单击左上角的 “+” 按钮打开 EasySSH 应用程序。 在出现的窗口（图 3）中，根据需要配置 SSH 连接。

*图 3：在 EasySSH 中添加连接很简单。*

添加连接后，它将显示在主窗口的左侧导航中（图 4）。

*图 4：EasySSH 主窗口。*

要在 EasySSH 中连接到远程服务器，请从左侧导航中选择它，然后单击 “<ruby> 连接 <rt>  Connect </rt></ruby>” 按钮（图 5）。

*图 5：使用 EasySSH 连接到远程服务器。*

EasySSH 的一个注意事项是你必须在连接配置中保存用户名和密码（否则连接将失败）。这意味着有权访问运行 EasySSH 的桌面的任何人都可以在不知道密码的情况下远程访问你的服务器。因此，你必须始终记得在外出时锁定桌面屏幕（并确保使用强密码）。你最不希望的就是避免服务器受到不必要的登录攻击。

### Terminator

Terminator 实际上不是 SSH GUI。相反，Terminator 的功能是作为一个单一窗口，使你可以一次运行多个终端（甚至一组终端）。实际上，你可以打开 Terminator，将窗口垂直和水平拆分（直到拥有所需的所有终端），然后通过标准 SSH 命令连接到所有远程 Linux 服务器（图 6）。

*图 6：Terminator 分为三个不同的窗口，每个窗口都连接到不同的 Linux 服务器。*

要安装 Terminator，请执行以下命令：

```
sudo apt-get install -y terminator

```

安装后，从桌面菜单或用命令 `terminator` 打开该工具。打开窗口后，你可以在 Terminator 内部右键单击，然后选择 “<ruby> 水平分割 <rt>  Split Horizontally </rt></ruby>” 或 “<ruby> 垂直分割 <rt>  Split Vertically </rt></ruby>”。继续拆分终端，直到你打开所需的终端为止，然后开始远程管理这些服务器。使用 Terminator 的注意事项是它不是标准的 SSH GUI 工具，因为它不会保存你的会话或使你可以快速访问这些服务器。换句话说，你将始终必须手动登录到远程 Linux 服务器。但是，能够并行查看远程 Secure Shell 会话确实使管理多个远程计算机变得容易得多。

### 少而精的选择

Linux 没有多少可用的 SSH GUI 工具。为什么呢？因为大多数管理员更喜欢简单地打开终端窗口，并使用标准命令行工具来远程访问服务器。但是，如果需要 GUI 工具，则有两个可靠的选择，和一个使登录多台计算机稍微容易一些的终端。尽管对于那些寻找 SSH GUI 工具的人来说只有少数选择，但是可用的那些肯定值得你花时间，根据需要尝试其中之一。

---

via: <https://www.linux.com/blog/learn/intro-to-linux/2018/11/three-ssh-guis-linux>

作者：[Jack Wallen](https://www.linux.com/users/jlwallen) 选题：[lujun9972](https://github.com/lujun9972) 译者：[stevenzdg988](https://github.com/stevenzdg988) 校对：[wxy](https://github.com/wxy)

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


从 Linux 命令行进行打印

Summary: 在 Linux 命令行进行打印的内容比单单一个 lp 命令多得多，让我们来看一些可用选项。

# 从 Linux 命令行进行打印

>
> 在 Linux 命令行进行打印的内容比单单一个 `lp` 命令多得多，让我们来看一些可用选项。
>
>
>

<img src="content_image/album_202101_13_230317cm4cjuiti44pgv8o.jpg" alt="Sherry " title="Sherry ">

Linux 命令行打印很容易。你可以使用 `lp` 命令来请求打印，并使用 `lpq` 命令来查看队列中有哪些打印作业，但是当你要双面打印或使用纵向模式时，这些会变得有些复杂。你可能还需要做很多其他事情，例如打印多份文档副本或取消打印作业。让我们来看看一些选项，当你从命令行打印时，如何让你的打印输出看起来如你所愿。

### 显示打印机配置

要从命令行查看打印机设置，请使用 `lpoptions` 命令。 输出应如下所示：

```
$ lpoptions
copies=1 device-uri=dnssd://HP%20Color%20LaserJet%20CP2025dn%20(F47468)._pdl-datastream._tcp.local/ finishings=3 job-cancel-after=10800 job-hold-until=no-hold job-priority=50 job-sheets=none,none marker-change-time=1553023232 marker-colors=#000000,#00FFFF,#FF00FF,#FFFF00 marker-levels=18,62,62,63 marker-names='Black\ Cartridge\ HP\ CC530A,Cyan\ Cartridge\ HP\ CC531A,Magenta\ Cartridge\ HP\ CC533A,Yellow\ Cartridge\ HP\ CC532A' marker-types=toner,toner,toner,toner number-up=1 printer-commands=none printer-info='HP Color LaserJet CP2025dn (F47468)' printer-is-accepting-jobs=true printer-is-shared=true printer-is-temporary=false printer-location printer-make-and-model='HP Color LaserJet cp2025dn pcl3, hpcups 3.18.7' printer-state=3 printer-state-change-time=1553023232 printer-state-reasons=none printer-type=167964 printer-uri-supported=ipp://localhost/printers/Color-LaserJet-CP2025dn sides=one-sided

```

如果将其空格转换为回车符，输出可能会更人性化，请注意列出了多少设置选项。

注意：在下面的输出中，一些行被重新链接，以使输出更具可读性。

```
$ lpoptions | tr " " '\n'
copies=1
device-uri=dnssd://HP%20Color%20LaserJet%20CP2025dn%20(F47468)._pdl-datastream._tcp.local/
finishings=3
job-cancel-after=10800
job-hold-until=no-hold
job-priority=50
job-sheets=none,none
marker-change-time=1553023232
marker-colors=#000000,#00FFFF,#FF00FF,#FFFF00
marker-levels=18,62,62,63
marker-names='Black\ Cartridge\ HP\ CC530A,
Cyan\ Cartridge\ HP\ CC531A,
Magenta\ Cartridge\ HP\ CC533A,
Yellow\ Cartridge\ HP\ CC532A'
marker-types=toner,toner,toner,toner
number-up=1
printer-commands=none
printer-info='HP Color LaserJet CP2025dn (F47468)'
printer-is-accepting-jobs=true
printer-is-shared=true
printer-is-temporary=false
printer-location
printer-make-and-model='HP Color LaserJet cp2025dn pcl3, hpcups 3.18.7'
printer-state=3
printer-state-change-time=1553023232
printer-state-reasons=none
printer-type=167964
printer-uri-supported=ipp://localhost/printers/Color-LaserJet-CP2025dn
sides=one-sided

```

使用 `-v` 选项时，`lpinfo` 命令将列出驱动程序和相关信息：

```
$ lpinfo -v
network ipp
network https
network socket
network beh
direct hp
network lpd
file cups-brf:/
network ipps
network http
direct hpfax
network dnssd://HP%20Color%20LaserJet%20CP2025dn%20(F47468)._pdl-datastream._tcp.local/ <== printer
network socket://192.168.0.23 <== printer IP

```

`lpoptions` 命令将显示默认打印机的设置。使用 `-p` 选项指定其中一个可用打印机代号：

```
$ lpoptions -p LaserJet

```

`lpstat -p` 命令显示打印机的状态，而 `lpstat -p -d` 列出可用打印机列表。

```
$ lpstat -p -d
printer Color-LaserJet-CP2025dn is idle. enabled since Tue 19 Mar 2019 05:07:45 PM EDT
system default destination: Color-LaserJet-CP2025dn

```

### 非常有用的命令

要在默认打印机上打印文档，只需使用 `lp` 命令，后跟要打印的文件名即可。 如果文件名包含空格（在 Linux 系统上很少见），请将该名称放在引号中或开始输入文件名并按 `Tab` 键调用空格的转义标志（如下面的第二个示例所示）。

```
$ lp "never leave home angry"
$ lp never\ leave\ home\ angry

```

`lpq` 命令显示打印队列：

```
$ lpq
Color-LaserJet-CP2025dn is ready and printing
Rank Owner Job File(s) Total Size
active shs 234 agenda 2048 bytes

```

使用 `-n` 选项时，`lp` 命令可用来指定所需打印输出的份数：

```
$ lp -n 11 agenda

```

要取消打印作业，可以使用 `cancel` 或 `lprm` 命令。如果没来得及执行，则可能会看到以下信息：

```
$ cancel 229
cancel: cancel-job failed: Job #229 is already completed - can't cancel.

```

### 双面打印

要以双面模式打印，你可以在 `lp` 命令中使用 `sides` 选项，该选项不但表示了在纸张的正反面进行打印，还表示了从纸张的哪个边开始打印。这个设置代表了你期望以双面纵向文档的正常方式打印。

```
$ lp -o sides=two-sided-long-edge Notes.pdf

```

如果要所有文档以双面模式打印，则可以使用 `lpoptions` 命令更改 `sides` 设置以修改 `lp` 的设置。

```
$ lpoptions -o sides=two-sided-short-edge

```

要恢复为单面打印，可以使用如下命令：

```
$ lpoptions -o sides=one-sided

```

#### 横向打印

要以横向模式打印，可以在 `lp` 命令中使用 `landscape` 选项。

```
$ lp -o landscape penguin.jpg

```

### CUPS（Unix 通用打印系统）

Linux 系统上使用的打印系统是基于标准的开源打印系统，称为 **CUPS**，原意是<ruby> Unix 通用打印系统 <rt>  Common Unix Printing System </rt></ruby>。 它允许计算机充当打印服务器。

---

via: <https://www.networkworld.com/article/3373502/printing-from-the-linux-command-line.html>

作者：[Sandra Henry-Stocker](https://www.networkworld.com/author/Sandra-Henry_Stocker/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[stevenzdg988](https://github.com/stevenzdg988) 校对：[wxy](https://github.com/wxy)

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


通过编写一个简单的游戏学习 C 语言

当年我的老师在 APPLE II 上用 pascal 语言也写了一个猜数游戏，不过是由人来出一个四位数字，然后电脑来猜这个数是什么。

# 通过编写一个简单的游戏学习 C 语言

>
> 当你学习一门新的编程语言时，这个“猜数字”游戏是一个很好的入门程序。下面是如何用 C 语言来编写它。
>
>
>

<img src="content_image/album_202101_13_232713lh91stdahcxfyfhe.jpg" alt="" title="">

我在小学时就开始 [自学编程](https://opensource.com/article/20/8/learn-open-source)。我的第一个程序是在 Apple II 上编写的，但最终，我还是通过看书和编程练习学会了 C 语言。练习编程的最佳方法是编写示例程序，它能帮助你练习新知识。

在我学习一种新的编程语言时，我喜欢编写一个简单的“猜数字”游戏来练习。电脑从 1 到 100 中随机挑选一个数字，你必须通过猜测来算出来。在另一篇文章中，我展示了如何用 Bash 语言编写这个[“猜数字”游戏](/article-12962-1.html)，我的同事也写了一些文章，介绍如何用 [Java](https://opensource.com/article/20/12/learn-java)、[Julia](https://opensource.com/article/20/12/julia) 和其他计算机语言编写它。

“猜数字”游戏的伟大之处在于它践行了几个编程概念：如何使用变量、如何比较值、如何打印输出以及如何读取输入。

整个夏天，我录制了[一个系列视频](https://opensource.com/article/20/8/teaching-c)，教人们如何用 [C 语言](https://opensource.com/article/20/8/c-programming-cheat-sheet)编写程序。从那以后，我听到了很多人都在跟着它学习 C 语言编程的消息。所以，我想接着用 C 语言写一个“猜数字”的游戏。

### 去取一个随机数

从写一个函数来选择一个随机数字来开始“猜数字”游戏。在编写函数时，优秀的程序员会尽量使它们具有灵活性，这样他们就可以重用它们来解决略有不同的问题。因此，与其硬编码函数来选择 1 到 100 之间的一个随机数，不如编写函数来选择 1 到某个整数 `maxval` 之间的一个随机数：

```
#include <stdio.h>
#include <sys/random.h>

int
randnum(int maxval)
{
  /* pick a random number from 1 to maxval */

  int randval;

  getrandom(&randval, sizeof(int), GRND_NONBLOCK);

  /* could be negative, so ensure it's positive */

  if (randval < 0) {
    return (-1 * randval % maxval + 1);
  }
  else {
    return (randval % maxval + 1);
  }
}

```

该函数使用 Linux 的系统调用 `getrandom` 来生成一系列随机数。你可以在手册页中了解关于这个系统调用的更多信息，但请注意，`getrandom` 将用随机的 0 和 1 填充变量。这意味着最终值可以是正的，也可以是负的，因此你需要在之后进行测试，以确保 `randnum` 函数的结果是正值。

### 编写程序

你可以用这个函数来写你的“猜数字”程序:

```
#include <stdio.h>
#include <sys/random.h>

int
randnum(int maxval)
{
  ...
}

int
main(void)
{
  int number;
  int guess;

  number = randnum(100);

  puts("Guess a number between 1 and 100");

  do {
    scanf("%d", &guess);

    if (guess < number) {
      puts("Too low");
    }
    else if (guess > number) {
      puts("Too high");
    }
  } while (guess != number);

  puts("That's right!");

  return 0;
}

```

程序首先使用 `randnum` 函数从 1 到 100 之间选择一个随机数。在向用户输出一个提示后，程序进入一个 `do-while` 循环，以便用户可以猜测数字。

在循环的每次迭代中，程序测试用户的猜测的数值。如果用户的猜测小于随机数，程序将输出“Too low”，如果猜测大于随机数，程序将输出“Too high”。循环继续，直到用户的猜测与随机数相同。

当循环退出时，程序输出 “That's right!”，然后立即结束：

```
$ gcc -o guess -Wall guess.c

$ ./guess
Guess a number between 1 and 100
50
Too high
30
Too low
40
Too low
45
Too high
42
Too low
43
Too low
44
That's right!

```

### 尝试动手

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

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

---

via: <https://opensource.com/article/21/1/learn-c>

作者：[Jim Hall](https://opensource.com/users/jim-hall) 选题：[lujun9972](https://github.com/lujun9972) 译者：[qfzy1233](https://github.com/qfzy1233) 校对：[wxy](https://github.com/wxy)

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


用 Portainer.io 来监控和管理 Docker 容器（2）

Summary: 作为第 1 部分的延续，本第 2 部分将介绍 Portainer 的其余功能。

好是挺好  就是不支持中文啊..

作为第 1 部分的延续，本第 2 部分将介绍 Portainer 的其余功能。

# 用 Portainer.io 来监控和管理 Docker 容器（2）

作为[第 1 部分](/article-12634-1.html)的延续，本第 2 部分将介绍 Portainer 的其余功能。

### 监控 docker 容器镜像

```
root@linuxtechi ~}$ docker ps -a
CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS   PORTS                             NAMES
9ab9aa72f015        ubuntu                "/bin/bash"         14 seconds ago      Exited (0) 12 seconds ago                  suspicious_shannon
305369d3b2bb        centos                "/bin/bash"         24 seconds ago      Exited (0) 22 seconds ago                  admiring_mestorf
9a669f3dc4f6        portainer/portainer   "/portainer"        7 minutes ago       Up 7 minutes   0.0.0.0:9000->9000/tcp      trusting_keller

```

包括 `portainer`（docker 容器镜像），所有已退出和当前正在运行的 docker 镜像都会显示出来。下面的 Portainer GUI 屏幕截图显示了相同的情况。

### 监视事件

单击 portainer 网页中的“Events”选项，如下所示。

基于 docker 容器活动生成和创建的各种事件将被提取并显示在此页面中.

现在检查并验证“Events”部分是如何工作的。创建一个新的 docker 容器镜像 `redis`，如下所述，在 docker 命令行检查状态：`docker ps –a`：

```
root@linuxtechi ~}$ docker ps -a
CONTAINER ID        IMAGE                 COMMAND                  CREATED              STATUS         PORTS                    NAMES
cdbfbef59c31        redis                 "docker-entrypoint.s…"   About a minute ago   Up About a minute         6379/tcp                 angry_varahamihira
9ab9aa72f015        ubuntu                "/bin/bash"              10 minutes ago       Exited (0) 10 minutes ago                            suspicious_shannon
305369d3b2bb        centos                "/bin/bash"              11 minutes ago       Exited (0) 11 minutes ago                            admiring_mestorf
9a669f3dc4f6        portainer/portainer   "/portainer"             17 minutes ago       Up 17 minutes         0.0.0.0:9000->9000/tcp   trusting_keller

```

单击顶部的“Event List”刷新事件列表，

现在事件的页面也更新了这个变化，

### 主机状态

下面是 portainer 显示主机状态的屏幕截图。这是一个简单的窗口。这显示了主机 Linux 机器的基本信息，如“CPU”、“主机名”、“操作系统信息”等。这个页面在不需要登录主机命令行的情况下提供了非常有用的信息，以便快速浏览。

### Portainer 中的仪表板

到目前为止，我们已经在“Local”部分看到了基于 portainer 的各种特性。现在跳到所选 Docker 容器镜像的“Dashboard”部分。

在 Portainer 的网页中单击“EndPoint”选项时，会出现以下窗口：

对于主机容器镜像，此仪表板有许多状态和选项。

#### Stacks

单击此选项可提供任何堆栈（如果有的话）的状态。因为这里没有堆栈，所以显示为零。

#### Images

单击此选项可提供主机中可用的容器镜像。此选项将显示所有活动和退出的容器镜像。

例如，再创建一个“Nginx”容器并刷新此列表以查看更新：

```
root@linuxtechi ~}$  sudo docker run nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
27833a3ba0a5: Pull complete
ea005e36e544: Pull complete
d172c7f0578d: Pull complete
Digest: sha256:e71b1bf4281f25533cf15e6e5f9be4dac74d2328152edf7ecde23abc54e16c1c
Status: Downloaded newer image for nginx:latest

```

下面是刷新后的镜像界面：

当 Nginx 镜像处于 `stopped`/`killed` 状态时，docker 的容器镜像会改变为 `unused` 状态。

**注**：你可以看到这里所有的镜像的细节都非常清楚，比如内存使用，创建日期和时间。与命令行选项相比，从这里维护和监视容器将非常容易。

#### Networks

此选项用于网络操作。例如分配 IP 地址、创建子网、提供 IP 地址范围、访问控制（管理员和普通用户）。下面的窗口提供了各种可能选项的详细信息。根据你的需要，可以进一步去自行研究这些选项。

输入所有各种网络参数后，单击“create network”按钮即可创建网络。

#### Container

此选项将提供容器状态。此列表将提供有关活动的和未运行的容器状态的详细信息。此输出类似于 docker ps 命令选项。

在该窗口中，通过选中复选框并选择上述按钮可以根据需要控制容器停止和启动。一个例子如下：

例如，“CentOS”和“Ubuntu”容器都处于停止状态，现在可以通过选中复选框并点击“Start”按钮来启动它们。

**注意：** 因为这两个都是 Linux 容器镜像，所以不会被启动。Portainer 尝试启动，但稍后又停止。试试启动“Nginx”，你会看到它变成了 `running` 状态。

#### Volume

参见本文章第一部分。

### Portainer 中的设置选项

到目前为止，我们已经在“Local”部分看到了基于 portainer 的各种特性。现在跳到所选 Docker 容器图像的“Settings”部分。

在 Portainer 的网页中单击“Settings”选项时，可以使用以下的配置选项：

#### Extensions

这是一个简单的 Portainer CE 订阅程序。详细信息和用途可以从附加的窗口中看到。这主要用于维护相应版本的许可证和订阅。

#### Users

此选项用于添加具有或不具有管理权限的“用户”。下面的示例提供了相同的示例。

在本例中输入你的想好的用户名比如“shashi”和你选择的密码，然后点击下面的“Create User”按钮。

类似地，可以通过选中复选框并点击 “remove” 按钮来删除刚刚创建的用户“shashi”。

#### Endpoints

此选项用于端点终端管理。终端可以添加和删除，如附加窗口中所示。

新终端“shashi”是使用如下所示的各种默认参数创建的，

类似地，可以通过单击复选框并单击移除按钮来移除此端点。

#### Registries

此选项用于注册管理。由于 docker hub 有各种镜像的注册，因此此功能可用于类似的目的。

使用默认选项就可以创建“shashi-registry”。

同样，如果不需要了，就可以移除它。

#### Settings

此选项用于设置以下各种选项，

* 设置快照间隔
* 设置自定义徽标
* 创建外部模板
* 安全功能，如：为非管理员禁用和启用 bin 挂载，为非管理员禁用/启用权限，启用主机管理功能

下面的屏幕截图显示了出于演示目的启用和禁用的一些选项。一旦全部完成点击“保存设置”按钮保存所有这些选项。

现在点开“Authentication settings”就会弹出 LDAP、Internal 和 OAuth（extension）选项，如下所示：

根据我们想要的环境安全特性级别，选择相应的选项。

以上就是本文的内容，我希望这些介绍 portainer 的文章能帮助你更有效地管理和监视容器。请分享你的反馈和意见。

---

via: <https://www.linuxtechi.com/monitor-manage-docker-containers-portainer-io-part-2/>

作者：[Shashidhar Soppin](https://www.linuxtechi.com/author/shashidhar/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[Chao-zhi](https://github.com/Chao-zhi) 校对：[wxy](https://github.com/wxy)

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


我最喜欢的 3 个开源生产力应用

Summary: 简化你的敏捷工作流程，提高你的工作效率。

我的

mpv

V2rayU

iTerm2

简化你的敏捷工作流程，提高你的工作效率。

# 我最喜欢的 3 个开源生产力应用

>
> 简化你的敏捷工作流程，提高你的工作效率。
>
>
>

生产力应用确实可以让你的工作流程变得更加轻松。在本文中，我将分享一些我用来简化工作流程、提高整体生产力的开源应用。本文中所有的生产力应用都是免费的 Linux 生产力应用。

### Tomboy/Gnote

[Tomboy](https://wiki.gnome.org/Apps/Tomboy) 是一款可以在 Linux、Windows 和 macOS 上使用的简单记事本应用。它是 GNU LGPLv2 下许可的开源软件。

Tomboy 使用起来非常简单。你写下一张纸条，选择是否将它贴在桌面上，完成后就可以删除它。

它（以及它的克隆版 [Gnote](https://wiki.gnome.org/Apps/Gnote)）是一个很好的快速记笔记的小程序。很多时候，当我在做某件事情的时候，会有一些想法或思路想要回忆。Tomboy 可以让我快速创建一个笔记，在我忘记之前把我的想法记下来。然后我就可以把这些想法转移到一个更长久的地方。

### Joplin

[Joplin](https://joplinapp.org/) 是一个开源的记事和待办事项应用。它是跨平台的，可以在 Linux、Windows、macOS、iOS 和 Android 上使用，并且采用 MIT 许可开源。

它可以使用 Dropbox、OneDrive、Nextcloud 等云同步服务进行同步。它很容易安装，可能就在你的 Linux 仓库里。当你在桌面上安装它时，它一个应用中实际包含了两个：你会得到一个标准的图形用户界面 （GUI），或者打开一个终端，在那里使用 Joplin。

桌面版有一个非常漂亮的界面。笔记被组织在笔记本中，这基本上使它们成为你的手册页。而且因为笔记是 Markdown 格式的，所以它们会被渲染，你可以实时编辑它们。我喜欢使用 Markdown，因为它让我写笔记的速度很快。你也可以导出或导入 Joplin 笔记。

### Evolution

[Evolution](https://wiki.gnome.org/Apps/Evolution) 是一款开源的个人信息管理应用，它与 Outlook 非常相似，但我认为更适合我使用。它具有电子邮件、工作、笔记、链接、日历和地址簿功能。它是 LGPL 和其他[许可证](https://gitlab.gnome.org/GNOME/evolution/-/blob/master/COPYING)下的开源软件。

我在运行 Fedora 的台式电脑上使用它。它的效率很高，绝对能帮助我度过繁忙的一天。它让我能够使用 Linux 开展业务；我还能要求什么呢？

要在 Fedora 中使用它，打开一个终端并输入以下命令：

```
sudo dnf remove evolution
sudo dnf update
sudo dnf install evolution
sudo dnf install evolution-ews

```

### 我的常用工具

这些工具是我的主力军，我已经依赖了一段时间。使用它们超越了它们的基本功能，使我更有效率、更有效果、更有生产力。作为一名技术产品经理和敏捷专家，我很自豪，我仍然使用 Linux 和其他开源软件，即使我工作的公司不这样做。

---

*本文原载于 [Course Hero](https://www.coursehero.com/file/74086904/Personal-productivity-using-open-source-software-tools/)，经授权转载。*

---

via: <https://opensource.com/article/21/1/open-source-productivity-apps>

作者：[Taz Brown](https://opensource.com/users/heronthecli) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


学习 Python 的好文章

fgggggggggggggggg

# 学习 Python 的好文章

>
> 无论你在 Python 编程过程中处于什么阶段，这些 Python 热门文章将会对你有很大帮助。
>
>
>

<img src="content_image/album_202101_15_231657xt8085wtyk5yvsz5.jpg" alt="" title="">

2019 年是 Python 的好年景。根据 [GitHub](https://octoverse.github.com/#top-languages) 和 [Stack Overflow](https://insights.stackoverflow.com/survey/2019) 的受欢迎资源程度来看，它正在成为全球第二大流行语言。（LCTT 译注：原文发表于 2019 年底，但是这里提及的文章并没有过时。）

>
> “在我们的调查中，Python 作为增长最快的程序设计语言，在程序设计语言中排名再次上升，今年排在 Java 之前，成为第二大最受欢迎的程序设计语言（仅次于 Rust ）。”
>
>
> — [Stack Overflow Insights](https://insights.stackoverflow.com/survey/2019)
>
>
>

同样，Python 的读者人数呈跳跃式激增。以下是按主题分组的 2019 年以来最热门的 Python 文章，供你仔细阅读。

### 为什么选择 Python ？

在众多的程序设计语言中，是什么使 Python 成为首选呢？从文章的阅读量来看，那就是因为它的灵活性。正如 Jigyasa Grover 解释的那样，Python 开发人员可以使用 [多种范例](https://opensource.com/article/19/10/python-programming-paradigms)，包括 Seth Kenlon 教程所展示的流行的 [面向对象程序设计](https://opensource.com/article/19/7/get-modular-python-classes)。

如果你是 Python 的长期用户，并且正在寻找 Python 为什么是完美的程序设计语言的高级例子，那么可以看 Moshe Zadka 的 [喜欢 Python 的 5 大理由](https://opensource.com/article/19/10/why-love-python)。如果这还不够的话，你也可以使用功能强大的工具来尝试，无需编写大量代码，例如 Parul Pandey 关于 [图像处理](/article-10679-1.html) 的教程。

### 配置 Python

随着 Python 的受欢迎程度不断提高，使用它的人越来越多。这些新手中的许多人都是在 Mac 操作系统上进行的，并且正在使用 Moshe 和我写的 [Python3 配置向导](https://opensource.com/article/19/5/python-3-default-mac)。

安装 Python 之后，接下来就是决定利用什么工具编写代码。关于文本编辑器和集成开发环境（IDE），有很多选择，但是读者似乎更喜欢图形界面，在有关该主题的文章中，Stephan Avenwedde 的关于 [Pythonic](https://opensource.com/article/19/5/graphically-programming-pythonic) 和我关于 [JupyterLab](https://opensource.com/article/19/5/jupyterlab-python-developers-magic) 的文章的读者最多。

在对程序设计语言充满信心的途径上，开发人员将不得不面对众多选择，来管理程序设计语言的版本和项目依赖。幸运的是，László Kiss Kollár 的文章 [Python 包管理](https://opensource.com/article/19/4/managing-python-packages) 让其变得更加容易。

当你准备好配置一个具有所有功能的 IDE，以最大限度地利用这门语言时，请一定尝试一下 [linter Black](/article-10864-1.html)，如 Moshe 说的，保持代码的清洁。

### 小结

无论你处在 Python 程序设计的哪个阶段，这些热门 Python 文章都将为你提供帮助。如果没有至少一次对测试重要性的认可，我无法对此进行总结，为此，Moshe 提供了另一篇 [关于 tox](https://opensource.com/article/19/5/python-tox) 的好文章。

---

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

作者：[Matthew Broberg](https://opensource.com/users/mbbroberg) 选题：[lujun9972](https://github.com/lujun9972) 译者：[stevenzdg988](https://github.com/stevenzdg988) 校对：[wxy](https://github.com/wxy)

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


我为什么喜欢 FED 文本编辑器

# 我为什么喜欢 FED 文本编辑器

>
> FED 编辑器让我可以轻松有效地对我的 FreeDOS 项目进行编码。学习如何充分利用这个灵活的 Linux、Windows 和 DOS 编辑器。
>
>
>

当我不[在我的 Linux 桌面上工作](https://opensource.com/article/19/9/business-creators-open-source-tools)的时候，我通常是在为一个旧版的 16 位系统写代码。[FreeDOS](https://opensource.com/article/19/6/freedos-anniversary) 是一个开源的 DOS 兼容操作系统，你可以用它来玩经典的 DOS 游戏，运行旧版的商业软件，或者开发嵌入式系统。任何在 MS-DOS 上运行的程序在 FreeDOS 上也应该可以运行。

我是和 DOS 一起长大的。我家的第一台个人电脑是一台 Apple II 兼容机，但我们最终升级到了一台运行 DOS 的 IBM PC。从 1980 年代早期到 1993 年我[发现 Linux](https://opensource.com/article/17/5/how-i-got-started-linux-jim-hall-freedos) 这段时间，我做了十多年的 DOS 用户。

Linux 和开源软件所提供的自由度给我留下了深刻的印象。所以当微软在 1994 年宣布 DOS 的终结，以及即将推出的 Windows 95 时，我决定编写自己的开源 DOS。这就是 [FreeDOS 的开始](https://opensource.com/article/17/5/how-i-got-started-linux-jim-hall-freedos)。

这么多年过去了，我还在继续研究 FreeDOS。它是一个很好的业余兴趣系统，在这里我可以运行我最喜欢的 DOS 应用和游戏。是的，我仍然在为 FreeDOS 写代码。

我最喜欢的 DOS 编程编辑器是 FED 编辑器。FED 是一个极简的文本编辑器，没有太多的视觉效果。这种精简的方法帮助我充分利用了 DOS 中标准的 80x25 屏幕。当编辑一个文件时，FED 会在屏幕底部显示一行状态行，让你在剩下的 24 行来编写你的代码。FED 还支持彩色语法高亮显示，以不同的颜色显示代码的不同部分，使你更容易发现错别字，以免它们变成错误。

*用 FED 写一个纸牌游戏*

当你需要菜单时，按下键盘上的 `Alt` 键，FED 就会在最上面一行显示一个菜单。FED 也支持键盘快捷键，但要注意默认值。例如，`Ctrl-C` 会关闭文件，`Ctrl-V` 会改变视图。如果你不喜欢这些默认键，你可以在 **Config** 菜单中更改键位映射。

*按下 Alt 键弹出菜单*

如果你不喜欢默认的白底黑字显示，你可以在 **Config** 菜单下更改颜色。我更喜欢蓝底白字的正文，关键词用亮白色，注释用亮蓝色，特殊字符用青色，数字用绿色。FED 可以很容易地设置你想要的颜色。

*我在 DOS 上编程时喜欢的颜色*

FED 也是一个折叠式文本编辑器，这意味着它可以折叠或展开我的部分代码，以便我可以看到更多的文件。在函数名上按下 `Ctrl-F`，FED 会折叠整个函数。折叠在其他代码上也可以使用。我也使用折叠来隐藏 `for` 和 `while` 循环或其他流程控制，如 `if` 和 `switch` 块。

*折叠函数可以让你看到更多的文件*

Shawn Hargreaves 从 1994 年至 2004 年编写并维护 FED。Robert Riebisch 从那时起就开始维护 FED。FED 在 GNU GPL 许可下发布，支持 DOS、Linux 和 Windows。

你可以在 <https://www.bttr-software.de/products/fed/> 下载 FED。

---

via: <https://opensource.com/article/21/1/fed-editor>

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