在你的 Python 游戏中添加投掷机制

# 在你的 Python 游戏中添加投掷机制

>
> 四处奔跑躲避敌人是一回事，反击敌人是另一回事。学习如何在这系列的第十二篇文章中在 Pygame 中创建平台游戏。
>
>
>

这是仍在进行中的关于使用 [Pygame](https://www.pygame.org/news) 模块在 [Python 3](https://www.python.org/) 中创建电脑游戏的第十二部分。先前的文章是：

1. [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程](/article-9071-1.html)
2. [使用 Python 和 Pygame 模块构建一个游戏框架](/article-10850-1.html)
3. [如何在你的 Python 游戏中添加一个玩家](/article-10858-1.html)
4. [用 Pygame 使你的游戏角色移动起来](/article-10874-1.html)
5. [如何向你的 Python 游戏中添加一个敌人](/article-10883-1.html)
6. [在 Pygame 游戏中放置平台](/article-10902-1.html)
7. [在你的 Python 游戏中模拟引力](/article-11780-1.html)
8. [为你的 Python 平台类游戏添加跳跃功能](/article-11790-1.html)
9. [使你的 Python 游戏玩家能够向前和向后跑](/article-11819-1.html)
10. [在你的 Python 平台类游戏中放一些奖励](/article-11828-1.html)
11. [添加计分到你的 Python 游戏](/article-11839-1.html)

我的上一篇文章本来是这一系列文章的最后一篇，它鼓励你为这个游戏编写自己的附加程序。你们很多人都这么做了！我收到了一些电子邮件，要求帮助我还没有涵盖的常用机制：战斗。毕竟，跳起来躲避坏人是一回事，但是有时候让他们走开是一件非常令人满意的事。在电脑游戏中向你的敌人投掷一些物品是很常见的，不管是一个火球、一支箭、一道闪电，还是其它适合游戏的东西。

与迄今为止你在这个系列中为你的平台游戏编程的任何东西不同，可投掷物品有一个*生存时间*。在你投掷一个物品后，它会如期在移动一段距离后消失。如果它是一支箭或其它类似的东西，它可能会在通过屏幕的边缘时而消失。如果它是一个火球或一道闪电，它可能会在一段时间后熄灭。

这意味着每次生成一个可投掷的物品时，也必须生成一个独特的衡量其生存时间的标准。为了介绍这个概念，这篇文章演示如何一次只投掷一个物品。（换句话说，每次仅存在一个投掷物品）。 一方面，这是一个游戏的限制条件，但另一方面，它却是游戏本身的运行机制。你的玩家不能每次同时投掷 50 个火球，因为每次仅允许一个投掷物品，所以当你的玩家释放一个火球来尝试击中一名敌人就成为了一项挑战。而在幕后，这也使你的代码保持简单。

如果你想启用每次投掷多个项目，在完成这篇教程后，通过学习这篇教程所获取的知识来挑战你自己。

### 创建 Throwable 类

如果你跟随学习这系列的其它文章，那么你应该熟悉在屏幕上生成一个新的对象基础的 `__init__` 函数。这和你用来生成你的 [玩家](/article-10858-1.html) 和 [敌人](/article-10883-1.html) 的函数是一样的。这里是生成一个 `throwable` 对象的 `__init__` 函数来：

```
class Throwable(pygame.sprite.Sprite):
    """
    生成一个 throwable 对象
    """
    def __init__(self, x, y, img, throw):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(os.path.join('images',img))
        self.image.convert_alpha()
        self.image.set_colorkey(ALPHA)
        self.rect   = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.firing = throw

```

同你的 `Player` 类或 `Enemy` 类的 `__init__` 函数相比，这个函数的主要区别是，它有一个 `self.firing` 变量。这个变量保持跟踪一个投掷的物品是否在当前屏幕上活动，因此当一个 `throwable` 对象创建时，将变量设置为 `1` 的合乎情理的。

### 判断存活时间

接下来，就像使用 `Player` 和 `Enemy` 一样，你需要一个 `update` 函数，以便投掷的物品在瞄准敌人抛向空中时，它会自己移动。

测定一个投掷的物品存活时间的最简单方法是侦测它何时离开屏幕。你需要监视的屏幕边缘取决于你投掷的物品的物理特性。

* 如果你的玩家正在投掷的物品是沿着水平轴快速移动的，像一只弩箭或箭或一股非常快的魔法力量，而你想监视你游戏屏幕的水平轴极限。这可以通过 `worldx` 定义。
* 如果你的玩家正在投掷的物品是沿着垂直方向或同时沿着水平方向和垂直方向移动的，那么你必须监视你游戏屏幕的垂直轴极限。这可以通过 `worldy` 定义。

这个示例假设你投掷的物品向前移动一点并最终落到地面上。不过，投掷的物品不会从地面上反弹起来，而是继续掉落出屏幕。你可以尝试不同的设置来看看什么最适合你的游戏：

```
    def update(self,worldy):
        '''
        投掷物理学
        '''
        if self.rect.y < worldy:   #垂直轴
            self.rect.x  += 15     #它向前移动的速度有多快
            self.rect.y  += 5      #它掉落的速度有多快
        else:
            self.kill()            #移除投掷对象
            self.firing = 0        #解除火力发射

```

为使你的投掷物品移动地更快，增加 `self.rect` 的动量值。

如果投掷物品不在屏幕上，那么该物品将被销毁，以及释放其所占用的寄存器。另外，`self.firing` 将被设置回 `0` 以允许你的玩家来进行另一次射击。

### 设置你的投掷对象

就像使用你的玩家和敌人一样，你必须在你的设置部分中创建一个精灵组来保持投掷对象。

此外，你必须创建一个非活动的投掷对象来供开始的游戏使用。如果在游戏开始时却没有一个投掷对象，那么玩家在第一次尝试投掷一柄武器时，投掷将失败。

这个示例假设你的玩家使用一个火球作为开始的武器，因此，每一个投掷实例都是由 `fire` 变量指派的。在后面的关卡中，当玩家获取新的技能时，你可以使用相同的 `Throwable` 类来引入一个新的变量以使用一张不同的图像。

在这代码块中，前两行已经在你的代码中，因此不要重新键入它们:

```
player_list = pygame.sprite.Group() #上下文
player_list.add(player)             #上下文
fire = Throwable(player.rect.x,player.rect.y,'fire.png',0)
firepower = pygame.sprite.Group()

```

注意，每一个投掷对象的起始位置都是和玩家所在的位置相同。这使得它看起来像是投掷对象来自玩家。在第一个火球生成时，使用 `0` 来显示 `self.firing` 是可用的。

### 在主循环中获取投掷行为

没有在主循环中出现的代码不会在游戏中使用，因此你需要在你的主循环中添加一些东西，以便能在你的游戏世界中获取投掷对象。

首先，添加玩家控制。当前，你没有火力触发器。在键盘上的按键是有两种状态的：释放的按键，按下的按键。为了移动，你要使用这两种状态：按下按键来启动玩家移动，释放按键来停止玩家移动。开火仅需要一个信号。你使用哪个按键事件（按键按下或按键释放）来触发你的投掷对象取决于你的品味。

在这个代码语句块中，前两行是用于上下文的：

```
            if event.key == pygame.K_UP or event.key == ord('w'):
                player.jump(platform_list)
            if event.key == pygame.K_SPACE:
                if not fire.firing:
                    fire = Throwable(player.rect.x,player.rect.y,'fire.png',1)
                    firepower.add(fire)

```

与你在设置部分创建的火球不同，你使用一个 `1` 来设置 `self.firing` 为不可用。

最后，你必须更新和绘制你的投掷物品。这个顺序很重要，因此把这段代码放置到你现有的 `enemy.move` 和 `player_list.draw` 的代码行之间：

```
    enemy.move()  # 上下文

    if fire.firing:
        fire.update(worldy)
        firepower.draw(world)
    player_list.draw(screen)  # 上下文
    enemy_list.draw(screen)   # 上下文

```

注意，这些更新仅在 `self.firing` 变量被设置为 1 时执行。如果它被设置为 0 ，那么 `fire.firing` 就不为 `true`，接下来就跳过更新。如果你尝试做上述这些更新，不管怎样，你的游戏都会崩溃，因为在游戏中将不会更新或绘制一个 `fire` 对象。

启动你的游戏，尝试挑战你的武器。

### 检测碰撞

如果你玩使用了新投掷技巧的游戏，你可能会注意到，你可以投掷对象，但是它却不会对你的敌人有任何影响。

原因是你的敌人没有被查到碰撞事故。一名敌人可能会被你的投掷物品所击中，但是敌人却从来不知道被击中了。

你已经在你的 `Player` 类中完成了碰撞检测，这非常类似。在你的 `Enemy` 类中，添加一个新的 `update` 函数：

```
    def update(self,firepower, enemy_list):
        """
        检测火力碰撞
        """
        fire_hit_list = pygame.sprite.spritecollide(self,firepower,False)
        for fire in fire_hit_list:
            enemy_list.remove(self)

```

代码很简单。每个敌人对象都检查并看看它自己是否被 `firepower` 精灵组的成员所击中。如果它被击中，那么敌人就会从敌人组中移除和消失。

为集成这些功能到你的游戏之中，在主循环中调用位于新触发语句块中的函数：

```
    if fire.firing:                             # 上下文
        fire.update(worldy)                     # 上下文
        firepower.draw(screen)                  # 上下文
        enemy_list.update(firepower,enemy_list) # 更新敌人

```

你现在可以尝试一下你的游戏了，大多数的事情都如预期般的那样工作。不过，这里仍然有一个问题，那就是投掷的方向。

### 更改投掷机制的方向

当前，你英雄的火球只会向右移动。这是因为 `Throwable` 类的 `update` 函数将像素添加到火球的位置，在 Pygame 中，在 X 轴上一个较大的数字意味着向屏幕的右侧移动。当你的英雄转向另一个方向时，你可能希望它投掷的火球也抛向左侧。

到目前为止，你已经知道如何实现这一点，至少在技术上是这样的。然而，最简单的解决方案却是使用一个变量，在一定程度上对你来说可能是一种新的方法。一般来说，你可以“设置一个标记”（有时也被称为“翻转一个位”）来标明你的英雄所面向的方向。在你做完后，你就可以检查这个变量来得知火球是向左移动还是向右移动。

首先，在你的 `Player` 类中创建一个新的变量来代表你的游戏所面向的方向。因为我的游戏天然地面向右侧，由此我把面向右侧作为默认值：

```
        self.score = 0
        self.facing_right = True  # 添加这行
        self.is_jumping = True

```

当这个变量是 `True` 时，你的英雄精灵是面向右侧的。当玩家每次更改英雄的方向时，变量也必须重新设置，因此，在你的主循环中相关的 `keyup` 事件中这样做：

```
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(steps, 0)
                player.facing_right = False  # 添加这行
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps, 0)
                player.facing_right = True   # 添加这行

```

最后，更改你的 `Throwable` 类的 `update` 函数，以检测英雄是否面向右侧，并恰当地添加或减去来自火球位置的像素：

```
        if self.rect.y < worldy:
            if player.facing_right:
                self.rect.x += 15
            else:
                self.rect.x -= 15
            self.rect.y += 5

```

再次尝试你的游戏，清除掉你游戏世界中的一些坏人。

作为一项额外的挑战，当彻底打败敌人时，尝试增加你玩家的得分。

### 完整的代码

```
#!/usr/bin/env python3
# 作者: Seth Kenlon

# GPLv3
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <[http://www.gnu.org/licenses/>][17].

import pygame
import pygame.freetype
import sys
import os

'''
变量
'''

worldx = 960
worldy = 720
fps = 40
ani = 4
world = pygame.display.set_mode([worldx, worldy])
forwardx  = 600
backwardx = 120

BLUE = (80, 80, 155)
BLACK = (23, 23, 23)
WHITE = (254, 254, 254)
ALPHA = (0, 255, 0)

tx = 64
ty = 64

font_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "fonts", "amazdoom.ttf")
font_size = tx
pygame.freetype.init()
myfont = pygame.freetype.Font(font_path, font_size)

'''
对象
'''

def stats(score, health):
    myfont.render_to(world, (4, 4), "Score:"+str(score), BLUE, None, size=64)
    myfont.render_to(world, (4, 72), "Health:"+str(health), BLUE, None, size=64)

class Throwable(pygame.sprite.Sprite):
    """
    生成一个投掷的对象
    """
    def __init__(self, x, y, img, throw):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(os.path.join('images', img))
        self.image.convert_alpha()
        self.image.set_colorkey(ALPHA)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.firing = throw

    def update(self, worldy):
        '''
        投掷物理学
        '''
        if self.rect.y < worldy:
            if player.facing_right:
                self.rect.x += 15
            else:
                self.rect.x -= 15
            self.rect.y += 5
        else:
            self.kill()
            self.firing = 0

# x 位置, y 位置, img 宽度, img 高度, img 文件
class Platform(pygame.sprite.Sprite):
    def __init__(self, xloc, yloc, imgw, imgh, img):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(os.path.join('images', img)).convert()
        self.image.convert_alpha()
        self.image.set_colorkey(ALPHA)
        self.rect = self.image.get_rect()
        self.rect.y = yloc
        self.rect.x = xloc

class Player(pygame.sprite.Sprite):
    """
    生成一名玩家
    """

    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.movex = 0
        self.movey = 0
        self.frame = 0
        self.health = 10
        self.damage = 0
        self.score = 0
        self.facing_right = True
        self.is_jumping = True
        self.is_falling = True
        self.images = []
        for i in range(1, 5):
            img = pygame.image.load(os.path.join('images', 'walk' + str(i) + '.png')).convert()
            img.convert_alpha()
            img.set_colorkey(ALPHA)
            self.images.append(img)
            self.image = self.images[0]
            self.rect = self.image.get_rect()

    def gravity(self):
        if self.is_jumping:
            self.movey += 3.2

    def control(self, x, y):
        """
        控制玩家移动
        """
        self.movex += x

    def jump(self):
        if self.is_jumping is False:
            self.is_falling = False
            self.is_jumping = True

    def update(self):
        """
        更新精灵位置
        """

        # 向左移动
        if self.movex < 0:
            self.is_jumping = True
            self.frame += 1
            if self.frame > 3 * ani:
                self.frame = 0
            self.image = pygame.transform.flip(self.images[self.frame // ani], True, False)

        # 向右移动
        if self.movex > 0:
            self.is_jumping = True
            self.frame += 1
            if self.frame > 3 * ani:
                self.frame = 0
            self.image = self.images[self.frame // ani]

        # 碰撞
        enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
        if self.damage == 0:
            for enemy in enemy_hit_list:
                if not self.rect.contains(enemy):
                    self.damage = self.rect.colliderect(enemy)
        if self.damage == 1:
            idx = self.rect.collidelist(enemy_hit_list)
            if idx == -1:
                self.damage = 0   # 设置伤害回 0
                self.health -= 1  # 减去 1 单位健康度

        ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
        for g in ground_hit_list:
            self.movey = 0
            self.rect.bottom = g.rect.top
            self.is_jumping = False  # 停止跳跃

        # 掉落世界
        if self.rect.y > worldy:
            self.health -=1
            print(self.health)
            self.rect.x = tx
            self.rect.y = ty

        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
        for p in plat_hit_list:
            self.is_jumping = False  # 停止跳跃
            self.movey = 0
            if self.rect.bottom <= p.rect.bottom:
               self.rect.bottom = p.rect.top
            else:
               self.movey += 3.2

        if self.is_jumping and self.is_falling is False:
            self.is_falling = True
            self.movey -= 33  # 跳跃多高

        loot_hit_list = pygame.sprite.spritecollide(self, loot_list, False)
        for loot in loot_hit_list:
            loot_list.remove(loot)
            self.score += 1
            print(self.score)

        plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)

        self.rect.x += self.movex
        self.rect.y += self.movey

class Enemy(pygame.sprite.Sprite):
    """
    生成一名敌人
    """

    def __init__(self, x, y, img):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(os.path.join('images', img))
        self.image.convert_alpha()
        self.image.set_colorkey(ALPHA)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.counter = 0

    def move(self):
        """
        敌人移动
        """
        distance = 80
        speed = 8

        if self.counter >= 0 and self.counter <= distance:
            self.rect.x += speed
        elif self.counter >= distance and self.counter <= distance * 2:
            self.rect.x -= speed
        else:
            self.counter = 0

        self.counter += 1

    def update(self, firepower, enemy_list):
        """
        检测火力碰撞
        """
        fire_hit_list = pygame.sprite.spritecollide(self, firepower, False)
        for fire in fire_hit_list:
            enemy_list.remove(self)

class Level:
    def ground(lvl, gloc, tx, ty):
        ground_list = pygame.sprite.Group()
        i = 0
        if lvl == 1:
            while i < len(gloc):
                ground = Platform(gloc[i], worldy - ty, tx, ty, 'tile-ground.png')
                ground_list.add(ground)
                i = i + 1

        if lvl == 2:
            print("Level " + str(lvl))

        return ground_list

    def bad(lvl, eloc):
        if lvl == 1:
            enemy = Enemy(eloc[0], eloc[1], 'enemy.png')
            enemy_list = pygame.sprite.Group()
            enemy_list.add(enemy)
        if lvl == 2:
            print("Level " + str(lvl))

        return enemy_list

    # x 位置, y 位置, img 宽度, img 高度, img 文件
    def platform(lvl, tx, ty):
        plat_list = pygame.sprite.Group()
        ploc = []
        i = 0
        if lvl == 1:
            ploc.append((200, worldy - ty - 128, 3))
            ploc.append((300, worldy - ty - 256, 3))
            ploc.append((550, worldy - ty - 128, 4))
            while i < len(ploc):
                j = 0
                while j <= ploc[i][2]:
                    plat = Platform((ploc[i][0] + (j * tx)), ploc[i][1], tx, ty, 'tile.png')
                    plat_list.add(plat)
                    j = j + 1
                print('run' + str(i) + str(ploc[i]))
                i = i + 1

        if lvl == 2:
            print("Level " + str(lvl))

        return plat_list

    def loot(lvl):
        if lvl == 1:
            loot_list = pygame.sprite.Group()
            loot = Platform(tx*5, ty*5, tx, ty, 'loot_1.png')
            loot_list.add(loot)

        if lvl == 2:
            print(lvl)

        return loot_list

'''
Setup 部分
'''

backdrop = pygame.image.load(os.path.join('images', 'stage.png'))
clock = pygame.time.Clock()
pygame.init()
backdropbox = world.get_rect()
main = True

player = Player()         # 生成玩家
player.rect.x = 0         # 转到 x
player.rect.y = 30        # 转到 y
player_list = pygame.sprite.Group()
player_list.add(player)
steps = 10
fire = Throwable(player.rect.x, player.rect.y, 'fire.png', 0)
firepower = pygame.sprite.Group()

eloc = []
eloc = [300, worldy-ty-80]
enemy_list = Level.bad(1, eloc)
gloc = []

i = 0
while i <= (worldx / tx) + tx:
    gloc.append(i * tx)
    i = i + 1

ground_list = Level.ground(1, gloc, tx, ty)
plat_list = Level.platform(1, tx, ty)
enemy_list = Level.bad( 1, eloc )
loot_list = Level.loot(1)

'''
主循环
'''

while main:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            try:
                sys.exit()
            finally:
                main = False

        if event.type == pygame.KEYDOWN:
            if event.key == ord('q'):
                pygame.quit()
                try:
                    sys.exit()
                finally:
                    main = False
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(-steps, 0)
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(steps, 0)
            if event.key == pygame.K_UP or event.key == ord('w'):
                player.jump()

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                player.control(steps, 0)
                player.facing_right = False
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                player.control(-steps, 0)
                player.facing_right = True
            if event.key == pygame.K_SPACE:
                if not fire.firing:
                    fire = Throwable(player.rect.x, player.rect.y, 'fire.png', 1)
                    firepower.add(fire)

    # 向向滚动世界
    if player.rect.x >= forwardx:
        scroll = player.rect.x - forwardx
        player.rect.x = forwardx
        for p in plat_list:
            p.rect.x -= scroll
        for e in enemy_list:
            e.rect.x -= scroll
        for l in loot_list:
            l.rect.x -= scroll

    # 向后滚动世界
    if player.rect.x <= backwardx:
        scroll = backwardx - player.rect.x
        player.rect.x = backwardx
        for p in plat_list:
            p.rect.x += scroll
        for e in enemy_list:
            e.rect.x += scroll
        for l in loot_list:
            l.rect.x += scroll

    world.blit(backdrop, backdropbox)
    player.update()
    player.gravity()
    player_list.draw(world)
    if fire.firing:
        fire.update(worldy)
        firepower.draw(world)
    enemy_list.draw(world)
    enemy_list.update(firepower, enemy_list)
    loot_list.draw(world)
    ground_list.draw(world)
    plat_list.draw(world)
    for e in enemy_list:
        e.move()
    stats(player.score, player.health)
    pygame.display.flip()
    clock.tick(fps)

```

---

via: <https://opensource.com/article/20/9/add-throwing-python-game>

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

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


使用 GIMP 轻松地设置图片透明度

Summary: 使用色键（绿屏）技巧来设置你电脑游戏中图片的透明度。

翻译不够好哇

使用色键（绿屏）技巧来设置你电脑游戏中图片的透明度。

# 使用 GIMP 轻松地设置图片透明度

>
> 使用色键（或绿屏）技巧来设置你电脑游戏中图片的透明度。
>
>
>

不管你是否正在使用 [Python](https://opensource.com/article/17/10/python-101) 或 [Lua](https://opensource.com/article/17/4/how-program-games-raspberry-pi) 编程一个游戏或一个 APP，你都有可能在你的游戏资源中使用 PNG 图像。PNG 格式图像的一个优点是能够存储一个 **alpha 通道**，这在一个 JPEG 格式的图像中是不可能获得的。alpha 在本质上是不可见的或透明的“颜色”。alpha 是你图像 *不可见* 的一部分。例如，你要绘制一个甜甜圈，甜甜圈的空洞将使用 alpha 填充，你就可以看到它后面的任何东西。

一个常见的问题是如何找到一幅图像的 alpha 部分。有时你的编程框架，不管它是 [Python Arcade](https://opensource.com/article/18/4/easy-2d-game-creation-python-and-arcade)、[Pygame](https://opensource.com/article/17/12/game-framework-python)、LÖVE，或者其它的任何东西都会检测出 alpha 通道，（在适当地调用函数后）将其作为透明处理。这意味着它将不会在 alpha 部分来渲染新的像素，而留下甜甜圈的空洞。100% 是透明的，0% 是不透明的，在功能上起到“不可见”的作用。

有些时候，你的框架与你的图像资源在 alpha 通道的位置上是不一致的（或者，alpha 通道根本就不存在），你在你想要透明度的地方却得到像素。

这篇文章描述了我所知道的最可靠的方法来解决透明度的问题。

### 色键

在计算机图形学中，有一些有助于确定每一个像素是如何渲染的值。<ruby> 色度 <rt>  Chrominance </rt></ruby>（或者 chroma），描述一个像素的饱和度或强度。<ruby> 色键 <rt>  chroma key </rt></ruby>技术（也称为<ruby> 绿屏 <rt>  green screening </rt></ruby>）最初是作为一种化学工艺而发展起来的，在复印一张底片时，使用一种特定的 **无光泽** 的颜色（最初是蓝色，后来是绿色）来故意遮掩，以允许使用另一幅图像来取代曾经有蓝色或绿色屏幕的地方。这是一种简化的解释，但是它说明了计算机图形学中被称为 alpha 通道的起源。

alpha 通道是保存在图像中的信息，用以标识要透明的像素。例如，RGB 图像有红、绿、蓝通道。RGBA 图像包含红、绿、蓝通道，以及 alpha 通道。alpha 值的范围可以从 0 到 1 ，使用小数是也有效的。

因为一个 alpha 通道可以用几种不同的方法表达，因此依赖于嵌入的 alpha 通道可能是有问题的。作为替代方案，你可以在你的游戏框架中选择一种颜色并将其转化为一个 0 的 alpha 值。要做到这一点，你必须知道在你图像中的颜色值。

### 准备你的图片

要准备一个专门为色度键保留明确颜色的图形，在你最喜欢的图片编辑器中打开图片。我建议使用 [GIMP](http://gimp.org) 或 [Glimpse](https://glimpse-editor.github.io)，但是 [mtPaint](https://opensource.com/article/17/2/mtpaint-pixel-art-animated-gifs) 或 [Pinta](https://www.pinta-project.com/)，甚至 [Inkscape](http://inkscape.org) 也能很好地工作，这取决于你的图像的性质，以及你将这些操作指南转换到一种不同图片编辑器工具的能力。

首先打开这幅 Tux 企鹅的图像:

### 选择图片

在图片打开后，转到 **窗口** 菜单，选择 **可停靠对话框** ，接下来选择 **图层**。在 **图层** 面板中 Tux 图层上右击。从弹出菜单中，选择 **Alpha 到选区** 。如果你的图像没有内置的 alpha 通道，那么你必须手动创建你自己的选区。

为手动创建一个选区，单击来自工具箱的 **路径** 工具。

使用 **路径** 工具，在图像周围移动鼠标，在其轮廓的每个主要交叉点处都单击和释放（不要拖动）。 不要担心沿着曲线走；只需要找到主要的交叉点和拐角。这将在每个点处创建一个节点，并在节点中间绘制一条条线段。你不需要闭合你的路径，因此当你最后到达你开始时的交叉点时，你就完成了。

在你创建你的轮廓路径后，转到 **窗口** 菜单，选择 **可停靠对话框** ，接下来选择 **工具选项** 。在 **工具选项** 面板中，选择 **编辑 (Ctrl)** 。随着这项操作的激活，你可以编辑你刚刚通过单击线或单击节点绘制的路径，并通过调整它们来更好地适应你的图像。你甚至能够将直线弯曲。

现在从 **窗口 > 可停靠对话框** 菜单中选择 **路径** 面板。在 **路径** 面板中，单击 **路径到选区** 按钮。你的绘图现在已经被选中了。

### 扩大选区

如果你觉得你的选区太紧贴了，你可以通过扩大来给予你自己的选区一些富余。当我想在一张图像周围采用或加厚一个边框时，我有时会这么扩大选区。

为扩大一个选区，单击 **选择** 菜单，选择 **扩大** 。输入一个像素值并单击 **确定** 。

### 反转选区

你已经选择了你的图形，但是你真正想选择的东西却 *不包括* 你所选择的图像。这是因为你要创建一个 alpha 蒙版来定义图像中的一些内容的来被其它一些内容所替换。换句话说，你需要标记那些将被转变为不可见的像素。

为了反转选择区，单击 **选择** 菜单，选择 **反转** 。现在除你的图像以外的一切东西都是被选择的。

### 使用 alpha 填充

随着选择了除了你的图像以外的一切东西，再选择你想使用的颜色来指定你的 alpha 蒙版。最常用的颜色是绿色（正如你可能从术语“绿屏”中所猜到的一样）。绿色不是什么神奇的颜色，甚至也不是特定的绿色色调。之所以使用它是因为人们经常处理不包含绿色色素的图像，这样人们能够很容易分离出绿色，而不会意外地分离出图像中重要的部分。当然，如果你的图像是一位绿色的外星人或一枚绿宝石或一些 *确实* 包含绿色的东西，那么你应该使用一种不同的颜色。只要你所选择的颜色是单一的单色，那么你就可以使用你所希望的任意颜色。如果你正在处理很多图像，你的选择应该在所有图像中保持一致。

使用你选择的颜色值来设置你的前景色。为确保你的选择是精确的，使用 [HTML](https://www.w3schools.com/colors/colors_picker.asp) 或 [HSV](https://en.wikipedia.org/wiki/HSL_and_HSV) 表示的颜色。例如，如果你正在使用纯绿色，它可以在 GIMP（以及大多数的开放源码图像应用程序）中表示为 `00ff00`（`00` 是红色，`FF` 是绿色，`00` 是蓝色，`F` 是最大值）。

不管你选择什么颜色，务必记录下 HTML 或 HSV 的值，以便你可以为每一张图像使用完全相同的颜色。

为填充你的 alpha 蒙版，单击 **编辑** 菜单，选择 **使用前景色填充** 。

### 平整和导出

如果你在你的图像周围留下边框，设置背景颜色来着色你想使用的边界笔刷。这通常是黑色或白色，但是它也可以是任何适宜你游戏审美观的颜色。

在你设置背景颜色后，单击 **图像** 菜单，选择 **平整图像**。不管你是否添加了边框，这样做都是安全的。这个过程将从图像中移除 alpha 通道，并使用背景色填充任何“透明的”像素。

你现在已经为你的游戏引擎准备好了一张图像。导出图像为你的游戏引擎喜欢的任何格式，接下来使用游戏引擎所需要的每一个函数来将图像导入的你的游戏中。在的代码中，设置 alpha 值为 `00ff00`（或你使用的任何颜色），接下来使用游戏引擎的图像转换器来将该颜色作为 alpha 通道处理。

### 其它的方法

这不是唯一能在你游戏图像中获取透明度的方法。查看你游戏引擎的文档来找出它是如何默认尝试处理 alpha 通道的，在你不确定的时候，尝试让你的游戏引擎来自动侦测图像中透明度，然后再去编辑它。有时，你游戏引擎的预期值和你图像的预设值恰巧匹配，那么你就可以直接获取透明度，而不需要做任何额外的工作。

不过，当这些尝试都失败时，尝试一下色键。它为电影业工作了将近 100 年，它也可以为你工作。

---

via: <https://opensource.com/article/20/9/chroma-key-gimp>

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

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


了解一下 Fossil，一个 Git 的替代品

# 了解一下 Fossil，一个 Git 的替代品

>
> Fossil 是一个集版本控制系统、bug 追踪、维基、论坛以及文档解决方案于一体的系统。
>
>
>

每个开发者都知道，追踪代码的修改是至关重要的。有时候你会处于好奇或者教育的目的需要展示你的项目开始和进化的历史。有时候你想让其他的开发者参与到你的项目中，因此你需要一种值得信赖的能合并不同代码分支的方法。更极端一点，有时候你为了解决一个问题而修改的代码导致已有的功能不能正常使用。

[Fossil](https://fossil-scm.org/home/doc/trunk/www/index.wiki) 源码管理系统是由著名的 [SQLite](https://www.sqlite.org/index.html) 数据库的作者开发的一个集版本控制系统、bug 追踪、维基、论坛以及文档解决方案于一体的系统。

### 安装 Fossil

Fossil 是一个独立的 C 程序，因此你可以从它的网站上[下载](https://fossil-scm.org/home/uv/download.html)后放在环境变量 [PATH](https://opensource.com/article/17/6/set-path-linux) 中的任意位置。例如，假定 `/usr/local/bin` 已经在你的环境变量中（默认情况下是在的）：

```
$ wget https://fossil-scm.org/home/uv/fossil-linux-x64-X.Y.tar.gz
$ sudo tar xvf fossil-linux-x64-X.Y.tar.gz --directory /usr/local/bin

```

你也可以通过包管理器从软件仓库中找到 Fossil，或者直接从源码编译。

### 创建一个 Fossil 仓库

如果你已经有一个代码项目，想用 Fossil 来追踪，那么第一步就是创建一个 Fossil 仓库：

```
$ fossil init myproject.fossil
project-id: 010836ac6112fefb0b015702152d447c8c1d8604
server-id:  54d837e9dc938ba1caa56d31b99c35a4c9627f44
admin-user: klaatu (initial password is "14b605")

```

创建 Fossil 仓库的过程中会返回三行信息：一个唯一的项目 ID、一个唯一的服务器 ID 以及管理员 ID 和密码。项目 ID 和服务器 ID 是版本数字。管理员凭证表明你对这个仓库的所有权，当你把 Fossil 作为服务器让其他用户来访问时可以使用管理员权限。

### Fossil 仓库工作流

在你使用 Fossil 仓库之前，你需要先为它的数据创建一个工作路径。你可以把这个过程类比为使用 Python 时创建一个虚拟环境或者解压一个只用来备份的 ZIP 文件。

创建一个工作目录并进入：

```
$ mkdir myprojectdir
$ cd myprojectdir

```

把你的 Fossil 打开到刚刚创建的目录：

```
$ fossil open ../myproject
project-name: <unnamed>
repository:   /home/klaatu/myprojectdir/../myproject
local-root:   /home/klaatu/myprojectdir/
config-db:    /home/klaatu/.fossil
project-code: 010836ac6112fefb0b015702152d447c8c1d8604
checkout:     9e6cd96dd675544c58a246520ad58cdd460d1559 2020-11-09 04:09:35 UTC
tags:         trunk
comment:      initial empty check-in (user: klaatu)
check-ins:    1

```

你可能注意到了，Fossil 在你的家目录下创建了一个名为 `.fossil` 的隐藏文件，用来追踪你的全局 Fossil 配置。这个配置不是只适用于你的一个项目的；这个文件只会在你第一次使用 Fossil 时生成。

#### 添加文件

使用 `add` 和 `commit` 子命令来向你的仓库添加文件。例如，创建一个简单的 `README` 文件，把它添加到仓库：

```
$ echo "My first Fossil project" > README
$ fossil add README
ADDED  README
$ fossil commit -m 'My first commit'
New_Version: 2472a43acd11c93d08314e852dedfc6a476403695e44f47061607e4e90ad01aa

```

#### 使用分支

Fossil 仓库开始时默认使用的主分支名为 `trunk`。当你想修改代码而又不影响主干代码时，你可以从 trunk 分支切走。创建新分支需要使用 `branch` 子命令，这个命令需要两个参数：一个新分支的名字，一个新分支的基分支名字。在本例中，只有一个分支 `trunk`，因此尝试创建一个名为 `dev` 的新分支：

```
$ fossil branch --help
Usage: fossil branch new BRANCH-NAME BASIS ?OPTIONS?
$ fossil branch new dev trunk
New branch: cb90e9c6f23a9c98e0c3656d7e18d320fa52e666700b12b5ebbc4674a0703695

```

你已经创建了一个新分支，但是你当前所在的分支仍然是 `trunk`：

```
$ fossil branch current
trunk

```

使用 `checkout` 命令切换到你的新分支 `dev`：

```
$ fossil checkout dev
dev

```

#### 合并修改

假设你在 `dev` 分支中添加了一个新文件，完成了测试，现在想把它合并到 `trunk`。这个过程叫做*合并*。

首先，切回目标分支（本例中目标分支为 `trunk`）：

```
$ fossil checkout trunk
trunk
$ ls
README

```

这个分支中没有你的新文件（或者你对其他文件的修改），而那些内容是合并的过程需要的信息：

```
$ fossil merge dev
 "fossil undo" is available to undo changes to the working checkout.
$ ls
myfile.lua  README

```

### 查看 Fossil 时间线

使用 `timeline` 选项来查看仓库的历史。这个命令列出了你的仓库的所有活动的详细信息，包括用来表示每次修改的哈希值、每次提交时填写的信息以及提交者：

```
$ fossil timeline
=== 2020-11-09 ===
06:24:16 [5ef06e668b] added exciting new file (user: klaatu tags: dev)
06:11:19 [cb90e9c6f2] Create new branch named "dev" (user: klaatu tags: dev)
06:08:09 [a2bb73e4a3] *CURRENT* some additions were made (user: klaatu tags: trunk)
06:00:47 [2472a43acd] This is my first commit. (user: klaatu tags: trunk)
04:09:35 [9e6cd96dd6] initial empty check-in (user: klaatu tags: trunk)
+++ no more data (5) +++

```

### 公开你的 Fossil 仓库

因为 Fossil 有个内置的 web 界面，所以 Fossil 不像 GitLab 和 Gitea 那样需要主机服务。Fossil 就是它自己的主机服务，只要你把它放在一台机器上就行了。在你公开你的 Fossil 仓库之前，你还需要通过 web 用户界面（UI）来配置一些信息：

使用 `ui` 子命令启动一个本地的实例：

```
$ pwd
/home/klaatu/myprojectdir/
$ fossil ui

```

“Users” 和 “Settings” 是安全相关的，“Configuration” 是项目属性相关的（包括一个合适的标题）。web 界面不仅仅是一个方便的功能。 它是能在生产环境中使用并作为 Fossil 项目的宿主机来使用的。它还有一些其他的高级选项，比如用户管理（或者叫自我管理）、在同一个服务器上与其他的 Fossil 仓库进行单点登录（SSO）。

当配置完成后，关掉 web 界面并按下 `Ctrl+C` 来停止 UI 引擎。像提交代码一样提交你的 web 修改。

```
$ fossil commit -m 'web ui updates'
New_Version: 11fe7f2855a3246c303df00ec725d0fca526fa0b83fa67c95db92283e8273c60

```

现在你可以配置你的 Fossil 服务器了。

1. 把你的 Fossil 仓库（本例中是 `myproject.fossil`）复制到服务器，你只需要这一个文件。
2. 如果你的服务器没有安装 Fossil，就在你的服务器上安装 Fossil。在服务器上安装的过程跟在本地一样。
3. 在你的 `cgi-bin` 目录下（或它对应的目录，这取决于你的 HTTP 守护进程）创建一个名为 `repo_myproject.cgi` 的文件：

```
#!/usr/local/bin/fossil
repository: /home/klaatu/public_html/myproject.fossil

```

添加可执行权限：

```
$ chmod +x repo_myproject.cgi

```

你需要做的都已经做完了。现在可以通过互联网访问你的项目了。

你可以通过 CGI 脚本来访问 web UI，例如 `https://example.com/cgi-bin/repo_myproject.cgi`。

你也可以通过命令行来进行交互：

```
$ fossil clone https://klaatu@example.com/cgi-bin/repo_myproject.cgi

```

在本地的克隆仓库中工作时，你需要使用 `push` 子命令把本地的修改推送到远程的仓库，使用 `pull` 子命令把远程的修改拉取到本地仓库：

```
$ fossil push https://klaatu@example.com/cgi-bin/repo_myproject.cgi

```

### 使用 Fossil 作为独立的托管

Fossil 将大量的权力交到了你的手中（以及你的合作者的手中），让你不再依赖托管服务。本文只是简单的介绍了基本概念。你的代码项目还会用到很多有用的 Fossil 功能。尝试一下 Fossil。它不仅会改变你对版本控制的理解；它会让你不再考虑其他的版本控制系统。

---

via: <https://opensource.com/article/20/11/fossil>

作者：[Klaatu](https://opensource.com/users/klaatu) 选题：[lujun9972](https://github.com/lujun9972) 译者：[lxbwolf](https://github.com/lxbwolf) 校对：[wxy](https://github.com/wxy)

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


添加声音到你的 Python 游戏

# 添加声音到你的 Python 游戏

>
> 通过添加声音到你的游戏中，听听当你的英雄战斗、跳跃、收集战利品时会发生什么。学习如何在这个 Pygame 系列的第十三篇文章中，创建一个声音平台类精灵。
>
>
>

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

在 [Python 3](https://www.python.org/) 中使用 [Pygame](https://www.pygame.org/news) 模块来创建电脑游戏的系列文章仍在进行中，这是第十三部分。先前的文章是:

1. [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程](/article-9071-1.html)
2. [使用 Python 和 Pygame 模块构建一个游戏框架](/article-10850-1.html)
3. [如何在你的 Python 游戏中添加一个玩家](/article-10858-1.html)
4. [用 Pygame 使你的游戏角色移动起来](/article-10874-1.html)
5. [如何向你的 Python 游戏中添加一个敌人](/article-10883-1.html)
6. [在 Pygame 游戏中放置平台](/article-10902-1.html)
7. [在你的 Python 游戏中模拟引力](/article-11780-1.html)
8. [为你的 Python 平台类游戏添加跳跃功能](/article-11790-1.html)
9. [使你的 Python 游戏玩家能够向前和向后跑](/article-11819-1.html)
10. [在你的 Python 平台类游戏中放一些奖励](/article-11828-1.html)
11. [添加计分到你的 Python 游戏](/article-11839-1.html)
12. [在你的 Python 游戏中加入投掷机制](/article-12872-1.html)

Pygame 提供了一种简单的方法来集成声音到你的 Python 电脑游戏中。Pygame 的 [mixer 模块](https://www.pygame.org/docs/ref/mixer.html) 可以依据命令播放一个或多个声音，并且你也可以将这些声音混合在一起，例如，你能够在听到你的英雄收集奖励或跳过敌人声音的同时播放背景音乐。

集成 `mixer` 模块到一个已存在的游戏中是很容易的，因此，与其给你代码示例来向你展示放置它们的位置，不如在这篇文章解释在你的应用程序中获得声音所需的四个步骤。

### 启动 mixer

首先，在你代码的设置部分，启动 `mixer` 进程。你的代码已经启动 Pygame 和 Pygame 字体，因此将它们归类到一起是一个很好的主意:

```
pygame.init()
pygame.font.init()
pygame.mixer.init() # add this line

```

### 定义声音

接下来，你必需定义你想要使用的声音。这样就要求你的计算机上有声音文件，就像使用字体就要求你有字体文件，使用图像就要求你有图像文件一样。

你还必需把这些声音与你的游戏捆绑在一起，以便任何玩你游戏的人都有这些声音文件。

为将一个声音与你的游戏捆绑在一起，首先在你的游戏目录中创建一个新的目录，就像你为你图像和字体创建的目录一样。称它为 `sound`:

```
s = 'sound'

```

尽管在互联网上有很多声音文件，下载这些声音文件并将其与你的游戏一起分发并不一定是合法的。这看起来是很奇怪的，因为这么多来自著名电脑游戏的声音是流行文化的一部分，但法律就是这样运作的。如果你想与你的游戏一起分发一个声音文件，你必需找到一个开源或[共创许可](https://opensource.com/article/20/1/what-creative-commons)的声音文件，它们准许与游戏一起提供声音。

这里有一些专门提供自由和合法的声音文件的网站，包括:

* [Freesound](https://freesound.org) 托管存储所有种类的音效。
* [Incompetech](https://incompetech.filmmusic.io) 托管存储背景音乐。
* [Open Game Art](https://opengameart.org) 托管存储一些音效和音乐。

一些声音文件只要你给予作曲家或声音设计师一个致谢就可以自由使用。在与你的游戏捆绑前，仔细阅读使用条件！音乐家和声音设计师在声音上的工作就像你在代码上的工作一样努力，因此即使他们不要求，给予他们致谢也是极好的。

给予你的声音源文件一些致谢，在一个名为 `CREDIT` 的文本文件中列出你使用的声音，并在你的游戏文件夹中放置该文本文件。

你也可以尝试制作你自己的音乐。优秀的 [LMMS](https://opensource.com/life/16/2/linux-multimedia-studio) 音频工作站易于使用，并带有很多有趣的声音。它在所有主要的平台上都可以使用，也可以导出为 [Ogg Vorbis](https://en.wikipedia.org/wiki/Vorbis)（OGG）音频格式。

### 添加声音到 Pygame

当你找到你喜欢的一个声音文件时，下载它。如果它是一个 ZIP 或 TAR 文件，提取它并将其移动到你游戏目录中的 `sound` 文件夹中。

如果声音文件的名字带有空格或特殊字符，重新命名它。文件名称是完全随意的，它的名称越简单，你就越容易输入到你的代码中。

大多数的电脑游戏使用 OGG 格式声音文件，因为这种格式可以占有较小空间而提供高质量的声音。当你下载一个声音文件时，它可能是一个 MP3、WAVE、FLAC 或者其它的音频格式。为保持你的文件的较高兼容性和降低下载文件大小，使用一个像 [fre:ac](https://www.freac.org/index.php/en/downloads-mainmenu-330) 或 [Miro](http://getmiro.com) 这样的工具来转换这些的文件格式为 Ogg 格式。

例如，假设你已经下载一个称为 `ouch.ogg` 的声音文件。

在你代码的设置部分中，创建一个变量，代表你想使用的声音文件：

```
ouch = pygame.mixer.Sound(os.path.join(s, 'ouch.ogg'))

```

### 触发一个声音

为使用一个声音，你所要做的就是在你想触发它的时候调用这个变量。例如，当你的玩家击中一名敌人时，触发 `OUCH` 声音效果:

```
for enemy in enemy_hit_list:
    pygame.mixer.Sound.play(ouch)
    score -= 1

```

你可以为各种动作创建声音，例如，跳跃、收集奖励、投掷、碰撞，以及其他任何你能想象到的动作。

### 添加背景音乐

如果你有想在你的游戏的背景中播放的音乐或令人激动的音效，你可以使用 Pygame 中的 `mixer` 模块中的 `music` 函数。在你的设置部分中，加载音乐文件：

```
music = pygame.mixer.music.load(os.path.join(s, 'music.ogg'))

```

然后，开始音乐:

```
pygame.mixer.music.play(-1)

```

`-1` 值告诉 Pygame 无限循环音乐文件。你可以将其设置为从 0 到更高的值之间的任意数值，以定义音乐在停止前循环多少次。

### 享受音效

音乐和声音可以为你的游戏添加很多韵味。尝试添加一些声音到你的 Pygame 工程!

---

via: <https://opensource.com/article/20/9/add-sound-python-game>

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

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


用 Recast.AI 创建一个聊天机器人

# 用 Recast.AI 创建一个聊天机器人

>
> 据 Gartner 2018 年 2 月的报告，“到 2020 年，25% 的客户服务和支持业务将在参与渠道中整合虚拟客户助理（VCA）或聊天机器人技术，而 2017 年只有不到 2%。”鉴于此，读者会发现本教程对理解开源的 Recast.AI 机器人创建平台的工作原理很有帮助。
>
>
>

聊天机器人，包括基于语音的以及其他技术的，已经实际使用了有一阵子了。从让用户参与谋杀解密游戏，到帮助完成房地产交易和医疗诊断，聊天机器人已经跨越了多个领域。

有许多平台可以让用户创建和部署机器人。Recast.AI（在被 SAP 收购之后现在是 SAP Conversational AI）是其中的先行者。

酷炫的界面、协作性以及它所提供的分析工具，让它成为流行的选择。

正如 Recast 官方网站说的，“它是一个创建、训练、部署和监控智能机器人的终极协作平台。”

### 创建一个基础的机器人

让我们来看看如何在 Recast 创建一个基础的机器人。

1. 在 <https://cai.tools.sap> 创建一个账户。注册可以使用电子邮箱或者 Github 账户。
2. 在你登录之后，你会进入仪表板。点击右上角 “+” 新建机器人图标新建一个机器人。
3. 在下一个界面，你会看到一系列可选的预定义技能。暂时选择<ruby> 问候 <rt>  Greetings </rt></ruby>”（图 1）。这个机器人已经经过训练，能够理解基本的问候。
4. 给机器人提供一个名字。目前来说，你可以让机器人讲一些笑话，所以我们将它命名为 Joke Bot，选择英语作为默认语言。
5. 因为你不会处理任何敏感信息，所以在数据策略下选择非个人数据。然后选择公共机器人选项并点击创建一个机器人。

所以这就是你在 Recast 平台创建的机器人。

### 开发一个机器人的五个阶段

用 Recast 官方博客的话说，在机器人的生命中有五个阶段。

* 训练——教授机器人需要理解的内容
* 构建——使用机器人构建工具创建你的对话流
* 编写代码——将机器人连接到外部 API 或数据库
* 连接——将机器人发布到一个或多个消息平台
* 监控——训练机器人让它更敏锐，并且了解其使用情况

### 通过意图训练机器人

你可以在仪表板上看到搜索、分叉或创建一个<ruby> 意图 <rt>  intent </rt></ruby>的选项。“‘意图’是一系列含义相同但构造不同的表达。‘意图’是你的机器人理解能力的核心。每个‘意图’代表了机器人可以理解的一种想法。”（摘自 Recast.AI 网站）

就像先前定的，你需要一个讲笑话的机器人。所以底线是这个机器人可以理解用户在要求它讲笑话，它不应该在用户仅仅说了“Hi”的情况下回复一个笑话——这可不妙。把用户可能说的话进行分组，比如：

```
Tell me a joke.（给我讲个笑话。）
Tell me a funny fact.（告诉我一个有趣的事实。）
Can you crack a joke?（你可以讲个笑话吗？）
What’s funny today?（今天有什么有趣的？）

```

……

在继续从头开始创建意图之前，让我们来看看搜索/分叉选项。在搜索框输入 “Joke”（图 3）。系统给出了全球的 Recast 用户创建的公开的意图清单，这就是为什么说 Recast 天然就是协作性质的。所以其实没有必要从头开始创建所有的意图，可以在已经创建的基础上进行构建。这就降低了训练具有常见意图的机器人所需的投入。

* 选择列表中的第一个意图并将其分叉到机器人上。
* 点击<ruby> 分叉 <rt>  Fork </rt></ruby>按钮。这个意图就添加到了机器人中（图 4）。
* 点击意图 @joke，会显示出这个意图中已经存在的<ruby> 表达 <rt>  expression </rt></ruby>列表（图 5）。
* 向其添加更多的表达（图 6）。

添加了一些表达之后，机器人会给出一些建议，像图 7 展示的那样。选择几个将它们添加到意图中。你还可以根据机器人的上下文，标记你自己的自定义实体来检测关键词。

### 技能

<ruby> 技能 <rt>  skill </rt></ruby>是一块有明确目的的对话，机器人可以据此运行并达到目标。它可以像打招呼那么简单，也可以更复杂，比如基于用户提供的信息提供电影建议。

技能需要的不能只是一对问答，它需要多次交互。比如考虑一个帮你学习汇率的机器人。它一开始会问原货币，然后是目标货币，最后给出准确回应。结合技能可以创建复杂的对话流。

下面是如何给笑话机器人创建技能：

* 去到 构建（Build） 页。点击 “+” 图标创建技能。
* 给技能命名 “Joke”（图 8）
* 创建之后，点击这个技能。你会看到四个标签。<ruby> 读我 <rt>  Read me </rt></ruby>、<ruby> 触发器 <rt>  Triggers </rt></ruby>、<ruby> 需求 <rt>  Requirements </rt></ruby>和 <ruby> 动作 <rt>  Actions </rt></ruby>。
* 切换到需求页面。只有在笑话意图存在的时候，你才应该存储信息。所以，像图 9 那样添加一个需求。

由于这个简单的使用范例，你不需要在需求选项卡中考虑任何特定的需求，但可以考虑只有当某些关键字或实体出现时才需要触发响应的情况——在这种情况下你需要需求。

需求是某个技能执行动作之前需要检索的意图或实体。需求是对话中机器人可以使用的重要信息。例如用户的姓名或位置。一旦一个需求完成，相关的值就会存储在机器人的内存中，供整个对话使用。

现在让我们转到动作页面设置<ruby> 回应 <rt>  response </rt></ruby>（参见图 10）。

点击添加<ruby> 新消息组 <rt>  new message group </rt></ruby>。然后选择<ruby> 发送消息 <rt>  Send message </rt></ruby>并添加一条文本消息，在这个例子中可以是任何笑话。当然，你肯定不想让你的机器人每次都说一样的笑话，你可以添加多条消息，每次从中随机选择一条。

### 频道集成

一个成功的机器人还依赖于它的易得性。Recast 有不少的内置消息频道集成，如 Skype for Business、Kik Messenger、Telegram、Line、Facebook Messenger、Slack、Alexa 等等。除此之外，Recast 还提供了 SDK 用于开发自定义的频道。

此外，Recast 还提供一个可立即使用的网页聊天（在连接页面中）。你可以自定义颜色主题、标题、机器人头像等。它给你提供了一个可以添加到页面的脚本标签。你的界面现在就可以使用了（图 12）。

网页聊天的代码是开源的，开发者可以更方便地定制外观、标准回应类型等等。面板提供了如何将机器人部署到各种频道的逐步过程说明。这个笑话机器人部署在 Telegram 和网页聊天上，就像图 13 展示的那样。

### 还有更多

Recast 支持多语言，创建机器人的时候选择一个语言作为基础，但之后你有选项可以添加更多你想要的语言。

这里的例子是一个简单的静态笑话机器人，实际使用中可能需要更多的和不同系统的交互。Recast 有 Web 钩子功能，用户可以连接到不同的系统来获取回应。同时它还有详细的 API 文档来帮助使用平台的每个独立功能。

至于分析，Recast 有一个监控面板，帮助你了解机器人的准确度以及更加深入地训练机器人。

---

via: <https://opensourceforu.com/2019/11/creating-a-chat-bot-with-recast-ai/>

作者：[Athira Lekshmi C.V](https://opensourceforu.com/author/athira-lekshmi/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[alim0x](https://github.com/alim0x) 校对：[wxy](https://github.com/wxy)

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


如何在 Silverblue 上变基到 Fedora 33？

# 如何在 Silverblue 上变基到 Fedora 33？

Silverblue 是[一个建立在 Fedora 之上的桌面操作系统](https://docs.fedoraproject.org/en-US/fedora-silverblue/)。它非常适合日常使用、开发和基于容器的工作流程。它提供了[众多的优势](https://fedoramagazine.org/give-fedora-silverblue-a-test-drive/)，例如在出现任何问题时能够回滚。如果你想在你的 Silverblue 系统上更新到 Fedora 33，这篇文章会告诉你如何做。它不仅告诉你该怎么做，还告诉你如果发生了不可预见的事情时该如何回退。

在实际做变基到 Fedora 33 之前，你应该应用任何挂起的更新。在终端中输入以下内容：

```
$ rpm-ostree update

```

或通过 GNOME 软件中心安装更新并重启。

### 使用 GNOME 软件中心变基

GNOME 软件中心会在更新界面显示有新版本的 Fedora 可用。

首先你需要做的是下载新镜像，点击 “Download” 按钮。这将需要一些时间，完成后你会看到更新已经准备好安装了。

点击 “Install” 按钮。这一步只需要几分钟，然后会提示你重启电脑。

点击 “Restart” 按钮就可以了。重启后，你将进入新的 Fedora 33 版本。很简单，不是吗？

### 使用终端变基

如果你喜欢在终端上做所有的事情，那么接下来的指南就适合你。

使用终端变基到 Fedora 33 很简单。首先，检查 33 版本分支是否可用：

```
$ ostree remote refs fedora

```

你应该在输出中看到以下内容：

```
fedora:fedora/33/x86_64/silverblue

```

接下来，将你的系统变基到 Fedora 33 分支。

```
$ rpm-ostree rebase fedora:fedora/33/x86_64/silverblue

```

最后要做的是重启你的电脑并启动到 Fedora 33。

### 如何回滚

如果有什么不好的事情发生。例如，如果你无法启动到 Fedora 33，那很容易回滚回去。在启动时选择 GRUB 菜单中的前一个条目，你的系统就会以切换到 Fedora 33 之前的状态启动。要使这一改变永久化，请使用以下命令：

```
$ rpm-ostree rollback

```

就是这样了。现在你知道如何将 Silverblue 变基为 Fedora 33 并回滚了。那为什么不在今天就做呢？

---

via: <https://fedoramagazine.org/how-to-rebase-to-fedora-33-on-silverblue/>

作者：[Michal Konečný](https://fedoramagazine.org/author/zlopez/) 选题：[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/) 荣誉推出


《代码英雄》第三季（4）：深入 Perl 语言的世界

# 《代码英雄》第三季（4）：深入 Perl 语言的世界

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

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

<img src="content_image/album_202012_03_123749k2lozf7zrtprxfjo.png" alt="" title="">

本文是《[代码英雄](https://www.redhat.com/en/command-line-heroes)》系列播客[《代码英雄》第三季（4）：深入 Perl 语言的世界](https://www.redhat.com/en/command-line-heroes/season-3/diving-for-perl)的[音频](https://cdn.simplecast.com/audio/a88fbe/a88fbe81-5614-4834-8a78-24c287debbe6/be4cc2b9-8548-476d-98d5-874a93439675/CLH_S3E4_The_Perl_Dive_vFINAL_tc.mp3)脚本。

>
> 导语：语言来了又走。只有少数几种语言具备了登峰造极的能力，而能留在那里的则更少。Perl 有过一次惊人的崛起，也有过一次平静的低迷，现在已经在编程世界中找到了自己的位置。
>
>
> Perl似乎注定要统治网络。Michael Stevenson 和 Mike Bursell 描述了 Perl 的设计如何使它成为早期 web 的理想选择。我们从 Conor Myhrvold 那里听到了它的座右铭：“<ruby> 实现它的方法不止一种 <rt>  There is more than one way to do it. </rt></ruby>”。Elizabeth Mattijsen 分享了，尽管 Perl 有优势，但漫长的开发周期如何减缓了 Perl 的发展。虽然它不再是最顶级的网络语言，但 John Siracusa 指出，Perl 作为一种小众工具仍然存在。
>
>
>

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

想象一下 Perl 语言的创建者 Larry Wall 在 1999 年的一次会议上站在麦克风前，留着他标志性的浓密胡子和梳理过的刘海，感觉相当好，因为他所发明的语言正在越来越受欢迎。

**00:00:19 - 配音演员**：

声音测试。

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

Perl 语言轻而易举地超越了 COBOL、Visual Basic，而 Python 呢? Python 仍然是一个不入流的竞争者，是Larry Wall 的几个玩笑素材。Wall 展示了 [dice.com](http://dice.com) 报告中的人群数据，在那时来看，Perl 语言的未来是非常、非常光明的。然而之后 Perl 的未来就不再如此了。20 年之后，[dice.com](http://dice.com) 在 2018 年夏天将 Perl 列为最可能灭绝的语言之一。短短 20 年之间发生了什么？

\*\*00:00:59 \*\*：

我是 Saron Yitbarek，这里是《代码英雄》，一档红帽公司的原创播客。这一季是关于我们编程语言的力量和前景的。在上一集我们追踪了搭上了互联网顺风车的 JavaScript 的疯狂崛起。

**00:01:19**：

并不是每一种语言都有一个不停地成长和成功的故事。大多数语言的出现，在当时非常特殊的生态系统中发挥了它们的作用，然后当我们的编程生活里需要其他类型的工具时它们就开始消退。

**00:01:37**：

本集我们将深入了解 Perl 世界。是什么造就了它早期的成功，为什么它的成功突然就被颠覆了？我们所处的环境，我们的基础设施，我们的硬件，各种因素都会决定哪些语言会繁荣，哪些会开始萎缩。这就是 Perl 语言的故事的魅力所在。

**00:02:08**：

我们知道 Perl 并没有统治世界，但是退回到上世纪 90 年代的时候，却看不到这一点。Tim Berners-Lee 在 1991 年发明了<ruby> 万维网 <rt>  World Wide Web </rt></ruby>（WWW），它迅速创造了一个全新的基于 Web 的开发领域。谁也说不准会是哪种编程语言在这个新领域取得成功。

**00:02:31 - Michael Stevenson**：

在 Web 出现的时候，所有人都等待着会有什么事情发生。那个时候整个世界都是令人兴奋的。

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

Michael Stevenson 是<ruby> 阿姆斯特丹大学 <rt>  University of Amsterdam </rt></ruby>媒体研究的副教授。他为我们描述了早期的 Web。人们见过 Usenet，也见过 ARPANET。他们看到了<ruby> 美国在线 <rt>  America Online </rt></ruby>，还有一些其它的东西。但是直到 Web 出现，互联网的全部发展潜力才真正得到体现。突然之间，你就可以通过鼠标和点击进入了这个巨大的互联世界。这是一个出乎意料的事情。

**00:03:09 - Michael Stevenson**：

你要是记得 1993 年，也就是 Web 开始崭露头角的那一年，也是《<ruby> 连线杂志 <rt>  Wired Magazine </rt></ruby>》开始出版的那年。在那之前类似《Mondo 2000》这类的杂志真的让电脑看起来像神秘知识的来源，让电脑看起来很酷。

**00:03:32**：

因此从这个意义上说，Web 也到达了一个相当特定的时期，人们已经准备好以这样的方式迎接技术的兴起。

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

故事在这个时候开始了：Larry Wall 在 1987 年创建了 Perl，4 年后万维网才开始兴起。Larry Wall 给世界带来的 Perl 最初只是一种通用的 Unix 脚本语言。当然，它确实很有用，但同时 Perl 还有一些秘密元素，这些元素使它成为即将到来的 Web 开发世界的理想语言。

**00:04:14 - Michael Stevenson**：

比较有名的是 Perl 是 Larry Wall 在参与的一个美国国家安全局（NSA）的秘密项目中发明的，基本上他所做的就是创建一个类似黑客版的新闻栏目，运行在 Usenet 上，因此 Perl 语言从一开始就是操作文本和将数据从一个地方移动到另一个地方，这完全符合 Web 的需求。而 Perl 作为一种易于使用的脚本语言，更加接近于自然语言。它可以用来快速开发东西，所有这些都让 Perl 成为了一个完美的契机，不仅适合专业用户，也适合新加入的业余爱好者。

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

很偶然的是，当 Web 出现时，Perl 已经为 Web 做好了准备。Larry Wall 不可能知道 Web 即将出现。但当它出现时正好相互吻合。但我认为还有一点很关键：Perl 是一种自由语言。Larry Wall 在<ruby> 自由软件基金会 <rt>  Free Software Foundation </rt></ruby>开发的 GPL（<ruby> 通用公共许可证 <rt>  General Public License </rt></ruby>）下发布了它。

**00:05:37**：

Larry Wall 让他的语言自由开源的决定，这完全符合刚刚开始出现的基于 Web 的新思维方式，同时 Perl 在其他方面也很有前瞻性。

**00:05:50 - Michael Stevenson**：

Perl 的开放性令人难以置信，在某种意义上，它总是愿意整合一些其他的东西，新的东西。这和 Larry Wall 的身份很相配。他是个很开放谦虚的人，总是仔细考虑别人的想法，并试着设身处地为别人着想。对我来说 Perl 作为一种语言，以及作为一个社区的性格，在很长一段时间里，真的很符合这一点。

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

同样 Perl 非常适合 Web 早期的、狂野的西部阶段，也就是黑客时代。实际上 Perl 的座右铭之一就是“<ruby> 实现它的方法不止一种 <rt>  There is more than one way to do it. </rt></ruby>”。

**00:06:39 - Michael Stevenson**：

在 Perl 的鼎盛时期和这个试验性的开放 Web 的鼎盛时期之间，存在着一种近乎浪漫的联系，在它变得如此被几个平台所控制之前。

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

记得上世纪 90 年代是互联网历史上的一段启蒙时期，那时人们还在争先恐后地想知道还有哪些是可能的。那时对编程的需求是巨大的，每个人都需要有一个网页，这意味着一群全新的开发人员，他们都对新的做事方法持开放态度。问题变成了“好吧，我们有了一个全新的领域，但用什么语言来完成这项工作呢？”

**00:07:26**：

Perl 虽然并不是这个问题的最终答案，但对于很多人来说 Perl 是他们首选的答案。

**00:07:34 - Michael Stevenson**：

我并不是说我更愿意看到加载速度超慢的网页、也没有谷歌搜索引擎的时代，但我确实认为那个时代有一些特别的东西，当时有一些人，在他们的宿舍里，创建了一个类似 slashdot 的东西。而现在随着 Web 已经变得如此主流、专业化和被集中在几个大公司中，我们确实怀念那个时代。对我来说，Perl 比其他任何早期的故事都更能象征这一点。

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

Michael Stevenson 是阿姆斯特丹大学媒体研究的副教授。

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

之后，随着 90 年代的到来，Perl 作为一种能适应早期互联网发展潜力的语言出现了，它是一个时代的语言。Larry Wall 和他所创造的 Perl 正好明白了 Web 的本质。

**00:08:40 - Mike Bursell**：

在网上你可以随意搜索，也可以随时创建网页，这是互联网的美丽新世界，你可以随时做这些事情。

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

这位是 Mike Bursell，红帽公司的首席安全架构师。Mike 是 90 年代中期发现和使用 Perl 的黑客之一。

**00:09:00 - Mike Bursell**：

对于 Web 来说，Perl 是许多人的起点。Java™ 语言还处于早期阶段，它在文本输入输出方面并不是很出色。如果你想进行查询和生成页面，Perl 是人们都在使用的工具。

**00:09:22**：

Perl 非常适合，因为它擅长获取文本，并使用文本做其他事情，而这正是 Web 所需要的。

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

顺便需要说一下的是，Larry Wall 有语言学背景，这就解释了为什么 Perl 有强大的文本解析能力。正如 Mike Bursell 提到的，这是一笔巨大的好处，因为在早期，Web 主要是一种基于文本的媒介，因为那时候人们没有足够的带宽来处理图像。

**00:09:51 - Mike Bursell**：

它很容易使用，也很容易复制。人们在分享方面非常开放，而且它的输出速度很快，这些都是好东西。

**00:10:02**：

哦，当然还有一点，就是你可以用管道使用它。所以，这是非常多的人所习惯的，而且非常容易测试，甚至离线测试，这都是非常有用的。

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

尤其对那些在 Web 的世界中重新规划自己生活的系统管理员来说非常有用。

**00:10:21 - Mike Bursell**：

Perl 是给系统管理员们的真正礼物。即使在那个年代，如果你做一些事情，你会得到很多日志。管理这些日志，分解它们，搜索它们，并能够以不同的方式显示它们，或获取任何其他大型文本语料库（主要就是日志），甚至可以对它们进行调试。除非你要在命令行里用管道方式传输 `orc`、`sed` 之类的东西，以及 `ed` 命令，那样的话你很快就会变得非常痛苦，而 Perl 正好适合让你去处理这些事情。

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

到上世纪 90 年代后期，Perl 5 已经聚集了一个强大的用户群体。像 Fortran 和 C 这样的旧语言依赖于庞大而昂贵的硬件，而 Perl 更有生命力，也更容易移植。在那样一个硬件成本急剧下降的世界里，Perl 的设计使得它得到了蓬勃发展，Perl 让所有的新程序员快速、轻松地工作。这是因为 Larry Wall 以牺牲 CPU 和内存的需求为代价，使 Perl 语法更人性化。所有这些元素组合在一起，使 Perl 成为一种很受新开发社区欢迎的语言。

**00:11:36 - Mike Bursell**：

在一个正在壮大的社区里，你可以去和他们聊聊社区里的事情，然后 PerlMonks 论坛出现了，那里是一个讨论的好地方，能在论坛里知道正在发生的事情。

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

这个社区确实拥有 Web 所能提供的最好的东西。他们发现了一个巨大的软件模块库，一个叫做 CPAN 的存储库，这些系统管理员都很喜欢它。它给 Perl 提供了更多的灵活性，许多人都可以部署由少数几个编程大师编写的代码。

**00:12:15 - Mike Bursell**：

它有很多库可以做你想做的任何事情，如果你找不到你想要的库，你可以去问一下，然后就会有好心人写出你想要的库。

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

Mike Bursell 是红帽公司的首席安全架构师。

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

正是由于 Perl 是免费的，它受到不断增长的模块库的支持，它是可移植的，而且它有一个蓬勃发展的社区。一切看起来都很好。Perl 可以在上世纪 90 年代 Web 开发新天地中发挥所有优势，但就在 90 年代即将结束的时候，互联网的发展前景又一次发生了变化，时代来了一个大的转变。

**00:12:57 - Alan Greenspan**：

但我们又能如何知道非理性繁荣何时已过度推高了资产价值，进而导致了意料之外的长期经济收缩?

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

“非理性繁荣”是时任美联储主席 Alan Greenspan 在 1996 年对<ruby> 美国企业协会 <rt>  American Enterprise Institute </rt></ruby>所说的话。他那句“非理性繁荣”是对 90 年代人人都经历过的网络泡沫的警告。所有早期使用 Perl 的 Web 开发人员都在那个泡沫中乘风破浪，但正如 Greenspan 预测的那样，泡沫在 2000 年破裂了。

**00:14:11 - Conor Myhrvold**:

大家好，我是 Conor Myhrvold 。在过去的五六年里我一直从事编程，现在在技术领域为 Uber 工作。

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

2000 年代初，当 Conor 还在高中的时候，Perl 仍然是一个非常重要的东西。但他越来越意识到一种与之竞争的语言，叫做 Python。

**00:14:31 - Conor Myhrvold**：

Python 所追求的是一种更结构化的语言，做很多不同的事情都有一种更显然的方式，它就是那样设计的。而 Perl 则喜欢做某件事有不止一种方法，但这让很多初学者感到困惑。

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

Perl 有这样一句座右铭“<ruby> 实现它的方法不止一种 <rt>  There is more than one way to do it. </rt></ruby>”。而 Python 的理念实际上是相反的，Python 为每个问题都提供了一个明显的解决方案，这意味着查看别人的 Python 代码很容易；而另一方面查看其他人的 Perl 代码可能会令人困惑。Perl 作为一个程序员的第三或第四种语言是有意义的，而正是因为它是一种脚本语言，而脚本是互联网连结的基础。

**00:15:23**：

但是 Python 是一种你可以真正深入研究的语言，即使你是一个新手。Perl 有一套特定的优势，比如在搜索文本和生物信息学上。但是 Python 就是这样一种简单通用的语言。Python 获得了越来越多的信任，成为人们首先想学的语言，这是一件大事。

**00:15:47 - Conor Myhrvold**：

越来越多的人开始上网，越来越多的人开始学习如何编程。尤其是相对于 Perl 而言，Python 受益于它本身相对容易学习，因为它更结构化。而这样结果是，在一个快速增长的世界里，如果你能得到更多增长的份额，那将最终意味着有更多的教程和更多的东西可供你使用。

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

在之前提到过的 CPAN，它是 Perl 用户可以使用的强大的中央存储库。这在 90 年代是一大亮点，但 CPAN 的价值也在变化。

**00:16:24 - Conor Myhrvold**：

这也不能真正帮助你学习一门语言，因为你是在“复制粘贴”，只是用最少的方式替换你需要的东西。从长远来看这是一个劣势，因为如果你让人们通过自己进行原始开发来学习如何使用一种编程语言，即使这需要花费更长的时间，他们也会觉得自己对它投入了更多，而且他们也了解在这中间发生了什么。

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

Python 没有像 CPAN 那样的集中式存储库，但是对于那些在新千年时代来到这里的开发人员来说，在一个互联网搜索功能如此强大的世界里，存储库并没有那么大的价值。

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

最终 Python 有了大量的教程，当然现在也有了像 GitHub 这样的平台。

**00:17:13 - Conor Myhrvold**：

最终发生的事情是 Perl 拥有的许多优势，是来自一个已经过时的时代的网络效应。

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

Conor Myhrvold 是 Uber 的一名工程师。

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

然而语言的兴衰很少是由外部力量单独决定的，而 Perl 的内部问题是，在它的发展过程中它似乎遇到了障碍。Python 正在以一种相当有序的方式发布新的迭代，而正如我们在本季度第一集中所了解到的，Perl 在 2000 年互联网泡沫破裂之时，Python 开始获得更多新开发人员的青睐。

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

每个人都期待着 Perl 6 的发布，人们都很兴奋。他们等啊，等啊，等啊……他们等了 14 年。

**00:18:15 - Elizabeth Mattijsen**：

人们提出了大约 300 多件 Perl 6 应该能够完成的事情，当然其中很多事情基本上都是相互排斥的。

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

这是 Elizabeth Mattijsen，她是 Perl 6 的核心开发人员。2000 年，Elizabeth 参加了在<ruby> 蒙特雷 <rt>  Monterey </rt></ruby>举办的 Perl 会议。那时开发者认为他们已经停滞不前了，所以 Perl 6 是必要的。Larry Wall 同意了，但是如果说 Perl 5 是他对 Perl 的重写，那么他希望 Perl 6 是由社区来对 Perl 进行重写。由于团队合作可能需要更长时间，甚至用了 14 年，对于那些开发者来说，这是一条漫长而艰难的道路。

**00:19:01 - Elizabeth Mattijsen**：

我们可以说当前实施的 Perl 6 项目实际上是实现它的第三次尝试。

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

按照 Elizabeth 的说法，在这 14 年里有过多次尝试。中间经历了漫长而痛苦的深度的尝试。开发者们心力交瘁；人们陷入了死胡同。到 2015 年圣诞节那天 Perl 6 终于问世时，世界上的许多地方已经开始了新的发展。而需要注意的是 Perl 6 并没有给予成为某种革命性的新事物，从而实现对 Python 的反击。Perl 6 是对原版进行了深思熟虑的重新设计。

**00:19:43 - Elizabeth Mattijsen**：

我认为 Larry Wall 在他的“<ruby> 洋葱状态 <rt>  State of the Onion </rt></ruby>”演讲中使用了一个很好的比喻。对他来说，Perl 5 就像《<ruby> 霍比特人 <rt>  The Hobbit </rt></ruby>》，而 Perl 6 就像《<ruby> 指环王 <rt>  Lord of the Rings </rt></ruby>》。如果你仔细看过《霍比特人》和《指环王》的故事，你会发现它们基本上是同一个故事。只是《霍比特人》比《指环王》小得多，情节漏洞也更多，没有《指环王》那么宏大的背景。我认为这很好地描述了 Perl 5 和 Perl 6 之间的区别。它基本上是同样的想法，同样的思路，同样的环境，只是对它的重新构想。

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

Elizabeth Mattijsen 是 Perl 6 的核心贡献者。

**00:20:32**：

如今，Perl 甚至可能不在前 20 种语言之列。在外部竞争和内部拖延之间，它还没有向大多数新开发人员证明自己。但这提出了一个大问题，我们真的应该根据一种语言的流行度来判断我们的编程语言吗？或者说我们应该根据其他方面来判断一种编程语言的价值？当昔日的超级巨星成为陪衬时，这到底意味着什么呢？

**00:21:06**：

在世纪之交时互联网泡沫破裂时，Perl 的统治地位开始衰退时，Larry Wall 发表了一个有趣的声明。他认为尽管 Perl 永远不会再成为世界上最流行的编程语言，但它可以成为较小类别中的领先者。Larry Wall 说那才是真正的目标。成为同类中最好的，而不是世界上最好的。正如他所说的，SUV 永远不会和赛车竞争。

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

我想深入研究这个想法，我想了解在细分类别中做到最好对编程语言的真正含义。

**00:21:48 - John Siracusa**：

我是 John Siracusa，我是一个程序员，也是一个播客主。

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

John 实际上共同主持了三个播客：《Accidental Tech Podcast》、《Reconcilable Differences》和《Robot or Not?》。我们刚刚聊到了 Perl 在当今世界的地位。

**00:22:06**：

Perl 在当今世界排名如何？它仍然是最好的计算机语言吗？

**00:22:10 - John Siracusa**：

Perl 6 具有其他语言没有的、其他语言应该具有的东西，我一直在等待其他语言偷学它。例如语法是将常见任务概念化的一种好方法，而在我看来，使用语法来解决解析问题比使用现有的工具更令人愉快、更干净、更好。

**00:22:31**：

在 Perl 中，对象系统的许多部分看起来很琐碎而无关紧要，但我完全期待其他语言最终会采用它，就像许多语言最终采纳了 Perl 5 中的许多思想一样。因此我认为 Perl 6 在许多方面都是最好的。遗憾的是，很少有人有机会使用它。

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

你认为 Perl 6 社区的发展需要做些什么？想让人们更多地参与到 Perl 6 中，需要做些什么？

**00:23:00 - John Siracusa**：

这有点像 Perl 6 的故事，就像它一直在寻找一个真正奇妙的实现一样。这是第二系统问题的一部分……<ruby> 第二系统综合症 <rt>  second-system syndrome </rt></ruby>，我想他们是这样称呼……

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

哦。

**00:23:12 - John Siracusa**：

Perl 6 的，人们希望修复世界上的所有问题。他们想要解决的问题之一是运行时环境。是什么在运行我们的代码？运行 Perl 5 和之前的 Perl 4 的东西是一个巨大的 C 程序，这是由具有独特编码风格的开发者编写的。还有大量的宏，它是一种相当难以理解的东西。

**00:23:33**：

Perl 6 的想法是让我们不要再那样做了，让我们不要制造大量的 C 代码。相反，让我们使用一个虚拟机，这在当时是一种时尚，有很多关于如何实现它的伟大想法。最终我们得到了几个中规中矩的虚拟机实现版本，有时这些还会相互竞争，但没有一个达到真正交付语言使用时需要的性能、稳定性和特性。

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

现如今 Perl 到底发生了什么？你对此有什么看法？

**00:24:06 - John Siracusa**：

Perl 5 绝对像是在走下坡路，因为与 Perl 5 同时代的所有其他语言都采纳了它的大部分最佳思想，并获得了更多的支持。也就是说，因为它在很长一段时间内都是王者，所以有很多 Perl 5 代码在运行一些大型的、重要的站点，人们需要维护和扩展这些代码。

**00:24:29**：

这需要很长时间才能消失。只要看看现今仍然存在的 COBOL，人们怎么还在雇佣人在 COBOL 上做维护吧？

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

嗯。是这样。

**00:24:36 - John Siracusa**：

你刚才问 Perl 是不是一门垂死的语言，我提到了 COBOL，这听起来并不乐观。Perl 6 本身会成为主流语言吗？看起来可能性不大。现在对其他语言有非常多的关注，如果 Perl 6 现在还没有得到开发者的关注，我不知道将会需要发生什么变化来让它流行起来。

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

如果你是这样想的，你对 Perl 有什么期望？你希望在 Perl 5 或 Perl 6 中看到什么，以及希望看到将来发生什么？

**00:25:04 - John Siracusa**：

我对 Perl 5 的希望是人们不要忽视它，因为尽管有其它更流行的语言，但今天许多公司仍然采用 Perl 5 做为解决问题的最佳方案。通常这些都是胶水类型语言的问题。如果你发现自己曾经编写过 shell 脚本，并且可能会说:“好吧，我不会用我的‘真正的编程语言’来做这件事。”不管是 Python，还是 Ruby，还是别的什么。但是 shell 脚本可以让我把一堆东西连接起来（胶水类型语言）。Perl 是完成这项工作的更好工具。编写正确的 Perl 脚本要比编写正确的 shell 脚本更容易。

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

我认为归根结底 Perl 可能不再是一个适合入门的语言，但对于经验更丰富的多语言开发人员来说，它是那个你永远不希望扔掉的工具箱中的小工具，而且特定的工具有时让你提升水平的工具。

**00:25:58 - John Siracusa**：

有时我为 Perl 6 感到难过和沮丧，认为它不会有任何进展，有时我想“好吧，这是个不错的小社区”。每个社区都不需要称霸世界，也不需要成为整个行业的主导语言。也许可以就这样一直走下去，就是，无限期地走下去。这就是开源，和编程语言的伟大之处。没人可以阻止你，你可以像以前一样继续开发 Perl 6。

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

John Siracusa 是一名程序员，也是三个科技播客的联合主持人。

**00:26:34**：

语言都是有生命周期的。当新的语言出现时它们能够精确地适应新的现实，像 Perl 这样的选择可能会占据更小的、更小众的领域，但这并不是一件坏事。我们的语言应该随着我们需求的变化而扩大或缩小它们的群体。在互联网开发的早期历史中，Perl 是一个至关重要的角色，它以各种方式与我们联系在一起，只要看一看它的历史就会发现它的存在。

**00:27:11**：

下次在《代码英雄》中，我们将讨论：是什么将一种语言变成了标准？以及在基于云的开发世界中，新的标准将如何出现？

**00:27:26**：

《代码英雄》是红帽的原创播客。如果你想深入了解 Perl 的故事，或者任何我们在第三季中探索的编程语言，请访问 [redhat.com/commandlineheroes](file:///Users/xingyuwang/develop/LCRH-wxy/translated/redhat.com/commandlineheroes) 。我们的网站里有许多精彩内容等你去探索。

**00:27:49**：

我是 Saron Yitbarek。下期之前，编码不止。

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

---

via: <https://www.redhat.com/en/command-line-heroes/season-3/diving-for-perl>

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

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


十年 Linux 桌面生存指南

一大堆命令，很难想象这就是桌面生存指南，很难生存的

同感，这对命令行最适合自娱自乐，自己怎么玩都行，但是现在私有格式那么多，老板发给你个word就gg了

说白了学那些最适合大众的东西成本最低

我用从fedora8开始，从来都是用桌面的，很好用啊

我用linux大概也有十年了，各种版本都尝试过了，最终选择折中的版本-debian，最小化安装，i3wm桌面管理。<br />
兼容的软件最多。稳定性非常好

都到这种程度了,你觉得是老板给word就跪的人物吗？这配置,这台机一开就只干一件事——写代码.其他事务给其他机器做,只要开这台机就完全沉浸到代码世界里了.

word太好解决了,转PDF,转HTML,转XML,转MARKDOWN,转ODT,要高级排版LATEX转XML或PDF之类的再转成word,想要什么格式随你挑,实在挑剔？让小弟搞排版,排 版这破事让技术人员做也亏你老板想的出来.

# 十年 Linux 桌面生存指南

从 2006 年开始转战 Linux 系统后，经过几年的实践，我的软件设置在过去十年内出人意料的固定。再过十年回顾一下，看看发生了什么，也许会非常有趣。在写这篇推文时，我迅速回顾了正在运行的内容：

### 动机

我的软件介绍排序不分先后：

* 程序应该运行在本地系统中以便我可以控制它，这其中并不包括云解决方案。
* 程序应在终端中运行，以便于在任何地方连贯地使用它们，包括性能稍差的电脑或手机。
* 通过使用终端软件，可以实现自动聚焦键盘。只有在一些有意义的地方，我会更喜欢使用鼠标，因为在打字期间一直伸手去拿鼠标感觉像在浪费时间。有时候过了一个小时我才注意到甚至还没有插鼠标。
* 最好使用快速高效的软件，我不喜欢听到风扇的声音和感到房间在变热。我还可以继续长久地运行旧硬件，已经使用了 10 年的 Thinkpad x200s 还能很好地支持我所使用的软件。
* 组合。我不想手动执行每个步骤，而是在需要时自动执行更多操作，这时自然是支持 shell。

### 操作系统

十二年前移除 Windows 系统后，我在 Linux 系统上经历了一个艰难的开始，当时我手上只有 [Gentoo Linux](https://gentoo.org/) 系统的安装光盘和一本打印的说明书，要用它们来实现一个可运行的 Linux 系统。虽然花费了几天的时间去编译和修整，但最终还是觉得自己受益颇多。

自此我再也没有转回 Windows 系统，但在持续的编译压力导致风扇失灵后，我将我的电脑系统切换到 [Arch Linux](https://www.archlinux.org/)。之后我将其他的电脑和私人服务器也切换到了 Arch Linux。作为一个滚动发布发行版，你可以随时升级软件包，但 [Arch Linux News](https://www.archlinux.org/news/) 已经详细报道了其中最主要的漏洞。

不过，令人烦恼的是一旦你更新了旧的内核模组，Arch Linux 就会移除旧版的相关信息。我经常注意到一旦我试着插入一个 USB 闪存盘，内核就无法加载相关组件。相反，每次内核升级后都应该进行重启。有一些 [方法](https://www.reddit.com/r/archlinux/comments/4zrsc3/keep_your_system_fully_functional_after_a_kernel/) 可以解决这个问题，但我还没有实际地使用它们。

其他程序也会出现类似的情况，通常 Firefox 、 cron 或者 Samba 在升级后都需要重启，但恼人的是，它们没有警告你存在这种情况。我在工作中使用的 [SUSE](https://www.suse.com/) 很好地提醒了这种情况。

对于 [DDNet](https://ddnet.tw/) 产品服务器，相较于 Arch Linux ，我更倾向于 [Debian](https://www.debian.org/) 系统，这样在每次升级时出现故障的几率更低。我的防火墙和路由器使用了 [OpenBSD](https://www.openbsd.org/) ，它拥有干净系统、文档和强大的 [pf 防火墙](https://www.openbsd.org/faq/pf/)，而我现在不需要一个单独的路由器。

### 窗口管理器

从我开始使用 Gentoo 后，我很快注意到 KDE 的编译时间非常长，这让我没办法继续使用它。我四处寻找更简单的解决方案，最初使用了 [Openbox](http://openbox.org/wiki/Main_Page) 和 [Fluxbox](http://fluxbox.org/)。某次，为了能更多进行纯键盘操作，我开始尝试转入平铺窗口管理器，并在研究其初始版本的时候学习了 [dwm](https://dwm.suckless.org/) 和 [awesome](https://awesomewm.org/)。

最终，由于 [xmonad](https://xmonad.org/)的灵活性、可扩展性以及使用纯 [Haskell](https://www.haskell.org/)（一种出色的函数编程语言）编写和配置，最终选择了它。一个例子是，我在家中运行一个 40" 4K 的屏幕，但经常会将它分为四个虚拟屏幕，每个虚拟屏幕显示一个工作区，每个工作区自动排列在我的窗口上。当然， xmonad 有一个对应的 [模块](http://hackage.haskell.org/package/xmonad-contrib-0.15/docs/XMonad-Layout-LayoutScreens.html)。

[dzen](http://robm.github.io/dzen/) 和 [conky](https://github.com/brndnmtthws/conky) 对我来说是一个非常简单的状态栏。我的整体 conky 配置看起来是这样的：

```
out_to_console yes
update_interval 1
total_run_times 0

TEXT
${downspeed eth0} ${upspeed eth0} | $cpu% ${loadavg 1} ${loadavg 2} ${loadavg 3} $mem/$memmax | ${time %F %T}

```

输入命令直接通过管道输入 dzen2：

```
conky | dzen2 -fn '-xos4-terminus-medium-r-normal-*-12-*-*-*-*-*-*-*' -bg '#000000' -fg '#ffffff' -p -e '' -x 1000 -w 920 -xs 1 -ta r

```

对我而言，一项重要功能是在完成工作后使终端发出蜂鸣声。只需要简单地在 zsh 中的 `PR_TITLEBAR` 变量中添加一个 `\a` 字符就可以做到，只要工作完成就可以发出蜂鸣声。当然，我使用了命令：

```
echo "blacklist pcspkr" > /etc/modprobe.d/nobeep.conf

```

将 `pcspkr` 内核模块列入黑名单来禁用实际的蜂鸣声。相反 urxvt 的 `URxvt.urgentOnBell: true` 设置会将声音变为尖锐。之后 xmonad 有一个 urgency 钩子来捕捉这类信号，并且我可以使用组合键自动聚焦到当前的发出紧急信号的窗口。在 dzen 中我可以看到一个漂亮且明亮的 `#ff0000` 紧急窗口。

在我笔记本上所得到的最终成品是：

我听说前几年 [i3](https://i3wm.org/) 变得非常流行，但它要求更多的手动窗口对齐而不是自动对齐。

我意识到也有像 [tmux](https://github.com/tmux/tmux/wiki) 那样的终端多路复用器，但我仍想要一些图形化应用程序，因此最终我没有有效地使用它们。

### 终端连续性

为了使终端保持活跃状态，我使用了 [dtach](http://dtach.sourceforge.net/) ，它只是模拟屏幕分离功能。为了使计算机上的每个终端都可连接和断开，我编写了一个小的封装脚本。 这意味着，即使必须重新启动 X 服务器，我也可以使所有终端都运行良好，包括本地和远程终端。

### Shell & 编程

对于 shell，我使用 [zsh](http://www.zsh.org/) 而不是 [bash](https://www.gnu.org/software/bash/)，因为它有众多的功能。

作为终端模拟，我发现 [urxvt](http://software.schmorp.de/pkg/rxvt-unicode.html) 足够轻巧，支持 Unicode 编码和 256 色，具有出色的性能。另一个重要的功能是可以分别运行 urxvt 客户端和守护进程。因此，即使大量终端也几乎不占用任何内存（回滚缓冲区除外）。

对我而言，只有一种字体看起来绝对干净和完美： [Terminus](http://terminus-font.sourceforge.net/)。 由于它是位图字体，因此所有内容都是完美像素，渲染速度极快且 CPU 使用率低。为了能使用 `CTRL-WIN-[1-7]` 在每个终端按需切换字体，我的 `~/.Xdefaults` 包含:

```
URxvt.font: -xos4-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*
dzen2.font: -xos4-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*

URxvt.keysym.C-M-1: command:\033]50;-xos4-terminus-medium-r-normal-*-12-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-2: command:\033]50;-xos4-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-3: command:\033]50;-xos4-terminus-medium-r-normal-*-18-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-4: command:\033]50;-xos4-terminus-medium-r-normal-*-22-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-5: command:\033]50;-xos4-terminus-medium-r-normal-*-24-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-6: command:\033]50;-xos4-terminus-medium-r-normal-*-28-*-*-*-*-*-*-*\007
URxvt.keysym.C-M-7: command:\033]50;-xos4-terminus-medium-r-normal-*-32-*-*-*-*-*-*-*\007

URxvt.keysym.C-M-n: command:\033]10;#ffffff\007\033]11;#000000\007\033]12;#ffffff\007\033]706;#00ffff\007\033]707;#ffff00\007
URxvt.keysym.C-M-b: command:\033]10;#000000\007\033]11;#ffffff\007\033]12;#000000\007\033]706;#0000ff\007\033]707;#ff0000\007

```

对于编程和书写，我使用 [Vim](https://www.vim.org/) 语法高亮显示和 [ctags](http://ctags.sourceforge.net/) 进行索引，以及一些带有 `grep` 、`sed` 和其他用于搜索和操作的常用终端窗口。这可能不像 IDE 那样舒适，但可以实现更多的自动化。

Vim 的一个问题是你已经习惯了它的键映射，因此希望在任何地方都使用它们。

在 shell 功能不够强大时，[Python](https://www.python.org/) 和 [Nim](https://nim-lang.org/) 作为脚本语言也不错。

### 系统监控

[htop](https://hisham.hm/htop/) （查看当前站点的后台运行，是托管服务器的实时视图）非常适合快速了解软件的当前运行状态。 [lm\_sensors](http://lm-sensors.org/) 可以监控硬件温度、风扇和电压。 [powertop](https://01.org/powertop/) 是一款由 Intel 发布的优秀省电小工具。 [ncdu](https://dev.yorhel.nl/ncdu) 可以交互式分析磁盘使用情况。

[nmap](https://nmap.org/)、 iptraf-ng、 [tcpdump](https://www.tcpdump.org/) 和 [Wireshark](https://www.wireshark.org/) 都是分析网络问题的基本工具。

当然还有很多更优秀的工具。

### 邮件 & 同步

在我的家庭服务器上，我为自己所有的邮箱账号运行了 [fetchmail](http://www.fetchmail.info/) 守护进程。fetchmail 只是检索收到的邮件并调用 [procmail](http://www.procmail.org/)：

```
#!/bin/sh
for i in /home/deen/.fetchmail/*; do
 FETCHMAILHOME=$i /usr/bin/fetchmail -m 'procmail -d %T' -d 60
done

```

配置非常简单，然后等待服务器通知我们有新的邮件：

```
poll imap.1und1.de protocol imap timeout 120 user "dennis@felsin9.de" password "XXX" folders INBOX keep ssl idle

```

我的 `.procmailrc` 配置包含一些备份全部邮件的规则，并将邮件整理在对应的目录下面。例如，基于邮件列表名或者邮件标题：

```
MAILDIR=/home/deen/shared/Maildir
LOGFILE=$HOME/.procmaillog
LOGABSTRACT=no
VERBOSE=off
FORMAIL=/usr/bin/formail
NL="
"

:0wc
* ! ? test -d /media/mailarchive/`date +%Y`
| mkdir -p /media/mailarchive/`date +%Y`

# Make backups of all mail received in format YYYY/YYYY-MM
:0c
/media/mailarchive/`date +%Y`/`date +%Y-%m`

:0
* ^From: .*(.*@.*.kit.edu|.*@.*.uka.de|.*@.*.uni-karlsruhe.de)
$MAILDIR/.uni/

:0
* ^list-Id:.*lists.kit.edu
$MAILDIR/.uni-ml/

[...]

```

我使用 [msmtp](https://marlam.de/msmtp/) 来发送邮件，它也很好配置：

```
account default
host smtp.1und1.de
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
auth on
from dennis@felsin9.de
user dennis@felsin9.de
password XXX

[...]

```

但是到目前为止，邮件还在服务器上。 我的文档全部存储在一个目录中，我使用 [Unison](https://www.cis.upenn.edu/~bcpierce/unison/) 在所有计算机之间进行同步。Unison 可以视为双向交互式 [rsync](https://rsync.samba.org/)，我的邮件是这个文件目录下的一部分，因此它们最终存储在我的电脑上。

这也意味着，尽管邮件会立即到达我的邮箱，但我只是按需拿取，而不是邮件一到达时就立即收到通知。

从此我使用 [mutt](http://www.mutt.org/) 阅读邮件，使用侧边栏显示我的邮件目录。 `/etc/mailcap` 文件对于显示非纯文本邮件（ HTML, Word 或者 PDF）不可或缺：

```
text/html;w3m -I %{charset} -T text/html; copiousoutput
application/msword; antiword %s; copiousoutput
application/pdf; pdftotext -layout /dev/stdin -; copiousoutput

```

### 新闻 & 通讯

[Newsboat](https://newsboat.org/) 是一个非常棒的终端 RSS/Atom 阅读器。我在一个有约 150 个提要的 `tach` 会话服务器上运行它。也可以在本地选择提要，例如：

```
ignore-article "https://forum.ddnet.tw/feed.php" "title =~ \"Map Testing •\" or title =~ \"Old maps •\" or title =~ \"Map Bugs •\" or title =~ \"Archive •\" or title =~ \"Waiting for mapper •\" or title =~ \"Other mods •\" or title =~ \"Fixes •\""

```

我以同样的方式使用 [Irssi](https://irssi.org/) 进行 IRC 通讯。

### 日历

[remind](https://www.roaringpenguin.com/products/remind) 是一个可以从命令行获取的日历。通过编辑 `rem` 文件可以设置新的提醒：

```
# One time events
REM 2019-01-20 +90 Flight to China %b

# Recurring Holidays
REM 1 May +90 Holiday "Tag der Arbeit" %b
REM [trigger(easterdate(year(today()))-2)] +90 Holiday "Karfreitag" %b

# Time Change
REM Nov Sunday 1 --7 +90 Time Change (03:00 -> 02:00) %b
REM Apr Sunday 1 --7 +90 Time Change (02:00 -> 03:00) %b

# Birthdays
FSET birthday(x) "'s " + ord(year(trigdate())-x) + " birthday is %b"
REM 16 Apr +90 MSG Andreas[birthday(1994)]

# Sun
SET $LatDeg 49
SET $LatMin 19
SET $LatSec 49
SET $LongDeg -8
SET $LongMin -40
SET $LongSec -24

MSG Sun from [sunrise(trigdate())] to [sunset(trigdate())]
[...]

```

遗憾的是，目前 remind 中还没有中国农历的提醒功能，因此中国的节日不易计算。

我给提醒设置了两个名字：

```
rem -m -b1 -q -g

```

按时间顺序查看待办事项清单

```
rem -m -b1 -q -cuc12 -w$(($(tput cols)+1)) | sed -e "s/\f//g" | less

```

显示适应终端宽度的日历：

### 字典

[rdictcc](https://github.com/tsdh/rdictcc) 是鲜为人知的字典工具，它可以使用 [dict.cc](https://www.dict.cc/) 很棒的词典并将他们转存在本地数据库中：

```
$ rdictcc rasch
====================[ A => B ]====================
rasch:
 - apace
 - brisk [speedy]
 - cursory
 - in a timely manner
 - quick
 - quickly
 - rapid
 - rapidly
 - sharpish [Br.] [coll.]
 - speedily
 - speedy
 - swift
 - swiftly
rasch [gehen]:
 - smartly [quickly]
Rasch {n} [Zittergras-Segge]:
 - Alpine grass [Carex brizoides]
 - quaking grass sedge [Carex brizoides]
Rasch {m} [regional] [Putzrasch]:
 - scouring pad
====================[ B => A ]====================
Rasch model:
 - Rasch-Modell {n}

```

### 记录和阅读

我有一个简单记录任务的备忘录，在 Vim 会话中基本上一直处于打开状态。我也使用备忘录作为工作中“已完成”工作的记录，这样就可以检查自己每天完成了哪些任务。

对于写文件、信件和演示文稿，我会使用 [LaTeX](https://www.latex-project.org/) 进行高级排版。德式的简单信件可以这样设置，例如：

```
\documentclass[paper = a4, fromalign = right]{scrlttr2}
\usepackage{german}
\usepackage{eurosym}
\usepackage[utf8]{inputenc}
\setlength{\parskip}{6pt}
\setlength{\parindent}{0pt}

\setkomavar{fromname}{Dennis Felsing}
\setkomavar{fromaddress}{Meine Str. 1\\69181 Leimen}
\setkomavar{subject}{Titel}

\setkomavar*{enclseparator}{Anlagen}

\makeatletter
\@setplength{refvpos}{89mm}
\makeatother

\begin{document}
\begin{letter} {Herr Soundso\\Deine Str. 2\\69121 Heidelberg}
\opening{Sehr geehrter Herr Soundso,}

Sie haben bei mir seit dem Bla Bla Bla.

Ich fordere Sie hiermit zu Bla Bla Bla auf.

\closing{Mit freundlichen Grüßen}

\end{letter}
\end{document}

```

在 [我的私人网站](http://felsin9.de/nnis/research/) 上可以找到更多的示例文档和演示文稿。

[Zathura](https://pwmt.org/projects/zathura/) 读取 PDF 文件速度很快，支持 Vim 类控件，还支持两种不同的 PDF 后端： Poppler 和 MuPDF。另一方面，在偶尔遇到 Zathura 无法打开的文件时，[Evince](https://wiki.gnome.org/Apps/Evince) 则显得更全能一些。

### 图片编辑

简便的选择是，[GIMP](https://www.gimp.org/) 和 [Inkscape](https://inkscape.org/) 分别用于照片编辑和交互式向量图形。

有时 [Imagemagick](https://imagemagick.org/Usage/) 已经足够好了，它可以从命令行直接使用，从而自动编辑图片。同样 [Graphviz](https://www.graphviz.org/) 和 [TikZ](https://sourceforge.net/projects/pgf/) 可以用来绘制曲线图和其他图表。

### Web 浏览器

对于 Web 浏览器，我一直在使用 [Firefox](https://www.mozilla.org/en-US/firefox/new/)。相较于 Chrome，它的可扩展性更好，资源使用率更低。

不幸的是，在 Firefox 完全改用 Chrome 风格的扩展之后， [Pentadactyl](https://github.com/5digits/dactyl) 扩展的开发就停止了，所以我的浏览器中再也没有令人满意的 Vim 类控件了。

### 媒体播放器

通过设置 `vo=gpu` 以及 `hwdec=vaapi`，具有硬件解码功能的 [mpv](https://mpv.io/) 在播放期间 CPU 的占用率保持在 5%。相较于默认的 PulseAudio，mpv 中的 `audio-channels=2` 似乎可以使我的立体扬声器/耳机获得更清晰的降级混频。一个很棒的小功能是用 `Shift-Q` 退出，而不是只用 `Q` 来保存回放位置。当你与母语是其他语言的人一起看视频时，你可以使用 `--secondary-sid=` 同时显示两种字幕，主字幕位于底部，次字幕位于屏幕顶部。

我的无线鼠标可以简单地通过一个小的配置文件（ `~/.config/mpv/input.conf` ）实现远程控制 mpv ：

```
MOUSE_BTN5 run "mixer" "pcm" "-2"
MOUSE_BTN6 run "mixer" "pcm" "+2"
MOUSE_BTN1 cycle sub-visibility
MOUSE_BTN7 add chapter -1
MOUSE_BTN8 add chapter 1

```

[youtube-dl](https://rg3.github.io/youtube-dl/) 非常适合观看在线托管的视频，使用 `-f bestvideo+bestaudio/best --all-subs --embed-subs` 命令可获得最高质量的视频。

作为音乐播放器， [MOC](http://moc.daper.net/) 不再活跃开发，但它仍是一个简易的播放器，可以播放各种可能的格式，包括最不常用的 Chiptune 格式。在 AUR 中有一个 [补丁](https://aur.archlinux.org/packages/moc-pulse/) 增加了 PulseAudio 支持。即使在 CPU 时钟频率降到 800 MHz， MOC 也只使用了单核 CPU 的 1-2% 。

我的音乐收藏夹保存在我的家庭服务器上，因此我可以从任何地方访问它。它使用 [SSHFS](https://github.com/libfuse/sshfs) 挂载并自动安装在 `/etc/fstab/` 目录下：

```
root@server:/media/media /mnt/media fuse.sshfs noauto,x-systemd.automount,idmap=user,IdentityFile=/root/.ssh/id_rsa,allow_other,reconnect 0 0

```

### 跨平台构建

除了 Linux 本身，它对于构建任何主流操作系统的软件包都很优秀！ 一开始，我使用 [QEMU](https://www.qemu.org/) 与旧版 Debian、 Windows 以及 Mac OS X VM 一起构建这些平台。

现在我在旧版 Debian 发行版上转而使用 chroot （以获得最大的 Linux 兼容性），在 Windows 上使用 [MinGW](http://www.mingw.org/) 进行交叉编译，在 Mac OS X 上则使用 [OSXCross](https://github.com/tpoechtrager/osxcross)。

用于 [构建 DDNet](https://github.com/ddnet/ddnet-scripts/blob/master/ddnet-release.sh) 的脚本以及 [更新库构建的说明](https://github.com/ddnet/ddnet-scripts/blob/master/ddnet-lib-update.sh) 的脚本都基于这个。

### 备份

通常，我们都会忘记备份。即使这是最后一节，它也不应该成为事后诸葛。

十年前我写了 [rrb](https://github.com/def-/rrb/blob/master/rrb) （反向 rsync 备份）重新封装了 `rsync` ，因此我只需要将备份服务器的 root SSH 权限授予正在备份的计算机。令人惊讶地是，尽管我一直在使用 rrb ，但它在过去十年里没有任何改变。

备份文件直接存储在文件系统中。使用硬链接实现增量备份（`--link-dest`）。一个简单的 [配置](https://github.com/def-/rrb/blob/master/config.example) 定义了备份保存时间，默认为：

```
KEEP_RULES=( \
   7  7 \ # One backup a day for the last 7 days
  31  8 \ # 8 more backups for the last month
 365 11 \ # 11 more backups for the last year
1825  4 \ # 4 more backups for the last 5 years
)

```

因为我的一些计算机没有静态 IP / DNS 但仍想使用 rrb 备份，那我会使用反向安全隧道（作为 systemd 服务）：

```
[Unit]
Description=Reverse SSH Tunnel
After=network.target

[Service]
ExecStart=/usr/bin/ssh -N -R 27276:localhost:22 -o "ExitOnForwardFailure yes" server
KillMode=process
Restart=always

[Install]
WantedBy=multi-user.target

```

现在，隧道运行备份时，服务器可以通过 `ssh -p 27276 localhost` 命令或者使用 `.ssh/config` 到达服务器端。

```
Host cr-remote
 HostName localhost
 Port 27276

```

在谈及 SSH 技巧时，有时由于某些中断的路由会很难访问到服务器。在那种情况下你可以借道其他服务器的 SSH 连接，以获得更好的路由。在这种情况下，你可能通过美国连接访问到我的中国服务器，而来自德国的不可靠连接可能需要几个周：

```
Host chn.ddnet.tw
 ProxyCommand ssh -q usa.ddnet.tw nc -q0 chn.ddnet.tw 22
 Port 22

```

### 结语

感谢阅读我工具的收藏。这其中我也许遗漏了许多日常中自然成习惯的步骤。让我们来看看我的软件设置在下一年里能多稳定吧。如果你有任何问题，随时联系我 [dennis@felsin9.de](mailto:dennis@felsin9.de) 。

在 [Hacker News](https://news.ycombinator.com/item?id=18979731) 下评论吧。

---

via: <https://hookrace.net/blog/linux-desktop-setup/>

作者：[Dennis Felsing](http://felsin9.de/nnis/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[chenmu-kk](https://github.com/chenmu-kk) 校对：[wxy](https://github.com/wxy)

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


Rails 之旅第 1 天：一个令人困惑的 Rails 错误信息

这教程真水。

直接全局搜索user，就能找到所有user，然后点击开，花2秒就知道自己抄错了。

devise确实比较复杂，但可用性是极高的。

# Rails 之旅第 1 天：一个令人困惑的 Rails 错误信息

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

今天，我开始了一个 Recurse Center 的班次学习！我认识了一些人，并开始了一个小小的有趣的 Rails 项目。我想我今天不会谈太多关于这个项目的实际内容，但这里有一些关于 Rails 一天的快速笔记。

### 一些关于开始的笔记

在建立 Rails 项目的过程中，我学到的主要是：

1. 它默认使用 sqlite，你必须告诉它使用 Postgres。
2. Rails 默认包含了大量的东西，你可以禁用。

我安装并 `rm -rf` Rails 大概 7 次后才满意，最后用了这个咒语：

```
rails new . -d postgresql --skip-sprockets --skip-javascript

```

主要是因为我想用 Postgres 而不是 sqlite，而且跳过 sprockets 和 javascript 似乎能让安装 Rails 的速度更快，而且我想如果我决定要它们的话，我可以在以后再安装。

### 官方的 Rails 指南真的很不错

我在创建我的 Rails 入门应用时主要参考了 2 个资源：

* DHH 在 2005 年的 Rails 原版演讲 <https://www.youtube.com/watch?v=Gzj723LkRJY>（这次我没有看，但上次我花了一天时间学习 Rails 时看了，我发现它很有启发和帮助）。
* 官方的 Rails 入门指南，似乎非常简短明了 <https://guides.rubyonrails.org/v5.0/getting_started.html>。

### 一个神秘的错误信息：`undefined method 'user'`

我喜欢 bug，所以今天我遇到了一个奇怪的 Rails 错误! 我有一些看起来像这样的代码：

```
@user = User.new(user_params)
@user.save

```

很简单吧？但当这段代码运行时，我得到了这个令人费解的错误信息：

```
undefined method `user' for #<User:0x00007fb6f4012ab8> Did you mean? super

```

我对这里发生的事情感到**超级**困惑，因为我没有调用一个叫做 `user` 的方法。我调用的是 `.save`。什么嘛？！我对此感到困惑和沮丧，大概呆了 20 分钟，最后我看了看我的 `User` 模型，发现了这段代码：

```
class User < ApplicationRecord
  has_secure_password

  validates :user, presence: true, uniqueness: true
end

```

`validates :user...` *应该*是一些 Rails 魔法，验证每个 `User` 都有一个 `username`，而且用户名必须是唯一的。但我犯了一个错，我写的是 `user` 而不是 `username`。我把这个问题解决了，然后一切都正常了！万岁。

我仍然不明白我应该如何调试这个问题：堆栈跟踪告诉我问题出在 `@user.save` 行，根本没有提到 `validates :user` 的事情。我觉得一定有办法调试这个问题，但我不知道是什么办法。

我学 Rails 的目的就是想看看 Rails 的魔力在实践中是如何发挥的，所以这是个很有意思的 bug，早早的就掉坑里了。

### 一个简单的用户管理系统

我决定在我的玩具应用中加入用户。我在网上搜索了一下，发现有一个非常流行的叫做 [devise](https://github.com/heartcombo/devise) 的工具可以处理用户。我发现它的 `README` 有点让人不知所措，而且我知道想要在我的玩具应用中建立一个非常简陋的用户管理系统，所以我遵循了这个名为《[Rails 5.2 中从零开始进行用户验证](https://medium.com/@wintermeyer/authentication-from-scratch-with-rails-5-2-92d8676f6836)》的指南，到目前为止，这个指南似乎还不错。Rails 似乎已经有了一大堆管理用户的内置东西，我真的很惊讶于这本指南的短小和我需要写的代码之少。

我在实现用户功能的时候了解到，Rails 有一个内置的神奇的会话管理系统（参见 [Rails 会话如何工作](https://www.justinweiss.com/articles/how-rails-sessions-work/)。默认情况下，所有的会话数据似乎都存储在用户电脑上的 cookie 中，不过我想如果 cookie 太大了，你也可以把会话数据存储在数据库中。

已经有了会话管理系统，有了 cookie 和用户，却不太清楚到底发生了什么，这肯定是有点奇怪的，但也是挺好玩的！我们会看看情况如何。我们将拭目以待。

### 明天：更多的 Rails!

也许明天我可以在实现我的有趣的 rails 应用的想法上取得一些进展!

---

via: <https://jvns.ca/blog/2020/11/09/day-1--a-little-rails-/>

作者：[Julia Evans](https://jvns.ca/) 选题：[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/) 荣誉推出


scanimage：从命令行扫描！

Summary: 我一直以为在 Linux 上使用打印机/扫描仪是一个噩梦，我真的很惊讶 scanimage 可以工作。

受本帖启发，写了一篇ChromeOS及内置Linux中使用扫描仪的指南。<br />
wildgun.net/2022/11/use_scanner_in_chromeos_and_linux/

不错不错~点个赞！

# scanimage：从命令行扫描！

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

这又是一篇关于我很喜欢的一个命令行工具的文章。

昨晚，出于官僚原因，我需要扫描一些文档。我以前从来没有在 Linux 上使用过扫描仪，我担心会花上好几个小时才弄明白。我从使用 `gscan2pdf` 开始，但在用户界面上遇到了麻烦。我想同时扫描两面（我知道我们的扫描仪支持），但无法使它工作。

### 遇到 scanimage！

`scanimage` 是一个命令行工具，在 `sane-utils` Debian 软件包中。我想所有的 Linux 扫描工具都使用 `sane` （“scanner access now easy”） 库，所以我猜测它和其他扫描软件有类似的能力。在这里，我不需要 OCR，所以我不打算谈论 OCR。

### 用 scanimage -L 得到你的扫描仪的名字

`scanimage -L` 列出了你所有的扫描设备。

一开始我不能让它工作，我有点沮丧，但事实证明，我把扫描仪连接到了我的电脑上，但没有插上电源。

插上后，它马上就能工作了。显然我们的扫描仪叫 `fujitsu:ScanSnap S1500:2314`。万岁！

### 用 --help 列出你的扫描仪选项

显然每个扫描仪有不同的选项（有道理！），所以我运行这个命令来获取我的扫描仪的选项：

```
scanimage --help -d 'fujitsu:ScanSnap S1500:2314'

```

我发现我的扫描仪支持 `--source` 选项（我可以用它来启用双面扫描）和 `--resolution` 选项（我把它改为 150，以减少文件大小，使扫描更快）。

### scanimage 不支持输出 PDF 文件（但你可以写一个小脚本）

唯一的缺点是：我想要一个 PDF 格式的扫描文件，而 scanimage 似乎不支持 PDF 输出。

所以我写了这个 5 行的 shell 脚本在一个临时文件夹中扫描一堆 PNG 文件，并将结果保存到 PDF 中。

```
#!/bin/bash
set -e

DIR=`mktemp -d`
CUR=$PWD
cd $DIR
scanimage -b --format png  -d 'fujitsu:ScanSnap S1500:2314' --source 'ADF Front' --resolution 150
convert *.png $CUR/$1

```

我像这样运行脚本：`scan-single-sided output-file-to-save.pdf`

你可能需要为你的扫描仪设置不同的 `-d` 和 `-source`。

### 这真是太简单了!

我一直以为在 Linux 上使用打印机/扫描仪是一个噩梦，我真的很惊讶 `scanimage` 可以工作。我可以直接运行我的脚本 `scan-single-sided receipts.pdf`，它将扫描文档并将其保存到 `receipts.pdf`！

---

via: <https://jvns.ca/blog/2020/07/11/scanimage--scan-from-the-command-line/>

作者：[Julia Evans](https://jvns.ca/) 选题：[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/) 荣誉推出


每天用 Jupyter 写 5 分钟的日记

Summary: 用 Jupyter 和 Python 在你的日常写作背后实现一些自动化。

用 Jupyter 和 Python 在你的日常写作背后实现一些自动化。

# 每天用 Jupyter 写 5 分钟的日记

>
> 用 Jupyter 和 Python 在你的日常写作背后实现一些自动化。
>
>
>

有些人会遵循传统，制定一年的计划。不过，一年的时间很长，所以我以季节性的主题或轨迹来规划。每个季度，我都会坐下来，看看即将到来的三个月的季节，并决定在这段时间里我将努力做什么。

对于我最新的主题，我决定要每天写一篇日记。我喜欢有明确的承诺，所以我承诺每天写 5 分钟。我也喜欢有可观察的承诺，哪怕只是对我而言，所以我把我的记录放在 Git 里。

我决定在写日记的过程中实现一些自动化，于是我使用了我最喜欢的自动化工具：[Jupyter](https://jupyter.org/)。Jupyter 有一个有趣的功能 [ipywidgets](https://ipywidgets.readthedocs.io/en/latest/)，这是一套用于 Jupyter Notebooks、JupyterLab 和 IPython 内核的交互式 HTML 组件。

如果你想跟着本文的代码走，请注意，让你的 JupyterLab 实例支持组件可能有点复杂，请按照[这些说明](https://ipywidgets.readthedocs.io/en/latest/user_install.html)来进行设置。

### 导入 ipywidgets 模块

首先，你需要导入一堆东西，比如 ipywidgets 和 [Twisted](https://twistedmatrix.com/trac/)。Twisted 模块可以用来创建一个异步时间计数器：

```
import twisted.internet.asyncioreactor
twisted.internet.asyncioreactor.install()
from twisted.internet import reactor, task
import ipywidgets, datetime, subprocess, functools, os

```

### 设置定时条目

用 Twisted 实现时间计数器是利用了 `task.LoopingCall`。然而，结束循环调用的唯一方法是用一个异常。倒计时时钟总会停止，所以你需要一个自定义的异常来指示“一切正常；计数器结束”：

```
class DoneError(Exception):
    pass

```

现在你已经写好了异常，你可以写定时器了。第一步是创建一个 `ipywidgets.Label` 的文本标签组件。循环使用 `divmod` 计算出分和秒，然后设置标签的文本值：

```
def time_out_counter(reactor):
    label = ipywidgets.Label("Time left: 5:00")
    current_seconds = datetime.timedelta(minutes=5).total_seconds()
    def decrement(count):
        nonlocal current_seconds
        current_seconds -= count
        time_left = datetime.timedelta(seconds=max(current_seconds, 0))
        minutes, left = divmod(time_left, minute)
        seconds = int(left.total_seconds())
        label.value = f"Time left: {minutes}:{seconds:02}"
        if current_seconds < 0:
            raise DoneError("finished")
    minute = datetime.timedelta(minutes=1)
    call = task.LoopingCall.withCount(decrement)
    call.reactor = reactor
    d = call.start(1)
    d.addErrback(lambda f: f.trap(DoneError))
    return d, label

```

### 从 Jupyter 组件中保存文本

下一步是写一些东西，将你输入的文字保存到一个文件中，并提交到 Git。另外，由于你要写 5 分钟的日记，你需要一个能给你提供写字区域的组件（滚动肯定是可以的，但一次能看到更多的文字就更好了）。

这就用到了组件 `Textarea`，这是一个你可以书写的文本字段，而 `Output` 则是用来给出反馈的。这一点很重要，因为 `git push` 可能会花点时间或失败，这取决于网络。如果备份失败，用反馈提醒用户很重要：

```
def editor(fname):
    textarea = ipywidgets.Textarea(continuous_update=False)
    textarea.rows = 20
    output = ipywidgets.Output()
    runner = functools.partial(subprocess.run, capture_output=True, text=True, check=True)
    def save(_ignored):
        with output:
            with open(fname, "w") as fpout:
                fpout.write(textarea.value)
            print("Sending...", end='')
            try:
                runner(["git", "add", fname])
                runner(["git", "commit", "-m", f"updated {fname}"])
                runner(["git", "push"])
            except subprocess.CalledProcessError as exc:
                print("Could not send")
                print(exc.stdout)
                print(exc.stderr)
            else:
                 print("Done")
    textarea.observe(save, names="value")
    return textarea, output, save

```

`continuous_update=False` 是为了避免每个字符都保存一遍并发送至 Git。相反，只要脱离输入焦点，它就会保存。这个函数也返回 `save` 函数，所以可以明确地调用它。

### 创建一个布局

最后，你可以使用 `ipywidgets.VBox` 把这些东西放在一起。这是一个包含一些组件并垂直显示的东西。还有一些其他的方法来排列组件，但这足够简单：

```
def journal():
    date = str(datetime.date.today())
    title = f"Log: Startdate {date}"
    filename = os.path.join(f"{date}.txt")
    d, clock = time_out_counter(reactor)
    textarea, output, save = editor(filename)
    box = ipywidgets.VBox([
        ipywidgets.Label(title),
        textarea,
        clock,
        output
    ])
    d.addCallback(save)
    return box

```

biu！你已经定义了一个写日记的函数了，所以是时候试试了。

```
journal()

```

你现在可以写 5 分钟了！

---

via: <https://opensource.com/article/20/11/daily-journal-jupyter>

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


下载 2020 年度 Linux 基金会年度报告

# 下载 2020 年度 Linux 基金会年度报告

2020 年对于 <ruby> Linux 基金会 <rt>  Linux Foundation </rt></ruby>（LF）和我们托管的社区来说，是充满挑战的一年。在这次大流行期间，我们都看到我们和世界各地许多同事、朋友和家人的日常生活完全改变了。在我们的社区中，有太多的人也为失去家人和朋友而悲痛。

看到 LF 的成员加入到对抗 COVID-19 的斗争中，令人振奋。我们在世界各地的成员为科研人员贡献了技术资源，为挣扎中的家庭和个人提供了援助，为国家和国际努力做出了贡献，有些人甚至一起在 [LF 公共卫生](https://www.lfph.io/) 下创建了开源项目，以帮助各国应对这场大流行病。

今年，我们的项目社区在继续发展，在许多开放技术领域、开放标准、开放数据和开放硬件方面都有新的举措。今年，我们迎接了 150 多个新社区加入 LF，包括 [FINOS 基金会](https://www.finos.org/)，它是开源金融服务项目的保护项目。

我们的 [活动团队](https://events.linuxfoundation.org/) 不得不进行了重大转型，在几周内，从面对面的活动转变为虚拟活动，参与者从不足 100 人到数万人不等。这些虚拟聚会帮助我们社区中的许多人在这个困难时期建立了联系。我们也学到了很多关于未来可能通过提供虚拟体验的<ruby> 混合亲身活动 <rt>  hybrid in-person events </rt></ruby>来提供更具包容性的体验。今年，我们很想念在我们的社区中见到的许多朋友，并期待着在安全的情况下再次见到你们。

我们的 [培训和认证](https://training.linuxfoundation.org/) 团队能够帮助 170 多万名报名参加我们免费培训课程的人。我要祝贺今年获得 LF 认证的 4 万多人。

《[LF 的 2020 年度工作报告](https://training.linuxfoundation.org/resources/2020-open-source-jobs-report/)》显示，尽管商业环境充满挑战，但经过培训和认证的开源专业人员仍有大有需求，并能轻松地展示其价值。

作为我们正在进行的多元化努力的一部分，在加入反对不平等的斗争中，我们的社区专注于该如何在项目中使用语言，并寻找导师来指导下一代的贡献者。我们的社区，如 Linux 内核团队和在北美 KubeCon 上发起的 <ruby>   包容性命名倡议  <rt>  Inclusive Naming Initiative </rt></ruby>，在加强我们的互动方式上取得了进展。

今年是我们<ruby> 联合发展基金会 <rt>  Joint Development Foundation </rt></ruby>（JDF）和<ruby> 开放标准社区 <rt>  open standards communities </rt></ruby>的突破性一年。我们迎来了六个建立开放标准的新项目。[JDF 还被批准为 ISO/IEC JTC 1 公开发布规范（PAS）提交者](https://www.linuxfoundation.org/blog/2020/05/joint-development-foundation-recognized-as-an-iso-iec-jtc-1-pas-submitter-and-submits-openchain-for-international-review/)。今年还标志着我们的第一个开放标准社区 OpenChain 通过 PAS 程序，被正式认可为国际标准。今天，Linux 基金会可以把我们的社区，从开源仓库带到一个公认的全球标准。

今年，我们生态系统中的许多人已经站出来帮助安全工作。一个新的社区 <ruby>   开源安全基金会  <rt>  Open Source Security Foundation </rt></ruby>（OpenSSF）启动了，以协调专注于提高开源软件安全性的努力。

当我们继续在美国与挑战作斗争时，[我们也重申 LF 是全球社区的一部分](https://www.linuxfoundation.org/blog/2020/08/open-source-collaboration-is-a-global-endeavor/)。

我们的成员必须得应对国际贸易政策变化的一年，并了解到开放源码在政治上的蓬勃发展。来自世界各地的我们的成员社区参与了<ruby> 开放合作 <rt>  open collaboration </rt></ruby>，因为它是开放、中立和透明的。这些参与者显然希望继续与全球同行合作，以应对大大小小的挑战。

在这困难的一年结束时，所有这些都让我们确信，开放合作是解决世界上最复杂挑战的模式。没有任何一个人、组织或政府能够单独创造出我们解决最紧迫问题所需的技术。我们代表整个 Linux 基金会团队，期待着帮助您和我们的社区应对接下来的任何挑战。

Jim Zemlin，Linux 基金会执行总监

* [下载 Linux 基金会 2020 年度报告](http://linuxfoundation.org/2020-annual-report)

这篇文章首先发布于 [Linux 基金会](https://www.linuxfoundation.org/)。

---

via: <https://www.linux.com/news/download-the-2020-linux-foundation-annual-report/>

作者：[The Linux Foundation](https://www.linuxfoundation.org/blog/2020/12/download-the-2020-linux-foundation-annual-report/) 选题：[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/) 荣誉推出


Rails 之旅第 2 天：Rails 关联和拖动 div

# Rails 之旅第 2 天：Rails 关联和拖动 div

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

大家好！今天是我搭建这个玩具项目的第 2 天。下面再来记录一下关于 Rails 的一些有趣的事情吧！

### 目标：做一个冰箱诗歌论坛

我想做一种无聊的标准网站来学习 Rails，并且其他人可以与之互动，就像一个论坛一样! 但如果人们真的可以在网站上打字，那就会产生各种各样的问题（如果他们是垃圾邮件发送者怎么办？又或者只是言语刻薄？）。

我想到的第一个想法是，可以让人们与网站互动，但实际上却不能在网站上打字，那就是一个“冰箱诗歌论坛”，只给你一组固定的字，你就可以随意组合。

所以，这就是我们的计划！

我这个项目的目标是想知道我是否能用 Rails 来做其他的小型网络项目（而不是像我通常做的那样，使用一些更基本的东西，比如 Flask，或者放弃后端，用 Javascript 来写所有东西）。

### 怎么把字拖来拖去呢？jQuery 的可拖放 UI！

我想让大家能够把文字拖动起来，但我又不想写很多 Javascript。结果发现这超级简单 —— 有一个 jQuery 库可以做到，它叫做 `draggable`！一开始，拖动并不成功。

一开始拖动在手机上是不行的，但是有一个技巧可以让 jQuery UI 在手机上工作，叫做 [jQuery UI touch punch](https://github.com/furf/jquery-ui-touch-punch)。下面是它的样子（有兴趣看工作原理的可以查看源码，代码很少）。

>
> `banana` `forest` `cake` `is`
>
>
>

### 一个有趣的 Rails 功能：关联

我以前从来没有使用过关系型 ORM，对于 Rails，我很兴奋的一件事就是想看看使用 Active Record 是什么样子的！今天我了解了 Rails 的 ORM 功能之一：[关联](https://guides.rubyonrails.org/association_basics.html)。如果你像我一样对 ORM 完全不了解的话，那就来看看是怎么回事吧。

在我的论坛中，我有：

* 用户
* 话题（我本来想把它叫做“线索”，但显然这在 Rails 中是一个保留词，所以现在叫做“话题”）。
* 帖子

当显示一个帖子时，我需要显示创建该帖子的用户的用户名。所以我想我可能需要写一些代码来加载帖子，并为每个帖子加载用户，就像这样（在 Rails 中，`Post.where` 和 `User.find` 将会运行 SQL 语句，并将结果转化为 Ruby 对象）：

```
@posts = Post.where(topic_id: id)
@posts.each do |post|
    user = User.find(post.user_id)
    post.user = user
end

```

这还不够好，它要为每个帖子做一次单独的 SQL 查询！我知道有一个更好的方法，我发现它叫做[关联](https://guides.rubyonrails.org/association_basics.html)。这个链接是来自 <https://guides.rubyonrails.org> 的指南，到目前为止，它对我很有帮助。

基本上我需要做的就是：

1. 在 `User` 模型中添加一行 `has_many :post`。
2. 在 `Post` 模型中添加一行 `belongs_to :user`。
3. Rails 现在知道如何将这两个表连接起来，尽管我没有告诉它要连接到什么列上！我认为这是因为我按照它所期望的惯例命名了 `posts` 表中的 `user_id` 列。
4. 对 `User` 和 `Topic` 做完全相同的事情（一个主题也有很多帖子：`has_many :posts`）。

然后我加载每一个帖子和它的关联用户的代码就变成了只有一行! 就是这一行：

```
@posts = @topic.posts.order(created_at: :asc).preload(:user)

```

比起只有一行更重要的是，它不是单独做一个查询来获取每个帖子的用户，而是在 1 个查询中获取所有用户。显然，在 Rails 中，有一堆[不同的方法](https://blog.bigbinary.com/2013/07/01/preload-vs-eager-load-vs-joins-vs-includes.html)来做类似的事情（预加载、急切加载、联接和包含？），我还不知道这些都是什么，但也许我以后会知道的。

### 一个有趣的 Rails 功能：脚手架！

Rails 有一个叫 `rails` 的命令行工具，它可以生成很多代码。例如，我想添加一个 `Topic` 模型/控制器。我不用去想在哪里添加所有的代码，可以直接运行

```
rails generate scaffold Topic title:text

```

并生成了一堆代码，这样我已经有了基本的端点来创建/编辑/删除主题（`Topic`）。例如，这是我的[现在的主题控制器](https://github.com/jvns/refrigerator-forum/blob/776b3227cfd7004cb1efb00ec7e3f82a511cbdc4/app/controllers/topics_controller.rb#L13-L15)，其中大部分我没有写（我只写了高亮的 3 行）。我可能会删除很多内容，但是有一个起点，让我可以扩展我想要的部分，删除我不想要的部分，感觉还不错。

### 数据库迁移！

`rails` 工具还可以生成数据库迁移! 例如，我决定要删除帖子中的 `title` 字段。

下面是我要做的：

```
rails generate migration RemoveTitleFromPosts title:string
rails db:migrate

```

就是这样 —— 只要运行几个命令行咒语就可以了! 我运行了几个这样的迁移，因为我改变了对我的数据库模式的设想。它是相当直接的，到目前为止 —— 感觉很神奇。

当我试图在一列中的某些字段为空的地方添加一个“不为空”（`not null`）约束时，情况就变得有点有趣了 —— 迁移失败。但我可以修复违例的记录，并轻松地重新运行迁移。

### 今天就到这里吧！

明天，如果我有更多的进展，也许我会把它放在互联网上。

---

via: <https://jvns.ca/blog/2020/11/10/day-2--rails-associations---dragging-divs-around/>

作者：[Julia Evans](https://jvns.ca/) 选题：[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/) 荣誉推出


使用 UEFI 双启动 Windows 和 Linux

一个小问题。如果打算使用Windows系统，而不是只做安装测试，20GB空间是远远不够的。20GB连平常安装系统安全更新都不够，不要说加装第三方软件。建议80G起步，最好能有100GB以上。

Win10 系统盘还是200G吧，100G都不够了的。建议256G的SSD单分区，128GSSD转移用户目录后勉强能用个3年。

虚拟机用得也可以的

# 使用 UEFI 双启动 Windows 和 Linux

>
> 这是一份在同一台机器上设置 Linux 和 Windows 双重启动的速成解释，使用统一可扩展固件接口（UEFI）。
>
>
>

我将强调一些重要点，而不是一步一步地指导你来如何配置你的系统以实现双重启动。作为一个示例，我将提到我在几个月之前新买的笔记本计算机。我先是安装 [Ubuntu Linux](https://www.ubuntu.com) 到整个硬盘中，这就摧毁了预装的 [Windows 10](https://www.microsoft.com/en-us/windows) 环境。几个月后，我决定安装一个不同的 Linux 发行版 [Fedora Linux](https://getfedora.org)，也决定在双重启动配置中与它一起再次安装 Windows 10 。我将强调一些极其重要的实际情况。让我们开始吧！

### 固件

双重启动不仅仅是软件问题。或者说它算是软件的问题，因为它需要更改你的固件，以告诉你的机器如何开始启动程序。这里有一些和固件相关的重要事项要铭记于心。

#### UEFI vs BIOS

在尝试安装前，确保你的固件配置是最佳的。现在出售的大多数计算机有一个名为 <ruby>   统一可扩展固件接口  <rt>  Unified Extensible Firmware Interface </rt></ruby> （UEFI）的新类型固件，UEFI 差不多取代了另一个名为 <ruby>   基本输入输出系统  <rt>  Basic Input Output System </rt></ruby>（BIOS）的固件类型，（包括很多固件供应商在内的很多人）通常称 BIOS 为<ruby> 传统启动模式 <rt>  Legacy Boot </rt></ruby>。

我不需要 BIOS，所以我选择了 UEFI 模式。

#### 安全启动

另一个重要的设置是<ruby> 安全启动 <rt>  Secure Boot </rt></ruby>。这个功能将检测启动路径是否被篡改，并阻止未经批准的操作系统的启动。现在，我禁用这个选项来确保我能够安装 Fedora Linux 。依据 Fedora 项目维基“[功能/安全启动](https://fedoraproject.org/wiki/Features/SecureBoot)”部分的介绍可知：Fedora Linux 在安全启动选项启用的时候也可以工作。这对其它的 Linux 发行版来说可能有所不同 — 我打算今后重新研究这项设置。

简而言之，如果你发现在这项设置启用时你不能安装你的 Linux 操作系统，那么禁用安全启动并再次重新尝试安装。

### 对启动驱动器进行分区

如果你选择双重启动并且两个操作系统都在同一个驱动器上，那么你必须将它分成多个分区。即使你使用两个不同的驱动器进行双重启动，出于各种各样的原因，大多数 Linux 环境也最好分成几个基本的分区。这里有一些选项值得考虑。

#### GPT vs MBR

如果你决定手动分区你的启动驱动器，在动手前，我建议使用<ruby>   GUID 分区表  <rt>  GUID Partition Table </rt></ruby>（GPT），而不是使用旧的<ruby>   主启动记录  <rt>  Master Boot Record </rt></ruby>（MBR） 。这种更改的原因之一是：MBR 有两个特定的限制，而 GPT 却没有：

* MBR 可以最多拥有 15 个分区，而 GPT 可以最多拥有 128 个分区。
* MBR 最多仅支持 2 TB 磁盘，而 GPT 使用 64 位地址，这使得它最多支持 800 万 TB 的磁盘。

如果你最近购买过硬盘，那么你可能会知道现代的很多硬盘都超过了 2 TB 的限制。

#### EFI 系统分区

如果你正在进行一次全新的安装或使用一块新的驱动器，那么这里可能没有可以开始的分区。在这种情况下，操作系统安装程序将先创建一个分区，即<ruby>   EFI 系统分区  <rt>  EFI System Partition </rt></ruby>（ESP）。如果你选择使用一个诸如 [gdisk](https://sourceforge.net/projects/gptfdisk/) 之类的工具来手动分区你的驱动器，你将需要使用一些参数来创建这个分区。基于现有的 ESP ，我设置它为约 500 MB 的大小，并分配它为 `ef00`（ EFI 系统 ）分区类型。UEFI 规范要求格式化为 FAT32/msdos ，很可能是因为这种格式被大量的操作系统所支持。

### 操作系统安装

在你完成先前的两个任务后，你就可以安装你的操作系统了。虽然我在这里关注的是 Windows 10 和 Fedora Linux ，当安装其它组合时的过程也是非常相似。

#### Windows 10

我开始 Windows 10 的安装，并创建了一个 20 GB 的 Windows 分区。因为我先前在我的笔记本计算机上安装了 Linux ，所以驱动器已经有了一个 ESP ，我选择保留它。我删除所有的现有 Linux 和交换分区来开始一次全新安装，然后开始我的 Windows 安装。Windows 安装程序自动创建另一个 16 MB 的小分区，称为 <ruby>   微软保留分区  <rt>  Microsoft Reserved Partition </rt></ruby>（MSR）。在这完成后，在 512 GB 启动驱动器上仍然有大约 400 GB 的未分配空间。

接下来，我继续完成了 Windows 10 安装过程。随后我重新启动到 Windows 来确保它是工作的，在操作系统第一次启动时，创建我的用户账号，设置 Wi-Fi ，并完成其它必须的任务。

#### Fedora Linux

接下来，我将心思转移到安装 Linux 。我开始了安装过程，当安装进行到磁盘配置的步骤时，我确保不会更改 Windows NTFS 和 MSR 分区。我也不会更改 ESP ，但是我设置它的挂载点为 `/boot/efi`。然后我创建常用的 ext4 格式分区， `/`（根分区）、`/boot` 和 `/home`。我创建的最后一个分区是 Linux 的交换分区（swap）。

像安装 Windows 一样，我继续完成了 Linux 安装，随后重新启动。令我高兴的是，在启动时<ruby>   大一统启动加载程序  <rt>  GRand Unified Boot Loader </rt></ruby>（GRUB）菜单提供选择 Windows 或 Linux 的选项，这意味着我不需要再做任何额外的配置。我选择 Linux 并完成了诸如创建我的用户账号等常规步骤。

### 总结

总体而言，这个过程是不难的，在过去的几年里，从 BIOS 过渡到 UEFI 有一些困难需要解决，加上诸如安全启动等功能的引入。我相信我们现在已经克服了这些障碍，可以可靠地设置多重启动系统。

我不再怀念 [Linux LOader](https://en.wikipedia.org/wiki/LILO_(boot_loader))（LILO）！

---

via: <https://opensource.com/article/19/5/dual-booting-windows-linux-uefi>

作者：[Alan Formy-Duval](https://opensource.com/users/alanfdoss/users/ckrzen) 选题：[lujun9972](https://github.com/lujun9972) 译者：[robsean](https://github.com/robsean) 校对：[wxy](https://github.com/wxy)

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


如何在 LibreOffice 中完全启用深色模式

# 如何在 LibreOffice 中完全启用深色模式

[LibreOffice](https://www.libreoffice.org) 是一款自由开源的跨平台办公生产力软件。如果你没有充分利用它，那么必须看下 [LibreOffice 小技巧](https://itsfoss.com/libreoffice-tips/)。

甚至在非编程人员中，深色主题也越来越受欢迎。它减轻了眼睛的压力，特别适合长时间使用屏幕。有人认为，这使文本看起来清晰明了，有助于提高生产率。

如今，某些 Linux 发行版例如 [Ubuntu 带有深色模式](https://itsfoss.com/dark-mode-ubuntu/)，使你的系统具有更暗的色彩。当你打开<ruby> 深色模式 <rt>  dark mode </rt></ruby>时，某些应用将自动切换到深色模式。

LibreOffice 也会这样，但你编辑的主区域除外：

你可以更改它。如果要让 LibreOffice 进入完全深色模式，只需更改一些设置。让我告诉你如何做。

### 如何在 LibreOffice 中完全启用深色模式

如前所述，你需要先启用系统范围的深色模式。这样可以确保窗口颜色（或标题栏）与应用内深色完全融合。

接下来，打开套件中的**任意** LibreOffice 应用，例如 **Writer**。然后从菜单中，依次点击 **Tools -> Options -> Application Colors**，然后选择 **Document background 和 Application background** 为 **Black** 或 **Automatic**（任意适合你的方式）。

如果图标不是深色，那么可以从菜单（如下图所示）中更改它们，**Tools -> Options -> View** ，我在 MX Linux 上的个人选择是 Ubuntu 的 [Yaru](https://extensions.libreoffice.org/en/extensions/show/yaru-icon-theme) 图标样式（如果你使用的图标包为深色版本，请选择它） 。

当然，你也可以尝试其他 Linux 发行版的 [icon 主题](https://itsfoss.com/best-icon-themes-ubuntu-16-04/)。

最终结果应如下所示：

#### LibreOffice flatpak 软件包的其他技巧

如果你使用的是 LibreOffice 套件的 [Flatpak 软件包](https://itsfoss.com/what-is-flatpak/)，那么 LibreOffice 的标题区域（或菜单区域）可能看起来是白色的。在这种情况下，你可以尝试进入 **Tools -> Options -> Personalization**，然后选择 “**灰色主题**”，如下截图所示。

它并不完全是黑色的，但应该可以使外观看起来更好。希望可以帮助你切换到深色主题的 LibreOffice 体验！

#### 总结

深色主题逐渐开始在我们的台式机中占主导地位，它具有现代品味并减少了眼睛疲劳，尤其是在弱光条件下。

LibreOffice 使你可以自由地将工作环境切换为深色主题或保留浅色主题元素。实际上，你将有大量的自定义选项来调整你喜欢的内容。你是否已在 LibreOffice 上切换为深色主题？你首选哪种颜色组合？在下面的评论中让我们知道！

---

via: <https://itsfoss.com/libreoffice-dark-mode/>

作者：[Dimitrios Savvopoulos](https://itsfoss.com/author/dimitrios/) 选题：[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 工作方式的七个技巧

>
> 这些有用的技巧将改变你使用这个流行的版本控制系统的工作方式。
>
>
>

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

Git 是目前最常见的版本控制系统之一，无论是私有系统还是公开托管的网站，都在使用它进行各种开发工作。但无论我对 Git 的使用有多熟练，似乎总有一些功能还没有被发现，下面是改变我使用 Git 工作方式的七个技巧。

### 1、Git 中的自动更正

我们有时都会打错字，但如果启用了 Git 的自动更正功能，就可以让 Git 自动修正打错的子命令。

假设你想用 `git status` 检查状态，却不小心输入了 `git stats`。正常情况下，Git 会告诉你 `stats` 不是一条有效的命令：

```
$ git stats
git: ‘stats’ is not a git command. See ‘git --help’.

The most similar command is
status

```

为了避免类似的情况发生，请在 Git 配置中启用 Git 自动更正功能：

```
$ git config --global help.autocorrect 1

```

如果你希望这个命令只适用于你当前的版本库，请省略 `--global` 选项。

这条命令启用了自动更正功能。更深入的教程可以在 [Git Docs](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_code_help_autocorrect_code) 中找到，但尝试一下和上面一样的错误命令，就能很好地了解这个配置的作用：

```
$ git stats
git: ‘stats’ is not a git command. See ‘git --help’.
On branch master
Your branch is up to date with ‘origin/master’.

nothing to commit, working tree clean

```

Git 现在不会建议使用其他子命令，而是直接运行最上面的建议，在本例中是 `git status`。

### 2、计算你的提交量

你需要计算提交数量可能有很多原因。例如，许多开发者通过计算提交数量来判断何时该增加构建版本号，或者只是想了解项目的进展情况。

要计算提交数量其实很简单直接，下面是 Git 的命令：

```
$ git rev-list --count branch-name

```

在上面的命令中，`branch-name` 应该是当前版本库中有效的分支名称：

```
$ git rev-list –count master
32
$ git rev-list –count dev
34

```

### 3、优化你的仓库

你的代码仓库不仅对你有价值，对你的组织也有价值。你可以通过一些简单的做法来保持你的版本库的清洁和更新。其中一个最好的做法是 [使用 .gitignore 文件](/article-12524-1.html)。使用这个文件，就是告诉 Git 不要存储许多不需要的文件，比如二进制文件、临时文件等等。

为了进一步优化你的版本库，你可以使用 Git 的垃圾收集功能：

```
$ git gc --prune=now --aggressive

```

当你或你的团队大量使用 `pull` 或 `push` 命令时，这条命令就会起到帮助作用。

这个命令是一个内部工具，可以清理仓库中无法访问或 “孤儿” Git 对象。

### 4、备份未被跟踪的文件

大多数时候，删除所有未被跟踪的文件是安全的。不过很多时候，你不仅要删除，还要为你的未跟踪文件创建一个备份，以备以后需要。

通过 Git 和一些 Bash 命令管道，可以很容易地为你的未被跟踪的文件创建一个压缩包：

```
$ git ls-files --others --exclude-standard -z |\
xargs -0 tar rvf ~/backup-untracked.zip

```

上面的命令制作了一个名为 `backup-untracked.zip` 的存档（并排除了 `.gitignore` 中列出的文件）。

### 5、了解你的 .git 文件夹

每个版本库都有一个 `.git` 文件夹。它是一个特殊的隐藏文件夹。

```
$ ls -a
. … .git

```

Git 的工作主要依赖于两个部分：

1. 工作树（你当前签出的文件状态）。
2. 你的 Git 仓库的路径（即你的 `.git` 文件夹的位置，其中包含版本信息）。

这个文件夹存储了所有的引用和其他重要的细节，比如配置、仓库数据、HEAD 状态、日志等等。

如果你删除这个文件夹，你的源代码的当前状态不会被删除，但你的远程信息，如你的项目历史记录，会被删除。删除这个文件夹意味着你的项目（至少是本地副本）不再处于版本控制之下。这意味着你不能跟踪你的变化；你不能从远程拉取或推送。

一般来说，不需要在 `.git` 文件夹里做什么，也没有什么应该做的。它是由 Git 管理的，基本上被认为是个禁区。然而，这个目录里有一些有趣的工件，包括 HEAD 的当前状态。

```
$ cat .git/HEAD
ref: refs/heads/master

```

它还可能包含对你的存储库的描述：

```
$ cat .git/description

```

这是一个未命名的仓库，编辑这个 `description` 文件可以命名这个仓库。

Git 钩子文件夹（`hooks`）也在这里，里面有一些钩子示例文件。你可以阅读这些示例来了解通过 Git 钩子可以实现什么，你也可以 [阅读 Seth Kenlon 的 Git 钩子介绍](https://opensource.com/life/16/8/how-construct-your-own-git-server-part-6)。

### 6、查看另一个分支的文件

有时你想查看另一个分支的文件的内容。用一个简单的 Git 命令就可以实现，而且不需要切换分支。

假设你有一个名为 [README.md](http://README.md) 的文件，它在 `main` 分支中，而你正在 `dev` 分支上工作。

使用下面的 Git 命令，你可以在终端上完成：

```
$ git show main:README.md

```

一旦你执行了这个命令，你就可以在你的终端上查看文件的内容。

### 7、在 Git 中搜索

只需一个简单的命令，你就可以像专业人士一样在 Git 中搜索。更棒的是，即使你不确定是在哪个提交或分支上做的修改，也可以在 Git 中搜索。

```
$ git rev-list --all | xargs git grep -F 'string'

```

例如，假设你想在你的版本库中搜索 `font-size: 52 px;` 这个字符串：

```
$ git rev-list –all | xargs git grep -F 'font-size: 52 px;'
F3022…9e12:HtmlTemplate/style.css: font-size: 52 px;
E9211…8244:RR.Web/Content/style/style.css: font-size: 52 px;

```

### 试试这些技巧

希望这些高级技巧对你有用，提高你的工作效率，为你节省很多时间。

你有喜欢的 [Git 小技巧](https://acompiler.com/git-tips/) 吗？在评论中分享吧。

---

via: <https://opensource.com/article/20/10/advanced-git-tips>

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


2020 年的 GitHub 年度报告，我看出了这些不一样的点

Summary: 作为一个开发者，我十分建议你下载一份官方的报告，细读其中的建议，让自己的开发更有效率；让自己的生活更加丰富多彩；让自己的代码，更少 Bug ！

# 2020 年的 GitHub 年度报告，我看出了这些不一样的点

距离 2020 年结束只剩下区区 24 天，我们即将结束魔幻的 2020 ，迎来新的一年，新的一年或好或坏，但终将到来。

### Github 年度报告的两点变化

在上周，Github 发布了 [2020 年度报告](https://octoverse.github.com/)，相信各位都有所耳闻。在对比了从 2016 年至 2020 年共计五年的报告后，我观察到了以下两点变化：

#### 1、从关注数据，到关注人

2020 年和往年的报告一个很不同的点在于，不再单纯的以数据讲话。这样的表现“很不程序员”，反倒是和我们所熟悉的另外一个科技巨头 —— 苹果很像。不再强调数据的绝对差值，更多关注技术对于人、事、物引发的变化。

在 2020 年的报告中，内容被分为了三个不同的部分：**寻找平衡、赋能社区、保护软件安全**。

这三者则分别对应在今年发生在每一个 GitHub 开发者身上的事件：

* Covid-19 新冠肺炎迫使开发者居家办公，以及由此引发的生活和工作平衡的思考；
* GitHub 为开源项目提供新的功能 Discussions 允许开源项目建立社区，让<ruby> 议题 <rt>  issue </rt></ruby>可以更加专业；
* GitHub 为所有项目开启安全检查项目，提醒开发者为项目更新出现安全问题的依赖和软件代码。

除了一个标准的概览之外，GitHub 还为这三个不同的大事件准备了详细的报告，如果你感兴趣，可以[下载](https://octoverse.github.com/static/2020-reports.zip)来看。

#### 2、从只给数据，到还给建议

和往年的报告只关注数据不同，今年的 GitHub 2020 年度报告给出了不少的建议，指导开发者们的下一步行动。而这些建议，无论你是否可以遵循，都可以给你一些新的思考维度，可以让你获得更全的信息。

比如，在生产力篇的第 6 页，GitHub 给出了一些建议，来帮助开发者获得工作和生活的平衡，谋求更好的开发体验。

类似的建议也出现在了安全篇的第 6 页，GitHub 为开发者提供了一些有效的保护软件安全的介绍，帮助开发者防御安全问题。

报告中的其他地方也出现了类似的建议，帮助更多的开发者避开问题。

### 值得关注的数据

除了 2020 年度报告的结构和行文变化，报告中的一些数据也值得我们关注：

#### GitHub 不仅仅是开源的代名词，更是工作的代名词

在 2020 年度报告中，GitHub 首次给出了按周计算的活跃度信息，可以看到，GitHub 的活跃用户信息和工作日、节日等节点高度重合，明显表现出了工作日和休息日的区别。

和我们曾经设想的，工作时做公司的事，下班的时候做开源不同。如今，GitHub 已经成为一种工作方式，我们会在工作的时候，因为工作需要而打开 GitHub，在休息的时候，因为个人兴趣而打开 GitHub。

**Github 如今已经成为了一种必备品，我们每天都在“面向 GitHub 编程”～**

#### 变大的总量，新加入的学生和占比减少的开发者们

GitHub 公布的 2020 年开发者总数为 5600 万开发者，而在 2019 年这一数字是 4000 万开发者，过去的一年里新增了 1600 万开发者，而纵观近几年数据，今年新增的数据是历年来新增用户最多的一年。

而总量变化的同时，开发者的比重在所有用户的所占比重在下降，从 2016 年的 60% 逐年递减至 2020 年的 54%，相反，我们可以看到，教育相关的学生用户、教师用户的比重在不断的攀升，到 2020 年，教育相关用户的比例已达 23%，几乎达到了 Github 用户比重的四分之一。

学生成为 GitHub 用户中的主力，在未来，我们将会看到更多基于 GitHub 的学习、教育的方式，GitHub 也将成为软件开发、计算机科学等相关领域的必修课。

#### 自动生成的安全更新和更快的软件安全更新

对于绝大多数的软件来说，对于第三方库和代码的依赖是不可避免的，特别是使用 JavaScript、Ruby 和 .Net 的开发者，对于第三方库的依赖都在 90% 以上；这些第三方依赖除了引入了更便捷完成代码逻辑以外，还带入了更多的安全问题。对于开发者来说，如何尽可能不受依赖影响，保障软件安全就成为了一个值得关注的问题。

GitHub 为开发者提供了无痛的自动生成安全更新<ruby> 拉取请求 <rt>  Pull Request </rt></ruby>（PR）功能，系统在后台自动检测依赖代码是否有相应的安全更新，并创建相应的拉取请求，为开发者提供了一键解决依赖库所引发的安全问题的功能。降低开发者维护软件的成本，将一部分可以自动化完成的工作，交由系统自动完成。根据 GitHub 的测算，在接入了该功能以后，**开源代码的安全更新速度相比于之前提升了 4.4 周时间，让软件的迭代更加的快捷。**

不过目前该功能仅支持六种不同的包管理器和编程语言，具体包括：Composer（PHP）、Maven（Java）、npm（Javascript）、NuGet（.Net）、PyPI（Python）、RubyGems（Ruby），其他语言暂时无法享受到来自 GitHub 提交的安全更新。

### 总结

今年的 Github 报告中的一些表现，和往年的报告有所不同，表现出的人性，让我们有了更加暖心的建议。作为一个开发者，我十分建议你[下载](https://octoverse.github.com/static/2020-reports.zip)一份官方的报告，细读其中的建议，让自己的开发更有效率；让自己的生活更加丰富多彩；让自己的代码，更少 Bug ！


如何在 1 分钟内阻止 7000 台机器的僵尸网络

# 如何在 1 分钟内阻止 7000 台机器的僵尸网络

>
> 对 CrowdSec 的配置更改，在不到一分钟的时间内阻止了一个 7000 台机器的僵尸网络的攻击。
>
>
>

2020 年，我们的生活和工作方式在短短几天内被彻底颠覆。随着 COVID-19 开始在全球范围内蔓延，我们将工作带回家，与同事、朋友和家人保持在线联系成为关键的必需品。这为黑客造成破坏打开了大门。例如，根据 Neustar 的数据，今年上半年全球的分布式拒绝服务（DDOS） 攻击[增长了 151%](https://www.businesswire.com/news/home/20200916005046/en/DDoS-Attacks-Increase-by-151-in-First-Half-Of-2020)。

[CrowdSec](https://crowdsec.net/) 是一个开源的安全引擎，它可以分析访问者的行为，并提供适应各种攻击的响应。它能解析来自各种来源的日志，并应用启发式方案来识别攻击性行为，并防范大多数攻击类别。并且，它与其它安装的 CrowdSec 系统共享该情报。每次 IP 地址被阻止时，它都会通知整个用户社区。这就创建了一个[实时、协作的 IP 信誉数据库](https://opensource.com/article/20/10/crowdsec)，利用人群的力量使互联网更加安全。

### CrowdSec 如何工作：案例研究

Sorf Networks 是一家总部位于土耳其的技术公司，为客户提供高配置的托管服务器和 DDoS 防护解决方案，它提供了一个 CrowdSec 工作的例子。Sorf 的一个客户每天都会遇到来自 1 万多台机器僵尸网络的 DDoS 攻击，并努力寻找一种能够满足技术要求的解决方案来及时处理这些攻击。

虽然客户采取了一般的预防措施来缓解这些攻击，比如引入 JavaScript（JS）<ruby> 挑战 <rt>  challenges </rt></ruby>、限速等，但这些措施在整个攻击面并不可行。一些 URL 需要被非常基本的软件使用，而这些软件不支持 JS 挑战。黑客就是黑客，这正是他们每天的目标：链条上最薄弱的环节。

Sorf Networks 首先使用 [Fail2ban](https://www.fail2ban.org)（这启发了 CrowdSec）为其客户建立了一个 DDoS 缓解策略。它在一定程度上帮助了客户，但它太慢了。它需要 50 分钟来处理日志和处理 7000 到 10000 台机器的 DDoS 攻击。这使得它在这种情况下没有效果。另外，因为它没有禁止 IP，日志会持续堆积，它需要每秒处理几千条日志，这是不可能的。

在使用租用的僵尸网络进行的 DDoS 测试中，一次攻击可以高达每秒 6700 个左右的请求，这些请求来自 8600 个独立 IP。这是对一台服务器流量的捕捉：

虽然 CrowdSec 技术可以应对巨大的攻击，但其默认设置每秒只能处理约 1000 个端点。Sorf 需要一个量身定做的配置来处理单台机器上这么多的流量。

Sorf 的团队对 CrowdSec 的配置进行了修改，以显著提高其吞吐量来处理日志。首先，它去掉了高消耗且非关键的<ruby> 富集 <rt>  enrichment </rt></ruby>解析器，例如 [GeoIP 富集](https://hub.crowdsec.net/author/crowdsecurity/configurations/geoip-enrich)。它还将允许的 goroutine 的默认数量从一个增加到五个。之后，团队又用 8000 到 9000 台主机做了一次实测，平均每秒 6000 到 7000 个请求。这个方案是有代价的，因为 CrowdSec 在运行过程中吃掉了 600% 的 CPU，但其内存消耗却保持在 270MB 左右。

然而，结果却显示出明显的成功：

* 在一分钟内，CrowdSec 能够处理所有的日志
* 95% 的僵尸网络被禁止，攻击得到有效缓解
* 15 个域现在受到保护，不受 DDoS 攻击

根据 Sorf Networks 的总监 Cagdas Aydogdu 的说法，CrowdSec 的平台使团队“能够在令人难以置信的短时间内提供一个世界级的高效防御系统”。

---

本文改编自[如何用 CrowdSec 在 1 分钟内阻止 7000 台机器的僵尸网络](https://crowdsec.net/2020/10/21/how-to-stop-a-botnet-with-crowdsec/)，原载于 CrowdSec 网站。

---

via: <https://opensource.com/article/20/12/open-source-vs-ddos-attacks>

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


如何在 Fedora 中添加第三方存储库以访问大量附加软件

# 如何在 Fedora 中添加第三方存储库以访问大量附加软件

在你安装 Fedora 后。你可能会发现你想要安装和使用的一些软件在软件商店中找不到。出于一些原因，这些软件包不能出现在 Fedora 存储库中。

不用担心，我将告诉你如何为 Fedora 添加第三方存储库来使这些软件包可使用。

### 在 Fedora 中的第三方存储库是什么？

操作系统开发人员通常会决定哪些软件包可以在其存储库中使用，哪些软件包不可以在其存储库中使用。Fedora 也是如此。依据 [Fedora 文档](https://docs.fedoraproject.org/en-US/quick-docs/setup_rpmfusion/#third-party-repositories) ，第三方存储库包含有 “拥有更为宽松的许可政策，并提供 Fedora 因各种原因所排除软件包” 的软件包。

Fedora 强制执行下面的 [准则](https://fedoraproject.org/wiki/Forbidden_items) ，当它打包软件包时:

* 如果它是专有的，它就不能包含在 Fedora 中
* 如果它在法律上被限制，它就不能包含在 Fedora 中
* 如果它违反美国法律（特别是联邦政府或适用于州政府的法律），它就不能包含在 Fedora 中

因此，有一些可以由用户自行添加的存储库。这使得用户能够访问附加的软件包。

### 在 Fedora 中启用 RPM Fusion 存储库

[RPM Fusion](https://rpmfusion.org/RPM%20Fusion) 是 Fedora 的第三方应用程序的主要来源。RPM Fusion 是由三个项目（Dribble、Freshrpms 和 Livna）合并而成的。RPM Fusion 提供两种不同的软件存储库。

* free 存储库：包含开源软件。
* nonfree 存储库：包含没有开源协议的软件，但是它们的源文件代码却是可以自由使用的。

这里有两种方法来启动 RPM Fusion：从终端启用，或通过点击几个按钮来启用。我们将逐一查看。

#### 方法 1：命令行方法

这是启用 RPM Fusion 存储库的最简单的方法。只需要输入下面的命令即可启用两个存储库：

```
sudo dnf install https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm

```

会要求你输入密码、确认是否你想要安装这些存储库。在你确认后，安装过程将在几秒钟或几分钟内完成。

#### 方法 2：图形用户界面方法

使用这个方法来启用 RPM Fusion 存储库，你需要访问 [RPM Fusion 网站](https://rpmfusion.org/Configuration) 。你将看到针对不同 Fedora 版本的两个存储库的链接。

RPM Fusion 建议先安装 free 存储库。因此，单击针对你 Fedora 版本的 free 存储库的链接。然后会打开一个窗口来询问你是否想安装该存储库。单击安装。

在安装过程完成后，返回并使用相同的步骤安装 nonfree 存储库。

### 启用 Fedora 的第三方存储库

Fedora 最近开始提供它自己的 [第三方应用程序存储库](https://fedoraproject.org/wiki/Workstation/Third_Party_Software_Repositories) 。在这个存储库中 [可使用的应用程序的数量](https://fedoraproject.org/wiki/Workstation/Third_party_software_list) 是非常少的。你可以 [使用它来在 Fedora 上安装 Chrome 浏览器](https://itsfoss.com/install-google-chrome-fedora/) 。除 Chrome 外，它也包含 Adobe Brackets、Atom、Steam、Vivaldi、Opera 等应用程序。

就像 RPM Fusion 一样，你可以通过终端或图形用户界面的方法来启用这个存储库。

#### 方法 1：命令行方法

为启用 Fedora 的第三方存储库，输入下面的命令到你的终端中：

```
sudo dnf install fedora-workstation-repositories

```

当被提示时，确保输入你的密码并输入 `Y` 来确认安装。

#### 方法 2：图形用户界面方法

如果你不习惯使用终端，你可以使用图形用户界面方法。

首先，你需要打开 Gnome “软件”。接下来，你需要单击右上角的汉堡菜单，并从菜单中选择“软件存储库”。

在软件存储库窗口中，你将在其顶部看到写着 “第三方存储库” 字样的部分。单击“安装”按钮。当你被提示时，输入你的密码。

随着这些附加存储库的启用，你可以安装软件到你的系统当中。你可以从软件中心管理器或使用 DNF 软件包管理器来轻松地安装它们。

如果你发现这篇文章很有趣，请花费一些时间来在社交媒体上分享它。

---

via: <https://itsfoss.com/fedora-third-party-repos/>

作者：[John Paul](https://itsfoss.com/author/john/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[robsean](https://github.com/robsean) 校对：[wxy](https://github.com/wxy)

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