mirror of
https://github.com/LCTT/TranslateProject.git
synced 2024-12-26 21:30:55 +08:00
commit
e9590ae38b
395
published/20191121 Simulate gravity in your Python game.md
Normal file
395
published/20191121 Simulate gravity in your Python game.md
Normal file
@ -0,0 +1,395 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11780-1.html)
|
||||
[#]: subject: (Simulate gravity in your Python game)
|
||||
[#]: via: (https://opensource.com/article/19/11/simulate-gravity-python)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
在你的 Python 游戏中模拟引力
|
||||
======
|
||||
|
||||
> 学习如何使用 Python 的 Pygame 模块编程电脑游戏,并开始操作引力。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/13/223923k6t4zmvvmo6tfmmg.png)
|
||||
|
||||
真实的世界充满了运动和生活。物理学使得真实的生活如此忙碌和动态。物理学是物质在空间中运动的方式。既然一个电脑游戏世界没有物质,它也就没有物理学规律,使用游戏程序员不得不*模拟*物理学。
|
||||
|
||||
从大多数电脑游戏来说,这里基本上仅有两个方面的物理学是重要的:引力和碰撞。
|
||||
|
||||
当你[添加一个敌人][2]到你的游戏中时,你实现了一些碰撞检测,但是这篇文章要添加更多的东西,因为引力需要碰撞检测。想想为什么引力可能涉及碰撞。如果你不能想到任何原因,不要担心 —— 它会随着你开发示例代码工作而且显然。
|
||||
|
||||
在真实世界中的引力是有质量的物体来相互吸引的倾向性。物体(质量)越大,它施加越大的引力作用。在电脑游戏物理学中,你不必创建质量足够大的物体来证明引力的正确;你可以在电脑游戏世界本身中仅编程一个物体落向假设的最大的对象的倾向。
|
||||
|
||||
### 添加一个引力函数
|
||||
|
||||
记住你的玩家已经有了一个决定动作的属性。使用这个属性来将玩家精灵拉向屏幕底部。
|
||||
|
||||
在 Pygame 中,较高的数字更接近屏幕的底部边缘。
|
||||
|
||||
在真实的世界中,引力影响一切。然而,在平台游戏中,引力是有选择性的 —— 如果你添加引力到你的整个游戏世界,你的所有平台都将掉到地上。反之,你可以仅添加引力到你的玩家和敌人精灵中。
|
||||
|
||||
首先,在你的 `Player` 类中添加一个 `gravity` 函数:
|
||||
|
||||
```
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # 玩家掉落的多快
|
||||
```
|
||||
|
||||
这是一个简单的函数。首先,不管你的玩家是否想运动,你设置你的玩家垂直运动。也就是说,你已经编程你的玩家总是在下降。这基本上就是引力。
|
||||
|
||||
为使引力函数生效,你必须在你的主循环中调用它。这样,当每一个处理循环时,Python 都应用下落运动到你的玩家。
|
||||
|
||||
在这代码中,添加第一行到你的循环中:
|
||||
|
||||
```
|
||||
player.gravity() # 检查引力
|
||||
player.update()
|
||||
```
|
||||
|
||||
启动你的游戏来看看会发生什么。要注意,因为它发生的很快:你是玩家从天空上下落,马上掉出了你的游戏屏幕。
|
||||
|
||||
你的引力模拟是工作的,但是,也许太好了。
|
||||
|
||||
作为一次试验,尝试更改你玩家下落的速度。
|
||||
|
||||
### 给引力添加一个地板
|
||||
|
||||
你的游戏没有办法发现你的角色掉落出世界的问题。在一些游戏中,如果一个玩家掉落出世界,该精灵被删除,并在某个新的位置重生。在另一些游戏中,玩家会丢失分数或一条生命。当一个玩家掉落出世界时,不管你想发生什么,你必须能够侦测出玩家何时消失在屏幕外。
|
||||
|
||||
在 Python 中,要检查一个条件,你可以使用一个 `if` 语句。
|
||||
|
||||
你必需查看你玩家**是否**正在掉落,以及你的玩家掉落的程度。如果你的玩家掉落到屏幕的底部,那么你可以做*一些事情*。简化一下,设置玩家精灵的位置为底部边缘上方 20 像素。
|
||||
|
||||
使你的 `gravity` 函数看起来像这样:
|
||||
|
||||
```
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # 玩家掉落的多快
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty
|
||||
```
|
||||
|
||||
然后,启动你的游戏。你的精灵仍然下落,但是它停在屏幕的底部。不过,你也许不能*看到*你在地面层之上的精灵。一个简单的解决方法是,在精灵碰撞游戏世界的底部后,通过添加另一个 `-ty` 到它的新 Y 位置,从而使你的精灵弹跳到更高处:
|
||||
|
||||
```
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # 玩家掉落的多快
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
```
|
||||
|
||||
现在你的玩家在屏幕底部弹跳,恰好在你地面精灵上面。
|
||||
|
||||
你的玩家真正需要的是反抗引力的方法。引力问题是,你不能反抗它,除非你有一些东西来推开引力作用。因此,在接下来的文章中,你将添加地面和平台碰撞以及跳跃能力。在这期间,尝试应用引力到敌人精灵。
|
||||
|
||||
到目前为止,这里是全部的代码:
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# draw a world
|
||||
# add a player and player control
|
||||
# add player movement
|
||||
# add enemy and basic collision
|
||||
# add platform
|
||||
# add gravity
|
||||
|
||||
# GNU All-Permissive License
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without any warranty.
|
||||
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
|
||||
'''
|
||||
Objects
|
||||
'''
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x location, y location, img width, img height, img file
|
||||
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.rect = self.image.get_rect()
|
||||
self.rect.y = yloc
|
||||
self.rect.x = xloc
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn a player
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.movex = 0
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.health = 10
|
||||
self.score = 1
|
||||
self.images = []
|
||||
for i in range(1,9):
|
||||
img = pygame.image.load(os.path.join('images','hero' + 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):
|
||||
self.movey += 3.2 # how fast player falls
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
|
||||
def control(self,x,y):
|
||||
'''
|
||||
control player movement
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
Update sprite position
|
||||
'''
|
||||
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
|
||||
# moving left
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# moving right
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
|
||||
# collisions
|
||||
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
for enemy in enemy_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn an enemy
|
||||
'''
|
||||
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):
|
||||
'''
|
||||
enemy movement
|
||||
'''
|
||||
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
|
||||
|
||||
class Level():
|
||||
def bad(lvl,eloc):
|
||||
if lvl == 1:
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
|
||||
enemy_list = pygame.sprite.Group() # create enemy group
|
||||
enemy_list.add(enemy) # add enemy to group
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return enemy_list
|
||||
|
||||
def loot(lvl,lloc):
|
||||
print(lvl)
|
||||
|
||||
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,'ground.png')
|
||||
ground_list.add(ground)
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
|
||||
def platform(lvl,tx,ty):
|
||||
plat_list = pygame.sprite.Group()
|
||||
ploc = []
|
||||
i=0
|
||||
if lvl == 1:
|
||||
ploc.append((0,worldy-ty-128,3))
|
||||
ploc.append((300,worldy-ty-256,3))
|
||||
ploc.append((500,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,'ground.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
|
||||
|
||||
'''
|
||||
Setup
|
||||
'''
|
||||
worldx = 960
|
||||
worldy = 720
|
||||
|
||||
fps = 40 # frame rate
|
||||
ani = 4 # animation cycles
|
||||
clock = pygame.time.Clock()
|
||||
pygame.init()
|
||||
main = True
|
||||
|
||||
BLUE = (25,25,200)
|
||||
BLACK = (23,23,23 )
|
||||
WHITE = (254,254,254)
|
||||
ALPHA = (0,255,0)
|
||||
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = world.get_rect()
|
||||
player = Player() # spawn player
|
||||
player.rect.x = 0
|
||||
player.rect.y = 0
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
steps = 10 # how fast to move
|
||||
|
||||
eloc = []
|
||||
eloc = [200,20]
|
||||
gloc = []
|
||||
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
|
||||
tx = 64 #tile size
|
||||
ty = 64 #tile size
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
gloc.append(i*tx)
|
||||
i=i+1
|
||||
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
plat_list = Level.platform( 1,tx,ty )
|
||||
|
||||
'''
|
||||
Main loop
|
||||
'''
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit(); sys.exit()
|
||||
main = False
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
print("LEFT")
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
print("RIGHT")
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
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'):
|
||||
print('jump')
|
||||
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.gravity() # check gravity
|
||||
player.update()
|
||||
player_list.draw(world)
|
||||
enemy_list.draw(world)
|
||||
ground_list.draw(world)
|
||||
plat_list.draw(world)
|
||||
for e in enemy_list:
|
||||
e.move()
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
这是仍在进行中的关于使用 [Pygame][4] 模块来在 [Python 3][3] 在创建电脑游戏的第七部分。先前的文章是:
|
||||
|
||||
* [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程][5]
|
||||
* [使用 Python 和 Pygame 模块构建一个游戏框架][6]
|
||||
* [如何在你的 Python 游戏中添加一个玩家][7]
|
||||
* [用 Pygame 使你的游戏角色移动起来][8]
|
||||
* [如何向你的 Python 游戏中添加一个敌人][2]
|
||||
* [在 Pygame 游戏中放置平台][9]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/simulate-gravity-python
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/space_stars_cosmic.jpg?itok=bE94WtN- (Cosmic stars in outer space)
|
||||
[2]: https://linux.cn/article-10883-1.html
|
||||
[3]: https://www.python.org/
|
||||
[4]: https://www.pygame.org
|
||||
[5]: https://linux.cn/article-9071-1.html
|
||||
[6]: https://linux.cn/article-10850-1.html
|
||||
[7]: https://linux.cn/article-10858-1.html
|
||||
[8]: https://linux.cn/article-10874-1.html
|
||||
[9]: https://linux.cn/article-10902-1.html
|
320
published/20191210 Lessons learned from programming in Go.md
Normal file
320
published/20191210 Lessons learned from programming in Go.md
Normal file
@ -0,0 +1,320 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lxbwolf)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11778-1.html)
|
||||
[#]: subject: (Lessons learned from programming in Go)
|
||||
[#]: via: (https://opensource.com/article/19/12/go-common-pitfalls)
|
||||
[#]: author: (Eduardo Ferreira https://opensource.com/users/edufgf)
|
||||
|
||||
Go 并发编程中的经验教训
|
||||
======
|
||||
|
||||
> 通过学习如何定位并发处理的陷阱来避免未来处理这些问题时的困境。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/13/150539n217ak1vcf717uzx.jpg)
|
||||
|
||||
在复杂的分布式系统进行任务处理时,你通常会需要进行并发的操作。在 [Mode.net][2] 公司,我们每天都要和实时、快速和灵活的软件打交道。而没有一个高度并发的系统,就不可能构建一个毫秒级的动态地路由数据包的全球专用网络。这个动态路由是基于网络状态的,尽管这个过程需要考虑众多因素,但我们的重点是链路指标。在我们的环境中,链路指标可以是任何跟网络链接的状态和当前属性(如链接延迟)有关的任何内容。
|
||||
|
||||
### 并发探测链接监控
|
||||
|
||||
我们的动态路由算法 [H.A.L.O.][4](<ruby>逐跳自适应链路状态最佳路由<rt>Hop-by-Hop Adaptive Link-State Optimal Routing</rt></ruby>)部分依赖于链路指标来计算路由表。这些指标由位于每个 PoP(<ruby>存活节点<rt>Point of Presence</rt></ruby>)上的独立组件收集。PoP 是表示我们的网络中单个路由实体的机器,通过链路连接并分布在我们的网络拓扑中的各个位置。某个组件使用网络数据包探测周围的机器,周围的机器回复数据包给前者。从接收到的探测包中可以获得链路延迟。由于每个 PoP 都有不止一个临近节点,所以这种探测任务实质上是并发的:我们需要实时测量每个临近连接点的延迟。我们不能串行地处理;为了计算这个指标,必须尽快处理每个探测。
|
||||
|
||||
![latency computation graph][6]
|
||||
|
||||
### 序列号和重置:一个重新排列场景
|
||||
|
||||
我们的探测组件互相发送和接收数据包,并依靠序列号进行数据包处理。这旨在避免处理重复的包或顺序被打乱的包。我们的第一个实现依靠特殊的序列号 0 来重置序列号。这个数字仅在组件初始化时使用。主要的问题是我们考虑了递增的序列号总是从 0 开始。在该组件重启后,包的顺序可能会重新排列,某个包的序列号可能会轻易地被替换成重置之前使用过的值。这意味着,后继的包都会被忽略掉,直到排到重置之前用到的序列值。
|
||||
|
||||
### UDP 握手和有限状态机
|
||||
|
||||
这里的问题是该组件重启前后的序列号是否一致。有几种方法可以解决这个问题,经过讨论,我们选择了实现一个带有清晰状态定义的三步握手协议。这个握手过程在初始化时通过链接建立会话。这样可以确保节点通过同一个会话进行通信且使用了适当的序列号。
|
||||
|
||||
为了正确实现这个过程,我们必须定义一个有清晰状态和过渡的有限状态机。这样我们就可以正确管理握手过程中的所有极端情况。
|
||||
|
||||
![finite state machine diagram][7]
|
||||
|
||||
会话 ID 由握手的初始化程序生成。一个完整的交换顺序如下:
|
||||
|
||||
1. 发送者发送一个 `SYN(ID)` 数据包。
|
||||
2. 接收者存储接收到的 `ID` 并发送一个 `SYN-ACK(ID)`。
|
||||
3. 发送者接收到 `SYN-ACK(ID)` 并发送一个 `ACK(ID)`。它还发送一个从序列号 0 开始的数据包。
|
||||
4. 接收者检查最后接收到的 `ID`,如果 ID 匹配,则接受 `ACK(ID)`。它还开始接受序列号为 0 的数据包。
|
||||
|
||||
### 处理状态超时
|
||||
|
||||
基本上,每种状态下你都需要处理最多三种类型的事件:链接事件、数据包事件和超时事件。这些事件会并发地出现,因此你必须正确处理并发。
|
||||
|
||||
* 链接事件包括网络连接或网络断开的变化,相应的初始化一个链接会话或断开一个已建立的会话。
|
||||
* 数据包事件是控制数据包(`SYN`/`SYN-ACK`/`ACK`)或只是探测响应。
|
||||
* 超时事件在当前会话状态的预定超时时间到期后触发。
|
||||
|
||||
这里面临的最主要的问题是如何处理并发的超时到期和其他事件。这里很容易陷入死锁和资源竞争的陷阱。
|
||||
|
||||
### 第一种方法
|
||||
|
||||
本项目使用的语言是 [Golang][8]。它确实提供了原生的同步机制,如自带的通道和锁,并且能够使用轻量级线程来进行并发处理。
|
||||
|
||||
![gophers hacking together][9]
|
||||
|
||||
*gopher 们聚众狂欢*
|
||||
|
||||
首先,你可以设计两个分别表示我们的会话和超时处理程序的结构体。
|
||||
|
||||
```go
|
||||
type Session struct {
|
||||
State SessionState
|
||||
Id SessionId
|
||||
RemoteIp string
|
||||
}
|
||||
|
||||
type TimeoutHandler struct {
|
||||
callback func(Session)
|
||||
session Session
|
||||
duration int
|
||||
timer *timer.Timer
|
||||
}
|
||||
```
|
||||
|
||||
`Session` 标识连接会话,内有表示会话 ID、临近的连接点的 IP 和当前会话状态的字段。
|
||||
|
||||
`TimeoutHandler` 包含回调函数、对应的会话、持续时间和指向调度计时器的指针。
|
||||
|
||||
每一个临近连接点的会话都包含一个保存调度 `TimeoutHandler` 的全局映射。
|
||||
|
||||
```
|
||||
SessionTimeout map[Session]*TimeoutHandler
|
||||
```
|
||||
|
||||
下面方法注册和取消超时:
|
||||
|
||||
```go
|
||||
// schedules the timeout callback function.
|
||||
func (timeout* TimeoutHandler) Register() {
|
||||
timeout.timer = time.AfterFunc(time.Duration(timeout.duration) * time.Second, func() {
|
||||
timeout.callback(timeout.session)
|
||||
})
|
||||
}
|
||||
|
||||
func (timeout* TimeoutHandler) Cancel() {
|
||||
if timeout.timer == nil {
|
||||
return
|
||||
}
|
||||
timeout.timer.Stop()
|
||||
}
|
||||
```
|
||||
|
||||
你可以使用类似下面的方法来创建和存储超时:
|
||||
|
||||
```go
|
||||
func CreateTimeoutHandler(callback func(Session), session Session, duration int) *TimeoutHandler {
|
||||
if sessionTimeout[session] == nil {
|
||||
sessionTimeout[session] := new(TimeoutHandler)
|
||||
}
|
||||
|
||||
timeout = sessionTimeout[session]
|
||||
timeout.session = session
|
||||
timeout.callback = callback
|
||||
timeout.duration = duration
|
||||
return timeout
|
||||
}
|
||||
```
|
||||
|
||||
超时处理程序创建后,会在经过了设置的 `duration` 时间(秒)后执行回调函数。然而,有些事件会使你重新调度一个超时处理程序(与 `SYN` 状态时的处理一样,每 3 秒一次)。
|
||||
|
||||
为此,你可以让回调函数重新调度一次超时:
|
||||
|
||||
```go
|
||||
func synCallback(session Session) {
|
||||
sendSynPacket(session)
|
||||
|
||||
// reschedules the same callback.
|
||||
newTimeout := NewTimeoutHandler(synCallback, session, SYN_TIMEOUT_DURATION)
|
||||
newTimeout.Register()
|
||||
|
||||
sessionTimeout[state] = newTimeout
|
||||
}
|
||||
```
|
||||
|
||||
这次回调在新的超时处理程序中重新调度自己,并更新全局映射 `sessionTimeout`。
|
||||
|
||||
### 数据竞争和引用
|
||||
|
||||
你的解决方案已经有了。可以通过检查计时器到期后超时回调是否执行来进行一个简单的测试。为此,注册一个超时,休眠 `duration` 秒,然后检查是否执行了回调的处理。执行这个测试后,最好取消预定的超时时间(因为它会重新调度),这样才不会在下次测试时产生副作用。
|
||||
|
||||
令人惊讶的是,这个简单的测试发现了这个解决方案中的一个问题。使用 `cancel` 方法来取消超时并没有正确处理。以下顺序的事件会导致数据资源竞争:
|
||||
|
||||
1. 你有一个已调度的超时处理程序。
|
||||
2. 线程 1:
|
||||
1. 你接收到一个控制数据包,现在你要取消已注册的超时并切换到下一个会话状态(如发送 `SYN` 后接收到一个 `SYN-ACK`)
|
||||
2. 你调用了 `timeout.Cancel()`,这个函数调用了 `timer.Stop()`。(请注意,Golang 计时器的停止不会终止一个已过期的计时器。)
|
||||
3. 线程 2:
|
||||
1. 在取消调用之前,计时器已过期,回调即将执行。
|
||||
2. 执行回调,它调度一次新的超时并更新全局映射。
|
||||
4. 线程 1:
|
||||
1. 切换到新的会话状态并注册新的超时,更新全局映射。
|
||||
|
||||
两个线程并发地更新超时映射。最终结果是你无法取消注册的超时,然后你也会丢失对线程 2 重新调度的超时的引用。这导致处理程序在一段时间内持续执行和重新调度,出现非预期行为。
|
||||
|
||||
### 锁也解决不了问题
|
||||
|
||||
使用锁也不能完全解决问题。如果你在处理所有事件和执行回调之前加锁,它仍然不能阻止一个过期的回调运行:
|
||||
|
||||
```go
|
||||
func (timeout* TimeoutHandler) Register() {
|
||||
timeout.timer = time.AfterFunc(time.Duration(timeout.duration) * time._Second_, func() {
|
||||
stateLock.Lock()
|
||||
defer stateLock.Unlock()
|
||||
|
||||
timeout.callback(timeout.session)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
现在的区别就是全局映射的更新是同步的,但是这还是不能阻止在你调用 `timeout.Cancel()` 后回调的执行 —— 这种情况出现在调度计时器过期了但是还没有拿到锁的时候。你还是会丢失一个已注册的超时的引用。
|
||||
|
||||
### 使用取消通道
|
||||
|
||||
你可以使用取消通道,而不必依赖不能阻止到期的计时器执行的 golang 函数 `timer.Stop()`。
|
||||
|
||||
这是一个略有不同的方法。现在你可以不用再通过回调进行递归地重新调度;而是注册一个死循环,这个循环接收到取消信号或超时事件时终止。
|
||||
|
||||
新的 `Register()` 产生一个新的 go 线程,这个线程在超时后执行你的回调,并在前一个超时执行后调度新的超时。返回给调用方一个取消通道,用来控制循环的终止。
|
||||
|
||||
```go
|
||||
func (timeout *TimeoutHandler) Register() chan struct{} {
|
||||
cancelChan := make(chan struct{})
|
||||
|
||||
go func () {
|
||||
select {
|
||||
case _ = <- cancelChan:
|
||||
return
|
||||
case _ = <- time.AfterFunc(time.Duration(timeout.duration) * time.Second):
|
||||
func () {
|
||||
stateLock.Lock()
|
||||
defer stateLock.Unlock()
|
||||
|
||||
timeout.callback(timeout.session)
|
||||
} ()
|
||||
}
|
||||
} ()
|
||||
|
||||
return cancelChan
|
||||
}
|
||||
|
||||
func (timeout* TimeoutHandler) Cancel() {
|
||||
if timeout.cancelChan == nil {
|
||||
return
|
||||
}
|
||||
timeout.cancelChan <- struct{}{}
|
||||
}
|
||||
```
|
||||
|
||||
这个方法给你注册的所有超时提供了取消通道。一个取消调用向通道发送一个空结构体并触发取消操作。然而,这并不能解决前面的问题;可能在你通过通道取消之前以及超时线程拿到锁之前,超时时间就已经到了。
|
||||
|
||||
这里的解决方案是,在拿到锁**之后**,检查一下超时范围内的取消通道。
|
||||
|
||||
```go
|
||||
case _ = <- time.AfterFunc(time.Duration(timeout.duration) * time.Second):
|
||||
func () {
|
||||
stateLock.Lock()
|
||||
defer stateLock.Unlock()
|
||||
|
||||
select {
|
||||
case _ = <- handler.cancelChan:
|
||||
return
|
||||
default:
|
||||
timeout.callback(timeout.session)
|
||||
}
|
||||
} ()
|
||||
}
|
||||
```
|
||||
|
||||
最终,这可以确保在拿到锁之后执行回调,不会触发取消操作。
|
||||
|
||||
### 小心死锁
|
||||
|
||||
这个解决方案看起来有效;但是还是有个隐患:[死锁][10]。
|
||||
|
||||
请阅读上面的代码,试着自己找到它。考虑下描述的所有函数的并发调用。
|
||||
|
||||
这里的问题在取消通道本身。我们创建的是无缓冲通道,即发送的是阻塞调用。当你在一个超时处理程序中调用取消函数时,只有在该处理程序被取消后才能继续处理。问题出现在,当你有多个调用请求到同一个取消通道时,这时一个取消请求只被处理一次。当多个事件同时取消同一个超时处理程序时,如连接断开或控制包事件,很容易出现这种情况。这会导致死锁,可能会使应用程序停机。
|
||||
|
||||
![gophers on a wire, talking][11]
|
||||
|
||||
*有人在听吗?*
|
||||
|
||||
(已获得 Trevor Forrey 授权。)
|
||||
|
||||
这里的解决方案是创建通道时指定缓存大小至少为 1,这样向通道发送数据就不会阻塞,也显式地使发送变成非阻塞的,避免了并发调用。这样可以确保取消操作只发送一次,并且不会阻塞后续的取消调用。
|
||||
|
||||
```go
|
||||
func (timeout* TimeoutHandler) Cancel() {
|
||||
if timeout.cancelChan == nil {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case timeout.cancelChan <- struct{}{}:
|
||||
default:
|
||||
// can’t send on the channel, someone has already requested the cancellation.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 总结
|
||||
|
||||
在实践中你学到了并发操作时出现的常见错误。由于其不确定性,即使进行大量的测试,也不容易发现这些问题。下面是我们在最初的实现中遇到的三个主要问题:
|
||||
|
||||
#### 在非同步的情况下更新共享数据
|
||||
|
||||
这似乎是个很明显的问题,但如果并发更新发生在不同的位置,就很难发现。结果就是数据竞争,由于一个更新会覆盖另一个,因此对同一数据的多次更新中会有某些更新丢失。在我们的案例中,我们是在同时更新同一个共享映射里的调度超时引用。(有趣的是,如果 Go 检测到在同一个映射对象上的并发读写,会抛出致命错误 — 你可以尝试下运行 Go 的[数据竞争检测器](https://golang.org/doc/articles/race_detector.html))。这最终会导致丢失超时引用,且无法取消给定的超时。当有必要时,永远不要忘记使用锁。
|
||||
|
||||
![gopher assembly line][13]
|
||||
|
||||
*不要忘记同步 gopher 们的工作*
|
||||
|
||||
#### 缺少条件检查
|
||||
|
||||
在不能仅依赖锁的独占性的情况下,就需要进行条件检查。我们遇到的场景稍微有点不一样,但是核心思想跟[条件变量][14]是一样的。假设有个一个生产者和多个消费者使用一个共享队列的经典场景,生产者可以将一个元素添加到队列并唤醒所有消费者。这个唤醒调用意味着队列中的数据是可访问的,并且由于队列是共享的,消费者必须通过锁来进行同步访问。每个消费者都可能拿到锁;然而,你仍然需要检查队列中是否有元素。因为在你拿到锁的瞬间并不知道队列的状态,所以还是需要进行条件检查。
|
||||
|
||||
在我们的例子中,超时处理程序收到了计时器到期时发出的“唤醒”调用,但是它仍需要检查是否已向其发送了取消信号,然后才能继续执行回调。
|
||||
|
||||
![gopher boot camp][15]
|
||||
|
||||
*如果你要唤醒多个 gopher,可能就需要进行条件检查*
|
||||
|
||||
#### 死锁
|
||||
|
||||
当一个线程被卡住,无限期地等待一个唤醒信号,但是这个信号永远不会到达时,就会发生这种情况。死锁可以通过让你的整个程序停机来彻底杀死你的应用。
|
||||
|
||||
在我们的案例中,这种情况的发生是由于多次发送请求到一个非缓冲且阻塞的通道。这意味着向通道发送数据只有在从这个通道接收完数据后才能返回。我们的超时线程循环迅速从取消通道接收信号;然而,在接收到第一个信号后,它将跳出循环,并且再也不会从这个通道读取数据。其他的调用会一直被卡住。为避免这种情况,你需要仔细检查代码,谨慎处理阻塞调用,并确保不会发生线程饥饿。我们例子中的解决方法是使取消调用成为非阻塞调用 — 我们不需要阻塞调用。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/go-common-pitfalls
|
||||
|
||||
作者:[Eduardo Ferreira][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lxbwolf](https://github.com/lxbwolf)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/edufgf
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/go-golang.png?itok=OAW9BXny (Goland gopher illustration)
|
||||
[2]: http://mode.net
|
||||
[3]: https://en.wikipedia.org/wiki/Metrics_%28networking%29
|
||||
[4]: https://people.ece.cornell.edu/atang/pub/15/HALO_ToN.pdf
|
||||
[5]: https://en.wikipedia.org/wiki/Point_of_presence
|
||||
[6]: https://opensource.com/sites/default/files/uploads/image2_0_3.png (latency computation graph)
|
||||
[7]: https://opensource.com/sites/default/files/uploads/image3_0.png (finite state machine diagram)
|
||||
[8]: https://golang.org/
|
||||
[9]: https://opensource.com/sites/default/files/uploads/image4.png (gophers hacking together)
|
||||
[10]: https://en.wikipedia.org/wiki/Deadlock
|
||||
[11]: https://opensource.com/sites/default/files/uploads/image5_0_0.jpg (gophers on a wire, talking)
|
||||
[12]: https://golang.org/doc/articles/race_detector.html
|
||||
[13]: https://opensource.com/sites/default/files/uploads/image6.jpeg (gopher assembly line)
|
||||
[14]: https://en.wikipedia.org/wiki/Monitor_%28synchronization%29#Condition_variables
|
||||
[15]: https://opensource.com/sites/default/files/uploads/image7.png (gopher boot camp)
|
@ -1,8 +1,8 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11776-1.html)
|
||||
[#]: subject: (Make VLC More Awesome With These Simple Tips)
|
||||
[#]: via: (https://itsfoss.com/simple-vlc-tips/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
@ -10,20 +10,21 @@
|
||||
这些简单的技巧使 VLC 更加出色
|
||||
======
|
||||
|
||||
如果 [VLC][1] 不是最好的播放器,那它是[最好的开源视频播放器][2]之一。大多数人不知道的是,它不仅仅是视频播放器。
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/13/090635eu9va9999rr4ue92.jpeg)
|
||||
|
||||
如果 [VLC][1] 不是最好的播放器,那它也是[最好的开源视频播放器][2]之一。大多数人不知道的是,它不仅仅是视频播放器。
|
||||
|
||||
你可以进行许多复杂的任务,如直播视频、捕捉设备等。只需打开菜单,你就可以看到它有多少选项。
|
||||
|
||||
它的 FOSS 页面有一个详细的教程,讨论一些[专业的 VLC 技巧][3],但这些对于普通用户太复杂。
|
||||
我们有一个详细的教程,讨论一些[专业的 VLC 技巧][3],但这些对于普通用户太复杂。
|
||||
|
||||
这就是为什么我写另一篇文章的原因,来向你展示一些可以在 VLC 中使用的简单技巧。
|
||||
这就是为什么我要写另一篇文章的原因,来向你展示一些可以在 VLC 中使用的简单技巧。
|
||||
|
||||
### 使用这些简单技巧让 VLC 做更多事
|
||||
|
||||
Let’s see what can you do with VLC other than just playing a video file.
|
||||
让我们看看除了播放视频文件之外,你还可以使用 VLC 做什么。
|
||||
|
||||
#### 1\. 使用 VLC 观看 YouTube 视频
|
||||
#### 1、使用 VLC 观看 YouTube 视频
|
||||
|
||||
![][4]
|
||||
|
||||
@ -31,65 +32,60 @@ Let’s see what can you do with VLC other than just playing a video file.
|
||||
|
||||
是的,在 VLC 上流式传输 YouTube 视频是非常容易的。
|
||||
|
||||
只需启动 VLC 播放器,前往媒体设置,然后单击 ”**Open Network Stream**“ 或使用快捷方式 **CTRL + N**。
|
||||
只需启动 VLC 播放器,前往媒体设置,然后单击 ”Open Network Stream“ 或使用快捷方式 `CTRL + N`。
|
||||
|
||||
![][6]
|
||||
|
||||
接下来,你只需要粘贴要观看的视频的 URL。有一些选项可以调整,但通常你无需担心这些。如果你好奇,你可以点击 ”**Advanced options**“ 来探索。
|
||||
接下来,你只需要粘贴要观看的视频的 URL。有一些选项可以调整,但通常你无需担心这些。如果你好奇,你可以点击 ”Advanced options“ 来探索。
|
||||
|
||||
你还可以通过这种方式向 YouTube 视频添加字幕。然而,[一个更简单的带字幕观看 Youtube 视频的办法是使用 Penguin 字幕播放器][7]。
|
||||
|
||||
#### 2\. 将视频转换为不同格式
|
||||
#### 2、将视频转换为不同格式
|
||||
|
||||
![][8]
|
||||
|
||||
你可以[在 Linux 命令行使用 ffmpeg 转换视频][9]。你还可以使用图形工具,如 [HandBrake 转换视频格式][10]。
|
||||
|
||||
但是,如果你不想用一个单独的应用转码视频,你可以使用 VLC 播放器来完成该工作。
|
||||
但是,如果你不想用一个单独的应用来转码视频,你可以使用 VLC 播放器来完成该工作。
|
||||
|
||||
为此,只需点击 VLC 上的媒体选项,然后单击 **Convert/Save**,或者在 VLC 播放器处于活动状态时按下快捷键 CTRL + R。
|
||||
为此,只需点击 VLC 上的媒体选项,然后单击 “Convert/Save”,或者在 VLC 播放器处于活动状态时按下快捷键 `CTRL + R`。接下来,你需要从计算机/硬盘或者 URL 导入你想保存/转换的的视频。
|
||||
|
||||
接下来,你需要从计算机/硬盘或者 URL 导入你想保存/转换的的视频。
|
||||
|
||||
不管什么来源,只需选择文件后点击 ”**Convert/Save**“ 按钮
|
||||
|
||||
你现在会看到另外一个窗口给你更改 ”**Profile**“ 设置。点击并选择你想转换的格式(并保存)。
|
||||
不管是什么来源,只需选择文件后点击 “Convert/Save” 按钮。你现在会看到另外一个窗口可以更改 “Profile” 设置。点击并选择你想转换的格式(并保存)。
|
||||
|
||||
你还可以在转换之前通过在屏幕底部设置目标文件夹来更改转换文件的存储路径。
|
||||
|
||||
#### 3\. 从源录制音频/视频
|
||||
#### 3、从源录制音频/视频
|
||||
|
||||
![Vlc Advanced Controls][11]
|
||||
|
||||
你是否想在 VLC 播放器中录制正在播放的音频/视频?
|
||||
|
||||
如果是的话,有一个简单的解决方案。只需**通过 View,然后点击 ”Advanced Controls“**。
|
||||
如果是的话,有一个简单的解决方案。只需通过 “View”,然后点击 “Advanced Controls”。
|
||||
|
||||
完成后,你会看到一个新按钮(包括 VLC 播放器中的红色录制按钮)。
|
||||
|
||||
#### 4\. 自动下载字幕
|
||||
#### 4、自动下载字幕
|
||||
|
||||
![][12]
|
||||
|
||||
是的,你可以[使用 VLC 自动下载字幕][13]。你甚至不必在单独的网站上查找字幕。你只需点击 **View->VLSub**。
|
||||
是的,你可以[使用 VLC 自动下载字幕][13]。你甚至不必在单独的网站上查找字幕。你只需点击
|
||||
“View”->“VLSub”。
|
||||
|
||||
默认情况下,它是禁用的,因此当你单击该选项时,它会被激活,并允许你搜索/下载想要的字幕。
|
||||
|
||||
[VLC 还能让你使用简单的键盘快捷键同步字幕][14]
|
||||
|
||||
#### 5\. 截图
|
||||
#### 5、截图
|
||||
|
||||
![][15]
|
||||
|
||||
你可以在观看视频时使用 VLC 获取一些视频的截图/图像。
|
||||
|
||||
你只需在视频播放/暂停时右击播放器,你会看到一组选项,点击 **Video->Take Snapshot**。
|
||||
你可以在观看视频时使用 VLC 获取一些视频的截图/图像。你只需在视频播放/暂停时右击播放器,你会看到一组选项,点击 “Video”->“Take Snapshot”。
|
||||
|
||||
如果安装了旧版本,你可能在右键时看到截图选项。
|
||||
|
||||
#### 额外技巧:给视频添加音频/视频效果
|
||||
|
||||
在菜单中,进入 ”**Tools**“ 选项。单击 ”**Effects and Filters**“,或者在 VLC 播放器窗口中按 **CTRL + E** 打开选项。
|
||||
在菜单中,进入 “Tools” 选项。单击 “Effects and Filters”,或者在 VLC 播放器窗口中按 `CTRL + E` 打开选项。
|
||||
|
||||
好了,你可以观察你给视频添加的音频和视频效果了。你也许无法实时看到效果,因此你需要调整并保存来看发生了什么。
|
||||
|
||||
@ -108,7 +104,7 @@ via: https://itsfoss.com/simple-vlc-tips/
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,8 +1,8 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11779-1.html)
|
||||
[#]: subject: (PaperWM: tiled window management for GNOME)
|
||||
[#]: via: (https://jvns.ca/blog/2020/01/05/paperwm/)
|
||||
[#]: author: (Julia Evans https://jvns.ca/)
|
||||
@ -10,13 +10,15 @@
|
||||
PaperWM:GNOME 下的平铺窗口管理
|
||||
======
|
||||
|
||||
当我开始在个人计算机上使用 Linux 时,令我兴奋的第一件事就是轻巧的窗口管理器,这主要是因为当时我的笔记本电脑只有有 32MB 的内存,且没有其他可使用的内存。
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/13/212936hsk572f4jrsj2jsh.jpg)
|
||||
|
||||
接着我开始接触 [xmonad][1] 之类的平铺窗口管理器!我可以用键盘管理窗口了!它们太快了!我可以通过编写 Haskell 程序来配置 xmonad!我可以用各种有趣的方式自定义所有内容(例如使用 [dmenu][2] 作为启动器)!这些年来,我用过 3,4 个不同的平铺窗口管理器,它们很有趣。
|
||||
当我开始在个人计算机上使用 Linux 时,首先令我兴奋的就是轻巧的窗口管理器,这主要是因为当时我的笔记本电脑只有有 32MB 的内存,其它的都运行不了了。
|
||||
|
||||
大约 6 年前,我觉得配置平铺窗口管理器对我来说不再是一件有趣的事情,因此转而使用 Ubuntu 桌面环境 Gnome。 (现在,我的笔记本电脑中的内存增加了 500 倍,这要快得多 :) )
|
||||
接着我开始接触 [xmonad][1] 之类的平铺窗口管理器!我可以用键盘管理窗口了!它们是如此之快!我可以通过编写 Haskell 程序来配置 xmonad!我可以用各种有趣的方式自定义所有内容(例如使用 [dmenu][2] 作为启动器)!这些年来,我用过 3、4 个不同的平铺窗口管理器,它们都很有趣。
|
||||
|
||||
我使用 Gnome 已有很长时间了,但是我仍然有点想念平铺窗口管理器。六个月前,一个朋友告诉我有关 [PaperWM][3] 的消息,它使你可以在 Gnome中 平铺窗口!我立即安装了它,并从那时起我一直在使用它。
|
||||
大约 6 年前,我觉得配置平铺窗口管理器对我来说不再是一件有趣的事情,因此转而使用 Ubuntu 桌面环境 Gnome。(现在,我的笔记本电脑中的内存增加了 500 倍,这要快得多 :) )
|
||||
|
||||
我使用 Gnome 已有很长时间了,但是我仍然有点想念平铺窗口管理器。六个月前,一个朋友告诉我有关 [PaperWM][3] 的消息,它使你可以在 Gnome 中平铺窗口!我立即安装了它,并从那时起我一直在使用它。
|
||||
|
||||
### PaperWM:Gnome 下的平铺窗口管理
|
||||
|
||||
@ -26,7 +28,7 @@ PaperWM:GNOME 下的平铺窗口管理
|
||||
|
||||
### “Paper” 表示你的所有窗户都在一行中
|
||||
|
||||
PaperWM 的主要思想是将所有窗口排成一行,这实际上与传统的平铺窗口管理器大不相同,在传统的平铺窗口管理器中,你可以按任意方式平铺窗口。这是我写这篇博客时在几个窗口之间切换/调整大小的 gif 图像(有一个浏览器和两个终端窗口):
|
||||
PaperWM 的主要想法是将所有窗口排成一行,这实际上与传统的平铺窗口管理器大不相同,在传统的平铺窗口管理器中,你可以按任意方式平铺窗口。这是我写这篇博客时在几个窗口之间切换/调整大小的 gif 图像(有一个浏览器和两个终端窗口):
|
||||
|
||||
![][4]
|
||||
|
||||
@ -38,16 +40,14 @@ PaperWM 的 Github README 链接了此视频:<http://10gui.com/video/>,它
|
||||
|
||||
还有很多其他功能,但这是我使用的功能:
|
||||
|
||||
* 在窗口之间左右移动(`Super + ,`, `Super + .`)
|
||||
* 按顺序向左/向右移动窗口(`Super+Shift+,`,`Super+Shift+.`)
|
||||
* 全屏显示窗口(`Super + f`)
|
||||
* 缩小窗口(`Super + r`)
|
||||
|
||||
|
||||
* 在窗口之间左右移动(`Super + ,`、`Super + .`)
|
||||
* 按顺序向左/向右移动窗口(`Super+Shift+,`、`Super+Shift+.`)
|
||||
* 全屏显示窗口(`Super + f`)
|
||||
* 缩小窗口(`Super + r`)
|
||||
|
||||
### 我喜欢不需要配置的工具
|
||||
|
||||
我在笔记本上使用 PaperWM 已经6个月了,我真的很喜欢它!即使它是可配置(通过编写 Javascript 配置文件),我也非常欣赏它,它自带我想要的功能,我无需研究如何去配置。
|
||||
我在笔记本上使用 PaperWM 已经 6 个月了,我真的很喜欢它!即使它是可配置(通过编写 Javascript 配置文件),我也非常欣赏它,它自带我想要的功能,我无需研究如何去配置。
|
||||
|
||||
[fish shell][5] 是另一个类似的令人愉悦的工具,我基本上没有配置 fish(除了设置环境变量等),我真的很喜欢它的默认功能。
|
||||
|
||||
@ -58,7 +58,7 @@ via: https://jvns.ca/blog/2020/01/05/paperwm/
|
||||
作者:[Julia Evans][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,40 +1,36 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: translator: (qianmingtian)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-11781-1.html)
|
||||
[#]: subject: (Bash Script to Send eMail With a List of User Accounts Expiring in “X” Days)
|
||||
[#]: via: (https://www.2daygeek.com/bash-script-to-check-user-account-password-expiry-linux/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
Bash Script to Send eMail With a List of User Accounts Expiring in “X” Days
|
||||
使用 Bash 脚本发送包含几天内到期的用户账号列表的电子邮件
|
||||
======
|
||||
|
||||
The password enforcement policy is common to all operating systems and applications.
|
||||
![](https://img.linux.net.cn/data/attachment/album/202001/13/232047vfxkrr47mn7eapda.jpg)
|
||||
|
||||
If you want to **[implement a password enforcement policy on Linux][1]**, go to the following article.
|
||||
密码强制策略对所有操作系统和应用程序都是通用的。如果要[在 Linux 上实现密码强制策略][1],请参阅以下文章。
|
||||
|
||||
The password enforcement policy will be enforced by most companies by default, but the time period will be different depending on the company’s requirements.
|
||||
默认情况下,大多数公司都会强制执行密码强制策略,但根据公司的要求,密码的时间周期会有所不同。通常每个人都使用 90 天的密码周期。用户只会在他们使用的一些服务器上[更改密码][2],而不会在他们不经常使用的服务器上更改密码。
|
||||
|
||||
Usually everyone uses a 90-days password cycle.
|
||||
特别地,大多数团队忘记更改服务帐户密码,这可能导致日常工作的中断,即使他们配置有[基于 SSH 密钥的身份验证][3]。如果用户帐户密码过期,基于SSH密钥的身份验证和 [cronjob][4] 将不起作用。
|
||||
|
||||
The user will only **[change the password][2]** on some of the servers they use, and they won’t change the password on the servers they don’t use often.
|
||||
为了避免这种情况,我们创建了一个 [shell 脚本][5]来向你发送 10 天内到期的用户帐户列表。
|
||||
|
||||
In particular, most team forget to change the service account password, which can lead to breaking regular jobs even if they are configured to work with **[SSH key-based authentication][3]**.
|
||||
本教程中包含两个 [bash 脚本][6]可以帮助你收集系统中用户到期天数的信息。
|
||||
|
||||
SSH key-based authentication and **[cronjobs][4]** will not work if the user account password expires.
|
||||
### 1) 检查 10 天后到期的用户帐户列表
|
||||
|
||||
To avoid this situation, we have created a **[shell script][5]** that sends you a list of user accounts that expire within 10 days.
|
||||
|
||||
There are two **[bash scripts][6]** included in this tutorial that will help you collect information about user expiration days on your system.
|
||||
|
||||
### 1) Bash Script to Check List of User Accounts Expiring in 10 Days
|
||||
|
||||
This script will help you to check the list of user accounts that expire in 10 days on your terminal.
|
||||
此脚本将帮助你在终端上检查 10 天内到期的用户帐户列表。
|
||||
|
||||
```
|
||||
# vi /opt/script/user-password-expiry.sh
|
||||
```
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
/tmp/user-expiry-1.txt
|
||||
/tmp/user-expiry.txt
|
||||
@ -54,13 +50,13 @@ cat /tmp/user-expiry.txt | awk '$2 <= 10' > /tmp/user-expiry-1.txt
|
||||
cat /tmp/user-expiry-1.txt | column -t
|
||||
```
|
||||
|
||||
Set an executable Linux file permission to **“user-password-expiry.sh”** file.
|
||||
将文件 `user-password-expiry.sh` 设置为可执行的 Linux 文件权限。
|
||||
|
||||
```
|
||||
# chmod +x /opt/script/user-password-expiry.sh
|
||||
```
|
||||
|
||||
You will get an output like the one below. But the username and days may be different
|
||||
你将得到如下输出,但用户与天数可能不同。
|
||||
|
||||
```
|
||||
# sh /opt/script/user-password-expiry.sh
|
||||
@ -74,18 +70,20 @@ u3 3
|
||||
u4 5
|
||||
```
|
||||
|
||||
### 2) Bash Script to Send eMail With a List of User Accounts Expiring in 10 Days
|
||||
### 2) 发送包含 10 天内到期的用户帐户列表的电子邮件
|
||||
|
||||
This script will send a mail with a list of user accounts expiring in 10 days.
|
||||
此脚本将发送一封包含 10 天内到期的用户帐户列表的邮件。
|
||||
|
||||
```
|
||||
# vi /opt/script/user-password-expiry-mail.sh
|
||||
```
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
SUBJECT="Information About User Password Expiration on "`date`""
|
||||
MESSAGE="/tmp/user-expiry.txt"
|
||||
MESSAGE1="/tmp/user-expiry-1.txt"
|
||||
TO="[email protected]"
|
||||
TO="magesh.m@rentacenter.com"
|
||||
echo "-------------------------------------------------" >> $MESSAGE1
|
||||
echo "UserName The number of days the password expires" >> $MESSAGE1
|
||||
echo "-------------------------------------------------" >> $MESSAGE1
|
||||
@ -104,20 +102,20 @@ rm $MESSAGE
|
||||
rm $MESSAGE1
|
||||
```
|
||||
|
||||
Set an executable Linux file permission to **“user-password-expiry-mail.sh”** file.
|
||||
将文件 `user-password-expiry-mail.sh` 设置为可执行的 Linux 文件权限。
|
||||
|
||||
```
|
||||
# chmod +x /opt/script/user-password-expiry-mail.sh
|
||||
```
|
||||
|
||||
Finally add a **[cronjob][4]** to automate this. It runs once in a day at 8AM.
|
||||
最后,添加一个 [cronjob][4] 去自动执行脚本。每天早上 8 点运行一次。
|
||||
|
||||
```
|
||||
# crontab -e
|
||||
0 8 * * * /bin/bash /opt/script/user-password-expiry-mail.sh
|
||||
```
|
||||
|
||||
You will receive a mail similar to the first shell script output.
|
||||
你将收到一封与第一个脚本输出类似的电子邮件。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -125,14 +123,15 @@ via: https://www.2daygeek.com/bash-script-to-check-user-account-password-expiry-
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
译者:[qianmingtian][c]
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/how-to-set-password-complexity-policy-on-linux/
|
||||
[c]: https://github.com/qianmingtian
|
||||
[1]: https://linux.cn/article-11709-1.html
|
||||
[2]: https://www.2daygeek.com/linux-passwd-chpasswd-command-set-update-change-users-password-in-linux-using-shell-script/
|
||||
[3]: https://www.2daygeek.com/configure-setup-passwordless-ssh-key-based-authentication-linux/
|
||||
[4]: https://www.2daygeek.com/linux-crontab-cron-job-to-schedule-jobs-task/
|
@ -1,88 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Huawei’s Linux Distribution openEuler is Available Now!)
|
||||
[#]: via: (https://itsfoss.com/openeuler/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
Huawei’s Linux Distribution openEuler is Available Now!
|
||||
======
|
||||
|
||||
Huawei offers a CentOS based enterprise Linux distribution called EulerOS. Recently, Huawei has released a community edition of EulerOS called [openEuler][1].
|
||||
|
||||
The source code of openEuler is released as well. You won’t find it on Microsoft owned GitHub – the source code is available at [Gitee][2], a Chinese [alternative of GitHub][3].
|
||||
|
||||
There are two separate repositories, one for the [source code][2] and the other as a [package source][4] to store software packages that help to build the OS.
|
||||
|
||||
![][5]
|
||||
|
||||
The openEuler infrastructure team shared their experience to make the source code available:
|
||||
|
||||
> We are very excited at this moment. It was hard to imagine that we will manage thousands of repositories. And to ensure that they can be compiled successfully, we would like to thank all those who participated in contributing
|
||||
|
||||
### openEuler is a Linux distribution based on CentOS
|
||||
|
||||
Like EulerOS, openEuler OS is also based on [CentOS][6] but is further developed by Huawei Technologies for enterprise applications.
|
||||
|
||||
It is tailored for ARM64 architecture servers and Huawei claims to have made changes to boost its performance. You can read more about it at [Huawei’s dev blog][7].
|
||||
|
||||
![][8]
|
||||
|
||||
At the moment, as per the official openEuler announcement, there are more than 50 contributors with nearly 600 commits for openEuler.
|
||||
|
||||
The contributors made it possible to make the source code available to the community.
|
||||
|
||||
It is also worth noting that the repositories also include two new projects (or sub-projects) associated with it, [iSulad][9] **and A-Tune**.
|
||||
|
||||
A-Tune is an AI-based OS tuning software and iSulad is a lightweight container runtime daemon that is designed for IoT and Cloud infrastructure, as mentioned on [Gitee][2].
|
||||
|
||||
Also, the official [announcement post][10] mentioned that these systems are built on the Huawei Cloud through script automation. So, that is definitely something interesting.
|
||||
|
||||
### Downloading openEuler
|
||||
|
||||
![][11]
|
||||
|
||||
As of now, you won’t find the documentation for it in English – so you will have to wait for it or choose to help them with the [documentation][12].
|
||||
|
||||
You can download the ISO directly from its [official website][13] to test it out:
|
||||
|
||||
[Download openEuler][13]
|
||||
|
||||
### What do you think of Huawei openEuler?
|
||||
|
||||
As per cnTechPost, Huawei had announced that EulerOS would become open source under the new name openEuler.
|
||||
|
||||
At this point, it’s not clear if openEuler is replacing EulerOS or both will exist together like CentOS (community edition) and Red Hat (commercial edition).
|
||||
|
||||
I haven’t tested it yet so I cannot say if openEuler is suitable for English speaking users or not.
|
||||
|
||||
Are you willing to give this a try? In case you’ve already tried it out, feel free to let me know your experience with it in the comments below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/openeuler/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://openeuler.org/en/
|
||||
[2]: https://gitee.com/openeuler
|
||||
[3]: https://itsfoss.com/github-alternatives/
|
||||
[4]: https://gitee.com/src-openeuler
|
||||
[5]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/01/openEuler-website.jpg?ssl=1
|
||||
[6]: https://www.centos.org/
|
||||
[7]: https://developer.huaweicloud.com/en-us/euleros/euleros-introduction.html
|
||||
[8]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/01/openeuler-gitee.jpg?ssl=1
|
||||
[9]: https://gitee.com/openeuler/iSulad
|
||||
[10]: https://openeuler.org/en/news/20200101.html
|
||||
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/openEuler.jpg?ssl=1
|
||||
[12]: https://gitee.com/openeuler/docs
|
||||
[13]: https://openeuler.org/en/download.html
|
@ -1,165 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Generating numeric sequences with the Linux seq command)
|
||||
[#]: via: (https://www.networkworld.com/article/3511954/generating-numeric-sequences-with-the-linux-seq-command.html)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
Generating numeric sequences with the Linux seq command
|
||||
======
|
||||
The Linux seq command can generate lists of numbers and at lightning speed. It's easy to use and flexible, too.
|
||||
[Jamie][1] [(CC BY 2.0)][2]
|
||||
|
||||
One of the easiest ways to generate a list of numbers in Linux is to use the **seq** (sequence) command. In its simplest form, **seq** will take a single number and then list all the numbers from 1 to that number. For example:
|
||||
|
||||
```
|
||||
$ seq 5
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
```
|
||||
|
||||
Unless directed otherwise, **seq** always starts with 1. You can start a sequence with a different number by inserting it before the final number.
|
||||
|
||||
```
|
||||
$ seq 3 5
|
||||
3
|
||||
4
|
||||
5
|
||||
```
|
||||
|
||||
### Specifying an increment
|
||||
|
||||
You can also specify an increment. Say you want to list multiples of 3. Specify your starting point (first 3 in this example), increment (second 3) and end point (18).
|
||||
|
||||
[][3]
|
||||
|
||||
BrandPost Sponsored by HPE
|
||||
|
||||
[Take the Intelligent Route with Consumption-Based Storage][3]
|
||||
|
||||
Combine the agility and economics of HPE storage with HPE GreenLake and run your IT department with efficiency.
|
||||
|
||||
```
|
||||
$ seq 3 3 18
|
||||
3
|
||||
6
|
||||
9
|
||||
12
|
||||
15
|
||||
18
|
||||
```
|
||||
|
||||
You can elect to go from larger to smaller numbers by using a negative increment (i.e., a decrement).
|
||||
|
||||
```
|
||||
$ seq 18 -3 3
|
||||
18
|
||||
15
|
||||
12
|
||||
9
|
||||
6
|
||||
3
|
||||
```
|
||||
|
||||
The **seq** command is also very fast. You can probably generate a list of a million numbers in under 10 seconds.
|
||||
|
||||
Advertisement
|
||||
|
||||
```
|
||||
$ time seq 1000000
|
||||
1
|
||||
2
|
||||
3
|
||||
…
|
||||
…
|
||||
999998
|
||||
999999
|
||||
1000000
|
||||
|
||||
real 0m9.290s <== 9+ seconds
|
||||
user 0m0.020s
|
||||
sys 0m0.899s
|
||||
```
|
||||
|
||||
## Using a separator
|
||||
|
||||
Another very useful option is to use a separator. Instead of listing a single number on each line, you can insert commas, colons or some other characters. The -s option followed by the character you wish to use.
|
||||
|
||||
```
|
||||
$ seq -s: 3 3 18
|
||||
3:6:9:12:15:18
|
||||
```
|
||||
|
||||
In fact, if you simply want your numbers to be listed on a single line, you can use a blank as your separator in place of the default linefeed.
|
||||
|
||||
**[ Also see [Invaluable tips and tricks for troubleshooting Linux][4]. ]**
|
||||
|
||||
```
|
||||
$ seq -s' ' 3 3 18
|
||||
3 6 9 12 15 18
|
||||
```
|
||||
|
||||
### Getting to the math
|
||||
|
||||
It may seem like a big leap to go from generating a sequence of numbers to doing math, but given the right separators, **seq** can easily prepare calculations that you can pass to **bc**. For example:
|
||||
|
||||
```
|
||||
$ seq -s* 5 | bc
|
||||
120
|
||||
```
|
||||
|
||||
What is going on in this command? Let’s take a look. First, **seq** is generating a list of numbers and using * as the separator.
|
||||
|
||||
```
|
||||
$ seq -s* 5
|
||||
1*2*3*4*5
|
||||
```
|
||||
|
||||
It’s then passing the string to the calculator (**bc**) which promptly multiplies the numbers. And you can do a fairly extensive calculation in a fraction of a second.
|
||||
|
||||
```
|
||||
$ time seq -s* 117 | bc
|
||||
39699371608087208954019596294986306477904063601683223011297484643104\
|
||||
22041758630649341780708631240196854767624444057168110272995649603642\
|
||||
560353748940315749184568295424000000000000000000000000000
|
||||
|
||||
real 0m0.003s
|
||||
user 0m0.004s
|
||||
sys 0m0.000s
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
||||
You only get to choose one separator, so your calculations will be very limited. Use **bc** by itself for more complicated math. In addition, **seq** only works with numbers. To generate a sequence of single letters, use a command like this instead:
|
||||
|
||||
```
|
||||
$ echo {a..g}
|
||||
a b c d e f g
|
||||
```
|
||||
|
||||
Join the Network World communities on [Facebook][5] and [LinkedIn][6] to comment on topics that are top of mind.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3511954/generating-numeric-sequences-with-the-linux-seq-command.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://creativecommons.org/licenses/by/2.0/
|
||||
[2]: https://creativecommons.org/licenses/by/2.0/legalcode
|
||||
[3]: https://www.networkworld.com/article/3440100/take-the-intelligent-route-with-consumption-based-storage.html?utm_source=IDG&utm_medium=promotions&utm_campaign=HPE21620&utm_content=sidebar ( Take the Intelligent Route with Consumption-Based Storage)
|
||||
[4]: https://www.networkworld.com/article/3242170/linux/invaluable-tips-and-tricks-for-troubleshooting-linux.html
|
||||
[5]: https://www.facebook.com/NetworkWorld/
|
||||
[6]: https://www.linkedin.com/company/network-world
|
@ -1,5 +1,5 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
|
@ -0,0 +1,60 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Use Stow for configuration management of multiple machines)
|
||||
[#]: via: (https://opensource.com/article/20/1/configuration-management-stow)
|
||||
[#]: author: (Kevin Sonney https://opensource.com/users/ksonney)
|
||||
|
||||
Use Stow for configuration management of multiple machines
|
||||
======
|
||||
Learn how to use Stow to manage configurations across machines in the
|
||||
second article in our series on 20 ways to be more productive with open
|
||||
source in 2020.
|
||||
![A person programming][1]
|
||||
|
||||
Last year, I brought you 19 days of new (to you) productivity tools for 2019. This year, I'm taking a different approach: building an environment that will allow you to be more productive in the new year, using tools you may or may not already be using.
|
||||
|
||||
### Manage symlinks with Stow
|
||||
|
||||
Yesterday, I explained how I keep my files in sync across multiple machines with [Syncthing][2]. But that's only one of the tools I use to keep my configurations consistent. The other is a seemingly simple tool called [Stow][3].
|
||||
|
||||
![Stow help screen][4]
|
||||
|
||||
Stow manages symlinks. By default, it makes symlinks from the directory it is in to the directory below it. There are also options to set a source and target directory, but I don't usually use them.
|
||||
|
||||
As I mentioned in the Syncthing [article][5], I use Syncthing to keep a directory called **myconfigs** consistent across all of my machines. The **myconfigs** directory has several subdirectories underneath it. Each subdirectory contains the configuration files for one of the applications I use regularly.
|
||||
|
||||
![myconfigs directory][6]
|
||||
|
||||
On each machine, I change to the **myconfigs** directory and run **stow -S <directory name>** to symlink the files inside the directory to my home directory. For example, under the **vim** directory, I have my **.vimrc** and **.vim** directories. On each machine, I run **stow -S vim** to create the symlinks **~/.vimrc** and **~/.vim**. When I make a change to my Vim configuration on one machine, it applies to ALL of my machines.
|
||||
|
||||
Sometimes, though, I need something machine-specific, which is why I have directories like **msmtp-personal** and **msmtp-elastic** (my employer). Since my **msmtp** SMTP client needs to know what email server to relay through, and each one has different setups and credentials, I can use Stow to swap between the two by "unstowing" one with the **-D** flag and then putting the other in place.
|
||||
|
||||
![Unstow one, stow the other][7]
|
||||
|
||||
Sometimes I find myself adding files to a configuration. For that, there is the "restow" option with **-R**. For example, I like to use a specific font when I use Vim as a graphical application and not a console. The **.gvimrc** file lets me set options that apply only to the graphical version, in addition to the standard **.vimrc** file. When I first set this up, I moved **~/.gvimrc** to **~/myconfigs/vim** and then ran **stow -R vim**, which unlinks and relinks everything in that directory.
|
||||
|
||||
Stow lets me switch between several configurations with a simple command line and, in combination with Syncthing, I can be sure that I have the setup I like for the tools I use ready to go, no matter where I am or where I make changes.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/configuration-management-stow
|
||||
|
||||
作者:[Kevin Sonney][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/ksonney
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/computer_keyboard_laptop_development_code_woman.png?itok=vbYz6jjb (A person programming)
|
||||
[2]: https://syncthing.net/
|
||||
[3]: https://www.gnu.org/software/stow/
|
||||
[4]: https://opensource.com/sites/default/files/uploads/productivity_2-1.png (Stow help screen)
|
||||
[5]: https://opensource.com/article/20/1/20-productivity-tools-syncthing
|
||||
[6]: https://opensource.com/sites/default/files/uploads/productivity_2-2.png (myconfigs directory)
|
||||
[7]: https://opensource.com/sites/default/files/uploads/productivity_2-3.png (Unstow one, stow the other)
|
@ -0,0 +1,105 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (What I learned going from prison to Python)
|
||||
[#]: via: (https://opensource.com/article/20/1/prison-to-python)
|
||||
[#]: author: (Shadeed "Sha" Wallace-Stepter https://opensource.com/users/shastepter)
|
||||
|
||||
What I learned going from prison to Python
|
||||
======
|
||||
How open source programming can offer opportunities after incarceration.
|
||||
![Programming books on a shelf][1]
|
||||
|
||||
Less than a year ago, I was in San Quentin State Prison serving a life sentence.
|
||||
|
||||
In my junior year in high school, I shot a man while robbing him. Now, it took a while for me to see or even admit that what I did was wrong, but after going through a jury trial and seeing the devastating consequences of my actions, I knew that I needed to make a change, and I did. And although it was a great thing that I had changed, I had still shot a man and nearly killed him. And there are consequences to doing something like that, and rightfully so. So at the age of 18, I was sentenced to life in prison.
|
||||
|
||||
Now prison is a terrible place; I do not recommend it. But I had to go and so I went. I’ll spare you the details, but you can rest assured it’s a place where there isn’t much incentive to change, and many people pick up more bad habits than they went in with.
|
||||
|
||||
I’m one of the lucky ones. While I was in prison, something different happened. I started to imagine a future for myself beyond the prison bars where, up until that point, I had spent all of my adult life.
|
||||
|
||||
Now YOU think about this: I’m black, with nothing more than a high school education. I had no work history, and if I ever were to leave prison, I would be a convicted felon upon my release. And I think I’m being fair when I say that the first thought for an employer who sees this profile is not "I need to hire this person."
|
||||
|
||||
My options weren’t clear, but my mind was made up. I needed to do something to survive that wouldn’t look anything like my life before prison.
|
||||
|
||||
### A path to Python
|
||||
|
||||
Eventually, I wound up in San Quentin State Prison, and I had no idea how lucky I was to be there. San Quentin offered several self-help and education programs. These [rehabilitation opportunities][2] ensured prisoners had skills that helped them avoid being repeat offenders upon release.
|
||||
|
||||
As part of one of these programs, I met [Jessica McKellar][3] in 2017 through her work with the San Quentin Media Program. Jessica is an enthusiast of the programming language [Python][4], and she started to sell me on how great Python is and how it’s the perfect language to learn for someone just starting out. And this is where the story becomes stranger than fiction.
|
||||
|
||||
|
||||
|
||||
> Thanks [@northbaypython][5] for letting [@ShaStepter][6] and me reprise our [@pycon][7] keynotes to get them recorded. I'm honored to share:
|
||||
>
|
||||
> From Prison to Python: <https://t.co/rcumoAgZHm>
|
||||
>
|
||||
> Mass Decarceration: If We Don't Hire People With Felony Convictions, Who Will? <https://t.co/fENDUFdxfX> [pic.twitter.com/Kpjo8d3ul6][8]
|
||||
>
|
||||
> — Jessica McKellar (@jessicamckellar) [November 5, 2019][9]
|
||||
|
||||
|
||||
|
||||
Jessica told me about these Python video tutorials that she did for a company called [O’Reilly Media][10], that they were online, and how great it would be if I could get access to them. Unfortunately, internet access in prison isn’t a thing. But, I had met this guy named Tim O’Reilly, who had recently come to San Quentin. It turns out that, after his visit, Tim had donated a ton of content from his company, O’Reilly Media, to the prison’s programming class. I wound up getting my hands on a tablet that had Jessica’s Python tutorials on it and learned how to code using those Python tutorials.
|
||||
|
||||
It was incredible. Total strangers with a very different background and life from my own had connected the dots in a way that led to me learning to code.
|
||||
|
||||
### The love of the Python community
|
||||
|
||||
After this point, I started meeting with Jessica pretty frequently, and she began to tell me about the open source community. What I learned is that, on a fundamental level, open source is about fellowship and collaboration. It works so well because no one is excluded.
|
||||
|
||||
And for me, someone who struggled to see where they fit, what I saw was a very basic form of love—love by way of collaboration and acceptance, love by way of access, love by way of inclusion. And my spirit yearned to be a part of it. So I continued my education with Python, and, unfortunately, I wasn’t able to get more tutorials, but I was able to draw from the vast wealth of written knowledge that has been compiled by the open source community. I read anything that even mentioned Python, from paperback books to obscure magazine articles, and I used the tablet that I had to solve the Python problems that I read about.
|
||||
|
||||
My passion for Python and programming wasn’t something that many of my peers shared. Aside from the very small group of people who were in the prison’s programming class, no one else that I knew had ever mentioned programming; it’s just not on the average prisoner’s radar. I believe that this is due to the perception that programming isn’t accessible to people who have experienced incarceration, especially if you are a person of color.
|
||||
|
||||
### Life with Python outside of prison
|
||||
|
||||
Then, on August 17, 2018, I got the surprise of my life. Then-Governor Jerry Brown commuted my 27-years-to-life sentence, and I was released from prison after serving almost 19 years.
|
||||
|
||||
But here’s the reality of my situation and why I believe that programming and the open source community are so valuable. I am a 37-year-old, black, convicted felon, with no work history, who just served 18 years in prison. There aren’t many professions that exist that would prevent me from being at the mercy of the stigmas and biases that inevitably accompany my criminal past. But one of the few exceptions is programming.
|
||||
|
||||
The people who are now returning back to society after incarceration are in desperate need of inclusion, but when the conversation turns to diversity in the workplace and how much it’s needed, you really don’t hear this group being mentioned or included.
|
||||
|
||||
|
||||
|
||||
> What else:
|
||||
>
|
||||
> 1\. Background checks: ask how they are used at your company.
|
||||
>
|
||||
> 2\. Entry-level roles: remove fake, unnecessary prerequisites that will exclude qualified people with records.
|
||||
>
|
||||
> 3\. Active outreach: partner with local re-entry programs to create hiring pipelines. [pic.twitter.com/WnzdEUTuxr][11]
|
||||
>
|
||||
> — Jessica McKellar (@jessicamckellar) [May 12, 2019][12]
|
||||
|
||||
|
||||
|
||||
So with that, I want to humbly challenge all of the programmers and members of the open source community to expand your thinking around inclusion and diversity. I proudly stand before you today as the representative of a demographic that most people don’t think about—formerly incarcerated people. But we exist, and we are eager to prove our value, and, above all else, we are looking to be accepted. Many challenges await us upon our reentry back into society, and I ask that you allow us to have the opportunity to demonstrate our worth. Welcome us, accept us, and, more than anything else, include us.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/20/1/prison-to-python
|
||||
|
||||
作者:[Shadeed "Sha" Wallace-Stepter][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/shastepter
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/books_programming_languages.jpg?itok=KJcdnXM2 (Programming books on a shelf)
|
||||
[2]: https://www.dailycal.org/2019/02/27/san-quentin-rehabilitation-programs-offer-inmates-education-a-voice/
|
||||
[3]: https://twitter.com/jessicamckellar?lang=en
|
||||
[4]: https://www.python.org/
|
||||
[5]: https://twitter.com/northbaypython?ref_src=twsrc%5Etfw
|
||||
[6]: https://twitter.com/ShaStepter?ref_src=twsrc%5Etfw
|
||||
[7]: https://twitter.com/pycon?ref_src=twsrc%5Etfw
|
||||
[8]: https://t.co/Kpjo8d3ul6
|
||||
[9]: https://twitter.com/jessicamckellar/status/1191601209917837312?ref_src=twsrc%5Etfw
|
||||
[10]: http://shop.oreilly.com/product/110000448.do
|
||||
[11]: https://t.co/WnzdEUTuxr
|
||||
[12]: https://twitter.com/jessicamckellar/status/1127640222504636416?ref_src=twsrc%5Etfw
|
387
sources/tech/20200113 How to setup a DNS server with bind.md
Normal file
387
sources/tech/20200113 How to setup a DNS server with bind.md
Normal file
@ -0,0 +1,387 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (How to setup a DNS server with bind)
|
||||
[#]: via: (https://fedoramagazine.org/how-to-setup-a-dns-server-with-bind/)
|
||||
[#]: author: (Curt Warfield https://fedoramagazine.org/author/rcurtiswarfield/)
|
||||
|
||||
How to setup a DNS server with bind
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
The Domain Name System, or DNS, as it’s more commonly known, translates or converts domain names into the IP addresses associated with that domain. DNS is the reason you are able to find your favorite website by name instead of typing an IP address into your browser. This guide shows you how to configure a Master DNS system and one client.
|
||||
|
||||
Here are system details for the example used in this article:
|
||||
|
||||
```
|
||||
dns01.fedora.local (192.168.1.160 ) - Master DNS server
|
||||
client.fedora.local (192.168.1.136 ) - Client
|
||||
```
|
||||
|
||||
### DNS server configuration
|
||||
|
||||
Install the bind packages using sudo:
|
||||
|
||||
```
|
||||
$ sudo dnf install bind bind-utils -y
|
||||
```
|
||||
|
||||
The _/etc/named.conf_ configuration file is provided by the _bind_ package to allow you to configure the DNS server.
|
||||
|
||||
Edit the _/etc/named.conf_ file:
|
||||
|
||||
```
|
||||
sudo vi /etc/named.conf
|
||||
```
|
||||
|
||||
Look for the following line:
|
||||
|
||||
```
|
||||
listen-on port 53 { 127.0.0.1; };
|
||||
```
|
||||
|
||||
Add the IP address of your Master DNS server as follows:
|
||||
|
||||
```
|
||||
listen-on port 53 { 127.0.0.1; 192.168.1.160; };
|
||||
```
|
||||
|
||||
Look for the next line:
|
||||
|
||||
```
|
||||
allow-query { localhost; };
|
||||
```
|
||||
|
||||
Add your local network range. The example system uses IP addresses in the 192.168.1.X range. This is specified as follows:
|
||||
|
||||
```
|
||||
allow-query { localhost; 192.168.1.0/24; };
|
||||
```
|
||||
|
||||
Specify a forward and reverse zone. Zone files are simply text files that have the DNS information, such as IP addresses and host-names, on your system. The forward zone file makes it possible for the translation of a host-name to its IP address. The reverse zone file does the opposite. It allows a remote system to translate an IP address to the host name.
|
||||
|
||||
Look for the following line at the bottom of the /etc/named.conf file:
|
||||
|
||||
```
|
||||
include "/etc/named.rfc1912.zones";
|
||||
```
|
||||
|
||||
Here, you’ll specify the zone file information _**directly above that line**_ as follows:
|
||||
|
||||
```
|
||||
zone "dns01.fedora.local" IN {
|
||||
type master;
|
||||
file "forward.fedora.local";
|
||||
allow-update { none; };
|
||||
};
|
||||
|
||||
zone "1.168.192.in-addr.arpa" IN {
|
||||
type master;
|
||||
file "reverse.fedora.local";
|
||||
allow-update { none; };
|
||||
};
|
||||
```
|
||||
|
||||
The _forward.fedora.local_ and the file _reverse.fedora.local_ are just the names of the zone files you will be creating. They can be called anything you like.
|
||||
|
||||
Save and exit.
|
||||
|
||||
#### Create the zone files
|
||||
|
||||
Create the forward and reverse zone files you specified in the /etc/named.conf file:
|
||||
|
||||
```
|
||||
$ sudo vi /var/named/forward.fedora.local
|
||||
```
|
||||
|
||||
Add the following lines:
|
||||
|
||||
```
|
||||
$TTL 86400
|
||||
@ IN SOA dns01.fedora.local. root.fedora.local. (
|
||||
2011071001 ;Serial
|
||||
3600 ;Refresh
|
||||
1800 ;Retry
|
||||
604800 ;Expire
|
||||
86400 ;Minimum TTL
|
||||
)
|
||||
@ IN NS dns01.fedora.local.
|
||||
@ IN A 192.168.1.160
|
||||
dns01 IN A 192.168.1.160
|
||||
client IN A 192.168.1.136
|
||||
```
|
||||
|
||||
Everything in _**bold**_ is specific to your environment. Save the file and exit. Next, edit the _reverse.fedora.local_ file:
|
||||
|
||||
```
|
||||
$ sudo vi /var/named/reverse.fedora.local
|
||||
```
|
||||
|
||||
Add the following lines:
|
||||
|
||||
```
|
||||
$TTL 86400
|
||||
@ IN SOA dns01.fedora.local. root.fedora.local. (
|
||||
2011071001 ;Serial
|
||||
3600 ;Refresh
|
||||
1800 ;Retry
|
||||
604800 ;Expire
|
||||
86400 ;Minimum TTL
|
||||
)
|
||||
@ IN NS dns01.fedora.local.
|
||||
@ IN PTR fedora.local.
|
||||
dns01 IN A 192.168.1.160
|
||||
client IN A 192.168.1.136
|
||||
160 IN PTR dns01.fedora.local.
|
||||
136 IN PTR client.fedora.local.
|
||||
```
|
||||
|
||||
Everything in _**bold**_ is also specific to your environment. Save the file and exit.
|
||||
|
||||
You’ll also need to configure SELinux and add the correct ownership for the configuration files.
|
||||
|
||||
```
|
||||
sudo chgrp named -R /var/named
|
||||
sudo chown -v root:named /etc/named.conf
|
||||
sudo restorecon -rv /var/named
|
||||
sudo restorecon /etc/named.conf
|
||||
```
|
||||
|
||||
Configure the firewall:
|
||||
|
||||
```
|
||||
sudo firewall-cmd --add-service=dns --perm
|
||||
sudo firewall-cmd --reload
|
||||
```
|
||||
|
||||
#### Check the configuration for any syntax errors
|
||||
|
||||
```
|
||||
sudo named-checkconf /etc/named.conf
|
||||
```
|
||||
|
||||
Your configuration is valid if no output or errors are returned.
|
||||
|
||||
Check the forward and reverse zone files.
|
||||
|
||||
```
|
||||
$ sudo named-checkzone forward.fedora.local /var/named/forward.fedora.local
|
||||
|
||||
$ sudo named-checkzone reverse.fedora.local /var/named/reverse.fedora.local
|
||||
```
|
||||
|
||||
You should see a response of OK:
|
||||
|
||||
```
|
||||
zone forward.fedora.local/IN: loaded serial 2011071001
|
||||
OK
|
||||
|
||||
zone reverse.fedora.local/IN: loaded serial 2011071001
|
||||
OK
|
||||
```
|
||||
|
||||
#### Enable and start the DNS service
|
||||
|
||||
```
|
||||
$ sudo systemctl enable named
|
||||
$ sudo systemctl start named
|
||||
```
|
||||
|
||||
#### Configuring the resolv.conf file
|
||||
|
||||
Edit the _/etc/resolv.conf_ file:
|
||||
|
||||
```
|
||||
$ sudo vi /etc/resolv.conf
|
||||
```
|
||||
|
||||
Look for your current name server line or lines. On the example system, a cable modem/router is serving as the name server and so it currently looks like this:
|
||||
|
||||
```
|
||||
nameserver 192.168.1.1
|
||||
```
|
||||
|
||||
This needs to be changed to the IP address of the Master DNS server:
|
||||
|
||||
```
|
||||
nameserver 192.168.1.160
|
||||
```
|
||||
|
||||
Save your changes and exit.
|
||||
|
||||
Unfortunately there is one caveat to be aware of. NetworkManager overwrites the _/etc/resolv.conf_ file if the system is rebooted or networking gets restarted. This means you will lose all of the changes that you made.
|
||||
|
||||
To prevent this from happening, make _/etc/resolv.conf_ immutable:
|
||||
|
||||
```
|
||||
$ sudo chattr +i /etc/resolv.conf
|
||||
```
|
||||
|
||||
If you want to set it back and allow it to be overwritten again:
|
||||
|
||||
```
|
||||
$ sudo chattr -i /etc/resolv.conf
|
||||
```
|
||||
|
||||
#### Testing the DNS server
|
||||
|
||||
```
|
||||
$ dig fedoramagazine.org
|
||||
```
|
||||
|
||||
```
|
||||
; <<>> DiG 9.11.13-RedHat-9.11.13-2.fc30 <<>> fedoramagazine.org
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8391
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 6
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 4096
|
||||
; COOKIE: c7350d07f8efaa1286c670ab5e13482d600f82274871195a (good)
|
||||
;; QUESTION SECTION:
|
||||
;fedoramagazine.org. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
fedoramagazine.org. 50 IN A 35.197.52.145
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
fedoramagazine.org. 86150 IN NS ns05.fedoraproject.org.
|
||||
fedoramagazine.org. 86150 IN NS ns02.fedoraproject.org.
|
||||
fedoramagazine.org. 86150 IN NS ns04.fedoraproject.org.
|
||||
|
||||
;; ADDITIONAL SECTION:
|
||||
ns02.fedoraproject.org. 86150 IN A 152.19.134.139
|
||||
ns04.fedoraproject.org. 86150 IN A 209.132.181.17
|
||||
ns05.fedoraproject.org. 86150 IN A 85.236.55.10
|
||||
ns02.fedoraproject.org. 86150 IN AAAA 2610:28:3090:3001:dead:beef:cafe:fed5
|
||||
ns05.fedoraproject.org. 86150 IN AAAA 2001:4178:2:1269:dead:beef:cafe:fed5
|
||||
|
||||
;; Query time: 830 msec
|
||||
;; SERVER: 192.168.1.160#53(192.168.1.160)
|
||||
;; WHEN: Mon Jan 06 08:46:05 CST 2020
|
||||
;; MSG SIZE rcvd: 266
|
||||
```
|
||||
|
||||
There are a few things to look at to verify that the DNS server is working correctly. Obviously getting the results back are important, but that by itself doesn’t mean the DNS server is actually doing the work.
|
||||
|
||||
The QUERY, ANSWER, and AUTHORITY fields at the top should show non-zero as it in does in our example:
|
||||
|
||||
```
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 6
|
||||
```
|
||||
|
||||
And the SERVER field should have the IP address of your DNS server:
|
||||
|
||||
```
|
||||
;; SERVER: 192.168.1.160#53(192.168.1.160)
|
||||
```
|
||||
|
||||
In case this is the first time you’ve run the _dig_ command, notice how it took 830 milliseconds for the query to complete:
|
||||
|
||||
```
|
||||
;; Query time: 830 msec
|
||||
```
|
||||
|
||||
If you run it again, the query will run much quicker:
|
||||
|
||||
```
|
||||
$ dig fedoramagazine.org
|
||||
```
|
||||
|
||||
```
|
||||
;; Query time: 0 msec
|
||||
;; SERVER: 192.168.1.160#53(192.168.1.160)
|
||||
```
|
||||
|
||||
### Client configuration
|
||||
|
||||
The client configuration will be a lot simpler.
|
||||
|
||||
Install the bind utilities:
|
||||
|
||||
```
|
||||
$ sudo dnf install bind-utils -y
|
||||
```
|
||||
|
||||
Edit the /etc/resolv.conf file and configure the Master DNS as the only name server:
|
||||
|
||||
```
|
||||
$ sudo vi /etc/resolv.conf
|
||||
```
|
||||
|
||||
This is how it should look:
|
||||
|
||||
```
|
||||
nameserver 192.168.1.160
|
||||
```
|
||||
|
||||
Save your changes and exit. Then, make the _/etc/resolv.conf_ file immutable to prevent it from be overwritten and going back to its default settings:
|
||||
|
||||
```
|
||||
$ sudo chattr +i /etc/resolv.conf
|
||||
```
|
||||
|
||||
#### Testing the client
|
||||
|
||||
You should get the same results as you did from the DNS server:
|
||||
|
||||
```
|
||||
$ dig fedoramagazine.org
|
||||
```
|
||||
|
||||
```
|
||||
; <<>> DiG 9.11.13-RedHat-9.11.13-2.fc30 <<>> fedoramagazine.org
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8391
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 6
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 4096
|
||||
; COOKIE: c7350d07f8efaa1286c670ab5e13482d600f82274871195a (good)
|
||||
;; QUESTION SECTION:
|
||||
;fedoramagazine.org. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
fedoramagazine.org. 50 IN A 35.197.52.145
|
||||
|
||||
;; AUTHORITY SECTION:
|
||||
fedoramagazine.org. 86150 IN NS ns05.fedoraproject.org.
|
||||
fedoramagazine.org. 86150 IN NS ns02.fedoraproject.org.
|
||||
fedoramagazine.org. 86150 IN NS ns04.fedoraproject.org.
|
||||
|
||||
;; ADDITIONAL SECTION:
|
||||
ns02.fedoraproject.org. 86150 IN A 152.19.134.139
|
||||
ns04.fedoraproject.org. 86150 IN A 209.132.181.17
|
||||
ns05.fedoraproject.org. 86150 IN A 85.236.55.10
|
||||
ns02.fedoraproject.org. 86150 IN AAAA 2610:28:3090:3001:dead:beef:cafe:fed5
|
||||
ns05.fedoraproject.org. 86150 IN AAAA 2001:4178:2:1269:dead:beef:cafe:fed5
|
||||
|
||||
;; Query time: 1 msec
|
||||
;; SERVER: 192.168.1.160#53(192.168.1.160)
|
||||
;; WHEN: Mon Jan 06 08:46:05 CST 2020
|
||||
;; MSG SIZE rcvd: 266
|
||||
```
|
||||
|
||||
Make sure the SERVER output has the IP Address of your DNS server.
|
||||
|
||||
Your DNS server is now ready to use and all requests from the client should be going through your DNS server now!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/how-to-setup-a-dns-server-with-bind/
|
||||
|
||||
作者:[Curt Warfield][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/rcurtiswarfield/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2020/01/dns-server-bind-1-816x345.png
|
@ -0,0 +1,368 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: ( )
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (16 Places to Buy a Pre-installed Linux Laptop Online)
|
||||
[#]: via: (https://www.2daygeek.com/buy-linux-laptops-computers-online/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
16 Places to Buy a Pre-installed Linux Laptop Online
|
||||
======
|
||||
|
||||
Linux runs on most hardware these days, but most retailers do not have Linux operating systems pre-installed on their hardware.
|
||||
|
||||
Gone are the days when users would only buy a Windows OS pre-installed laptop.
|
||||
|
||||
Over the years, developers have purchased many Linux laptops as they work on major Linux applications related to Docker, Kubernetes, AI, cloud-native computing and machine learning.
|
||||
|
||||
But now-a-days users are eager to buy a Linux laptop instead of Windows, which allows many vendors to choose Linux OS.
|
||||
|
||||
### Why Pre-installed Linux?
|
||||
|
||||
Now-a-days normal users also started using Linux OS because of its open source nature, security and reliability.
|
||||
|
||||
But most of the retailers around the world do not sell Linux operating system pre-installed.
|
||||
|
||||
It is difficult for Linux aspirants to find the compatible hardware and drivers to get Linux OS installed.
|
||||
|
||||
So, we recommend to have Linux OS pre-installed computers instead of figuring out compatibility issues.
|
||||
|
||||
Here we list the top 16 (not in particular order) manufacturer/vendor best known for preloaded Linux OS computers.
|
||||
|
||||
### 1) Dell
|
||||
|
||||
Dell is a US multinational computer technology company that commenced to sell and distribute pre-installed Ubuntu Linux computers for several years now.
|
||||
|
||||
Initially it was started on 2012 as a community project called Sputnik.
|
||||
|
||||
The strong community support to the project became a product. Over the year they launched the first Dell XPS 13 Developer Edition (Sputnik 3) after fixing some major issues in Sputnik 1 and Sputnik 2.
|
||||
|
||||
[![][1]][2]
|
||||
|
||||
They sells Red hat Enterprise Linux and Ubuntu Linux-based laptop for business use, developers and sysadmins.
|
||||
|
||||
All systems are preloaded with Ubuntu but few of them were certified to install Red Hat Enterprise Linux 7.5 and RHEL 8.
|
||||
|
||||
I hope you can install other distro as well if you want to run but i didn’t try it.
|
||||
|
||||
The signature Linux products of Dell are **[XPS developer edition][3]**, **[Precision mobile workstation][4]** and Precision tower workstation.
|
||||
|
||||
* **Availability:** Worldwide
|
||||
* **Product Details:** [Dell Linux Systems][5]
|
||||
|
||||
|
||||
|
||||
### 2) System 76
|
||||
|
||||
**[System76][6]** is an American computer manufacturer based in Denver, Colorado specializing in the sale of notebooks, desktops, and servers.
|
||||
|
||||
From the year 2003, Sytem76 started to computers with the Linux operating system installed.
|
||||
|
||||
They developed Linux distribution named Pop!_OS based on Ubuntu using the GNOME Desktop Environment for developers and professionals.
|
||||
|
||||
[![][1]][7]
|
||||
|
||||
The products are categorized majorly based on portability, storage, graphics and CPU performance.
|
||||
|
||||
Lower laptop model Galago Pro is costing around $950 and higher models such as Adder WS and Serval WS are costing around $2000.
|
||||
|
||||
They provide Destops(Thelio variants) in range of $800 to $2600.
|
||||
|
||||
They also sell mini servers(Meerkat) ranges from $500 and Larger Servers(Jackal, Ibex and Starling) with preloaded Ubuntu ranges from $3000.
|
||||
|
||||
They provide the laptop with the coreboot open source firmware, which is an alternative to the proprietary BIOS firmware.
|
||||
|
||||
System76 ships their products to 60 countries all around the world in Africa, Europe, Asia, North America, South America, Australia and Zealandia.
|
||||
|
||||
* **Availability:** To 60 countries worldwide
|
||||
* **Product Details:** [System76][8]
|
||||
|
||||
|
||||
|
||||
### 3) Purism
|
||||
|
||||
Purism is a US-based company that commenced its operation in 2014.
|
||||
|
||||
It manufactures the Librem personal computing devices with a focus on software freedom, computer security, and Internet privacy.
|
||||
|
||||
[![][1]][9]
|
||||
|
||||
Purism sell their products with PureOS installed, a Linux distribution based on Debain developed by purism.
|
||||
|
||||
They sell multiple customized products such as Laptops, Tablets, Smartphones, Server and Librem key.
|
||||
|
||||
* **Availability:** Worldwide
|
||||
* **Product Details:** [Purism][10]
|
||||
|
||||
|
||||
|
||||
### 4) Slimbook
|
||||
|
||||
**[Slimbook][11]** commenced their operation in 2015 based in spain.
|
||||
|
||||
It is a Linux friendly product that offers Laptops, Desktops, Mini Pc’s,All in one PC’s and Servers.
|
||||
|
||||
[![][1]][12]
|
||||
|
||||
It sell their products with with preloaded variety of Linux distributions, windows or both.
|
||||
|
||||
They were the first to sell KDE OS installed. It is ideal for Linux beginners, since it is easy to use and easy to learn.
|
||||
|
||||
The Laptop body is made of metal alloy based on aluminum and magnesium.
|
||||
|
||||
* **Availability:** Worldwide
|
||||
* **Product Details:** [Slimbook][13]
|
||||
|
||||
|
||||
|
||||
### 5) Tuxedo Computers
|
||||
|
||||
Tuxedo computers a german based company sells notebooks, desktops and mini computers with preloaded Linux.
|
||||
|
||||
Their products desktop cost starts from around 480EUR, mini computers starts from 430EUR and notebooks starts from around 815EUR.
|
||||
|
||||
They have both intel and AMD processors and come up with 5 years warranty and lifetime support.
|
||||
|
||||
TUXEDO Computers are individually built computers and PCs being fully Linux-suitable. They sell their products to most part of Europe and USA.
|
||||
|
||||
* **Availability:** Ships to many countries
|
||||
* **Product Details:** [Tuxedo Computers][14]
|
||||
|
||||
|
||||
|
||||
### 6) ThinkPenguin
|
||||
|
||||
ThinkPengine is a US based company started their operation in 2008 to improve support for GNU/Linux and other free software operating systems.
|
||||
|
||||
They sell desktops, notebooks, network equipment, storage devices, printers, scanners and other accessories that are compatible with Linux.
|
||||
|
||||
They provide warranty from 90days to 3years based on the products.
|
||||
|
||||
* **Availability:** Worldwide
|
||||
* **Product Details:** [ThinkPenguin][15]
|
||||
|
||||
|
||||
|
||||
### 7) Emperor Linux
|
||||
|
||||
EmperorLinux is a US based company,since 1999 they provides Linux laptops with full hardware support under Linux.
|
||||
|
||||
They offers Linux laptops with unique features such as Molecule RD3D using Sharp’s ground-breaking Auto-Stereo 3D display, Panasonic’s ToughBook line of rugged & semi-rugged Linux laptops.
|
||||
|
||||
They also sell fully-functional Linux tablets, the Raven tablet (based on the ThinkPad X series).
|
||||
|
||||
* **Availability:** USA (International shipping is available upon request).
|
||||
* **Product Details:** [Emperor Linux][16]
|
||||
|
||||
|
||||
|
||||
### 8) ZaReason
|
||||
|
||||
ZaReason opened for business in the year 2007 based in US.
|
||||
|
||||
They mainly focuses on R&D labs, businesses both small and large, universities and people’s homes.
|
||||
|
||||
It has a long career building hardware for different distros such as Debian, Fedora, Ubuntu, Kubuntu, Edubuntu and Linux Mint Preloaded.
|
||||
|
||||
[![][1]][17]
|
||||
|
||||
And customer can even choose Linux disros of their choose other than specified.
|
||||
|
||||
Their laptop ranges from $999 to $1699. Their desktop and mini computers ranges from $499 to $1199.
|
||||
|
||||
They do sell desktop specific for game lovers (Gamebox9400).
|
||||
|
||||
Default warranty will be for a year. Additional cost includes for extending the warranty till 3 years.
|
||||
|
||||
* **Availability:** USA and Canada
|
||||
* **Product Details:** [ZaReason][18]
|
||||
|
||||
|
||||
|
||||
### 9) LAC Portland
|
||||
|
||||
LAC(Los Alamos Computers) Portland is a US based company, provides Linux-based computers configured and supported by GNU and Linux professionals since 2000.
|
||||
|
||||
They sell Lenovo desktops(ThinkCentre and ThinkStation) ranges from $845 to $2215 and laptops(ThinkPad) ranges from $926 to $2380.
|
||||
|
||||
They install and sell Linux distors such as Ubuntu, Linux Mint, Debain, Fedora, CentOS, Scientific Linux, Open SUSE and Free DOS.
|
||||
|
||||
They provide five years hardware and labor warranty with on-site support options backed worldwide by Lenovo.
|
||||
|
||||
* **Availability:** USA
|
||||
* **Product Details:** [LAC Portland][19]
|
||||
|
||||
|
||||
|
||||
### 10) Entroware
|
||||
|
||||
Entroware is a UK based company specialized in providing Ubuntu based computing solutions and services since early 2014 based on customers requirements.
|
||||
|
||||
They sell Ubuntu and Ubuntu MATE powered Desktops, Laptops, and Servers using modern and high quality components.
|
||||
|
||||
[![][1]][20]
|
||||
|
||||
They do sell mini computers and All-in-one computers.
|
||||
|
||||
Desktop ranges from $499 to $1900, laptops ranges from $740 to $1900 and server ranges from $1150 to $2000.
|
||||
|
||||
They also sell accessories such as OS recovery drive, external hard drive, etc.
|
||||
|
||||
The default warranty is for 3 years, they have three warranty plans for which additional may include. They also provide software support.
|
||||
|
||||
Entroware currently ships to UK, Republic of Ireland, France, Germany, Italy and Spain.
|
||||
|
||||
* **Availability:** UK and other European countries (Republic of Ireland, France, Germany, Italy and Spain).
|
||||
* **Product Details:** [Entroware][21]
|
||||
|
||||
|
||||
|
||||
### 11) Vikings
|
||||
|
||||
Viking is based in Germany, sells Libre-friendly hardware certified by the Free Software Foundation with preinstalled Debian, Trisquel or Parabola Linux based on customer requirement.
|
||||
|
||||
They sell desktops, laptops, servers, routers, mainboards, key generators, PCI cards and usb sound adaptors compatible with Linux.
|
||||
|
||||
The Linux laptops and desktops by Vikings come with core boot or Libreboot.
|
||||
|
||||
Their desktop ranges from 895EUR, laptop ranges from 250EUR and servers ranges from 990EUR.
|
||||
|
||||
They provide refurbished/used parts: mainboard, CPU(s) with rigorous testing of all parts and also gives a comprehensive guarantee for all parts of the system.
|
||||
|
||||
Their product warranty varies from 1year to 3year, with subsequent additional charges.
|
||||
|
||||
They ship to all part of the world with very few exceptions such as North Korea.
|
||||
|
||||
* **Availability:** Worldwide
|
||||
* **Product Details:** [Viking][22]
|
||||
|
||||
|
||||
|
||||
### 12) Juno Computers
|
||||
|
||||
Juno Computers is company based in UK comes with pre-installed elementary OS or Ubuntu.
|
||||
|
||||
They provide an application known as Kronos which allows for quick and easy installation of commercial applications such as Chrome, Dropbox, Spotify, Skype, etc.
|
||||
|
||||
Their laptop ranges from $945/357EUR to $999/933EUR and mini PC ranges around $549/490EUR.
|
||||
|
||||
They provide a 1-year limited warranty on all manufacture problems.
|
||||
|
||||
Currently they ship to mainland USA, some Canadian provinces, and most part of the world includes South Africa, Asia and Europe.
|
||||
|
||||
* **Availability:** Worldwide
|
||||
* **Product Details:** [Juno Computers][23]
|
||||
|
||||
|
||||
|
||||
### 13) Pine64
|
||||
|
||||
Pine64 is a US based community platform that offers laptops (**[Pinebooks][24]**), Pine Phones, Pine Watches(PineTime), Single board computers and other compatible Linux accessories.
|
||||
|
||||
It commenced its operation in the year 2016 powered by ARM devices.
|
||||
|
||||
[![][1]][25]
|
||||
|
||||
The laptops ranges from $100 to $200.
|
||||
|
||||
All single board and accessories sold on the Pine store are entitled to a 30 days Limited Warranty against defects in materials and workmanship, but provide online support through their forum.
|
||||
|
||||
They almost ship to most part of the country, refer site shipping policy for more details.
|
||||
|
||||
* **Availability:** Worldwide
|
||||
* **Product Details:** [Pine64][26]
|
||||
|
||||
|
||||
|
||||
### 14) Libiquity
|
||||
|
||||
Libiquity is a US based company with R&D investments and its own personal computer brand since 2011.
|
||||
|
||||
They offer laptop(Taurinus X200) preloaded with Trisquel and comes with ProteanOS, a free/libre and open source embedded operating system distribution endorsed by Free Software Foundation.
|
||||
|
||||
Laptop ranges starts from $375. Product comes with limited warrant of 1 year. Currently their shipping are limited to US.
|
||||
|
||||
* **Availability:** US
|
||||
* **Product Details:** [Libiquity][27]
|
||||
|
||||
|
||||
|
||||
### 15) LinuxCertified
|
||||
|
||||
LinuxCertified an US based company offers lenovo desktops and laptops with Linux distros preinstalled.
|
||||
|
||||
Various preloaded Linux distros offered are Ubuntu, Fedora, Open SUSE, CentOS, Redhat Enterprise Linux and Oracle Enterprise Linux.
|
||||
|
||||
Desktops(ThinkStation) ranges from $899 to $2199 and laptops(Z1, LC series) ranges from $899 to $2199.
|
||||
|
||||
Product warranty is for one year. They ship their product within US.
|
||||
|
||||
* **Availability:** Worldwide
|
||||
* **Product Details:** [LinuxCertified][28]
|
||||
|
||||
|
||||
|
||||
### 16) Star Labs
|
||||
|
||||
**[Star Labs][29]** was created by a group of Linux users, who created the ultimate Linux laptop for their own use.
|
||||
|
||||
It’s based in the United Kingdom which sells laptops with Linux pre-installed.
|
||||
|
||||
[![][1]][30]
|
||||
|
||||
Star Labs offer a range of laptops designed and built specifically for Linux.
|
||||
|
||||
All of their laptops come with a choice of Ubuntu Linux, Linux Mint or Zorin OS pre-installed.
|
||||
|
||||
It is not limited to the above three distributions, and you can install any Linux distros on their hardware, and it runs flawlessly.
|
||||
|
||||
* **Availability:** Worldwide
|
||||
* **Product Details:** [Star Labs][31]
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/buy-linux-laptops-computers-online/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[2]: https://www.2daygeek.com/wp-content/uploads/2020/01/dell-xps-13-developer-deition-2.png
|
||||
[3]: https://www.linuxtechnews.com/dells-new-xps-13-developer-edition-is-powered-by-the-10th-generation/
|
||||
[4]: https://www.linuxtechnews.com/dell-launches-three-new-dell-precision-developer-editions-laptops-preloaded-with-ubuntu-linux/
|
||||
[5]: https://www.dell.com/en-us/work/shop/overview/cp/linuxsystems
|
||||
[6]: https://www.linuxtechnews.com/system76-has-announced-new-gazelle-laptops/
|
||||
[7]: https://www.2daygeek.com/wp-content/uploads/2020/01/system76-1.jpg
|
||||
[8]: https://system76.com/laptops
|
||||
[9]: https://www.2daygeek.com/wp-content/uploads/2020/01/librem-1.jpg
|
||||
[10]: https://puri.sm/products/
|
||||
[11]: https://www.linuxtechnews.com/slimbook-is-offering-a-new-laptop-called-slimbook-pro-x/
|
||||
[12]: https://www.2daygeek.com/wp-content/uploads/2020/01/slimbook.jpg
|
||||
[13]: https://slimbook.es/en/comparison-slimbook-pro-x-with-other-ultrabooks
|
||||
[14]: https://www.tuxedocomputers.com/en/Linux-Hardware/Linux-Notebooks.tuxedo
|
||||
[15]: https://www.thinkpenguin.com/catalog/notebook-computers-gnu-linux-2
|
||||
[16]: http://www.emperorlinux.com/systems/
|
||||
[17]: https://www.2daygeek.com/wp-content/uploads/2020/01/zareason-1.jpg
|
||||
[18]: https://zareason.com/Laptops/
|
||||
[19]: https://shop.lacpdx.com/laptops/
|
||||
[20]: https://www.2daygeek.com/wp-content/uploads/2020/01/entroware.jpg
|
||||
[21]: https://www.entroware.com/store/laptops
|
||||
[22]: https://store.vikings.net/libre-friendly-hardware/x200-ryf-certfied
|
||||
[23]: https://junocomputers.com/store/
|
||||
[24]: https://www.linuxtechnews.com/pinebook-pro-199-linux-laptop-pre-orders-ansi-iso-keyboards/
|
||||
[25]: https://www.2daygeek.com/wp-content/uploads/2020/01/Pinebook_Pro-photo-1.jpg
|
||||
[26]: https://store.pine64.org/
|
||||
[27]: https://shop.libiquity.com/
|
||||
[28]: https://www.linuxcertified.com/linux_laptops.html
|
||||
[29]: https://www.linuxtechnews.com/star-labs-offering-a-range-of-linux-laptops-with-zorin-os-15-pre-installed/
|
||||
[30]: https://www.2daygeek.com/wp-content/uploads/2020/01/starlabs-1.jpg
|
||||
[31]: https://earth.starlabs.systems/pages/laptops
|
@ -0,0 +1,90 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (qianmingtian)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Huawei’s Linux Distribution openEuler is Available Now!)
|
||||
[#]: via: (https://itsfoss.com/openeuler/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
华为的linux发行版 openEuler 可以使用了!
|
||||
======
|
||||
|
||||
华为提供了一个基于 CentOS 的企业 Linux 发行版 EulerOS 。最近,华为发布了一个名为 [openEuler][1] 的 EulerOS 社区版。
|
||||
|
||||
openEuler 的源代码也被发布了。你在微软旗下的 GitHub 上找不到它——源代码可以在 [Gitee][2] 找到,这是一个中文的 [GitHub 的替代品][3] 。
|
||||
|
||||
它有两个独立的存储库,一个用于存储[源代码][2],另一个作为[包源][4] 存储有助于构建操作系统的软件包。
|
||||
|
||||
![][5]
|
||||
|
||||
openuler 基础架构团队分享了他们使源代码可用的经验:
|
||||
|
||||
>我们现在很兴奋。很难想象我们会管理成千上万的仓库。为了确保它们能被成功地编译,我们要感谢所有参与贡献的人。
|
||||
|
||||
### openEuler 是基于 CentOS 的 Linux 发行版
|
||||
|
||||
与 EulerOS 一样,openEuler OS 也是基于 [CentOS][6],但华为技术有限公司为企业应用进一步开发了该操作系统。
|
||||
|
||||
它是为 ARM64 架构的服务器量身定做的,同时华为声称已经做了一些改变来提高其性能。你可以在[华为发展博客][7]上了解更多。
|
||||
|
||||
![][8]
|
||||
|
||||
|
||||
目前,根据 openEuler 的官方声明,有 50 多名贡献者为 openEuler 贡献了近 600 个提交。
|
||||
|
||||
贡献者使源代码对社区可用成为可能。
|
||||
|
||||
值得注意的是,存储库还包括两个与之相关的新项目(或子项目),[iSulad][9] 和 **A-Tune**。
|
||||
|
||||
A-Tune 是一个基于 AI 的操作系统调优软件, iSulad 是一个轻量级的容器运行时守护进程,如[Gitee][2]中提到的那样,它是为物联网和云基础设施设计的。
|
||||
|
||||
另外,官方的[公告][10]提到,这些系统是在华为云上通过脚本自动化构建的。这确实十分有趣。
|
||||
|
||||
### 下载 openEuler
|
||||
|
||||
![][11]
|
||||
|
||||
到目前为止,你找不到它的英文文档,所以你必须等待或选择通过[文档][12]帮助他们。
|
||||
|
||||
你可以直接从它的[官方网站][13]下载 ISO 来测试它:
|
||||
|
||||
[下载 openEuler ][13]
|
||||
|
||||
### 你认为华为的 openEuler 怎么样?
|
||||
|
||||
据 cnTechPost 报道,华为曾宣布 EulerOS 将以新名字 openEuler 成为开源软件。
|
||||
|
||||
目前还不清楚 openEuler 是否会取代 EulerOS ,或者两者会像 CentOS (社区版)和 Red Hat (商业版)一样同时存在。
|
||||
|
||||
我还没有测试过它,所以我不能说 openEuler 是否适合英文用户。
|
||||
|
||||
你愿意试一试吗?如果你已经尝试过了,欢迎在下面的评论中告诉我你的体验。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/openeuler/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[qianmingtian][c]
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[c]: (https://github.com/qianmingtian)
|
||||
[1]: https://openeuler.org/en/
|
||||
[2]: https://gitee.com/openeuler
|
||||
[3]: https://itsfoss.com/github-alternatives/
|
||||
[4]: https://gitee.com/src-openeuler
|
||||
[5]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/01/openEuler-website.jpg?ssl=1
|
||||
[6]: https://www.centos.org/
|
||||
[7]: https://developer.huaweicloud.com/en-us/euleros/euleros-introduction.html
|
||||
[8]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2020/01/openeuler-gitee.jpg?ssl=1
|
||||
[9]: https://gitee.com/openeuler/iSulad
|
||||
[10]: https://openeuler.org/en/news/20200101.html
|
||||
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2020/01/openEuler.jpg?ssl=1
|
||||
[12]: https://gitee.com/openeuler/docs
|
||||
[13]: https://openeuler.org/en/download.html
|
@ -1,401 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Simulate gravity in your Python game)
|
||||
[#]: via: (https://opensource.com/article/19/11/simulate-gravity-python)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
在你的 Python 游戏中模拟引力
|
||||
======
|
||||
学习如何使用 Python 的 Pygame 模块编程电脑游戏,并开始操作引力。
|
||||
![Cosmic stars in outer space][1]
|
||||
|
||||
真实的世界充满了运动和生活。物理学使得真实的生活如此忙碌和动态。物理学是物质在空间中运动的方式。既然一个电脑游戏世界没有物质,它也就没有物理学规律,使用游戏程序员不得不 _模拟_ 物理学。
|
||||
|
||||
从大多数电脑游戏来说,这里基本上仅有物理学的两个方向是重要的:引力和碰撞。
|
||||
|
||||
当你[添加一个敌人][2]到你的游戏中时,你实现了一下碰撞检测,但是这篇文章添加更多的东西,因为引力需要碰撞检测。想想为什么引力可能涉及碰撞。如果你不能想到任何原因,不要担心—当你通过示例代码工作时,它将变得明显。
|
||||
|
||||
引力在真实世界中的是有质量的物体来相互吸引的趋势。物体(质量)越大,它施加越大的引力作用。在电脑游戏物理学中,你不必创建质量足够大的物体来证明引力的正确;你可以在电脑游戏世界本身中仅编程一个物体落向假设的最大的对象的倾向。
|
||||
|
||||
### 添加一个引力函数
|
||||
|
||||
记住你的玩家已经有一个属性来决定动作。使用这个属性来将玩家精灵拉向屏幕底部。
|
||||
|
||||
在 Pygame 中,较高的数字更接近屏幕的底部边缘。
|
||||
|
||||
在真实的世界中,引力影响一切。然而,在平台中,引力是有选择性的—如果你添加引力到你的整个游戏世界,你所有平台都将掉到地上。作为替换,你仅添加引力到你的玩家和敌人精灵中。
|
||||
|
||||
首先,在你的 **Player** 类中添加一个 **引力** 函数:
|
||||
|
||||
|
||||
```
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # how fast player falls
|
||||
```
|
||||
|
||||
这是一个简单的函数。首先,不管你的玩家是否想运动,你设置你的玩家垂直运动。也就是说,你已经编程你的玩家总是在下降。这基本上就是引力。
|
||||
|
||||
为使引力函数生效,你必需在你的主循环中调用它。这样,当每一个时钟滴答作响时,Python 应用下落运动到你玩家。
|
||||
|
||||
在这代码中,添加第一行到你的循环中:
|
||||
|
||||
|
||||
```
|
||||
player.gravity() # check gravity
|
||||
player.update()
|
||||
```
|
||||
|
||||
启动你的游戏来看看会发生什么。注意,因为它快速地发生:你是玩家从天空上下落,恰好从你的游戏屏幕落下。
|
||||
|
||||
你的引力模拟是工作的,但是,也许太好了。
|
||||
|
||||
作为一次试验,尝试更改你玩家下落的速度。
|
||||
|
||||
### 添加一个地板到引力
|
||||
|
||||
你的游戏没有办法发现你的角色掉落出世界的问题。在一些游戏中,如果一个玩家掉落出世界,该精灵被删除,并在新的位置重生。在其它的游戏中,玩家丢失分数或一条生命。当一个玩家掉落出世界时,不管你想发生什么,你必需能够侦测出玩家何时消失在屏幕外。
|
||||
|
||||
在 Python 中,要检查一个条件,你可以使用一个 **if** 语句。
|
||||
|
||||
你必需查看你玩家**是否**正在掉落,以及你的玩家掉落到什么程度。如果你的玩家掉落到屏幕的底部,那么你可以做 _一些事情_ 。 为保持事情简单,设置玩家精灵的位置为底部边缘上方20像素。
|
||||
|
||||
使你的 **引力** 函数看起来像这样:
|
||||
|
||||
|
||||
```
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # how fast player falls
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty
|
||||
```
|
||||
|
||||
然后,启动你的游戏。你的精灵仍然下落,但是它停在屏幕的底部。不过,你也许不能 _看到_ 你在地面层下的精灵。一个简单的解决方法是,在精灵碰撞游戏世界的底部后,通过添加另一个 **-ty** 到它的新 Y 位置,从而使你的精灵弹跳更高:
|
||||
|
||||
|
||||
```
|
||||
def gravity(self):
|
||||
self.movey += 3.2 # how fast player falls
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
```
|
||||
|
||||
现在你的玩家在屏幕底部弹跳,恰好在你地面层精灵的后面。
|
||||
|
||||
你的玩家真正需要的是反抗引力的方法。引力问题是,你不能反抗它,除非你有一些东西来推开引力作用。因此,在接下来的文章中,你将添加地面和平台碰撞以及跳跃能力。在这期间,尝试应用引力到敌人精灵。
|
||||
|
||||
|
||||
到目前为止,这里是全部的代码:
|
||||
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# draw a world
|
||||
# add a player and player control
|
||||
# add player movement
|
||||
# add enemy and basic collision
|
||||
# add platform
|
||||
# add gravity
|
||||
|
||||
# GNU All-Permissive License
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without any warranty.
|
||||
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
|
||||
'''
|
||||
Objects
|
||||
'''
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x location, y location, img width, img height, img file
|
||||
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.rect = self.image.get_rect()
|
||||
self.rect.y = yloc
|
||||
self.rect.x = xloc
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn a player
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.movex = 0
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.health = 10
|
||||
self.score = 1
|
||||
self.images = []
|
||||
for i in range(1,9):
|
||||
img = pygame.image.load(os.path.join('images','hero' + 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):
|
||||
self.movey += 3.2 # how fast player falls
|
||||
|
||||
if self.rect.y > worldy and self.movey >= 0:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
|
||||
def control(self,x,y):
|
||||
'''
|
||||
control player movement
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
Update sprite position
|
||||
'''
|
||||
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
|
||||
# moving left
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# moving right
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
|
||||
# collisions
|
||||
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
for enemy in enemy_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn an enemy
|
||||
'''
|
||||
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):
|
||||
'''
|
||||
enemy movement
|
||||
'''
|
||||
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
|
||||
|
||||
class Level():
|
||||
def bad(lvl,eloc):
|
||||
if lvl == 1:
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
|
||||
enemy_list = pygame.sprite.Group() # create enemy group
|
||||
enemy_list.add(enemy) # add enemy to group
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return enemy_list
|
||||
|
||||
def loot(lvl,lloc):
|
||||
print(lvl)
|
||||
|
||||
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,'ground.png')
|
||||
ground_list.add(ground)
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
|
||||
def platform(lvl,tx,ty):
|
||||
plat_list = pygame.sprite.Group()
|
||||
ploc = []
|
||||
i=0
|
||||
if lvl == 1:
|
||||
ploc.append((0,worldy-ty-128,3))
|
||||
ploc.append((300,worldy-ty-256,3))
|
||||
ploc.append((500,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,'ground.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
|
||||
|
||||
'''
|
||||
Setup
|
||||
'''
|
||||
worldx = 960
|
||||
worldy = 720
|
||||
|
||||
fps = 40 # frame rate
|
||||
ani = 4 # animation cycles
|
||||
clock = pygame.time.Clock()
|
||||
pygame.init()
|
||||
main = True
|
||||
|
||||
BLUE = (25,25,200)
|
||||
BLACK = (23,23,23 )
|
||||
WHITE = (254,254,254)
|
||||
ALPHA = (0,255,0)
|
||||
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = world.get_rect()
|
||||
player = Player() # spawn player
|
||||
player.rect.x = 0
|
||||
player.rect.y = 0
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
steps = 10 # how fast to move
|
||||
|
||||
eloc = []
|
||||
eloc = [200,20]
|
||||
gloc = []
|
||||
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
|
||||
tx = 64 #tile size
|
||||
ty = 64 #tile size
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
gloc.append(i*tx)
|
||||
i=i+1
|
||||
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
plat_list = Level.platform( 1,tx,ty )
|
||||
|
||||
'''
|
||||
Main loop
|
||||
'''
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit(); sys.exit()
|
||||
main = False
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
print("LEFT")
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
print("RIGHT")
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
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'):
|
||||
print('jump')
|
||||
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.gravity() # check gravity
|
||||
player.update()
|
||||
player_list.draw(world)
|
||||
enemy_list.draw(world)
|
||||
ground_list.draw(world)
|
||||
plat_list.draw(world)
|
||||
for e in enemy_list:
|
||||
e.move()
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
* * *
|
||||
|
||||
这是仍在进行中的关于使用 [Pygame][4] 模块来在 [Python 3][3] 在创建电脑游戏的第七部分。先前的文章是:
|
||||
|
||||
* [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程][5]
|
||||
* [使用 Python 和 Pygame 模块构建一个游戏框架][6]
|
||||
* [如何在你的 Python 游戏中添加一个玩家][7]
|
||||
* [用 Pygame 使你的游戏角色移动起来][8]
|
||||
* [如何向你的 Python 游戏中添加一个敌人][2]
|
||||
* [在 Pygame 游戏中放置平台][9]
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/11/simulate-gravity-python
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/space_stars_cosmic.jpg?itok=bE94WtN- (Cosmic stars in outer space)
|
||||
[2]: https://opensource.com/article/18/5/pygame-enemy
|
||||
[3]: https://www.python.org/
|
||||
[4]: https://www.pygame.org
|
||||
[5]: https://opensource.com/article/17/10/python-101
|
||||
[6]: https://opensource.com/article/17/12/game-framework-python
|
||||
[7]: https://opensource.com/article/17/12/game-python-add-a-player
|
||||
[8]: https://opensource.com/article/17/12/game-python-moving-player
|
||||
[9]: https://opensource.com/article/18/7/put-platforms-python-game
|
@ -7,28 +7,27 @@
|
||||
[#]: via: (https://opensource.com/article/19/12/jumping-python-platformer-game)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
Add jumping to your Python platformer game
|
||||
为你的 Python 平台类游戏添加跳跃功能
|
||||
======
|
||||
Learn how to fight gravity with jumping in this installment on
|
||||
programming video games with Python's Pygame module.
|
||||
![Arcade games][1]
|
||||
在本期使用 Python Pygame 模块编写视频游戏中,学会如何使用跳跃来对抗重力。
|
||||
![游戏厅中的游戏][1]
|
||||
|
||||
In the [previous article][2] in this series, you simulated gravity, but now you need to give your player a way to fight against gravity by jumping.
|
||||
在本系列的 [前一篇文章][2] 中,你已经模拟了重力。但现在,你需要赋予你的角色跳跃的能力来对抗重力。
|
||||
|
||||
A jump is a temporary reprieve from gravity. For a few moments, you jump _up_ instead of falling down, the way gravity is pulling you. But once you hit the peak of your jump, gravity kicks in again and pulls you back down to earth.
|
||||
跳跃是对重力作用的暂时延缓。在这一小段时间里,你是向_上_跳,而不是被重力拉着向下落。但你一旦到达了跳跃的最高点,重力就会重新发挥作用,将你拉回地面。
|
||||
|
||||
In code, this translates to variables. First, you must establish variables for the player sprite so that Python can track whether or not the sprite is jumping. Once the player sprite is jumping, then gravity is applied to the player sprite again, pulling it back down to the nearest object.
|
||||
在代码中,跳跃被表示为变量。首先,你需要为玩家对象建立一个变量,使得 Python 能够跟踪对象是否正在跳跃中。一旦玩家对象开始跳跃,他就会再次受到重力的作用,并被拉回最近的物体。
|
||||
|
||||
### Setting jump state variables
|
||||
### 设置跳跃状态变量
|
||||
|
||||
You must add two new variables to your Player class:
|
||||
你需要为你的 Player 类添加两个新变量:
|
||||
|
||||
* One to track whether your player is jumping or not, determined by whether or not your player sprite is standing on solid ground
|
||||
* One to bring the player back down to the ground
|
||||
* 一个是为了跟踪你的角色是否正在跳跃中,可通过你的玩家对象是否站在坚实的地面来确定
|
||||
* 一个是为了将玩家带回地面
|
||||
|
||||
|
||||
|
||||
Add these variables to your **Player** class. In the following code, the lines above the comment are for context, so just add the final two lines:
|
||||
将如下两个变量添加到你的 **Player** 类中。在下方的代码中,注释前的部分用于提示上下文,因此只需要添加最后两行:
|
||||
|
||||
|
||||
```
|
||||
@ -36,20 +35,20 @@ Add these variables to your **Player** class. In the following code, the lines a
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.health = 10
|
||||
# gravity variables here
|
||||
# 此处是重力相关变量
|
||||
self.collide_delta = 0
|
||||
self.jump_delta = 6
|
||||
```
|
||||
|
||||
The first variable (**collide_delta**) is set to 0 because, in its natural state, the player sprite is not in a mid-jump. The other variable (**jump_delta**) is set to 6 to prevent the sprite from bouncing (actually, jumping) when it first lands in the game world. When you've finished this article's examples, try setting it to 0 to see what happens.
|
||||
第一个变量 **collide_delta** 被设为 0 是因为在正常状态下,玩家对象没有处在跳跃中的状态。另一个变量 **jump_delta** 被设为 6,是为了防止对象在第一次进入游戏世界时就发生反弹(实际上就是跳跃)。当你完成了本篇文章的示例,尝试把该变量设为 0 看看会发生什么。
|
||||
|
||||
### Colliding mid-jump
|
||||
### 跳跃中的碰撞
|
||||
|
||||
If you jump on a trampoline, your jumps are pretty impressive. But what would happen if you jumped into a wall? (Don't try it to find out!) Your jump, no matter how impressively it started, would end very quickly when you collide with something much larger and much more solid than you.
|
||||
如果你是跳到一个蹦床上,那你的跳跃一定非常优美。但是如果你是跳向一面墙会发生什么呢?(千万不要去尝试!)不管你的起跳多么令人印象深刻,当你撞到比你更大更硬的物体时,你都会立马停下。(译注:原理参考动量守恒定律)
|
||||
|
||||
To mimic this in your video game, you must set the **self.collide_delta** variable to 0 whenever your player sprite collides with something, like the ground. If **self.collide_delta** is anything other than 0, then your player is jumping, and your player can't jump when it hits a wall or the ground.
|
||||
为了在你的视频游戏中模拟这一点,你需要在你的玩家对象与地面等东西发生碰撞时,将 **self.collide_delta** 变量设为 0。如果你的 **self.collide_delta** 不是 0 而是其它的什么值,那么你的玩家就会发生跳跃,并且当你的玩家与墙或者地面发生碰撞时无法跳跃。
|
||||
|
||||
In the **update** function of your **Player** class, modify the ground collision block to look like this:
|
||||
在你的 **Player** 类的 **update** 方法中,将地面碰撞相关代码块修改为如下所示:
|
||||
|
||||
|
||||
```
|
||||
@ -57,54 +56,54 @@ In the **update** function of your **Player** class, modify the ground collision
|
||||
for g in ground_hit_list:
|
||||
self.movey = 0
|
||||
self.rect.y = worldy-ty-ty
|
||||
self.collide_delta = 0 # stop jumping
|
||||
self.collide_delta = 0 # 停止跳跃
|
||||
if self.rect.y > g.rect.y:
|
||||
self.health -=1
|
||||
print(self.health)
|
||||
```
|
||||
|
||||
This code block checks for collisions happening between the ground sprites and the player sprite. In the event of a collision, it sets the Y-position of the player to a value equal to the height of the game window (**worldy**) minus the height of a tile minus the height of another tile (so that the player sprite appears to be standing on top of the ground and not in the middle of it). It also sets **self.collide_delta** to 0 so that the program is aware that the player is not in mid-jump. Additionally, it sets **self.movey** to 0 so that the program is aware that the player is not being pulled by gravity (it's a quirk of game physics that you don't need to continue to pull your player toward earth once the player has been grounded).
|
||||
这段代码块检查了地面对象和玩家对象之间发生的碰撞。当发生碰撞时,它会将玩家 Y 方向的坐标值设置为游戏窗口的高度减去一个瓷贴的高度再减去另一个瓷贴的高度。以此保证了玩家对象是站在地面**上**,而不是嵌在地面里。同时它也将 **self.collide_delta** 设为 0,使得程序能够知道玩家未处在跳跃中。除此之外,它将 **self.movey** 设为 0,使得程序能够知道玩家当前未受到重力的牵引作用(这是游戏物理引擎的奇怪之处,一旦玩家落地,也就没有必要继续将玩家拉向地面)。
|
||||
|
||||
The **if** statement detects whether the player has descended _below_ the level of the ground; if so, it deducts health points as a penalty. This assumes that you want your player to lose health points for falling off the world. That's not strictly necessary; it's just a common convention in platformers. More than likely, you want this event to trigger something, though, or else your real-world player will be stuck playing a game with a sprite that's fallen off the screen. An easy recovery is to set **self.rect.y** to 0 again so that when the player sprite falls off the world, it respawns at the top of the world and falls back onto solid ground.
|
||||
此处 **if** 语句用来检测玩家是否已经落到地面之_下_,如果是,那就扣除一点生命值作为惩罚。此处假定了你希望当你的玩家落到地图之外时失去生命值。这个设定不是必需的,它只是平台类游戏的一种惯例。更有可能的是,你希望这个事件能够触发另一些事件,或者说是一种能够让你的现实世界玩家沉迷于让对象掉到屏幕之外的东西。一种简单的恢复方式是在玩家对象掉落到地图之外时,将 **self.rect.y** 重新设置为 0,这样它就会在地图上方重新生成,并落到坚实的地面上。
|
||||
|
||||
### Hitting the ground
|
||||
### 撞向地面
|
||||
|
||||
Your simulated gravity wants your player's Y-axis movement to be 0 or more. To create a jump, write code that sends your player sprite off solid ground and into the air.
|
||||
模拟的重力使你玩家的 Y 坐标不断增大(译注:此处原文中为 0,但在 Pygame 中越靠下方 Y 坐标应越大)。要实现跳跃,完成如下代码使你的玩家对象离开地面,飞向空中。
|
||||
|
||||
In the **update** function of your **Player** class, add a temporary reprieve from gravity:
|
||||
在你的 **Player** 类的 **update** 方法中,添加如下代码来暂时延缓重力的作用:
|
||||
|
||||
|
||||
```
|
||||
if self.collide_delta < 6 and self.jump_delta < 6:
|
||||
self.jump_delta = 6*2
|
||||
self.movey -= 33 # how high to jump
|
||||
self.movey -= 33 # 跳跃的高度
|
||||
self.collide_delta += 6
|
||||
self.jump_delta += 6
|
||||
```
|
||||
|
||||
According to this code, a jump sends the player sprite 33 pixels into the air. It's _negative_ 33 because a lower number in Pygame means it's closer to the top of the screen.
|
||||
根据此代码所示,跳跃使玩家对象向空中移动了 33 个像素。此处是_负_ 33 是因为在 Pygame 中,越小的数代表距离屏幕顶端越近。
|
||||
|
||||
This event is conditional, though. It only happens if **self.collide_delta** is less than 6 (its default value established in the **init** function of your **Player** sprite) and if **self.jump_delta** is less than 6. This condition prevents the player from triggering another jump until it collides with a platform. In other words, it prevents mid-air jumps.
|
||||
不过此事件视条件而定,只有当 **self.collide_delta** 小于 6(缺省值定义在你 **Player** 类的 **init** 方法中)并且 **self.jump_delta** 也于 6 的时候才会发生。此条件能够保证直到玩家碰到一个平台,才能触发另一次跳跃。换言之,它能够阻止空中二段跳。
|
||||
|
||||
You don't have to prevent mid-air jumps, or you can allow for mid-air jumps under special conditions; for instance, if a player obtains a special loot item, then you could grant it the ability to perform mid-air jumps until the next time it is hit by an enemy.
|
||||
在某些特殊条件下,你可能不想阻止空中二段跳,或者说你允许玩家进行空中二段跳。举个栗子,如果玩家获得了某个战利品,那么在他被敌人攻击到之前,都能够拥有空中二段跳的能力。
|
||||
|
||||
When you've finished this article's examples, try setting **self.collide_delta** and **self.jump_delta** to 0 for a 100% chance to jump in mid-air.
|
||||
当你完成本篇文章中的示例,尝试将 **self.collide_delta** 和 **self.jump_delta** 设置为 0,从而获得百分之百的几率触发空中二段跳。
|
||||
|
||||
### Landing on a platform
|
||||
### 在平台上着陆
|
||||
|
||||
So far, you've defined an anti-gravity condition for when the player sprite hits the ground, but the game code keeps platforms and the ground in separate lists. (As with so many choices made in this article, that's not strictly necessary, and you can experiment with treating the ground as just another platform.) To enable a player sprite to stand on top of a platform, you must detect a collision between the player sprite and a platform sprite and then perform the same actions as you did for a ground collision. Place this code into your **update** function:
|
||||
目前你已经定义了再玩家对象摔落地面时的抵抗重力条件,但此时你的游戏代码仍保持平台与地面置于不同的列表中(就像本文中做的很多其他选择一样,这个设定并不是必需的,你可以尝试将地面作为另一种平台)。为了允许玩家对象站在平台之上,你必须像检测地面碰撞一样,检测玩家对象与平台对象之间的碰撞。将如下代码放于你的 **update** 方法中:
|
||||
|
||||
|
||||
```
|
||||
plat_hit_list = pygame.sprite.spritecollide(self, plat_list, False)
|
||||
for p in plat_hit_list:
|
||||
self.collide_delta = 0 # stop jumping
|
||||
self.collide_delta = 0 # 跳跃结束
|
||||
self.movey = 0
|
||||
```
|
||||
|
||||
There's one additional concern, though: platforms hang in the air, meaning the player can interact with them by approaching them from either the top or the bottom.
|
||||
但此处还有一点需要考虑:平台悬在空中,也就意味着玩家可以通过从上面或者从下面接触平台来与之互动。
|
||||
|
||||
It's up to you how you want the platforms to react to your player. It's not uncommon to block a player from accessing a platform from below. Add this code to the above code block to treat platforms as a sort of ceiling or pergola, such that the player sprite can jump onto a platform as long as it jumps higher than the platform's topside, but obstructs the player when it tries to jump from beneath:
|
||||
确定平台如何与玩家互动取决于你,阻止玩家从下方到达平台也并不稀奇。将如下代码加到上方的代码块中,使得平台表现得像天花板或者说是藤架。只有在玩家对象跳得比平台上沿更高时才能跳到平台上,但会阻止玩家从平台下方跳上来:
|
||||
|
||||
|
||||
```
|
||||
@ -114,17 +113,17 @@ It's up to you how you want the platforms to react to your player. It's not unco
|
||||
self.rect.y = p.rect.y-ty
|
||||
```
|
||||
|
||||
The first clause of this **if** statement blocks the player from accessing a platform when the player sprite is directly under the platform. It does this by detecting that the player sprite's position is greater (in Pygame, that means lower on the screen, from top to bottom), and then sets the player sprite's new Y position to its current Y position plus the height of a tile, effectively keeping the player one tile's height away from ever passing through a platform from beneath it.
|
||||
此处 **if** 语句代码块的第一个子句阻止玩家对象从平台正下方跳到平台上。如果它检测到玩家对象的坐标比平台更大(在 Pygame 中,坐标更大意味着在屏幕的更下方),那么将玩家对象新的 Y 坐标设置为当前平台的 Y 坐标加上一个瓷贴的高度。实际效果就是保证玩家对象距离平台一个瓷贴的高度,防止其从下方穿过平台。
|
||||
|
||||
The **else** clause does the opposite. If the program is running this code, then the player sprite's Y position is _not_ greater than the platforms, meaning that the player sprite is falling from the sky (either because it has been freshly spawned there or because the player has jumped). In this event, the player sprite's position is set to the position of the platform minus one tile's height (because, remember, in Pygame, lower numbers mean something is higher up on the screen). This keeps the player on top of the platform unless it jumps or walks off of it.
|
||||
**else** 子句做了相反的事情。当程序运行到此处时,如果玩家对象的 Y 坐标_不_比平台的更大,意味着玩家对象是从空中落下(不论是由于玩家刚刚从此处生成,或者是玩家执行了跳跃)。在这种情况下,玩家对象的 Y 坐标被设为平台的 Y 坐标减去一个瓷贴的高度(切记,在 Pygame 中更小的 Y 坐标代表在屏幕上的更高处)。这样就能保证玩家在平台_上_,除非他从平台上跳下来或者走下来。
|
||||
|
||||
You can experiment with other ways of dealing with sprite and platform interaction. For instance, maybe the player is assumed to be "in front" of the platforms and can seamlessly jump through them to stand on top of one. Or a platform could slow a player's leap but not block it entirely. You can even mix and match by grouping platforms into different lists.
|
||||
你也可以尝试其他的方式来处理玩家与平台之间的互动。举个栗子,也许玩家对象被设定为处在平台的“前面”,他能够无障碍地跳跃穿过平台并站在上面。或者你可以设计一种平台会减缓而又不完全阻止玩家的跳跃过程。甚至你可以通过将不同平台分到不同列表中来混合搭配使用。
|
||||
|
||||
### Triggering a jump
|
||||
### 触发一次跳跃
|
||||
|
||||
Your code now simulates all the necessary jump conditions, but it still lacks a jump trigger. Your player sprite's **self.jump_delta** is set to 6 initially, and the jump update code is triggered only when it's less than 6.
|
||||
目前为此,你的代码已经模拟了所有必需的跳跃条件,但仍缺少一个跳跃触发器。你的玩家对象的 **self.jump_delta** 初始值被设置为 6,只有当它比 6 小时才会触发更新跳跃的代码。
|
||||
|
||||
To trigger a new setting for the jumping variable, create a **jump** function in your **Player** class that sets the **self.jump_delta** to less than 6, causing gravity to be temporarily reprieved by sending your player sprite 33 pixels into the air:
|
||||
为跳跃变量设置一个新的设置方法,在你的 **Player** 类中创建一个 **jump** 方法,并将 **self.jump_delta** 设为小于 6 的值。通过使玩家对象向空中移动 33 个像素,来暂时减缓重力的作用。
|
||||
|
||||
|
||||
```
|
||||
@ -132,17 +131,17 @@ To trigger a new setting for the jumping variable, create a **jump** function in
|
||||
self.jump_delta = 0
|
||||
```
|
||||
|
||||
That's all the **jump** function requires, believe it or not. The rest happens in the **update** function, and you've already written that code.
|
||||
不管你相信与否,这就是 **jump** 方法的全部。剩余的部分在 **update** 方法中,你已经在前面实现了相关代码。
|
||||
|
||||
There's one last thing to do before jumping is functional in your game. If you can't think of what it is, try playing your game to see how jumping works for you.
|
||||
要使你游戏中的跳跃功能生效,还有最后一件事情要做。如果你想不起来是什么,运行游戏并观察跳跃是如何生效的。
|
||||
|
||||
The problem is that nothing in your main loop is calling the **jump** function. You made a placeholder keypress for it early on, but right now, all the jump key does is print **jump** to the terminal.
|
||||
问题就在于你的主循环中没有调用 **jump** 方法。先前你已经为该方法创建了一个按键占位符,现在,跳跃键所做的就是将 **jump** 打印到终端。
|
||||
|
||||
### Calling the jump function
|
||||
### 调用 jump 方法
|
||||
|
||||
In your main loop, change the result of the Up arrow from printing a debug statement to calling the **jump** function.
|
||||
在你的主循环中,将_上_方向键的效果从打印一条调试语句,改为调用 **jump** 方法。
|
||||
|
||||
Notice that the **jump** function, like the **update** function, needs to know about collisions, so you have to tell it which **plat_list** to use.
|
||||
注意此处,与 **update** 方法类似,**jump** 方法也需要检测碰撞,因此你需要告诉它使用哪个 **plat_list**。
|
||||
|
||||
|
||||
```
|
||||
@ -150,13 +149,13 @@ Notice that the **jump** function, like the **update** function, needs to know a
|
||||
player.jump(plat_list)
|
||||
```
|
||||
|
||||
If you would rather use the Spacebar for jumping, set the key to **pygame.K_SPACE** instead of **pygame.K_UP**. Alternately, you can use both (as separate **if** statements) so that the player has a choice.
|
||||
如果你倾向于使用空格键作为跳跃键,使用 **pygame.K_SPACE** 替代 **pygame.K_UP** 作为按键。另一种选择,你可以同时使用两种方式(使用单独的 **if** 语句),给玩家多一种选择。
|
||||
|
||||
Try your game now. In the next article, you'll make your world scroll.
|
||||
现在来尝试你的游戏吧!在下一篇文章中,你将让你的游戏卷动起来。
|
||||
|
||||
![Pygame platformer][3]
|
||||
![Pygame 平台类游戏][3]
|
||||
|
||||
Here's all the code so far:
|
||||
以下是目前为止的所有代码:
|
||||
|
||||
|
||||
```
|
||||
@ -184,7 +183,7 @@ Objects
|
||||
'''
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x location, y location, img width, img height, img file
|
||||
# x 坐标,y 坐标,图像宽度,图像高度,图像文件
|
||||
def __init__(self,xloc,yloc,imgw,imgh,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img)).convert()
|
||||
@ -195,7 +194,7 @@ class Platform(pygame.sprite.Sprite):
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn a player
|
||||
生成一个玩家
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
@ -227,34 +226,34 @@ class Player(pygame.sprite.Sprite):
|
||||
|
||||
def control(self,x,y):
|
||||
'''
|
||||
control player movement
|
||||
控制玩家移动
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
Update sprite position
|
||||
更新对象位置
|
||||
'''
|
||||
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
|
||||
# moving left
|
||||
# 向左移动
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# moving right
|
||||
# 向右移动
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
|
||||
# collisions
|
||||
# 碰撞
|
||||
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
for enemy in enemy_hit_list:
|
||||
self.health -= 1
|
||||
@ -286,7 +285,7 @@ class Player(pygame.sprite.Sprite):
|
||||
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn an enemy
|
||||
生成一个敌人
|
||||
'''
|
||||
def __init__(self,x,y,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
@ -302,7 +301,7 @@ class Enemy(pygame.sprite.Sprite):
|
||||
|
||||
def move(self):
|
||||
'''
|
||||
enemy movement
|
||||
敌人移动
|
||||
'''
|
||||
distance = 80
|
||||
speed = 8
|
||||
@ -337,9 +336,9 @@ class Enemy(pygame.sprite.Sprite):
|
||||
class Level():
|
||||
def bad(lvl,eloc):
|
||||
if lvl == 1:
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
|
||||
enemy_list = pygame.sprite.Group() # create enemy group
|
||||
enemy_list.add(enemy) # add enemy to group
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # 生成敌人
|
||||
enemy_list = pygame.sprite.Group() # 创建敌人组
|
||||
enemy_list.add(enemy) # 将敌人添加到敌人组
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
@ -392,8 +391,8 @@ Setup
|
||||
worldx = 960
|
||||
worldy = 720
|
||||
|
||||
fps = 40 # frame rate
|
||||
ani = 4 # animation cycles
|
||||
fps = 40 # 帧率
|
||||
ani = 4 # 动画循环
|
||||
clock = pygame.time.Clock()
|
||||
pygame.init()
|
||||
main = True
|
||||
@ -406,7 +405,7 @@ ALPHA = (0,255,0)
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = world.get_rect()
|
||||
player = Player() # spawn player
|
||||
player = Player() # 生成玩家
|
||||
player.rect.x = 0
|
||||
player.rect.y = 0
|
||||
player_list = pygame.sprite.Group()
|
||||
@ -418,8 +417,8 @@ eloc = []
|
||||
eloc = [200,20]
|
||||
gloc = []
|
||||
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
|
||||
tx = 64 #tile size
|
||||
ty = 64 #tile size
|
||||
tx = 64 # 瓷贴尺寸
|
||||
ty = 64 # 瓷贴尺寸
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
@ -431,7 +430,7 @@ ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
plat_list = Level.platform( 1,tx,ty )
|
||||
|
||||
'''
|
||||
Main loop
|
||||
主循环
|
||||
'''
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
@ -464,26 +463,26 @@ while main == True:
|
||||
|
||||
# world.fill(BLACK)
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.gravity() # check gravity
|
||||
player.gravity() # 检查重力
|
||||
player.update()
|
||||
player_list.draw(world) #refresh player position
|
||||
enemy_list.draw(world) # refresh enemies
|
||||
ground_list.draw(world) # refresh enemies
|
||||
plat_list.draw(world) # refresh platforms
|
||||
player_list.draw(world) # 刷新玩家位置
|
||||
enemy_list.draw(world) # 刷新敌人
|
||||
ground_list.draw(world) # 刷新地面
|
||||
plat_list.draw(world) # 刷新平台
|
||||
for e in enemy_list:
|
||||
e.move()
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
This is the 7th installment in an ongoing series about creating video games in [Python 3][4] using the [Pygame][5] module. Previous articles are:
|
||||
本期是使用 [Pygame][5] 模块在 [Python 3][4] 中创建视频游戏连载系列的第 7 期。往期文章为:
|
||||
|
||||
* [Learn how to program in Python by building a simple dice game][6]
|
||||
* [Build a game framework with Python using the Pygame module][7]
|
||||
* [How to add a player to your Python game][8]
|
||||
* [Using Pygame to move your game character around][9]
|
||||
* [What's a hero without a villain? How to add one to your Python game][10]
|
||||
* [Simulate gravity in your Python game][2]
|
||||
* [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程][6]
|
||||
* [使用 Python 和 Pygame 模块构建一个游戏框架][7]
|
||||
* [如何在你的 Python 游戏中添加一个玩家][8]
|
||||
* [用 Pygame 使你的游戏角色移动起来][9]
|
||||
* [如何向你的 Python 游戏中添加一个敌人][10]
|
||||
* [在你的 Python 游戏中模拟重力][2]
|
||||
|
||||
|
||||
|
||||
@ -493,7 +492,7 @@ via: https://opensource.com/article/19/12/jumping-python-platformer-game
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
译者:[cycoe](https://github.com/cycoe)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
@ -1,318 +0,0 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lxbwolf)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Lessons learned from programming in Go)
|
||||
[#]: via: (https://opensource.com/article/19/12/go-common-pitfalls)
|
||||
[#]: author: (Eduardo Ferreira https://opensource.com/users/edufgf)
|
||||
|
||||
Go 编程中的经验教训
|
||||
======
|
||||
通过学习如何定位并发处理的陷阱来避免未来处理这些问题时的困境。
|
||||
![Goland gopher illustration][1]
|
||||
|
||||
在复杂的分布式系统进行任务处理时,你通常会需要进行并发的操作。[Mode.net][2] 公司系统每天要处理实时、快速和灵活的以毫秒为单位动态路由数据包的全球专用网络和数据,需要高度并发的系统。这个动态路由是基于网络状态的,而这个过程需要考虑众多因素,我们只考虑关系链的监控。在我们的环境中,调用关系链监控可以是任何跟网络调用关系链有关的状态和当前属性(如链接延迟)。
|
||||
|
||||
### 并发探测链接监控
|
||||
|
||||
[H.A.L.O.][4] (Hop-by-Hop Adaptive Link-State Optimal Routing,译注:逐跳自适应链路状态最佳路由),我们的动态路由算法部分依赖于链路度量来计算路由表。 这些指标由位于每个PoP(译注:存活节点)上的独立组件收集。PoP是表示我们的网络中单个路由实体的机器,通过链路连接并分布在我们的网络拓扑中的各个位置。某个组件使用网络数据包探测周围的机器,周围的机器回复数据包给前者。从接收到的探测包中可以获得链路延迟。由于每个 PoP 都有不止一个临近节点,所以这种探测任务实质上是并发的:我们需要实时测量每个临近连接点的延迟。我们不能串行地处理;为了计算这个指标,必须尽快处理每个探针。
|
||||
|
||||
![latency computation graph][6]
|
||||
|
||||
### 序列号和重置:一个记录场景
|
||||
|
||||
我们的探测组件互相发送和接收数据包并依靠序列号进行数据包处理。旨在避免处理重复的包或顺序被打乱的包。我们的第一个实现依靠特殊的序列号 0 来重置序列号。这个数字仅在组件初始化时使用。主要的问题是我们只考虑了始终从 0 开始递增的序列号。组件重启后,包的顺序可能会重新排列,某个包的序列号可能会轻易地被替换成重置之前使用过的值。这意味着,直到排到重置之前用到的值之前,它后面的包都会被忽略掉。
|
||||
|
||||
### UDP 握手和有限状态机
|
||||
|
||||
这里的问题是重启前后的序列号是否一致。有几种方法可以解决这个问题,经过讨论,我们选择了实现一个带有清晰状态定义的三向交握协议。这个握手过程在初始化时通过链接建立 session。这样可以确保节点通过同一个 session 进行通信且使用了适当的序列号。
|
||||
|
||||
为了正确实现这个过程,我们必须定义一个有清晰状态和过渡的有限状态机。这样我们就可以正确管理握手过程中的所有极端情况。
|
||||
|
||||
![finite state machine diagram][7]
|
||||
|
||||
session ID 由握手的初始化程序生成。一个完整的交换顺序如下:
|
||||
|
||||
1. sender 发送一个 **SYN (ID)** 数据包。
|
||||
2. receiver 存储接收到的 **ID** 并发送一个 **SYN-ACK (ID)**.
|
||||
3. sender 接收到 **SYN-ACK (ID)** _并发送一个 **ACK (ID)**_。它还发送一个从序列号 0 开始的数据包。
|
||||
4. receiver 检查最后接收到的 **ID**,如果 ID 匹配,_则接受 **ACK (ID)**_。它还开始接受序列号为 0 的数据包。
|
||||
|
||||
### 处理状态超时
|
||||
|
||||
基本上,每种状态下你都需要处理最多三种类型的事件:链接事件、数据包事件和超时事件。这些事件会并发地出现,因此你必须正确处理并发。
|
||||
|
||||
* 链接事件包括连接和断开,连接时会初始化一个链接 session,断开时会断开一个已建立的 seesion。
|
||||
* 数据包事件是控制数据包 **(SYN/SYN-ACK/ACK)** 或只是探测响应。
|
||||
* 超时事件在当前 session 状态的预定超时时间到期后触发。
|
||||
|
||||
这里面临的最主要的问题是如何处理并发超时到期和其他事件。这里很容易陷入死锁和资源竞争的陷阱。
|
||||
|
||||
### 第一种方法
|
||||
|
||||
本项目使用的语言是 [Golang][8]. 它确实提供了原生的同步机制,如自带的 channel 和锁,并且能够使用轻量级线程来进行并发处理。
|
||||
|
||||
![gophers hacking together][9]
|
||||
|
||||
gopher 们聚众狂欢
|
||||
|
||||
首先,你可以设计两个分别表示我们的 **Session** 和 **Timeout Handlers** 的结构体。
|
||||
|
||||
```go
|
||||
type Session struct {
|
||||
State SessionState
|
||||
Id SessionId
|
||||
RemoteIp string
|
||||
}
|
||||
|
||||
type TimeoutHandler struct {
|
||||
callback func(Session)
|
||||
session Session
|
||||
duration int
|
||||
timer *timer.Timer
|
||||
}
|
||||
```
|
||||
|
||||
**Session** 标识连接 session,内有表示 session ID、临近的连接点的 IP 和当前 session 状态的字段。
|
||||
|
||||
**TimeoutHandler** 包含回调函数、对应的 session、持续时间和指向调度计时器的 timer 指针。
|
||||
|
||||
每一个临近连接点的 session 都包含一个保存调度 `TimeoutHandler` 的全局 map。
|
||||
|
||||
```
|
||||
`SessionTimeout map[Session]*TimeoutHandler`
|
||||
```
|
||||
|
||||
下面方法注册和取消超时:
|
||||
|
||||
```go
|
||||
// schedules the timeout callback function.
|
||||
func (timeout* TimeoutHandler) Register() {
|
||||
timeout.timer = time.AfterFunc(time.Duration(timeout.duration) * time.Second, func() {
|
||||
timeout.callback(timeout.session)
|
||||
})
|
||||
}
|
||||
|
||||
func (timeout* TimeoutHandler) Cancel() {
|
||||
if timeout.timer == nil {
|
||||
return
|
||||
}
|
||||
timeout.timer.Stop()
|
||||
}
|
||||
```
|
||||
|
||||
你可以使用类似下面的方法来创建和存储超时:
|
||||
|
||||
```go
|
||||
func CreateTimeoutHandler(callback func(Session), session Session, duration int) *TimeoutHandler {
|
||||
if sessionTimeout[session] == nil {
|
||||
sessionTimeout[session] := new(TimeoutHandler)
|
||||
}
|
||||
|
||||
timeout = sessionTimeout[session]
|
||||
timeout.session = session
|
||||
timeout.callback = callback
|
||||
timeout.duration = duration
|
||||
return timeout
|
||||
}
|
||||
```
|
||||
|
||||
超时 handler 创建后,会在经过了设置的 _duration_ 时间(秒)后执行回调函数。然而,有些事件会使你重新调度一个超时 handler(与 **SYN** 状态时的处理一样 — 每 3 秒一次)。
|
||||
|
||||
为此,你可以让回调函数重新调度一次超时:
|
||||
|
||||
```go
|
||||
func synCallback(session Session) {
|
||||
sendSynPacket(session)
|
||||
|
||||
// reschedules the same callback.
|
||||
newTimeout := NewTimeoutHandler(synCallback, session, SYN_TIMEOUT_DURATION)
|
||||
newTimeout.Register()
|
||||
|
||||
sessionTimeout[state] = newTimeout
|
||||
}
|
||||
```
|
||||
|
||||
这次回调在新的超时 handler 中重新调度自己,并更新全局 map **sessionTimeout**。
|
||||
|
||||
### 数据竞争和引用
|
||||
|
||||
你的解决方案已经有了。可以通过检查计时器到期后超时回调是否执行来进行一个简单的测试。为此,注册一个超时,在 *duration* 时间内 sleep,然后检查是否执行了回调的处理。执行这个测试后,最好取消预定的超时时间(因为它会重新调度),这样才不会在下次测试时产生副作用。
|
||||
|
||||
令人惊讶的是,这个简单的测试发现了这个解决方案中的一个 bug。使用 cancel 方法来取消超时并没有正确处理。以下顺序的事件会导致数据资源竞争:
|
||||
|
||||
1. 你有一个已调度的超时 handler。
|
||||
2. 线程 1:
|
||||
a)你接收到一个控制数据包,现在你要取消已注册的超时并切换到下一个 session 状态(如 发送 **SYN** 后接收到一个 **SYN-ACK**)
|
||||
b)你调用了 **timeout.Cancel()**,这个函数调用了 **timer.Stop()**。(请注意,Golang 计时器的 stop 不会终止一个已过期的计时器。)
|
||||
3. 线程 2:
|
||||
a)在调用 cancel 之前,计时器已过期,回调即将执行。
|
||||
b)执行回调,它调度一次新的超时并更新全局 map。
|
||||
4. 线程 1:
|
||||
a)切换到新的 session 状态并注册新的超时,更新全局 map。
|
||||
|
||||
两个线程同时更新超时 map。最终结果是你无法取消注册的超时,然后你也会丢失对线程 2 重新调度的超时的引用。这导致 handler 在一段时间内持续执行和重新调度,出现非预期行为。
|
||||
|
||||
### 锁也解决不了问题
|
||||
|
||||
使用锁也不能完全解决问题。如果你在处理所有事件和执行回调之前加锁,它仍然不能阻止一个过期的回调运行:
|
||||
|
||||
```go
|
||||
func (timeout* TimeoutHandler) Register() {
|
||||
timeout.timer = time.AfterFunc(time.Duration(timeout.duration) * time._Second_, func() {
|
||||
stateLock.Lock()
|
||||
defer stateLock.Unlock()
|
||||
|
||||
timeout.callback(timeout.session)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
现在的区别就是全局 map 的更新是同步的,但是这还是不能阻止在你调用 **timeout.Cancel() ** 后回调的执行 — 这种情况出现在调度计时器过期了但是还没有拿到锁的时候。你还是会丢失一个已注册的超时的引用。
|
||||
|
||||
### 使用取消 channel
|
||||
|
||||
你可以使用取消 channel,而不必依赖不能阻止到期的计时器执行的 golang 函数 **timer.Stop()**。
|
||||
|
||||
这是一个略有不同的方法。现在你可以不用再通过回调进行递归地重新调度;而是注册一个死循环,这个循环接收到取消信号或超时事件时终止。
|
||||
|
||||
新的 **Register()** 产生一个新的 go 协程,这个协程在在超时后执行你的回调,并在前一个超时执行后调度新的超时。返回给调用方一个取消 channel,用来控制循环的终止。
|
||||
|
||||
```go
|
||||
func (timeout *TimeoutHandler) Register() chan struct{} {
|
||||
cancelChan := make(chan struct{})
|
||||
|
||||
go func () {
|
||||
select {
|
||||
case _ = <- cancelChan:
|
||||
return
|
||||
case _ = <- time.AfterFunc(time.Duration(timeout.duration) * time.Second):
|
||||
func () {
|
||||
stateLock.Lock()
|
||||
defer stateLock.Unlock()
|
||||
|
||||
timeout.callback(timeout.session)
|
||||
} ()
|
||||
}
|
||||
} ()
|
||||
|
||||
return cancelChan
|
||||
}
|
||||
|
||||
func (timeout* TimeoutHandler) Cancel() {
|
||||
if timeout.cancelChan == nil {
|
||||
return
|
||||
}
|
||||
timeout.cancelChan <- struct{}{}
|
||||
}
|
||||
```
|
||||
|
||||
这个方法提供了你注册的所有超时的取消 channel。对 cancel 的一次调用向 channel 发送一个空结构体并触发取消操作。然而,这并不能解决前面的问题;可能在你通过 channel 调用 cancel 超时线程还没有拿到锁之前,超时时间就已经到了。
|
||||
|
||||
这里的解决方案是,在拿到锁之后,检查一下超时范围内的取消 channel。
|
||||
|
||||
```go
|
||||
case _ = <- time.AfterFunc(time.Duration(timeout.duration) * time.Second):
|
||||
func () {
|
||||
stateLock.Lock()
|
||||
defer stateLock.Unlock()
|
||||
|
||||
select {
|
||||
case _ = <- handler.cancelChan:
|
||||
return
|
||||
default:
|
||||
timeout.callback(timeout.session)
|
||||
}
|
||||
} ()
|
||||
}
|
||||
```
|
||||
|
||||
最终,这可以确保在拿到锁之后执行回调,不会触发取消操作。
|
||||
|
||||
### 小心死锁
|
||||
|
||||
这个解决方案看起来有效;但是还是有个隐患:[死锁][10]。
|
||||
|
||||
请阅读上面的代码,试着自己找到它。考虑下描述的所有函数的并发调用。
|
||||
|
||||
这里的问题在取消 channel 本身。我们创建的是无缓冲 channel,即发送是阻塞调用。当你在一个超时 handler 中调用取消函数时,只有在该 handler 被取消后才能继续处理。问题出现在,当你有多个调用请求到同一个取消 channel 时,这时一个取消请求只被处理一次。当多个事件同时取消同一个超时 handler 时,如链接断开或控制包事件,很容易出现这种情况。这会导致死锁,可能会使应用程序 halt。
|
||||
|
||||
![gophers on a wire, talking][11]
|
||||
|
||||
有人在听吗?
|
||||
|
||||
已获得 Trevor Forrey 授权。
|
||||
|
||||
这里的解决方案是创建 channel 时指定大小至少为 1,这样向 channel 发送数据就不会阻塞,也显式地使发送变成非阻塞的,避免了并发调用。这样可以确保取消操作只发送一次,并且不会阻塞后续的取消调用。
|
||||
|
||||
```go
|
||||
func (timeout* TimeoutHandler) Cancel() {
|
||||
if timeout.cancelChan == nil {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case timeout.cancelChan <- struct{}{}:
|
||||
default:
|
||||
// can’t send on the channel, someone has already requested the cancellation.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 总结
|
||||
|
||||
在实践中你学到了并发操作时出现的常见错误。由于其不确定性,即使进行大量的测试,也不容易发现这些问题。下面是我们在最初的实现中遇到的三个主要问题:
|
||||
|
||||
#### 在非同步的情况下更新共享数据
|
||||
|
||||
这似乎是个很明显的问题,但如果并发更新发生在不同的位置,就很难发现。结果就是数据竞争,由于一个更新会覆盖另一个,因此对同一数据的多次更新中会有某些更新丢失。在我们的案例中,我们是在同时更新同一个共享 map 里的调度超时引用。有趣的是,(如果 Go 检测到在同一个 map 对象上的并发读写,会抛出 fatal 错误 — 你可以尝试下运行 Go 的[数据竞争检测器](https://golang.org/doc/articles/race_detector.html))。这最终会导致丢失超时引用,且无法取消给定的超时。当有必要时,永远不要忘记使用锁。
|
||||
|
||||
![gopher assembly line][13]
|
||||
|
||||
不要忘记同步 gopher 们的工作
|
||||
|
||||
#### 缺少条件检查
|
||||
|
||||
在不能仅依赖锁的独占性的情况下,就需要进行条件检查。我们遇到的场景稍微有点不一样,但是核心思想跟[条件变量][14]是一样的。假设有个经典的一个生产者和多个消费者使用一个共享队列的场景,生产者可以将一个元素添加到队列并唤醒所有消费者。这个唤醒调用意味着队列中的数据是可访问的,并且由于队列是共享的,消费者必须通过锁来进行同步访问。每个消费者都可能拿到锁;然而,你仍然需要检查队列中是否有元素。因为在你拿到锁的瞬间并不知道队列的状态,所以还是需要进行条件检查。
|
||||
|
||||
在我们的例子中,超时 handler 收到了计时器到期时发出的「唤醒」调用,但是它仍需要检查是否已向其发送了取消信号,然后才能继续执行回调。
|
||||
|
||||
![gopher boot camp][15]
|
||||
|
||||
如果你要唤醒多个 gopher,可能就需要进行条件检查
|
||||
|
||||
#### 死锁
|
||||
|
||||
当一个线程被卡住,无限期地等待一个唤醒信号,但是这个信号永远不会到达时,就会发生这种情况。死锁可以通过让你的整个程序 halt 来彻底杀死你的应用。
|
||||
|
||||
在我们的案例中,这种情况的发生是由于多次发送请求到一个非缓冲且阻塞的 channel。这意味着向 channel 发送数据只有在从这个 channel 接收完数据后才能 return。我们的超时线程循环迅速从取消 channel 接收信号;然而,在接收到第一个信号后,它将跳出循环,并且再也不会从这个 channel 读取数据。其他的调用会一直被卡住。为避免这种情况,你需要仔细检查代码,谨慎处理阻塞调用,并确保不会发生线程饥饿。我们例子中的解决方法是使取消调用成为非阻塞调用 — 我们不需要阻塞调用。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/12/go-common-pitfalls
|
||||
|
||||
作者:[Eduardo Ferreira][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lxbwolf](https://github.com/lxbwolf)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/edufgf
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/go-golang.png?itok=OAW9BXny (Goland gopher illustration)
|
||||
[2]: http://mode.net
|
||||
[3]: https://en.wikipedia.org/wiki/Metrics_%28networking%29
|
||||
[4]: https://people.ece.cornell.edu/atang/pub/15/HALO_ToN.pdf
|
||||
[5]: https://en.wikipedia.org/wiki/Point_of_presence
|
||||
[6]: https://opensource.com/sites/default/files/uploads/image2_0_3.png (latency computation graph)
|
||||
[7]: https://opensource.com/sites/default/files/uploads/image3_0.png (finite state machine diagram)
|
||||
[8]: https://golang.org/
|
||||
[9]: https://opensource.com/sites/default/files/uploads/image4.png (gophers hacking together)
|
||||
[10]: https://en.wikipedia.org/wiki/Deadlock
|
||||
[11]: https://opensource.com/sites/default/files/uploads/image5_0_0.jpg (gophers on a wire, talking)
|
||||
[12]: https://golang.org/doc/articles/race_detector.html
|
||||
[13]: https://opensource.com/sites/default/files/uploads/image6.jpeg (gopher assembly line)
|
||||
[14]: https://en.wikipedia.org/wiki/Monitor_%28synchronization%29#Condition_variables
|
||||
[15]: https://opensource.com/sites/default/files/uploads/image7.png (gopher boot camp)
|
@ -7,21 +7,20 @@
|
||||
[#]: via: (https://opensource.com/article/20/1/python-web-api-pyramid-cornice)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez)
|
||||
|
||||
How to write a Python web API with Pyramid and Cornice
|
||||
如何使用 Pyramid 和 Cornice 编写 Python Web API
|
||||
======
|
||||
Use Pyramid and Cornice to build and document scalable RESTful web
|
||||
services.
|
||||
使用 Pyramid 和 Cornice 构建可扩展的 RESTful Web 服务。
|
||||
![Searching for code][1]
|
||||
|
||||
[Python][2] is a high-level, object-oriented programming language known for its simple syntax. It is consistently among the top-rated programming languages for building RESTful APIs.
|
||||
[Python][2] 是一种高级的,面向对象的编程语言,它以其简单的语法而闻名。它一直是构建 RESTful API 的顶级编程语言之一。
|
||||
|
||||
[Pyramid][3] is a Python web framework designed to scale up with an application: it's simple for simple applications but can grow for big, complex applications. Among other things, Pyramid powers PyPI, the Python package index. [Cornice][4] provides helpers to build and document REST-ish web services with Pyramid.
|
||||
[Pyramid][3] 是一个 Python Web 框架,旨在随着应用的扩展而扩展:这对于简单的应用来说很简单,对于大型、复杂的应用也可以做到。Pyramid 为 PyPI (Python 软件包索引)提供了强大的支持。[Cornice][4] 提供了使用 Pyramid 构建 RESTful Web 服务的助手。
|
||||
|
||||
This article will use the example of a web service to get famous quotes to show how to use these tools.
|
||||
本文将使用 Web 服务的例子来获取名人名言,来展示如何使用这些工具。
|
||||
|
||||
### Set up a Pyramid application
|
||||
### 建立 Pyramid 应用
|
||||
|
||||
Start by creating a virtual environment for your application and a file to hold the code:
|
||||
首先为你的应用创建一个虚拟环境,并创建一个文件来保存代码:
|
||||
|
||||
|
||||
```
|
||||
@ -33,9 +32,9 @@ $ source env/bin/activate
|
||||
(env) $ pip3 install cornice twisted
|
||||
```
|
||||
|
||||
### Import the Cornice and Pyramid modules
|
||||
### 导入 Cornice 和 Pyramid 模块
|
||||
|
||||
Import these modules with:
|
||||
使用以下命令导入这些模块:
|
||||
|
||||
|
||||
```
|
||||
@ -43,9 +42,9 @@ from pyramid.config import Configurator
|
||||
from cornice import Service
|
||||
```
|
||||
|
||||
### Define the service
|
||||
### 定义服务
|
||||
|
||||
Define the quotes service as a **Service** object:
|
||||
将引用服务定义为 **Service** 对象:
|
||||
|
||||
|
||||
```
|
||||
@ -54,9 +53,9 @@ QUOTES = Service(name='quotes',
|
||||
description='Get quotes')
|
||||
```
|
||||
|
||||
### Write the quotes logic
|
||||
### 编写引用逻辑
|
||||
|
||||
So far, this only supports **GET**ing quotes. Decorate the function with **QUOTES.get**; this is how you can tie in the logic to the REST service:
|
||||
到目前为止,这仅支持 **GET** 获取名言。用 **QUOTES.get** 装饰函数。这是将逻辑绑定到 REST 服务的方法:
|
||||
|
||||
|
||||
```
|
||||
@ -73,13 +72,13 @@ def get_quote(request):
|
||||
}
|
||||
```
|
||||
|
||||
Note that unlike in other frameworks, the **get_quote** function is _not_ changed by the decorator. If you import this module, you can still call the function regularly and inspect the result.
|
||||
请注意,与其他框架不同,装饰器_不能_更改 **get_quote** 函数。如果导入此模块,你仍然可以定期调用该函数并检查结果。
|
||||
|
||||
This is useful when writing unit tests for Pyramid RESTful services.
|
||||
在为 Pyramid RESTful 服务编写单元测试时,这很有用。
|
||||
|
||||
### Define the application object
|
||||
### 定义应用对象
|
||||
|
||||
Finally, use **scan** to find all decorated functions and add them to the configuration:
|
||||
最后,使用 **scan** 查找所有修饰的函数并将其添加到配置中:
|
||||
|
||||
|
||||
```
|
||||
@ -89,18 +88,18 @@ with Configurator() as config:
|
||||
application = config.make_wsgi_app()
|
||||
```
|
||||
|
||||
The default for scan is to scan the current module. You can also give the name of a package if you want to scan all modules in a package.
|
||||
默认扫描当前模块。如果要扫描软件包中的所有模块,你也可以提供软件包的名称。
|
||||
|
||||
### Run the service
|
||||
### 运行服务
|
||||
|
||||
I use Twisted's WSGI server to run the application, but you can use any other [WSGI][5] server, like Gunicorn or uWSGI, if you want:
|
||||
我使用 Twisted 的 WSGI 服务器运行该应用,但是如果需要,你可以使用任何其他 [WSGI][5] 服务器,例如 Gunicorn 或 uWSGI。
|
||||
|
||||
|
||||
```
|
||||
`(env)$ python -m twisted web --wsgi=main.application`
|
||||
```
|
||||
|
||||
By default, Twisted's WSGI server runs on port 8080. You can test the service with [HTTPie][6]:
|
||||
默认情况下,Twisted 的 WSGI 服务器运行在端口 8080 上。你可以使用 [HTTPie][6] 测试该服务:
|
||||
|
||||
|
||||
```
|
||||
@ -129,11 +128,11 @@ X-Content-Type-Options: nosniff
|
||||
}
|
||||
```
|
||||
|
||||
### Why use Pyramid?
|
||||
### 为什么要使用 Pyramid?
|
||||
|
||||
Pyramid is not the most popular framework, but it is used in some high-profile projects like [PyPI][7]. I like Pyramid because it is one of the frameworks that took unit testing seriously: because the decorators do not modify the function and there are no thread-local variables, functions are callable directly from unit tests. For example, functions that need access to the database will get it from the **request** object passed in via **request.config**. This allows a unit tester to put a mock (or real) database object in the request, instead of carefully setting globals, thread-local variables, or other framework-specific things.
|
||||
Pyramid 不是最受欢迎的框架,但它已在 [PyPI][7] 等一些引人注目的项目中使用。我喜欢 Pyramid,因为它是认真对待单元测试的框架之一:因为装饰器不会修改函数并且没有线程局部变量,所以可以直接从单元测试中调用函数。例如,需要访问数据库的函数将从通过 **request.config** 传递的 **request.config** 对象中获取它。这允许单元测试人员将模拟(或真实)数据库对象放入请求中,而不用仔细设置全局变量,线程局部变量或其他特定于框架的东西。
|
||||
|
||||
If you're looking for a well-tested library to build your next API, give Pyramid a try. You won't be disappointed.
|
||||
如果你正在寻找一个经过测试的库来构建你接下来的 API,请尝试使用 Pyramid。你不会失望的。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
@ -0,0 +1,153 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Generating numeric sequences with the Linux seq command)
|
||||
[#]: via: (https://www.networkworld.com/article/3511954/generating-numeric-sequences-with-the-linux-seq-command.html)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
使用 Linux seq 命令生成数字序列
|
||||
======
|
||||
Linux seq 命令可以以闪电般的速度生成数字列表。它易于使用而且灵活。
|
||||
[Jamie][1] [(CC BY 2.0)][2]
|
||||
|
||||
在 Linux 中生成数字列表的最简单方法之一是使用 **seq**(sequence)命令。最简单的形式是,**seq** 接收一个数字,并输出从 1 到该数字的列表。例如:
|
||||
|
||||
```
|
||||
$ seq 5
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
```
|
||||
|
||||
除非另有指定,否则 **seq** 始终以 1 开头。你可以在最终数字前面插上不同数字开始。
|
||||
|
||||
```
|
||||
$ seq 3 5
|
||||
3
|
||||
4
|
||||
5
|
||||
```
|
||||
|
||||
### 指定增量
|
||||
|
||||
你还可以指定增量。假设你要列出 3 的倍数。指定起点(在此示例中为第一个 3 ),增量(第二个 3)和终点(18)。
|
||||
|
||||
```
|
||||
$ seq 3 3 18
|
||||
3
|
||||
6
|
||||
9
|
||||
12
|
||||
15
|
||||
18
|
||||
```
|
||||
|
||||
你可以选择使用负增量(即减量)将数字从大变小。
|
||||
|
||||
```
|
||||
$ seq 18 -3 3
|
||||
18
|
||||
15
|
||||
12
|
||||
9
|
||||
6
|
||||
3
|
||||
```
|
||||
|
||||
**seq** 命令也非常快。你或许可以在 10 秒内生成一百万个数字的列表。
|
||||
|
||||
Advertisement
|
||||
|
||||
```
|
||||
$ time seq 1000000
|
||||
1
|
||||
2
|
||||
3
|
||||
…
|
||||
…
|
||||
999998
|
||||
999999
|
||||
1000000
|
||||
|
||||
real 0m9.290s <== 9+ seconds
|
||||
user 0m0.020s
|
||||
sys 0m0.899s
|
||||
```
|
||||
|
||||
## 使用分隔符
|
||||
|
||||
另一个非常有用的选项是使用分隔符。你可以插入逗号,冒号或其他一些字符,而不是在每行上列出单个数字。-s 选项后跟要使用的字符。
|
||||
|
||||
```
|
||||
$ seq -s: 3 3 18
|
||||
3:6:9:12:15:18
|
||||
```
|
||||
|
||||
实际上,如果只是希望将数字列在一行上,那么可以使用空格代替默认的换行符。
|
||||
|
||||
```
|
||||
$ seq -s' ' 3 3 18
|
||||
3 6 9 12 15 18
|
||||
```
|
||||
|
||||
### 开始数学运算
|
||||
|
||||
从生成数字序列到进行数学运算似乎是一个巨大的飞跃,但是有了正确的分隔符,**seq** 可以轻松地传递给 **bc** 进行计算。例如:
|
||||
|
||||
```
|
||||
$ seq -s* 5 | bc
|
||||
120
|
||||
```
|
||||
|
||||
该命令中发生了什么?让我们来看看。首先,**seq** 生成一个数字列表,并使用 \* 作为分隔符。
|
||||
|
||||
```
|
||||
$ seq -s* 5
|
||||
1*2*3*4*5
|
||||
```
|
||||
|
||||
然后,它将字符串传递给计算器 (**bc**),计算器立即将数字相乘。你可以在不到一秒的时间内进行相当广泛的计算。
|
||||
|
||||
```
|
||||
$ time seq -s* 117 | bc
|
||||
39699371608087208954019596294986306477904063601683223011297484643104\
|
||||
22041758630649341780708631240196854767624444057168110272995649603642\
|
||||
560353748940315749184568295424000000000000000000000000000
|
||||
|
||||
real 0m0.003s
|
||||
user 0m0.004s
|
||||
sys 0m0.000s
|
||||
```
|
||||
|
||||
### 局限性
|
||||
|
||||
你只能选择一个分隔符,因此计算将非常有限。单独使用 **bc** 可进行更复杂的数学运算。此外,**seq** 仅适用于数字。要生成单个字母序列,请改用如下命令:
|
||||
|
||||
```
|
||||
$ echo {a..g}
|
||||
a b c d e f g
|
||||
```
|
||||
|
||||
加入 [Facebook][5] 和 [LinkedIn][6] 上的 Network World 社区,评论热门主题。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3511954/generating-numeric-sequences-with-the-linux-seq-command.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://creativecommons.org/licenses/by/2.0/
|
||||
[2]: https://creativecommons.org/licenses/by/2.0/legalcode
|
||||
[5]: https://www.facebook.com/NetworkWorld/
|
||||
[6]: https://www.linkedin.com/company/network-world
|
Loading…
Reference in New Issue
Block a user