齐市北关献彩票3d字谜:文章 – 伯乐在线 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net Fri, 22 Mar 2019 15:31:04 +0000 zh-CN hourly 1 https://wordpress.org/?v=4.5.17 优化 JavaScript 条件语句的5个技巧 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114671/ //www.brhi.net/114671/#respond Fri, 22 Mar 2019 15:27:36 +0000 //www.brhi.net/?p=114671 转译自:https://sco...

优化 JavaScript 条件语句的5个技巧,首发于山西十一选五手机版。

]]>

转译自:https://scotch.io/tutorials/5-tips-to-write-better-conditionals-in-javascript

在使用 JavaScript 时,我们会处理很多条件语句,这里有 5 个技巧可以帮助您编写更好、更简洁的条件语句。

1、对多个条件使用 Array.includes

让我们看看下面的例子:

// condition
function test(fruit) {
  if (fruit == 'apple' || fruit == 'strawberry') {
    console.log('red');
  }
}

乍一看,上面的例子看起来不错。然而,如果还有更多红颜色的水果需要判断呢,比如樱桃和小红莓,我们要用更多的 || 来扩展这个表述吗?

我们可以用 Array.includes 重写上面的条件

function test(fruit) {
  // extract conditions to array
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  if (redFruits.includes(fruit)) {
    console.log('red');
  }
}

我们将红色水果(条件)提取到一个数组中。这样做之后,代码看起来更整洁。

2、更少的嵌套,尽早返回

让我们扩展前面的示例,以包含另外两个条件:

  • 如果没有提供水果(名称),抛出错误。
  • 如果(红色水果)数量超过 10 个,接受并打印。

看看上面的代码,我们有:

  • 1 组过滤无效条件的 if/else 语句
  • 3层的 if 嵌套语句(条件 1、2 和 3)

我个人遵循的一般规则是,当发现无效条件时,提前返回。

/_ return early when invalid conditions found _/

function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  // condition 1: throw error early
  if (!fruit) throw new Error('No fruit!');

  // condition 2: must be red
  if (redFruits.includes(fruit)) {
    console.log('red');

    // condition 3: must be big quantity
    if (quantity > 10) {
      console.log('big quantity');
    }
  }
}

这样,我们就少了一层嵌套。这种编码风格很好,尤其是当你有很长的 if 语句时(想象一下,你需要滚动到最底部才能知道还有一个 else 语句,这并不酷)。

通过反转条件和提早返回,我们可以进一步减少嵌套??纯聪旅娴奶跫?2,我们是怎么做的:

/_ return early when invalid conditions found _/

function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  if (!fruit) throw new Error('No fruit!'); // condition 1: throw error early
  if (!redFruits.includes(fruit)) return; // condition 2: stop when fruit is not red

  console.log('red');

  // condition 3: must be big quantity
  if (quantity > 10) {
    console.log('big quantity');
  }
}

通过反转条件 2 的条件,我们的代码现在没有嵌套语句。当我们有很长的逻辑要处理时,这种技术是有用的,当一个条件没有满足时,我们想要停止进一步的处理。

然而,这并不是严格的规则。问问自己,这个版本(没有嵌套)是否比前一个版本(嵌套的条件 2)更好、更易读?

对于我来说,我将把它保留为以前的版本(条件 2 和嵌套)。这是因为:

  • 代码简短而直接,如果嵌套,代码就更清晰了
  • 反转条件可能会导致更多的思考过程(增加认知负担)

因此,总是以更少的嵌套及尽早返回为目标,但不要过度。如果你感兴趣的话,StackOverflow 有一篇相关的文章讨论了这个话题:

3、使用默认的函数参数和解构

我想下面的代码对您来说可能很熟悉,我们在使用 JavaScript 时总是需要检查 null 或 undefined 值并分配默认值:

function test(fruit, quantity) {
  if (!fruit) return;
  const q = quantity || 1; // if quantity not provided, default to one

  console.log(`We have ${q} ${fruit}!`);
}

//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

事实上,我们可以通过指定默认的函数参数来消除变量 q。

function test(fruit, quantity = 1) { // if quantity not provided, default to one
  if (!fruit) return;
  console.log(`We have ${quantity} ${fruit}!`);
}

//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

更简单和直观,不是吗?请注意,每个参数都可以有自己的默认函数参数。例如,我们也可以为 fruit 赋值:function test(fruit = 'unknown', quantity = 1)。

如果我们的 fruit 是一个对象呢?我们可以指定默认参数吗?

function test(fruit) {
  // printing fruit name if value provided
  if (fruit && fruit.name)  {
    console.log (fruit.name);
  } else {
    console.log('unknown');
  }
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

请看上面的示例,如果 fruit.name 是可用的,我们将打印该水果名称,否则我们将打印 unknown。我们可以避免使用与默认函数参数和解构对条件 fruit && fruit.name 进行检查。

// destructing - get name property only
// assign default empty object {}
function test({name} = {}) {
  console.log (name || 'unknown');
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

因为我们只需要水果中的属性 name,所以我们可以使用 {name} 来解构,然后我们可以在代码中使用 name 作为变量,而不是 fruit.name。

我们还将空对象 {} 指定为默认值。如果我们不这样做,当执行 test(undefined),不能解构 undefinednull 的属性名时,您将会得到错误。因为在 undefined中没有 name 属性。

如果您不介意使用第三方库,有一些方法可以减少 null 检查:

  • 使用 Lodash 的 get 函数
  • 使用 Facebook 的开源库 idx(以及 Babeljs)

这是使用 Lodash 的例子:

// Include lodash library, you will get _
function test(fruit) {
  console.log(__.get(fruit, 'name', 'unknown'); // get property name, if not available, assign default value 'unknown'
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

您可以在 这里 运行演示代码。此外,如果你喜欢函数式编程(FP),你可以选择使用 Lodash fp, 即 Lodash 的函数式版本(方法改为 getgetOr)。

4、选择 Map 或对象字面量,而不是 Switch 语句

让我们看看下面的例子,我们想要基于颜色打印水果名称:

function test(color) {
  // use switch case to find fruits in color
  switch (color) {
    case 'red':
      return ['apple', 'strawberry'];
    case 'yellow':
      return ['banana', 'pineapple'];
    case 'purple':
      return ['grape', 'plum'];
    default:
      return [];
  }
}

//test results
test(null); // []
test('yellow'); // ['banana', 'pineapple']

上面的代码似乎没有什么问题,但我发现它相当冗长。同样的结果可以通过对象字面量和更简洁的语法来实现:

// use object literal to find fruits in color
  const fruitColor = {
    red: ['apple', 'strawberry'],
    yellow: ['banana', 'pineapple'],
    purple: ['grape', 'plum']
  };

function test(color) {
  return fruitColor[color] || [];
}

或者,可以使用 Map 来实现相同的结果:

// use Map to find fruits in color
  const fruitColor = new Map()
    .set('red', ['apple', 'strawberry'])
    .set('yellow', ['banana', 'pineapple'])
    .set('purple', ['grape', 'plum']);

function test(color) {
  return fruitColor.get(color) || [];
}

Map 是 ES2015 以后可用的对象类型,允许您存储键值对。

我们应该禁止使用 switch 语句吗?不要把自己局限于此。就我个人而言,我尽可能地使用对象字面量,但是我不会设置严格的规则来阻止它,使用对您的场景有意义的任何一个。

Todd Motto 有一篇文章深入讨论 switch 语句与对象字面量,你可以在 这里 阅读。

TL;DR; 重构的语法

对于上面的示例,我们实际上可以重构代码,以使用 Array.filter 获得相同的结果。

const fruits = [
   { name: 'apple', color: 'red' },
   { name: 'strawberry', color: 'red' },
   { name: 'banana', color: 'yellow' },
   { name: 'pineapple', color: 'yellow' },
   { name: 'grape', color: 'purple' },
   { name: 'plum', color: 'purple' }
];

function test(color) {
 // use Array filter to find fruits in color

 return fruits.filter(f => f.color == color);
}

总有不止一种方法可以达到同样的效果。我们展示了 4 个相同效果的例子。编码是有趣的!

5、所有或部分使用 Array.every & Array.some 的条件

最后一个技巧是关于使用新的(但不是很新)Javascript 数组函数来减少代码行??纯聪旅娴拇?,我们想检查所有的水果是否都是红色的:

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];

function test() {
  let isAllRed = true;

  // condition: all fruits must be red
  for (let f of fruits) {
    if (!isAllRed) break;
    isAllRed = (f.color == 'red');
  }

  console.log(isAllRed); // false
}

代码太长了!我们可以用 Array.every 来减少行数:

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];

function test() {
  // condition: short way, all fruits must be red
  const isAllRed = fruits.every(f => f.color == 'red');

  console.log(isAllRed); // false
}

现在干净多了,对吧?类似地,如果我们想用一行代码来判断任何一个水果是否为红色,我们可以使用 Array.some。

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
];

function test() {
  // condition: if any fruit is red
  const isAnyRed = fruits.some(f => f.color == 'red');

  console.log(isAnyRed); // true
}

总结

让我们一起生成更多可读的代码。我希望你能从这篇文章中学到一些新的东西。

这就是本文的全部内容~ 编码快乐!

优化 JavaScript 条件语句的5个技巧,首发于山西十一选五手机版。

]]>
//www.brhi.net/114671/feed/ 0
5 个好用的开发者 Vim 插件 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114666/ //www.brhi.net/114666/#respond Sun, 24 Feb 2019 14:27:00 +0000 //www.brhi.net/?p=114666 通过这 5 个插件扩展 Vim 功能来提升你的编码效率。

5 个好用的开发者 Vim 插件,首发于山西十一选五手机版。

]]>

通过这 5 个插件扩展 Vim 功能来提升你的编码效率。

我用 Vim 已经超过 20 年了,两年前我决定把它作为我的首要文本编辑器。我用 Vim 来编写代码、配置文件、博客文章及其它任意可以用纯文本表达的东西。Vim 有很多超级棒的功能,一旦你适合了它,你的工作会变得非常高效。

在日常编辑工作中,我更倾向于使用 Vim 稳定的原生功能,但开源社区对 Vim 开发了大量的插件,可以扩展 Vim 的功能、改进你的工作流程和提升工作效率。

以下列举 5 个非常好用的可以用于编写任意编程语言的插件。

1、Auto Pairs

Auto Pairs 插件可以帮助你插入和删除成对的文字,如花括号、圆括号或引号。这在编写代码时非常有用,因为很多编程语言都有成对标记的语法,就像圆括号用于函数调用,或引号用于字符串定义。

Auto Pairs 最基本的功能是在你输入一个左括号时会自动补全对应的另一半括号。比如,你输入了一个 [,它会自动帮你补充另一半 ]。相反,如果你用退格键删除开头的一半括号,Auto Pairs 会删除另一半。

如果你设置了自动缩进,当你按下回车键时 Auto Pairs 会在恰当的缩进位置补全另一半括号,这比你找到放置另一半的位置并选择一个正确的括号要省劲多了。

例如下面这段代码:

package main

import "fmt"

func main() {
    x := true
    items := []string{"tv", "pc", "tablet"}

    if x { 
        for _, i := range items
    } 
}

items 后面输入一个左花括号按下回车会产生下面的结果:

package main

import "fmt"

func main() {
    x := true
    items := []string{"tv", "pc", "tablet"}

    if x {
        for _, i := range items  {
            | (cursor here)
        }
    }
}

Auto Pairs 提供了大量其它选项(你可以在 GitHub 上找到),但最基本的功能已经很让人省时间了。

2、NERD Commenter

NERD Commenter 插件给 Vim 增加了代码注释的功能,类似在 IDEintegrated development environment 中注释功能。有了这个插件,你可以一键注释单行或多行代码。

NERD Commenter 可以与标准的 Vim filetype 插件配合,所以它能理解一些编程语言并使用合适的方式来注释代码。

最易上手的方法是按 Leader+Space 组合键来切换注释当前行。Vim 默认的 Leader 键是 \。

在可视化模式Visual mode中,你可以选择多行一并注释。NERD Commenter 也可以按计数注释,所以你可以加个数量 n 来注释 n 行。

还有个有用的特性 “Sexy Comment” 可以用 Leader+cs 来触发,它的块注释风格更漂亮一些。例如下面这段代码:

package main

import "fmt"

func main() {
    x := true
    items := []string{"tv", "pc", "tablet"}

    if x {
        for _, i := range items {
            fmt.Println(i)
        }
    }
}

选择 main 函数中的所有行然后按下 Leader+cs 会出来以下注释效果:

package main

import "fmt"

func main() {
/*
 *    x := true
 *    items := []string{"tv", "pc", "tablet"}
 *
 *    if x {
 *        for _, i := range items {
 *            fmt.Println(i)
 *        }
 *    }
 */
}

因为这些行都是在一个块中注释的,你可以用 Leader+Space 组合键一次去掉这里所有的注释。

NERD Commenter 是任何使用 Vim 写代码的开发者都必装的插件。

3、VIM Surround

Vim Surround 插件可以帮你“环绕”现有文本插入成对的符号(如括号或双引号)或标签(如 HTML 或 XML 标签)。它和 Auto Pairs 有点儿类似,但是用于处理已有文本,在编辑文本时更有用。

比如你有以下一个句子:

"Vim plugins are awesome !"

当你的光标处于引起来的句中任何位置时,你可以用 ds" 组合键删除句子两端的双引号。

Vim plugins are awesome !

你也可以用 cs"' 把双端的双引号换成单引号:

'Vim plugins are awesome !'

或者再用 cs'[ 替换成中括号:

[ Vim plugins are awesome ! ]

它对编辑 HTML 或 XML 文本中的标签tag尤其在行。假如你有以下一行 HTML 代码:

<p>Vim plugins are awesome !</p>

当光标在 “awesome” 这个单词的任何位置时,你可以按 ysiw<em>?直接给它加上着重标签(<em>):

<p>Vim plugins are <em>awesome</em> !</p>

注意它聪明地加上了?</em>? 闭合标签。

Vim Surround 也可以用 ySS 缩进文本并加上标签。比如你有以下文本:

<p>Vim plugins are <em>awesome</em> !</p>

你可以用 ySS?<div class="normal">?加上 div 标签,注意生成的段落是自动缩进的。

<div class="normal">
        <p>Vim plugins are <em>awesome</em> !</p>
</div>

Vim Surround 有很多其它选项,你可以参照 GitHub 上的说明尝试它们。

4、Vim Gitgutter

Vim Gitgutter 插件对使用 Git 作为版本控制工具的人来说非常有用。它会在 Vim 的行号列旁显示 git diff 的差异标记。假设你有如下已提交过的代码:

? 1 package main
? 2
? 3 import "fmt"
? 4
? 5 func main() {
? 6 ? ? x := true
? 7 ? ? items := []string{"tv", "pc", "tablet"}
? 8
? 9 ? ? if x {
?10 ? ? ? ? for _, i := range items {
?11 ? ? ? ? ? ? fmt.Println(i)
?12 ? ? ? ? }
?13 ? ? }
?14 }

当你做出一些修改后,Vim Gitgutter 会显示如下标记:

? ? 1 package main
? ? 2
? ? 3 import "fmt"
? ? 4
_ ? 5 func main() {
? ? 6 ? ? items := []string{"tv", "pc", "tablet"}
? ? 7
~ ? 8 ? ? if len(items) > 0 {
? ? 9 ? ? ? ? for _, i := range items {
? ?10 ? ? ? ? ? ? fmt.Println(i)
+ ?11 ? ? ? ? ? ? fmt.Println("------")
? ?12 ? ? ? ? }
? ?13 ? ? }
? ?14 }

_ 标记表示在第 5 行和第 6 行之间删除了一行。~ 表示第 8 行有修改,+ 表示新增了第 11 行。

另外,Vim Gitgutter 允许你用 [c]c 在多个有修改的块之间跳转,甚至可以用 Leader+hs 来暂存某个变更集。

这个插件提供了对变更的即时视觉反馈,如果你用 Git 的话,有了它简直是如虎添翼。

5、VIM Fugitive

Vim Fugitive 是另一个将 Git 工作流集成到 Vim 中的超棒插件。它对 Git 做了一些封装,可以让你在 Vim 里直接执行 Git 命令并将结果集成在 Vim 界面里。这个插件有超多的特性,更多信息请访问它的 GitHub 项目页面。

这里有一个使用 Vim Fugitive 的基础 Git 工作流示例。设想我们已经对下面的 Go 代码做出修改,你可以用 :Gblame 调用 git blame 来查看每行最后的提交信息:

e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? ?1 package main
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? ?2
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? ?3 import "fmt"
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? ?4
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│_ ? 5 func main() {
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? ?6 ? ? items := []string{"tv", "pc", "tablet"}
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? ?7
00000000 (Not Committed Yet 2018-12-05 18:55:00 -0500)│~ ? 8 ? ? if len(items) > 0 {
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? ?9 ? ? ? ? for _, i := range items {
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? 10 ? ? ? ? ? ? fmt.Println(i)
00000000 (Not Committed Yet 2018-12-05 18:55:00 -0500)│+ ?11 ? ? ? ? ? ? fmt.Println("------")
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? 12 ? ? ? ? }
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? 13 ? ? }
e9949066 (Ricardo Gerardi ? 2018-12-05 18:17:19 -0500)│ ? 14 }

可以看到第 8 行和第 11 行显示还未提交。用 :Gstatus 命令检查仓库当前的状态:

? 1 # On branch master
? 2 # Your branch is up to date with 'origin/master'.
? 3 #
? 4 # Changes not staged for commit:
? 5 # ? (use "git add <file>..." to update what will be committed)
? 6 # ? (use "git checkout -- <file>..." to discard changes in working directory)
? 7 #
? 8 # ? ? ? modified: ? vim-5plugins/examples/test1.go
? 9 #
?10 no changes added to commit (use "git add" and/or "git commit -a")
--------------------------------------------------------------------------------------------------------
? ? 1 package main
? ? 2
? ? 3 import "fmt"
? ? 4
_ ? 5 func main() {
? ? 6 ? ? items := []string{"tv", "pc", "tablet"}
? ? 7
~ ? 8 ? ? if len(items) > 0 {
? ? 9 ? ? ? ? for _, i := range items {
? ?10 ? ? ? ? ? ? fmt.Println(i)
+ ?11 ? ? ? ? ? ? fmt.Println("------")
? ?12 ? ? ? ? }
? ?13 ? ? }
? ?14 }

Vim Fugitive 在分割的窗口里显示 git status 的输出结果。你可以在该行按下 - 键用该文件的名字暂存这个文件的提交,再按一次 - 可以取消暂存。这个信息会随着你的操作自动更新:

? 1 # On branch master
? 2 # Your branch is up to date with 'origin/master'.
? 3 #
? 4 # Changes to be committed:
? 5 # ? (use "git reset HEAD <file>..." to unstage)
? 6 #
? 7 # ? ? ? modified: ? vim-5plugins/examples/test1.go
? 8 #
--------------------------------------------------------------------------------------------------------
? ? 1 package main
? ? 2
? ? 3 import "fmt"
? ? 4
_ ? 5 func main() {
? ? 6 ? ? items := []string{"tv", "pc", "tablet"}
? ? 7
~ ? 8 ? ? if len(items) > 0 {
? ? 9 ? ? ? ? for _, i := range items {
? ?10 ? ? ? ? ? ? fmt.Println(i)
+ ?11 ? ? ? ? ? ? fmt.Println("------")
? ?12 ? ? ? ? }
? ?13 ? ? }
? ?14 }

现在你可以用 :Gcommit 来提交修改了。Vim Fugitive 会打开另一个分割窗口让你输入提交信息:

? 1 vim-5plugins: Updated test1.go example file
? 2 # Please enter the commit message for your changes. Lines starting
? 3 # with '#' will be ignored, and an empty message aborts the commit.
? 4 #
? 5 # On branch master
? 6 # Your branch is up to date with 'origin/master'.
? 7 #
? 8 # Changes to be committed:
? 9 # ? ? ? modified: ? vim-5plugins/examples/test1.go
?10 #

:wq 保存文件完成提交:

[master c3bf80f] vim-5plugins: Updated test1.go example file
?1 file changed, 2 insertions(+), 2 deletions(-)
Press ENTER or type command to continue

然后你可以再用 :Gstatus 检查结果并用 :Gpush 把新的提交推送到远程。

? 1 # On branch master
? 2 # Your branch is ahead of 'origin/master' by 1 commit.
? 3 # ? (use "git push" to publish your local commits)
? 4 #
? 5 nothing to commit, working tree clean

Vim Fugitive 的 GitHub 项目主页有很多屏幕录像展示了它的更多功能和工作流,如果你喜欢它并想多学一些,快去看看吧。

接下来?

这些 Vim 插件都是程序开发者的神器!还有另外两类开发者常用的插件:自动完成插件和语法检查插件。它些大都是和具体的编程语言相关的,以后我会在一些文章中介绍它们。

你在写代码时是否用到一些其它 Vim 插件?请在评论区留言分享。

5 个好用的开发者 Vim 插件,首发于山西十一选五手机版。

]]>
//www.brhi.net/114666/feed/ 0
14 个依然很棒的 Linux ASCII 游戏 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114663/ //www.brhi.net/114663/#comments Tue, 19 Feb 2019 11:50:11 +0000 //www.brhi.net/?p=114663 你或许不相信,有一些 ASCII 游戏被证明是非常容易上瘾的

14 个依然很棒的 Linux ASCII 游戏,首发于山西十一选五手机版。

]]>
基于文本的(或者我应该说是基于终端的)游戏在十年前非常流行 —— 当时还没有像战神God Of War、荒野大镖客:救赎 2Red Dead Redemption 2或蜘蛛侠Spiderman这样的视觉游戏大作。

当然,Linux 平台有很多好游戏 —— 虽然并不总是“最新和最好”。但是,有一些 ASCII 游戏,却是你永远不会玩腻的。

你或许不相信,有一些 ASCII 游戏被证明是非常容易上瘾的(所以,我可能需要一段时间才能继续写下一篇文章,或者我可能会被解雇? —— 帮帮我?。?/p>

哈哈,开个玩笑。让我们来看看最好的 ASCII 游戏吧。

注意:安装 ASCII 游戏可能要花费不少时间(有些可能会要求你安装其他依赖项或根本不起作用)。你甚至可能会遇到一些需要你从源代码构建的 ASCII 游戏。因此,我们只筛选出那些易于安装和运行的产品 —— 不用费劲。

在运行和安装 ASCII 游戏之前需要做的事情

如果你没有安装的话,某些 ASCII 游戏可能需要你安装 Simple DirectMedia Layer。因此,以防万一,你应该先尝试安装它,然后再尝试运行本文中提到的任何游戏。

要安装它,你需要键入如下命令:

sudo apt install libsdl2-2.0
sudo apt install libsdl2_mixer-2.0

Linux 上最好的 ASCII 游戏

Best Ascii games for Linux

如下列出的游戏排名不分先后。

1、战争诅咒

Curse of War ascii games

战争诅咒Curse of War是一个有趣的策略游戏。一开始你可能会发现它有点令人困惑,但一旦你掌握了,就会喜欢上它。在启动游戏之前,我建议你在其 主页 上查看该游戏规则。

你将建设基础设施、?;ぷ试床⒅富幽愕木咏姓蕉?。你所要做的就是把你的旗帜放在一个合适的位置,让你的军队来完成其余的任务。不仅仅是攻击敌人,你还需要管理和?;ぷ试匆园镏谜蕉?。

如果你之前从未玩过任何 ASCII 游戏,请耐心花一些时间来学习它、体验它的全部潜力。

如何安装?

你可以在官方软件库里找到它。键入如下命令来安装它:

sudo apt install curseofwar

2、ASCII 领域

ascii sector

讨厌策略游戏?不用担心,ASCII 领域ASCII Sector是一款具有空间环境的游戏,可让你进行大量探索。

此外,不仅仅局限于探索,你还想要采取一些行动吗?也是可以的。当然,虽然战斗体验不是最好的,但它也很有趣。当你看到各种基地、任务和探索时,会让你更加兴奋。你会在这个小小的游戏中遇到一个练级系统,你必须赚取足够的钱或进行交易才能升级你的宇宙飞船。

而这个游戏最好的地方是你可以创建自己的任务,也可以玩其他人的任务。

如何安装?

你需要先从其 官方网站 下载并解压缩归档包。完成后,打开终端并输入这些命令(将 “Downloads” 文件夹替换为你解压缩文件夹所在的位置,如果解压缩文件夹位于你的主目录中,则忽略它):

cd Downloads
cd asciisec
chmod +x asciisec
./asciisec

3、DoomRL

doom ascii game

你肯定知道经典游戏“毁灭战士DOOM”,所以,如果你想把它像 Rogue 类游戏一样略微体验一下,DoomRL 就是适合你的游戏。它是一个基于 ASCII 的游戏,这或许让你想不到。

这是一个非常小的游戏,但是可以玩很久。

如何安装?

与你对 “ASCII 领域”所做的类似,你需要从其 下载页面 下载官方归档文件,然后将其解压缩到一个文件夹。

解压缩后,输入以下命令:

cd Downloads // navigating to the location where the unpacked folder exists
cd doomrl-linux-x64-0997
chmod +x doomrl
./doomrl

4、金字塔建造者

Pyramid Builder ascii game for Linux

金字塔建造者Pyramid Builder 是一款创新的 ASCII 游戏,你可以通过帮助建造金字塔来提升你的文明。

你需要指导工人耕种、卸载货物、并移动巨大的石头,以成功建造金字塔。

这确实是一个值得下载的 ASCII 游戏。

如何安装?

只需前往其官方网站并下载包以解压缩。提取后,导航到该文件夹并运行可执行文件。

cd Downloads
cd pyramid_builder_linux
chmod?+x pyramid_builder_linux.x86_64
./pyramid_builder_linux.x86_64

5、DiabloRL

Diablo ascii RPG game

如果你是一位狂热的游戏玩家,你一定听说过暴雪的暗黑破坏神Diablo 1 代,毫无疑问这是一个精彩的游戏。

现在你有机会玩一个该游戏的独特演绎版本 —— 一个 ASCII 游戏。DiabloRL 是一款非常棒的基于回合制的 Rogue 类的游戏。你可以从各种职业(战士、巫师或盗贼)中进行选择。每个职业都具有一套不同的属性,可以带来不同游戏体验。

当然,个人偏好会有所不同,但它是一个不错的暗黑破坏神“降级版”。你觉得怎么样?

6、Ninvaders

Ninvaders terminal game for Linux

Ninvaders 是最好的 ASCII 游戏之一,因为它是如此简单,且可以消磨时间的街机游戏。

你必须防御入侵者,需要在它们到达之前击败它们。这听起来很简单,但它极具挑战性。

如何安装?

与“战争诅咒”类似,你可以在官方软件库中找到它。所以,只需输入此命令即可安装它:

sudo apt install ninvaders?

7、帝国

Empire terminal game

帝国Empire这是一款即时战略游戏,你需要互联网连接。我个人不是实时战略游戏的粉丝,但如果你是这类游戏的粉丝,你可以看看他们的 指南 来玩这个游戏,因为学习起来非常具有挑战性。

游戏区域包含城市、土地和水。你需要用军队、船只、飞机和其他资源扩展你的城市。通过快速扩张,你可以通过在对方动作之前摧毁它们来捕获其他城市。

如何安装?

安装很简单,只需输入以下命令:

sudo apt install empire

8、Nudoku

Nudoku is a terminal version game of Sudoku

喜欢数独游戏?好吧,你也有个 Nudoku 游戏,这是它的克隆。这是当你想放松时的一个完美的消磨时间的 ASCII 游戏。

它为你提供三个难度级别:简单、正常和困难。如果你想要挑战电脑,其难度会非常难!如果你只是想放松一下,那么就选择简单难度吧。

如何安装?

安装它很容易,只需在终端输入以下命令:

sudo apt install nudoku

9、Nethack

最好的地下城式 ASCII 游戏之一。如果你已经知道一些 Linux 的 ASCII 游戏,我相信这是你的最爱之一。

它具有许多不同的层(约 45 个),并且包含一堆武器、卷轴、药水、盔甲、戒指和宝石。你也可以选择“永久死亡”模式来玩试试。

在这里可不仅仅是杀戮,你还有很多需要探索的地方。

如何安装?

只需按照以下命令安装它:

sudo apt install nethack

10、ASCII 滑雪

ascii jump game

ASCII 滑雪ASCII Jump 是一款简单易玩的游戏,你必须沿着各种轨道滑动,同时跳跃、改变位置,并尽可能长时间地移动以达到最大距离。

即使看起来很简单,但是看看这个 ASCII 游戏视觉上的表现也是很神奇的。你可以从训练模式开始,然后进入世界杯比赛。你还可以选择你的竞争对手以及你想要开始游戏的山丘。

如何安装?

只需按照以下命令安装它:

sudo apt install asciijump

11、Bastet

Bastet is tetris game in ascii form

不要被这个名字误导,它实际上是俄罗斯方块游戏的一个有趣的克隆。

你不要觉得它只是另一个普通的俄罗斯方块游戏,它会为你丢下最糟糕的砖块。祝你玩得开心!

如何安装?

打开终端并键入如下命令:

sudo apt install bastet

12、Bombardier

Bomabrdier game in ascii form

Bombardier 是另一个简单的 ASCII 游戏,它会让你迷上它。

在这里,你有一架直升机(或许你想称之为飞机),每一圈它都会降低,你需要投掷炸弹才能摧毁你下面的街区/建筑物。当你摧毁一个街区时,游戏还会在它显示的消息里面添加一些幽默。很好玩。

如何安装?

Bombardier 可以在官方软件库中找到,所以只需在终端中键入以下内容即可安装它:

sudo apt install bombardier

13、Angband

Angband ascii game

一个很酷的地下城探索游戏,界面整洁。在探索该游戏时,你可以在一个屏幕上看到所有重要信息。

它包含不同种类的种族可供选择角色。你可以是精灵、霍比特人、矮人或其他什么,有十几种可供选择。请记住,你需要在最后击败黑暗之王,所以尽可能升级你的武器并做好准备。

如何安装?

直接键入如下命令:

sudo apt install angband

14、GNU 国际象棋

GNU Chess is a chess game that you can play in Linux terminal

为什么不下盘棋呢?这是我最喜欢的策略游戏了!

但是,除非你知道如何使用代表的符号来描述下一步行动,否则 GNU 国际象棋可能很难玩。当然,作为一个 ASCII 游戏,它不太好交互,所以它会要求你记录你的移动并显示输出(当它等待计算机思考它的下一步行动时)。

如何安装?

如果你了解国际象棋的代表符号,请输入以下命令从终端安装它:

sudo apt install gnuchess

一些荣誉奖

正如我之前提到的,我们试图向你推荐最好的(也是最容易在 Linux 机器上安装的那些) ASCII 游戏。

然而,有一些标志性的 ASCII 游戏值得关注,它们需要更多的安装工作(你可以获得源代码,但需要构建它/安装它)。

其中一些游戏是:

你可以按照我们的 从源代码安装软件的完全指南 来进行。

总结

我们提到的哪些 ASCII 游戏适合你?我们错过了你最喜欢的吗?

请在下面的评论中告诉我们你的想法。

 

14 个依然很棒的 Linux ASCII 游戏,首发于山西十一选五手机版。

]]>
//www.brhi.net/114663/feed/ 1
Python 中星号的本质及其使用方式 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114655/ //www.brhi.net/114655/#comments Sat, 16 Feb 2019 14:10:13 +0000 //www.brhi.net/?p=114655 在 Python 中有很多地方可以看到*和**。在某些情形下,无论是对于新手程序员,还是从其他很多没有完全相同操作符的编程语言迁移过来的人来说,这两个操作符都可能有点神秘。因此,我想讨论一下这些操作符的本质及其使用方式。

Python 中星号的本质及其使用方式,首发于山西十一选五手机版。

]]>
在 Python 中有很多地方可以看到***。在某些情形下,无论是对于新手程序员,还是从其他很多没有完全相同操作符的编程语言迁移过来的人来说,这两个操作符都可能有点神秘。因此,我想讨论一下这些操作符的本质及其使用方式。

多年以来,***操作符的功能不断增强。在本文中,我将讨论目前这些操作符所有的使用方法,并指出哪些使用方法只能在目前的 Python 版本中应用。因此,如果你学习过 Python 2 中***的使用方法,那么我建议你至少浏览一下本文,因为 Python 3 中添加了许多***的新用途。

如果你是新接触 Python 不久,还不熟悉关键字参数(亦称为命名参数),我建议你首先阅读我有关Python中的关键字参数的文章。

不属于我们讨论范围的内容

在本文中, 当我讨论***时,我指的是*** 前缀 操作符,而不是 中缀 操作符。

也就是说,我讲述的不是乘法和指数运算:

>>> 2 * 5
10 
>>> 2 ** 5 
32

那么我们在讨论什么内容呢?

我们讨论的是***前缀运算符,即在变量前使用的***运算符。例如:

>>> numbers = [2, 1, 3, 4, 7]
>>> more_numbers = [*numbers, 11, 18]
>>> print(*more_numbers, sep=', ')
2, 1, 3, 4, 7, 11, 18

上述代码中展示了*的两种用法,没有展示**的用法。

这其中包括:

  1. 使用***向函数传递参数
  2. 使用***捕获被传递到函数中的参数
  3. 使用*接受只包含关键字的参数
  4. 使用*在元组解包时捕获项
  5. 使用*将迭代项解压到列表/元组中
  6. 使用**将字典解压到其他字典中

即使你认为自己已经熟悉***的所有使用方法,我还是建议你查看下面的每个代码块,以确保都是你熟悉的内容。在过去的几年里,Python 核心开发人员不断地为这些操作符添加新的功能,对于使用者来说很容易忽略***‘的一些新用法。

星号用于将可迭代对象拆分并分别作为函数参数

当调用函数时,*运算符可用于将一个迭代项解压缩到函数调用中的参数中:

 

>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato']
>>> print(fruits[0], fruits[1], fruits[2], fruits[3])
lemon pear watermelon tomato 
>>> print(*fruits)
lemon pear watermelon tomato

print(*fruits)代码行将fruits列表中的所有项作为独立的参数传递给print函数调用,甚至不需要我们知道列表中有多少个参数。

*运算符在这里远不止是语法糖而已。要想用一个特定的迭代器将所有项作为独立的参数传输,若不使用*是不可能做到的,除非列表的长度是固定的。

下面是另一个例子:

 

def transpose_list(list_of_lists):
    return [
        list(row)
        for row in zip(*list_of_lists)
    ]

 

这里我们接受一个二维列表并返回一个“转置”的二维列表。

>>> transpose_list([[1, 4, 7], [2, 5, 8], [3, 6, 9]])
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

**操作符完成了类似的操作,只不过使用了关键字参数。**运算符允许我们获取键-值对字典,并在函数调用中将其解压为关键字参数。

>>> date_info = {'year': "2020", 'month': "01", 'day': "01"} 
>>> filename = "{year}-{month}-{day}.txt".format(**date_info) 
>>> filename '2020-01-01.txt' `

根据我的经验,使用**将关键字参数解压缩到函数调用中并不常见。我最??吹剿牡胤绞窃谑迪旨坛惺保憾?code>uper()的调用通常包括***。

如 Python 3.5 那样,在函数调用中,***都可以被多次使用。

有时,多次使用*会很方便:

>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato'] 
>>> numbers = [2, 1, 3, 4, 7] 
>>> print(*numbers, *fruits) 
2 1 3 4 7 lemon pear watermelon tomato `

 

多次使用**也可以达到相似的效果:

>>> date_info = {'year': "2020", 'month': "01", 'day': "01"} 
>>> track_info = {'artist': "Beethoven", 'title': 'Symphony No 5'} 
>>> filename = "{year}-{month}-{day}-{artist}-{title}.txt".format( 
...     **date_info,
...     **track_info,
... ) 
>>> filename 
'2020-01-01-Beethoven-Symphony No 5.txt'

不过,在多次使用**时需要特别小心。Python 中的函数不能多次指定相同的关键字参数,因此在每个字典中与**一起使用的键必须能够相互区分,否则会引发异常。

星号用于压缩被传递到函数中的参数

在定义函数时,*运算符可用于捕获传递给函数的位置参数。位置参数的数量不受限制,捕获后被存储在一个元组中。

from random import randint  

def roll(*dice):     
    return sum(randint(1, die) for die in dice)

这个函数接受的参数数量不受限制:

>>> roll(20) 
18 
>>> roll(6, 6) 
9 
>>> roll(6, 6, 6) 
8

Python 的printzip函数接受的位置参数数量不受限制。*的这种参数压缩用法,允许我们创建像printzip一样的函数,接受任意数量的参数。

**运算符也有另外一个功能:我们在定义函数时,可以使用** 捕获传进函数的任何关键字参数到一个字典当中:

def tag(tag_name, **attributes):
    attribute_list = [
        f'{name}="{value}"'
        for name, value in attributes.items()
    ]     
    return f"<{tag_name} {' '.join(attribute_list)}>"

** 将捕获我们传入这个函数中的任何关键字参数,并将其放入一个字典中,该字典将引用attributes参数。

>>> tag('a', )
'<a  
>>> tag('img', height=20, width=40, src="face.jpg") 
'<img height="20" width="40" src="face.jpg">'

只有关键字参数的位置参数

在 Python 3 中,我们现在拥有了一种特殊的语法来接受只有关键字的函数参数。只有关键字的参数是只能 使用关键字语法来指定的函数参数,也就意味着不能按照位置来指定它们。

在定义函数时,为了接受只有关键字的参数,我们可以将命名参数放在*后:

def get_multiple(*keys, dictionary, default=None):
    return [
        dictionary.get(key, default)
        for key in keys
    ]

上面的函数可以像这样使用:

>>> fruits = {'lemon': 'yellow', 'orange': 'orange', 'tomato': 'red'} 
>>> get_multiple('lemon', 'tomato', 'squash', dictionary=fruits, default='unknown')
['yellow', 'red', 'unknown']

参数dictionarydefault*keys后面,这意味着它们只能 被指定为关键字参数。如果我们试图按照位置来指定它们,我们会得到一个报错:

>>> fruits = {'lemon': 'yellow', 'orange': 'orange', 'tomato': 'red'} 
>>> get_multiple('lemon', 'tomato', 'squash', fruits, 'unknown') 
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: get_multiple() missing 1 required keyword-only argument: 'dictionary'

 

这种行为是通过 PEP 3102 被引入到 Python 中的。

没有位置参数关键字的参数

只使用关键字参数的特性很酷,但是如果您希望只使用关键字参数而不捕获无限的位置参数呢?

Python 使用一种有点奇怪的 单独* 语法来实现:

def with_previous(iterable, *, fillvalue=None):
    """Yield each iterable item along with the item before it."""     
    previous = fillvalue     
    for item in iterable:         
        yield previous, item         
        previous = item

这个函数接受一个迭代器参数,可以按照位置或名字来指定此参数(作为第一个参数),以及关键字参数fillvalue,这个填充值参数只使用关键字。这意味着我们可以像下面这样调用 with_previous:

>>> list(with_previous([2, 1, 3], fillvalue=0)) 
[(0, 2), (2, 1), (1, 3)]

但像这样就不可以:

>>> list(with_previous([2, 1, 3], 0))
Traceback (most recent call last):  
File "<stdin>", line 1, in <module> 
TypeError: with_previous() takes 1 positional argument but 2 were given `

这个函数接受两个参数,其中fillvalue参数必须被指定为关键字参数。

我通常在获取任意数量的位置参数时只使用关键字参数,但我有时使用这个*强制按照位置指定一个参数。

实际上,Python 的内置sorted函数使用了这种方法。如果你查看sorted的帮助信息,将看到以下信息:

>>> help(sorted) 
Help on built-in function sorted in module builtins: 

sorted(iterable, /, *, key=None, reverse=False)     
    Return a new list containing all items from the iterable in ascending order.  
    A custom key function can be supplied to customize the sort order, and the     
    reverse flag can be set to request the result in descending order.

sorted的官方说明中,有一个单独的*参数。

星号用于元组拆包

Python 3 还新添了一种 * 运算符的使用方式,它只与上面定义函数时和调用函数时*的使用方式相关。

现在,*操作符也可以用于元组拆包:

>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato'] 
>>> first, second, *remaining = fruits 
>>> remaining 
['watermelon', 'tomato'] 
>>> first, *remaining = fruits 
>>> remaining 
['pear', 'watermelon', 'tomato'] 
>>> first, *middle, last = fruits 
>>> middle 
['pear', 'watermelon']

如果你想知道什么情况下可以在你自己的代码中使用它,请查看我关于 Python 中的 tuple 解包 文章中的示例。在那篇文章中,我将展示如何使用*操作符作为序列切片的替代方法。

通常当我教*的时候,我告诉大家只能在多重赋值语句中使用一个*表达式。实际来说这是不正确的,因为可以在嵌套解包中使用两个*(我在元组解包文章中讨论了嵌套解包):

>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato'] 
>>> first, second, *remaining = fruits 
>>> remaining 
['watermelon', 'tomato'] 
>>> first, *remaining = fruits 
>>> remaining 
['pear', 'watermelon', 'tomato'] 
>>> first, *middle, last = fruits 
>>> middle 
['pear', 'watermelon']

但是,我从来没见过它有什么实际用处,即使你因为它看起来有点神秘而去寻找一个例子,我也并不推荐这种使用方式。

将此添加到 Python 3.0 中的 PEP 是 PEP 3132,其篇幅不是很长。

列表文字中的星号

Python 3.5 通过 PEP 448 引入了大量与*相关的新特性。其中最大的新特性之一是能够使用*将迭代器转储到新列表中。

假设你有一个函数,它以任一序列作为输入,返回一个列表,其中该序列和序列的倒序连接在了一起:

def palindromify(sequence):   
    return list(sequence) + list(reversed(sequence))

此函数需要多次将序列转换为列表,以便连接列表并返回结果。在 Python 3.5 中,我们可以这样编写函数:

def palindromify(sequence):  
    return [*sequence, *reversed(sequence)]

这段代码避免了一些不必要的列表调用,因此我们的代码更高效,可读性更好。

下面是另一个例子:

def rotate_first_item(sequence):     
    return [*sequence[1:], sequence[0]]

该函数返回一个新列表,其中给定列表(或其他序列)中的第一项被移动到了新列表的末尾。

* 运算符的这种使用是将不同类型的迭代器连接在一起的好方法。* 运算符适用于连接任何种类的迭代器,然而 + 运算符只适用于类型都相同的特定序列。

除了创建列表存储迭代器以外,我们还可以将迭代器转储到新的元组或集合中:

>>> fruits = ['lemon', 'pear', 'watermelon', 'tomato'] 
>>> (*fruits[1:], fruits[0]) 
('pear', 'watermelon', 'tomato', 'lemon') 
>>> uppercase_fruits = (f.upper() for f in fruits) 
>>> {*fruits, *uppercase_fruits}
{'lemon', 'watermelon', 'TOMATO', 'LEMON', 'PEAR', 'WATERMELON', 'tomato', 'pear'}

注意,上面的最后一行使用了一个列表和一个生成器,并将它们转储到一个新的集合中。在此之前,并没有一种简单的方法可以在一行代码中完成这项工作。曾经有一种方法可以做到这一点,可是并不容易被记住或发现:

 

两个星号用于字典文本

PEP 448 还通过允许将键/值对从一个字典转储到一个新字典扩展了**操作符的功能:

>>> date_info = {'year': "2020", 'month': "01", 'day': "01"} 
>>> track_info = {'artist': "Beethoven", 'title': 'Symphony No 5'} 
>>> all_info = {**date_info, **track_info} 
>>> all_info
{'year': '2020', 'month': '01', 'day': '01', 'artist': 'Beethoven', 'title': 'Symphony No 5'}

我还写了另一篇文章:在Python中合并字典的惯用方法。

不过,**操作符不仅仅可以用于合并两个字典。

例如,我们可以在复制一个字典的同时添加一个新值:

>>> date_info = {'year': '2020', 'month': '01', 'day': '7'} 
>>> event_info = {**date_info, 'group': "Python Meetup"} 
>>> event_info 
{'year': '2020', 'month': '01', 'day': '7', 'group': 'Python Meetup'}

或者在复制/合并字典的同时重写特定的值:

>>> event_info = {'year': '2020', 'month': '01', 'day': '7', 'group': 'Python Meetup'} 
>>> new_info = {**event_info, 'day': "14"}
>>> new_info
{'year': '2020', 'month': '01', 'day': '14', 'group': 'Python Meetup'}

Python 的星号非常强大

Python 的 *** 运算符不仅仅是语法糖。 *** 运算符允许的某些操作可以通过其他方式实现,但是往往更麻烦和更耗费资源。而且 *** 运算符提供的某些特性没有替代方法实现:例如,函数在不使用 * 时就无法接受任意数量的位置参数。

在阅读了*** 运算符的所有特性之后,您可能想知道这些奇怪操作符的名称。不幸的是,它们的名字并不简练。我听说过* 被称为“打包”和“拆包“运算符。我还听说过其被称为“splat”(来自 Ruby 世界),也听说过被简单地称为“star”。

我倾向于称这些操作符为“星”和“双星”或“星星”。这种叫法并不能区分它们和它们的中缀关系(乘法和指数运算),但是通常我们可以从上下文清楚地知道是在讨论前缀运算符还是中缀运算符。

请勿在不理解*** 运算符的前提下记住它们的所有用法!这些操作符有很多用途,记住每种操作符的具体用法并不重要,重要的是了解你何时能够使用这些操作符。我建议使用这篇文章作为一个备忘单或者制作你自己的备忘单来帮助你在 Python 中使用解*** 。

喜欢我的教学风格吗?

想了解更多关于 Python 的知识吗?我每周通过实时聊天分享我最喜欢的 Python 资源、回答 Python 问题。在下方注册,我将回答你提出的关于如何使 Python 代码更具有描述性、可读性和更 Python 化的问题。

Python 中星号的本质及其使用方式,首发于山西十一选五手机版。

]]>
//www.brhi.net/114655/feed/ 1
克劳德·香农(信息论之父):天才的解决问题之道 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114648/ //www.brhi.net/114648/#comments Sat, 16 Feb 2019 13:37:51 +0000 //www.brhi.net/?p=114648 信息论之父香农是如何思考和解决问题的,虽然我们不太可能遇到他面临的问题,但这种解决问题的方法及其背后的推理还是有很多可以借鉴的地方,当我们了解了之后,或许可以帮我们更敏锐地思考。

克劳德·香农(信息论之父):天才的解决问题之道,首发于山西十一选五手机版。

]]>

克劳德·香农花了差不多10年,制定了一套完整的、具有开创性意义的信息理论。

一开始在研究生院时,他只是想给当时的众多信息技术(如电话,收音机,电视机)建立一套通用的技术基础。

直到1948年,他发表了一篇名为《通信的数学理论》的论文。

他的重大贡献可不止这篇论文。当他还在MIT攻读硕士时,年仅21岁的他发表了一篇论文,这也被大家认为是20世纪最重要的硕士论文。

对于普通人来说,这无足轻重。香农并不是一个家喻户晓的名字。但如果没有他的贡献,我们所说的现代计算机可能不会存在。他巨大的的影响力不仅在计算机科学领域,而且涉及物理学和工程学。

天才一词被到处滥用,但如克劳德·香农这样真正配得上这个称号的人却屈指可数。他思考的方式异于常人,而且充满趣味。

讲到成就这样一位天才背后的原因,不得不提的就是他解决问题的方式。他不只是提出问题然后解决问题,而是他通过自己的方法,开发了一套思考过程来指导他洞察问题的本质。

虽然我们不太可能遇到他面临的问题,但这种解决问题的方法及其背后的推理还是有很多可以借鉴的地方,当我们了解了之后,或许可以帮我们更敏锐地思考。

一切问题都有其表现形式。我们必须先去理解问题,再着手解决问题。

在抠细节前先找到问题核心

找到问题答案固然很重要,但找到一种提问方式,以便更易于找到问题答案的重要性却被大多数人忽视了。

我们通常把目光聚焦在不同的细节上,想找到它们之间的联系,却没有集中精力去培养一种寻找问题本质的直觉。

香农恰恰反其道而行之。在他的自传《A Mind at Play》中,他坦言一些同时代的数学家觉得他思考不够严谨,步骤不够连贯。他们想要的是细节。

但是香农的推理方式是,只有你去掉问题中无关的细节,才能看到问题的本质,才能找到问题的答案。

通常情况下,当你看到问题的本质时,你可能觉得这根本不是之前思考的那个问题。所以,拿到一个问题时,在你纠结细节之前,以更宏观的角度来思考是很重要的,否则,你一开始就可能走歪了。

细节很重要,也很有用。很多细节的重要程度、能起多大作用跟其表现方式有关。但同样的,很多细节也没什么用。

如果一开始没有找到问题的关键所在,而是带着错误的细节信息就出发了,那样只会收集越来越多的错误信息,直到走进死胡同里。

从剔除无关细节开始,这样你才不会深陷迷雾当中,然后才能找到问题的本质。

找到问题的真正形式,这几乎和问题答案一样重要。

善用重构和对比

1952年,香农在贝尔实验室发表了一次演讲。演讲中,他深入分析了他如何创造性地思考他面临的问题。

除了简化问题和寻找问题本质,他还提到了其他的方法,这些方法在表面上似乎没什么作用,但对于创造性思考却至关重要。

当我们在一个问题上纠结了很长时间之后,通?;嵝纬梢恢止芸嘉?,使得思路一直在一条链路上徘徊。逻辑思考总是从一个点出发,运用推理,建立连接,如果一切顺利,每次都会把我们带到同一个终点。

创造性思维则有些不同。它也建立连接,但比起逻辑思维,它是一种更发散、更具偶然性的思考模式。

香农的其中一个诀窍就是,他会把一个问题通过各种各样的方式来重构并进行对比。这可能会对问题进行夸大、或简略、或改变措辞、或转换不同的角度、或反转。

这样做是为了获得一种大局观,让他能更全面地看问题。

在思考问题时,我们很容易陷入自我的思维怪圈之中,要打破这种思维怪圈,最好的方式是改变参考点。不必颠覆自己对问题的直觉认知或是已认定的问题本质,而仅仅是改变表达方式。

例如,我们可以问:这个问题最好的解决方式是什么?并且也可以问:最糟糕的解决方式呢?两者都包含信息,所以我们都需要剖析。

就如同一个问题有多种形式,也有多种不同的外在呈现的模样。 不同的模样蕴含着不同的真相。

增加输入信息蕴含的本质

想法的质量固然重要,但其数量的作用也不容小觑。但并不是仅关注于想法的总数,而是关注你得到这些想法的过程。

为了解决问题,你必须有一个好想法。但反过来,要有一个好想法,首先你需要筛选掉很多平庸的念头。然而,即便如此,并不意味着要把脑子里所有的想法都拿出来过一遍吗,肯定有比这更好的方法。

在二战期间,香农遇到了同为计算机科学先驱的艾伦·图灵。图灵在美国的那段时间,他们几乎每天一起喝茶。多年来,他们仍保持着联系,两人都尊重彼此的想法并享受对方的陪伴。

当被问到天才具有什么特质时,香农用了一个图灵告诉他的类比,也是图灵经过敏锐地观察后得出的。以下是他的原话:
“有些人,你给他/她一个点子,他/她只会回你半个,但有些人能回你两个”
香农谦逊地否认了自己属于后者,而牛顿这样的人才是。但他的话也说明了,真正对解决问题发挥作用的,决不仅仅是点子的数量。

每个输入的信息都有其核心的精髓,能帮我们揭开掩盖真相的面纱。这个真相是不同问题的多种不同解决方案的基础。

我认为,香农想说的是,要产生好点子,就要善于发掘每个输入信息所蕴含的本质。如果你弄错了其本质,就可能产生出平庸的点子,但只要你能够尽可能的触及问题的本质,就能越快地得出真知灼见。

让你的想法产生双倍的成效只是第一步,真正带来本质区别的是要抓住本质。

总结

人的一生大部分时刻,无论在你的工作中、人际交往中、还是关系到你的幸福感的时候,归结起来就是发现问题,解决问题,好让你能继续前进。

克劳德·香农或许是一个具有独特思维的天才,但他思考问题的过程并非常人所不可及。他的长处在于善于运用该过程来解决问题。

优秀的解决问题的能力关乎批判性思维和创造性思维。而综合两者的最好的方式便是在思考的过程中,让两者各放异彩。

思维模式塑造我们的思想。正确的思维模式应该成为我们追求的目标。

克劳德·香农(信息论之父):天才的解决问题之道,首发于山西十一选五手机版。

]]>
//www.brhi.net/114648/feed/ 1
Vim 命令合集 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114641/ //www.brhi.net/114641/#comments Fri, 18 Jan 2019 13:07:28 +0000 //www.brhi.net/?p=114641 本文整理了常用的 Vim 命令,欢迎补充。

Vim 命令合集,首发于山西十一选五手机版。

]]>
齐市北关献彩票3d字谜:命令历史

以:和/开头的命令都有历史纪录,可以首先键入:或/然后按上下箭头来选择某个历史命令。

启动vim

在命令行窗口中输入以下命令即可

vim 直接启动vim

vim filename 打开vim并创建名为filename的文件

文件命令

打开单个文件

vim file

同时打开多个文件

vim file1 file2 file3 …

在vim窗口中打开一个新文件

:open file

在新窗口中打开文件

:split file

切换到下一个文件

:bn

切换到上一个文件

:bp

查看当前打开的文件列表,当前正在编辑的文件会用[]括起来。

:args

打开远程文件,比如ftp或者share folder

:e ftp://192.168.10.76/abc.txt

:e \\qadrive\test\1.txt

vim的模式

正常模式(按Esc或Ctrl+[进入) 左下角显示文件名或为空
插入模式(按i键进入) 左下角显示–INSERT–
可视模式(不知道如何进入) 左下角显示–VISUAL–

导航命令

% 括号匹配

插入命令

i 在当前位置生前插入

I 在当前行首插入

a 在当前位置后插入

A 在当前行尾插入

o 在当前行之后插入一行

O 在当前行之前插入一行

查找命令

/text  查找text,按n健查找下一个,按N健查找前一个。

?text  查找text,反向查找,按n健查找下一个,按N健查找前一个。

vim中有一些特殊字符在查找时需要转义  .*[]^%/?~$

:set ignorecase  忽略大小写的查找

:set noignorecase  不忽略大小写的查找

查找很长的词,如果一个词很长,键入麻烦,可以将光标移动到该词上,按*或#键即可以该单词进行搜索,相当于/搜索。而#命令相当于?搜索。

:set hlsearch  高亮搜索结果,所有结果都高亮显示,而不是只显示一个匹配。

:set nohlsearch  关闭高亮搜索显示

:nohlsearch  关闭当前的高亮显示,如果再次搜索或者按下n或N键,则会再次高亮。

:set incsearch  逐步搜索模式,对当前键入的字符进行搜索而不必等待键入完成。

:set wrapscan  重新搜索,在搜索到文件头或尾时,返回继续搜索,默认开启。

替换命令

ra 将当前字符替换为a,当期字符即光标所在字符。

s/old/new/ 用old替换new,替换当前行的第一个匹配

s/old/new/g 用old替换new,替换当前行的所有匹配

%s/old/new/ 用old替换new,替换所有行的第一个匹配

%s/old/new/g 用old替换new,替换整个文件的所有匹配

:10,20 s/^/??? /g 在第10行知第20行每行前面加四个空格,用于缩进。

ddp 交换光标所在行和其下紧邻的一行。

移动命令

h 左移一个字符
l 右移一个字符,这个命令很少用,一般用w代替。
k 上移一个字符
j 下移一个字符
以上四个命令可以配合数字使用,比如20j就是向下移动20行,5h就是向左移动5个字符,在Vim中,很多命令都可以配合数字使用,比如删除10个字符10x,在当前位置后插入3个!,3a!<Esc>,这里的Esc是必须的,否则命令不生效。

w 向前移动一个单词(光标停在单词首部),如果已到行尾,则转至下一行行首。此命令快,可以代替l命令。

b 向后移动一个单词 2b 向后移动2个单词

e,同w,只不过是光标停在单词尾部

ge,同b,光标停在单词尾部。

^ 移动到本行第一个非空白字符上。

0(数字0)移动到本行第一个字符上,

<HOME> 移动到本行第一个字符。同0健。

$ 移动到行尾 3$ 移动到下面3行的行尾

gg 移动到文件头。 = [[

G(shift + g) 移动到文件尾。 = ]]

f(find)命令也可以用于移动,fx将找到光标后第一个为x的字符,3fd将找到第三个为d的字符。

F 同f,反向查找。

跳到指定行,冒号+行号,回车,比如跳到240行就是 :240回车。另一个方法是行号+G,比如230G跳到230行。

Ctrl + e 向下滚动一行

Ctrl + y 向上滚动一行

Ctrl + d 向下滚动半屏

Ctrl + u 向上滚动半屏

Ctrl + f 向下滚动一屏

Ctrl + b 向上滚动一屏

撤销和重做

u 撤销(Undo)
U 撤销对整行的操作
Ctrl + r 重做(Redo),即撤销的撤销。

删除命令

x 删除当前字符

3x 删除当前光标开始向后三个字符

X 删除当前字符的前一个字符。X=dh

dl 删除当前字符, dl=x

dh 删除前一个字符

dd 删除当前行

dj 删除上一行

dk 删除下一行

10d 删除当前行开始的10行。

D 删除当前字符至行尾。D=d$

d$ 删除当前字符之后的所有字符(本行)

kdgg 删除当前行之前所有行(不包括当前行)

jdG(jd shift + g) ? 删除当前行之后所有行(不包括当前行)

:1,10d 删除1-10行

:11,$d 删除11行及以后所有的行

:1,$d 删除所有行

J(shift + j)  删除两行之间的空行,实际上是合并两行。

拷贝和粘贴

yy 拷贝当前行

nyy 拷贝当前后开始的n行,比如2yy拷贝当前行及其下一行。

p? 在当前光标后粘贴,如果之前使用了yy命令来复制一行,那么就在当前行的下一行粘贴。

shift+p 在当前行前粘贴

:1,10 co 20 将1-10行插入到第20行之后。

:1,$ co $ 将整个文件复制一份并添加到文件尾部。

正常模式下按v(逐字)或V(逐行)进入可视模式,然后用jklh命令移动即可选择某些行或字符,再按y即可复制

ddp交换当前行和其下一行

xp交换当前字符和其后一个字符

剪切命令

正常模式下按v(逐字)或V(逐行)进入可视模式,然后用jklh命令移动即可选择某些行或字符,再按d即可剪切

ndd 剪切当前行之后的n行。利用p命令可以对剪切的内容进行粘贴

:1,10d 将1-10行剪切。利用p命令可将剪切后的内容进行粘贴。

:1, 10 m 20 将第1-10行移动到第20行之后。

退出命令

:wq 保存并退出

ZZ 保存并退出

:q! 强制退出并忽略所有更改

:e! 放弃所有修改,并打开原来文件。

窗口命令

:split或new 打开一个新窗口,光标停在顶层的窗口上

:split file或:new file 用新窗口打开文件

split打开的窗口都是横向的,使用vsplit可以纵向打开窗口。

Ctrl+ww 移动到下一个窗口

Ctrl+wj 移动到下方的窗口

Ctrl+wk 移动到上方的窗口

关闭窗口

:close 最后一个窗口不能使用此命令,可以防止意外退出vim。

:q 如果是最后一个被关闭的窗口,那么将退出vim。

ZZ 保存并退出。

关闭所有窗口,只保留当前窗口

:only

录制宏

按q键加任意字母开始录制,再按q键结束录制(这意味着vim中的宏不可嵌套),使用的时候@加宏名,比如qa。。。q录制名为a的宏,@a使用这个宏。

执行shell命令

:!command

:!ls 列出当前目录下文件

:!perl -c script.pl 检查perl脚本语法,可以不用退出vim,非常方便。

:!perl script.pl 执行perl脚本,可以不用退出vim,非常方便。

:suspend或Ctrl – Z 挂起vim,回到shell,按fg可以返回vim。

注释命令

perl程序中#开始的行为注释,所以要注释某些行,只需在行首加入#

3,5 s/^/#/g 注释第3-5行

3,5 s/^#//g 解除3-5行的注释

1,$ s/^/#/g 注释整个文档。

:%s/^/#/g 注释整个文档,此法更快。

帮助命令

:help or F1 显示整个帮助
:help xxx 显示xxx的帮助,比如 :help i, :help CTRL-[(即Ctrl+[的帮助)。
:help ‘number’ Vim选项的帮助用单引号括起
:help <Esc> 特殊键的帮助用<>扩起
:help -t Vim启动参数的帮助用-
:help i_<Esc> 插入模式下Esc的帮助,某个模式下的帮助用模式_主题的模式
帮助文件中位于||之间的内容是超链接,可以用Ctrl+]进入链接,Ctrl+o(Ctrl + t)返回

其他非编辑命令

. 重复前一次命令

:set ruler?  查看是否设置了ruler,在.vimrc中,使用set命令设制的选项都可以通过这个命令查看

:scriptnames  查看vim脚本文件的位置,比如.vimrc文件,语法文件及plugin等。

:set list 显示非打印字符,如tab,空格,行尾等。如果tab无法显示,请确定用set lcs=tab:>-命令设置了.vimrc文件,并确保你的文件中的确有tab,如果开启了expendtab,那么tab将被扩展为空格。

Vim教程
在Unix系统上
$ vimtutor
在Windows系统上
:help tutor

:syntax 列出已经定义的语法项
:syntax clear 清除已定义的语法规则
:syntax case match 大小写敏感,int和Int将视为不同的语法元素
:syntax case ignore 大小写无关,int和Int将视为相同的语法元素,并使用同样的配色方案

Vim 命令合集,首发于山西十一选五手机版。

]]>
//www.brhi.net/114641/feed/ 2
能从远程获得乐趣的 Linux 命令 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114638/ //www.brhi.net/114638/#comments Sun, 13 Jan 2019 14:32:53 +0000 //www.brhi.net/?p=114638 使用这些工具从远程了解天气、阅读资料等。

能从远程获得乐趣的 Linux 命令,首发于山西十一选五手机版。

]]>

使用这些工具从远程了解天气、阅读资料等。

我们即将结束为期 24 天的 Linux 命令行玩具日历。希望你有一直在看,如果没有,请回到开始,从头看过来。你会发现 Linux 终端有很多游戏、消遣和奇怪之处。

虽然你之前可能已经看过我们日历中的一些玩具,但我们希望每个人都遇见一个新事物。

今天的玩具(实际是玩具集合)有点不同。到目前为止,我主要是想把重点放在那些独立的玩具上,并且完全可在开源许可下使用。但是我从读者那里得到了一些很好的建议,利用开源工具远程访问一些开源或者不开源的东西。今天,我将介绍其中的一些。

第一个是经典之作:使用 Telnet 观看星球大战的 ASCII 演绎版本。你的系统可能已经安装了 Telnet,因此你只需运行:

$ telnet towel.blinkenlights.nl

我第一次看到它是十年之前,因此我对于它还存在有点惊奇。如果你还没看过,请留出一点时间看一下。你不会后悔的。

接下来,Opensource.com 的撰稿人 Manuel Dewald 提出了一种从终端获取当地天气的方法。它很简单,你只需安装 curl(或者,wget)。

$ curl wttr.in

最后,在假期中虽然你可以从命令行 Web 浏览器浏览你喜欢的网站(包括 Opensource.com),但有一些我最喜欢的网站可以通过专用客户端更轻松地浏览。其中两个是 Reddit 和 Hacker News,有人推荐给我一些它们的客户端,你可能也想尝试,它们都使用开源许可。我尝试过 haxor-news (Hacker News) 和 rtv (Reddit),它们都还不错。

 

 

能从远程获得乐趣的 Linux 命令,首发于山西十一选五手机版。

]]>
//www.brhi.net/114638/feed/ 1
5 款 Linux 街机游戏 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114636/ //www.brhi.net/114636/#respond Fri, 11 Jan 2019 12:50:36 +0000 //www.brhi.net/?p=114636 本文首先介绍 Linux 开源游戏中的街机类型游戏,在之后的文章中,我将介绍桌面和卡牌游戏,解谜游戏,竞速游戏,以及策略模拟游戏。

5 款 Linux 街机游戏,首发于山西十一选五手机版。

]]>

长久以来,游戏都是 Linux 的软肋。近些年,Steam、GOG 等游戏发布平台上不少商业游戏都开始支持 Linux,这对于 Linux 的游戏生态来说是件好事,但是我们能在这些平台上玩到的游戏通常是不开源的商业作品。当然,这些游戏在一个开源的操作系统上运行,但对于一个开源提倡者来说这似乎还不够纯粹。

那么,我们能找到既自由开源又能给玩家带来完整游戏体验的优质游戏吗?当然!虽然绝大多数的开源游戏很难和 3A 商业游戏大作竞争,但仍然有不少各种类型的开源游戏,不仅内容有趣而且直接可以通过几大 Linux 发行版本库中直接安装。

本文首先介绍 Linux 开源游戏中的街机类型游戏,在之后的文章中,我将介绍桌面和卡牌游戏,解谜游戏,竞速游戏,以及策略模拟游戏。

太空?;鶤stroMenace

太空?;?/a> 是一个近现代太空背景下的滚动页面射击游戏??⒊跗谒且桓霰赵从蜗?,但它的代码和素材而后以开源许可证发布了。游戏玩法和大多数此类游戏大同小异,但它有质量极高的 3D 画面。飞船和武器升级可以通过击杀敌人所获得的点数购买。游戏的难度可以选择,因此适合新手以及想要追求挑战的硬核玩家。

安装太空?;?,你只需要在终端下运行以下指令:

  • Fedora 用户: dnf install astromenace
  • Debian/Ubuntu 用户: apt install astromenace

坦克战役Battle Tanks

坦克战役 是一个俯瞰式视角的快节奏坦克战斗游戏。玩家可以选择三种不同的陆地坦克,操纵其在地图上前行,收集道具并且尝试炸飞敌军。它有四种游戏模式,死亡竞赛(又称“死斗”)、团队死斗、夺旗模式和合作模式。死斗和夺旗模式下,分别有 9 张地图可供玩家选择,合作模式则有 4 张。该游戏支持分屏本地双人游戏,以及在线多人竞技。游戏节奏很快,默认一次战役仅 5 分钟,因此,坦克战役十分适合想要利用零碎时间快速来一局的玩家。

安装坦克战役,你只需要在终端下运行以下指令:

  • Fedora 用户: dnf install btanks
  • Debian/Ubuntu 用户: apt install btanks

火星M.A.R.S.

火星 是一个自上而下的太空射击游戏,游戏机制类似传统街机游戏 “爆破彗星Asteroids”。玩家在操控一个太空船的同时向敌方射击并躲避敌军的弹幕射击。游戏有标准的死斗和团体死斗模式,除此之外也有更新鲜的比赛形式 —— 例如在一个模式下,玩家需要控制一个球使其进入敌方母星。该游戏支持本地多人游戏,但遗憾的是不支持多人联机。该游戏的开发更新似乎已经停止,所以该游戏之后增加联机模式的几率很小,但就算没有联机支持,这个游戏仍然值得一试。

安装火星,你只需要在终端下运行以下指令:

  • Fedora 用户: dnf install marsshooter
  • Debian/Ubuntu 用户: apt install marsshooter

不存在之球Neverball

不存在之球 的游戏灵感来源自世嘉的 “超级猴子球Super Monkey Ball” ,玩家需要将一个球在 3D 球场上运动起来,但是玩家控制的不是球,而是球场。游戏任务是在规定的时限内,收集足够多的金币从而打开该关卡的出口并且将小球落进该洞中。游戏可以调整难度,从休闲到难以超乎想象,可以适应不同的玩家需求。该游戏支持键盘/鼠标以及控制杆操作。

安装不存在之球,你只需要在终端下运行以下指令:

  • Fedora 用户:dnf install neverball
  • Debian/Ubuntu 用户:apt install neverball

超级 TuxSuperTux

超级 Tux 是继任天堂超级马里奥后的一款 2D 的平台跳跃游戏。Linux 的吉祥物企鹅 Tux 代替了马里奥,而鸡蛋对应着马里奥系列中的蘑菇能力提升。当 Tux 获得了鸡蛋得到了能力提升,它便可以收集花朵,而花朵可以带来新的附加特殊能力?;鹧婊ㄔ诠乜ㄖ凶钗<?,收集了火焰花的 Tux 可以掷出火球。除此之外,冰冻花/空气花/土地花也在游戏的程序中。收集星星的能力提升能使 Tux 暂时变得隐形,就如同马里奥系列游戏。该游戏最基础的一组关卡,冰之岛也有 30 关之多,因此游戏的内容和流程和超级马里奥系列一般长。SuperTux 还有一些附加关卡,例如三个额外奖励小岛、一个森林之岛、一个万圣节岛、一个孵化处,以及很多测试关卡。SuperTux 有一个自带的关卡编辑器,所以玩家可以创建他们的原创关卡。

安装超级 Tux,你只需要在终端下运行以下指令:

  • Fedora 用户:dnf install supertux
  • Debian/Ubuntu 用户: apt install supertux

如果我没有在上文中提及你最喜欢的开源街机游戏,欢迎在评论中分享。

有关作者

Joshua Allen Holm – 是 Opensource.com 的社区协调者之一。他的主要兴趣有数字人文、学术开放以及公开教育资源。你可以在 GitHub、GitLab、LinkedIn 和 Zotero 上找到他??梢酝ü?holmja@opensource.com 联系到他。

 

5 款 Linux 街机游戏,首发于山西十一选五手机版。

]]>
//www.brhi.net/114636/feed/ 0
“三次握手,四次挥手”你真的懂吗? - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114633/ //www.brhi.net/114633/#comments Wed, 09 Jan 2019 02:03:01 +0000 //www.brhi.net/?p=114633 作为程序员,要有“刨根问底”的精神。知其然,更要知其所以然。这篇文章希望能抽丝剥茧,还原背后的原理。

“三次握手,四次挥手”你真的懂吗?,首发于山西十一选五手机版。

]]>
记得刚毕业找工作面试的时候,经?;岜晃实剑耗阒馈?次握手,4次挥手”吗?这时候我会“胸有成竹”地“背诵”前期准备好的“答案”,第一次怎么怎么,第二次……答完就没有下文了,面试官貌似也没有深入下去的意思,深入下去我也不懂,皆大欢喜!

作为程序员,要有“刨根问底”的精神。知其然,更要知其所以然。这篇文章希望能抽丝剥茧,还原背后的原理。

什么是“3次握手,4次挥手”

TCP是一种面向连接的单播协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连接”,其实是客户端和服务器的内存里保存的一份关于对方的信息,如ip地址、端口号等。

TCP可以看成是一种字节流,它会处理IP层或以下的层的丢包、重复以及错误问题。在连接的建立过程中,双方需要交换一些连接的参数。这些参数可以放在TCP头部。

TCP提供了一种可靠、面向连接、字节流、传输层的服务,采用三次握手建立一个连接。采用4次挥手来关闭一个连接。

TCP服务模型

在了解了建立连接、关闭连接的“三次握手和四次挥手”后,我们再来看下TCP相关的东西。

一个TCP连接由一个4元组构成,分别是两个IP地址和两个端口号。一个TCP连接通常分为三个阶段:启动、数据传输、退出(关闭)。

当TCP接收到另一端的数据时,它会发送一个确认,但这个确认不会立即发送,一般会延迟一会儿。ACK是累积的,一个确认字节号N的ACK表示所有直到N的字节(不包括N)已经成功被接收了。这样的好处是如果一个ACK丢失,很可能后续的ACK就足以确认前面的报文段了。

一个完整的TCP连接是双向和对称的,数据可以在两个方向上平等地流动。给上层应用程序提供一种双工服务。一旦建立了一个连接,这个连接的一个方向上的每个TCP报文段都包含了相反方向上的报文段的一个ACK。

序列号的作用是使得一个TCP接收端可丢弃重复的报文段,记录以杂乱次序到达的报文段。因为TCP使用IP来传输报文段,而IP不提供重复消除或者保证次序正确的功能。另一方面,TCP是一个字节流协议,绝不会以杂乱的次序给上层程序发送数据。因此TCP接收端会被迫先保持大序列号的数据不交给应用程序,直到缺失的小序列号的报文段被填满。

TCP头部

tcp header

源端口和目的端口在TCP层确定双方进程,序列号表示的是报文段数据中的第一个字节号,ACK表示确认号,该确认号的发送方期待接收的下一个序列号,即最后被成功接收的数据字节序列号加1,这个字段只有在ACK位被启用的时候才有效。

当新建一个连接时,从客户端发送到服务端的第一个报文段的SYN位被启用,这称为SYN报文段,这时序列号字段包含了在本次连接的这个方向上要使用的第一个序列号,即初始序列号ISN,之后发送的数据是ISN加1,因此SYN位字段会消耗一个序列号,这意味着使用重传进行可靠传输。而不消耗序列号的ACK则不是。

头部长度(图中的数据偏移)以32位字为单位,也就是以4bytes为单位,它只有4位,最大为15,因此头部最大长度为60字节,而其最小为5,也就是头部最小为20字节(可变选项为空)。

ACK —— 确认,使得确认号有效。
RST —— 重置连接(经??吹降膔eset by peer)就是此字段搞的鬼。
SYN —— 用于初如化一个连接的序列号。
FIN —— 该报文段的发送方已经结束向对方发送数据。

当一个连接被建立或被终止时,交换的报文段只包含TCP头部,而没有数据。

状态转换

三次握手和四次挥手的状态转换如下图。
tcp connect

为什么要“三次握手,四次挥手”

三次握手

换个易于理解的视角来看为什么要3次握手。

客户端和服务端通信前要进行连接,“3次握手”的作用就是双方都能明确自己和对方的收、发能力是正常的。

第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。
从客户端的视角来看,我接到了服务端发送过来的响应数据包,说明服务端接收到了我在第一次握手时发送的网络包,并且成功发送了响应数据包,这就说明,服务端的接收、发送能力正常。而另一方面,我收到了服务端的响应数据包,说明我第一次发送的网络包成功到达服务端,这样,我自己的发送和接收能力也是正常的。

第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力,服务端的发送、接收能力是正常的。
第一、二次握手后,服务端并不知道客户端的接收能力以及自己的发送能力是否正常。而在第三次握手时,服务端收到了客户端对第二次握手作的回应。从服务端的角度,我在第二次握手时的响应数据发送出去了,客户端接收到了。所以,我的发送能力是正常的。而客户端的接收能力也是正常的。

经历了上面的三次握手过程,客户端和服务端都确认了自己的接收、发送能力是正常的。之后就可以正常通信了。

每次都是接收到数据包的一方可以得到一些结论,发送的一方其实没有任何头绪。我虽然有发包的动作,但是我怎么知道我有没有发出去,而对方有没有接收到呢?

而从上面的过程可以看到,最少是需要三次握手过程的。两次达不到让双方都得出自己、对方的接收、发送能力都正常的结论。其实每次收到网络包的一方至少是可以得到:对方的发送、我方的接收是正常的。而每一步都是有关联的,下一次的“响应”是由于第一次的“请求”触发,因此每次握手其实是可以得到额外的结论的。比如第三次握手时,服务端收到数据包,表明看服务端只能得到客户端的发送能力、服务端的接收能力是正常的,但是结合第二次,说明服务端在第二次发送的响应包,客户端接收到了,并且作出了响应,从而得到额外的结论:客户端的接收、服务端的发送是正常的。

用表格总结一下:

视角 客收 客发 服收 服发
客视角 一 + 二 一 + 二
服视角 二 + 三 二 + 三

四次挥手

TCP连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了。这时对方会回一个ACK,此时一个方向的连接关闭。但是另一个方向仍然可以继续传输数据,等到发送完了所有的数据后,会发送一个FIN段来关闭此方向上的连接。接收方发送ACK确认关闭连接。注意,接收到FIN报文的一方只能回复一个ACK, 它是无法马上返回对方一个FIN报文段的,因为结束数据传输的“指令”是上层应用层给出的,我只是一个“搬运工”,我无法了解“上层的意志”。

“三次握手,四次挥手”怎么完成?

其实3次握手的目的并不只是让通信双方都了解到一个连接正在建立,还在于利用数据包的选项来传输特殊的信息,交换初始序列号ISN。

3次握手是指发送了3个报文段,4次挥手是指发送了4个报文段。注意,SYN和FIN段都是会利用重传进行可靠传输的。

三次握手

三次握手

  1. 客户端发送一个SYN段,并指明客户端的初始序列号,即ISN(c).
  2. 服务端发送自己的SYN段作为应答,同样指明自己的ISN(s)。为了确认客户端的SYN,将ISN(c)+1作为ACK数值。这样,每发送一个SYN,序列号就会加1. 如果有丢失的情况,则会重传。
  3. 为了确认服务器端的SYN,客户端将ISN(s)+1作为返回的ACK数值。

四次挥手

四次挥手

  1. 客户端发送一个FIN段,并包含一个希望接收者看到的自己当前的序列号K. 同时还包含一个ACK表示确认对方最近一次发过来的数据。
  2. 服务端将K值加1作为ACK序号值,表明收到了上一个包。这时上层的应用程序会被告知另一端发起了关闭操作,通常这将引起应用程序发起自己的关闭操作。
  3. 服务端发起自己的FIN段,ACK=K+1, Seq=L
  4. 客户端确认。ACK=L+1

为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方ACK和FIN一般都会分开发送。

“三次握手,四次挥手”进阶

ISN

三次握手的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按序列号组装数据。

如果ISN是固定的,攻击者很容易猜出后续的确认号。

ISN = M + F(localhost, localport, remotehost, remoteport)

M是一个计时器,每隔4毫秒加1。
F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。要保证hash算法不能被外部轻易推算得出。

序列号回绕

因为ISN是随机的,所以序列号容易就会超过2^31-1. 而tcp对于丢包和乱序等问题的判断都是依赖于序列号大小比较的。此时就出现了所谓的tcp序列号回绕(sequence wraparound)问题。怎么解决?

/*
* The next routines deal with comparing 32 bit unsigned ints
* and worry about wraparound (automatic with unsigned arithmetic).
*/
static inline int before(__u32 seq1, __u32 seq2)
{
    return (__s32)(seq1-seq2) < 0;
}

#define after(seq2, seq1) before(seq1, seq2)

上述代码是内核中的解决回绕问题代码。__s32是有符号整型的意思,而__u32则是无符号整型。序列号发生回绕后,序列号变小,相减之后,把结果变成有符号数了,因此结果成了负数。

假设seq1=255, seq2=1(发生了回绕)。
seq1 = 1111 1111 seq2 = 0000 0001
我们希望比较结果是
 seq1 - seq2=
 1111 1111
-0000 0001
-----------
 1111 1110

由于我们将结果转化成了有符号数,由于最高位是1,因此结果是一个负数,负数的绝对值为
 0000 0001 + 1 = 0000 0010 = 2

因此seq1 - seq2 < 0

syn flood攻击

最基本的DoS攻击就是利用合理的服务请求来占用过多的服务资源,从而使合法用户无法得到服务的响应。syn flood属于Dos攻击的一种。

如果恶意的向某个服务器端口发送大量的SYN包,则可以使服务器打开大量的半开连接,分配TCB(Transmission Control Block), 从而消耗大量的服务器资源,同时也使得正常的连接请求无法被相应。当开放了一个TCP端口后,该端口就处于Listening状态,不停地监视发到该端口的Syn报文,一 旦接收到Client发来的Syn报文,就需要为该请求分配一个TCB,通常一个TCB至少需要280个字节,在某些操作系统中TCB甚至需要1300个字节,并返回一个SYN ACK命令,立即转为SYN-RECEIVED即半开连接状态。系统会为此耗尽资源。

常见的防攻击方法有:

无效连接的监视释放

监视系统的半开连接和不活动连接,当达到一定阈值时拆除这些连接,从而释放系统资源。这种方法对于所有的连接一视同仁,而且由于SYN Flood造成的半开连接数量很大,正常连接请求也被淹没在其中被这种方式误释放掉,因此这种方法属于入门级的SYN Flood方法。

延缓TCB分配方法

消耗服务器资源主要是因为当SYN数据报文一到达,系统立即分配TCB,从而占用了资源。而SYN Flood由于很难建立起正常连接,因此,当正常连接建立起来后再分配TCB则可以有效地减轻服务器资源的消耗。常见的方法是使用Syn Cache和Syn Cookie技术。

Syn Cache技术

系统在收到一个SYN报文时,在一个专用HASH表中保存这种半连接信息,直到收到正确的回应ACK报文再分配TCB。这个开销远小于TCB的开销。当然还需要保存序列号。

Syn Cookie技术

Syn Cookie技术则完全不使用任何存储资源,这种方法比较巧妙,它使用一种特殊的算法生成Sequence Number,这种算法考虑到了对方的IP、端口、己方IP、端口的固定信息,以及对方无法知道而己方比较固定的一些信息,如MSS(Maximum Segment Size,最大报文段大小,指的是TCP报文的最大数据报长度,其中不包括TCP首部长度。)、时间等,在收到对方 的ACK报文后,重新计算一遍,看其是否与对方回应报文中的(Sequence Number-1)相同,从而决定是否分配TCB资源。

使用SYN Proxy防火墙

一种方式是防止墙dqywb连接的有效性后,防火墙才会向内部服务器发起SYN请求。防火墙代服务器发出的SYN ACK包使用的序列号为c, 而真正的服务器回应的序列号为c’, 这样,在每个数据报文经过防火墙的时候进行序列号的修改。另一种方式是防火墙确定了连接的安全后,会发出一个safe reset命令,client会进行重新连接,这时出现的syn报文会直接放行。这样不需要修改序列号了。但是,client需要发起两次握手过程,因此建立连接的时间将会延长。

连接队列

在外部请求到达时,被服务程序最终感知到前,连接可能处于SYN_RCVD状态或是ESTABLISHED状态,但还未被应用程序接受。

tcp queue

对应地,服务器端也会维护两种队列,处于SYN_RCVD状态的半连接队列,而处于ESTABLISHED状态但仍未被应用程序accept的为全连接队列。如果这两个队列满了之后,就会出现各种丢包的情形。

查看是否有连接溢出
netstat -s | grep LISTEN

半连接队列满了

在三次握手协议中,服务器维护一个半连接队列,该队列为每个客户端的SYN包开设一个条目(服务端在接收到SYN包的时候,就已经创建了request_sock结构,存储在半连接队列中),该条目表明服务器已收到SYN包,并向客户发出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于Syn_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。

目前,Linux下默认会进行5次重发SYN-ACK包,重试的间隔时间从1s开始,下次的重试间隔时间是前一次的双倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s, 总共31s, 称为指数退避,第5次发出后还要等32s才知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s, TCP才会把断开这个连接。由于,SYN超时需要63秒,那么就给攻击者一个攻击服务器的机会,攻击者在短时间内发送大量的SYN包给Server(俗称SYN flood攻击),用于耗尽Server的SYN队列。对于应对SYN 过多的问题,linux提供了几个TCP参数:tcp_syncookies、tcp_synack_retries、tcp_max_syn_backlog、tcp_abort_on_overflow 来调整应对。

参数 作用
tcp_syncookies SYNcookie将连接信息编码在ISN(initialsequencenumber)中返回给客户端,这时server不需要将半连接保存在队列中,而是利用客户端随后发来的ACK带回的ISN还原连接信息,以完成连接的建立,避免了半连接队列被攻击SYN包填满。
tcp_syncookies 内核放弃建立连接之前发送SYN包的数量。
tcp_synack_retries 内核放弃连接之前发送SYN+ACK包的数量
tcp_max_syn_backlog 默认为1000. 这表示半连接队列的长度,如果超过则放弃当前连接。
tcp_abort_on_overflow 如果设置了此项,则直接reset. 否则,不做任何操作,这样当服务器半连接队列有空了之后,会重新接受连接。Linux坚持在能力许可范围内不忽略进入的连接??突Ф嗽谡馄诩浠嶂馗捶⑺蛃ys包,当重试次数到达上限之后,会得到connection time out响应。

全连接队列满了

当第三次握手时,当server接收到ACK包之后,会进入一个新的叫 accept 的队列。

当accept队列满了之后,即使client继续向server发送ACK的包,也会不被响应,此时ListenOverflows+1,同时server通过tcp_abort_on_overflow来决定如何返回,0表示直接丢弃该ACK,1表示发送RST通知client;相应的,client则会分别返回read timeout 或者 connection reset by peer。另外,tcp_abort_on_overflow是0的话,server过一段时间再次发送syn+ack给client(也就是重新走握手的第二步),如果client超时等待比较短,就很容易异常了。而客户端收到多个 SYN ACK 包,则会认为之前的 ACK 丢包了。于是促使客户端再次发送 ACK ,在 accept队列有空闲的时候最终完成连接。若 accept队列始终满员,则最终客户端收到 RST 包(此时服务端发送syn+ack的次数超出了tcp_synack_retries)。

服务端仅仅只是创建一个定时器,以固定间隔重传syn和ack到服务端

参数 作用
tcp_abort_on_overflow 如果设置了此项,则直接reset. 否则,不做任何操作,这样当服务器半连接队列有空了之后,会重新接受连接。Linux坚持在能力许可范围内不忽略进入的连接??突Ф嗽谡馄诩浠嶂馗捶⑺蛃ys包,当重试次数到达上限之后,会得到connection time out响应。
min(backlog, somaxconn) 全连接队列的长度。

命令

netstat -s命令

[root@server ~]#  netstat -s | egrep "listen|LISTEN" 
667399 times the listen queue of a socket overflowed
667399 SYNs to LISTEN sockets ignored

上面看到的 667399 times ,表示全连接队列溢出的次数,隔几秒钟执行下,如果这个数字一直在增加的话肯定全连接队列偶尔满了。

[root@server ~]#  netstat -s | grep TCPBacklogDrop

查看 Accept queue 是否有溢出

ss命令

[root@server ~]#  ss -lnt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN     0      128 *:6379 *:*
LISTEN     0      128 *:22 *:*

如果State是listen状态,Send-Q 表示第三列的listen端口上的全连接队列最大为50,第一列Recv-Q为全连接队列当前使用了多少。
非 LISTEN 状态中 Recv-Q 表示 receive queue 中的 bytes 数量;Send-Q 表示 send queue 中的 bytes 数值。

小结

当外部连接请求到来时,TCP??榛崾紫炔榭磎ax_syn_backlog,如果处于SYN_RCVD状态的连接数目超过这一阈值,进入的连接会被拒绝。根据tcp_abort_on_overflow字段来决定是直接丢弃,还是直接reset.

从服务端来说,三次握手中,第一步server接受到client的syn后,把相关信息放到半连接队列中,同时回复syn+ack给client. 第三步当收到客户端的ack, 将连接加入到全连接队列。

一般,全连接队列比较小,会先满,此时半连接队列还没满。如果这时收到syn报文,则会进入半连接队列,没有问题。但是如果收到了三次握手中的第3步(ACK),则会根据tcp_abort_on_overflow字段来决定是直接丢弃,还是直接reset.此时,客户端发送了ACK, 那么客户端认为三次握手完成,它认为服务端已经准备好了接收数据的准备。但此时服务端可能因为全连接队列满了而无法将连接放入,会重新发送第2步的syn+ack, 如果这时有数据到来,服务器TCP??榛峤荽嫒攵恿兄?。一段时间后,client端没收到回复,超时,连接异常,client会主动关闭连接。

“三次握手,四次挥手”redis实例分析

  1. 我在dev机器上部署redis服务,端口号为6379,
  2. 通过tcpdump工具获取数据包,使用如下命令

tcpdump -w /tmp/a.cap port 6379 -s0
-w把数据写入文件,-s0设置每个数据包的大小默认为68字节,如果用-S 0则会抓到完整数据包

  1. 在dev2机器上用redis-cli访问dev:6379, 发送一个ping, 得到回复pong
  2. 停止抓包,用tcpdump读取捕获到的数据包

tcpdump -r /tmp/a.cap -n -nn -A -x| vim -
(-x 以16进制形式展示,便于后面分析)

共收到了7个包。

抓到的是IP数据包,IP数据包分为IP头部和IP数据部分,IP数据部分是TCP头部加TCP数据部分。

IP的数据格式为:
ip head
它由固定长度20B+可变长度构成。

10:55:45.662077 IP dev2.39070 > dev.6379: Flags [S], seq 4133153791, win 29200, options [mss 1460,sackOK,TS val 2959270704 ecr 0,nop,wscale 7], length 0
        0x0000:  4500 003c 08cf 4000 3606 14a5 0ab3 b561
        0x0010:  0a60 5cd4 989e 18eb f65a ebff 0000 0000
        0x0020:  a002 7210 872f 0000 0204 05b4 0402 080a
        0x0030:  b062 e330 0000 0000 0103 0307

对着IP头部格式,来拆解数据包的具体含义。

字节值 字节含义
0x4 IP版本为ipv4
0x5 首部长度为5 * 4字节=20B
0x00 服务类型,现在基本都置为0
0x003c 总长度为3*16+12=60字节,上面所有的长度就是60字节
0x08cf 标识。同一个数据报的唯一标识。当IP数据报被拆分时,会复制到每一个数据中。
0x4000 3bit 标志 + 13bit 片偏移。3bit 标志对应 R、DF、MF。目前只有后两位有效,DF位:为1表示不分片,为0表示分片。MF:为1表示“更多的片”,为0表示这是最后一片。13bit 片位移:本分片在原先数据报文中相对首位的偏移位。(需要再乘以8 )
0x36 生存时间TTL。IP报文所允许通过的路由器的最大数量。每经过一个路由器,TTL减1,当为 0 时,路由器将该数据报丢弃。TTL 字段是由发送端初始设置一个 8 bit字段.推荐的初始值由分配数字 RFC 指定。发送 ICMP 回显应答时经常把 TTL 设为最大值 255。TTL可以防止数据报陷入路由循环。 此处为54.
0x06 协议类型。指出IP报文携带的数据使用的是哪种协议,以便目的主机的IP层能知道要将数据报上交到哪个进程。TCP 的协议号为6,UDP 的协议号为17。ICMP 的协议号为1,IGMP 的协议号为2。该 IP 报文携带的数据使用 TCP 协议,得到了验证。
0x14a5 16bitIP首部校验和。
0x0ab3 b561 32bit源ip地址。
0x0a60 5cd4 32bit目的ip地址。

剩余的数据部分即为TCP协议相关的。TCP也是20B固定长度+可变长度部分。

字节值 字节含义
0x989e 16bit源端口。1161616+81616+1416+11=39070
0x18eb 16bit目的端口6379
0xf65a ebff 32bit序列号。4133153791
0x0000 0000 32bit确认号。
0xa 4bit首部长度,以4byte为单位。共10*4=40字节。因此TCP报文的可选长度为40-20=20
0b000000 6bit保留位。目前置为0.
0b000010 6bitTCP标志位。从左到右依次是紧急 URG、确认 ACK、推送 PSH、复位 RST、同步 SYN 、终止 FIN。
0x7210 滑动窗口大小,滑动窗口即tcp接收缓冲区的大小,用于tcp拥塞控制。29200
0x872f 16bit校验和。
0x0000 紧急指针。仅在 URG = 1时才有意义,它指出本报文段中的紧急数据的字节数。当 URG = 1 时,发送方 TCP 就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍是普通数据。

可变长度部分,协议如下:

字节值 字节含义
0x0204 05b4 最大报文长度为,05b4=1460. 即可接收的最大包长度,通常为MTU减40字节,IP头和TCP头各20字节
0x0402 表示支持SACK
0x080a b062 e330 0000 0000 时间戳。Ts val=b062 e330=2959270704, ecr=0
0x01 无操作
0x03 0307 窗口扩大因子为7. 移位7, 乘以128

这样第一个包分析完了。dev2向dev发送SYN请求。也就是三次握手中的第一次了。
SYN seq(c)=4133153791

第二个包,dev响应连接,ack=4133153792. 表明dev下次准备接收这个序号的包,用于tcp字节注的顺序控制。dev(也就是server端)的初始序号为seq=4264776963, syn=1.
SYN ack=seq(c)+1 seq(s)=4264776963

第三个包,client包确认,这里使用了相对值应答。seq=4133153792, 等于第二个包的ack. ack=4264776964.
ack=seq(s)+1, seq=seq(c)+1
至此,三次握手完成。接下来就是发送ping和pong的数据了。

接着第四个包。

10:55:48.090073 IP dev2.39070 > dev.6379: Flags [P.], seq 1:15, ack 1, win 229, options [nop,nop,TS val 2959273132 ecr 3132256230], length 14
        0x0000:  4500 0042 08d1 4000 3606 149d 0ab3 b561
        0x0010:  0a60 5cd4 989e 18eb f65a ec00 fe33 5504
        0x0020:  8018 00e5 4b5f 0000 0101 080a b062 ecac
        0x0030:  bab2 6fe6 2a31 0d0a 2434 0d0a 7069 6e67
        0x0040:  0d0a

tcp首部长度为32B, 可选长度为12B. IP报文的总长度为66B, 首部长度为20B, 因此TCP数据部分长度为14B. seq=0xf65a ec00=4133153792
ACK, PSH. 数据部分为2a31 0d0a 2434 0d0a 7069 6e67 0d0a

0x2a31         -> *1
0x0d0a         -> \r\n
0x2434         -> $4
0x0d0a         -> \r\n
0x7069 0x6e67  -> ping
0x0d0a         -> \r\n

dev2向dev发送了ping数据,第四个包完毕。

第五个包,dev2向dev发送ack响应。
序列号为0xfe33 5504=4264776964, ack确认号为0xf65a ec0e=4133153806=(4133153792+14).

第六个包,dev向dev2响应pong消息。序列号fe33 5504,确认号f65a ec0e, TCP头部可选长度为12B, IP数据报总长度为59B, 首部长度为20B, 因此TCP数据长度为7B.
数据部分2b50 4f4e 470d 0a, 翻译过来就是+PONG\r\n.

至此,Redis客户端和Server端的三次握手过程分析完毕。

总结

“三次握手,四次挥手”看似简单,但是深究进去,还是可以延伸出很多知识点的。比如半连接队列、全连接队列等等。以前关于TCP建立连接、关闭连接的过程很容易就会忘记,可能是因为只是死记硬背了几个过程,没有深入研究背后的原理。

所以,“三次握手,四次挥手”你真的懂了吗?

参考资料

【redis】https://segmentfault.com/a/1190000015044878
【tcp option】https://blog.csdn.net/wdscq1234/article/details/52423272
【滑动窗口】https://www.zhihu.com/question/32255109
【全连接队列】//jm.taobao.org/2017/05/25/525-1/
【client fooling】 https://github.com/torvalds/linux/commit/5ea8ea2cb7f1d0db15762c9b0bb9e7330425a071
【backlog RECV_Q】//blog.51cto.com/59090939/1947443
【定时器】https://www.cnblogs.com/menghuanbiao/p/5212131.html
【队列图示】https://www.itcodemonkey.com/article/5834.html
【tcp flood攻击】https://www.cnblogs.com/hubavyn/p/4477883.html
【MSS MTU】https://blog.csdn.net/LoseInVain/article/details/53694265

“三次握手,四次挥手”你真的懂吗?,首发于山西十一选五手机版。

]]>
//www.brhi.net/114633/feed/ 2
从软件工程的角度解读任正非的新年公开信 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114605/ //www.brhi.net/114605/#respond Tue, 08 Jan 2019 05:52:57 +0000 //www.brhi.net/?p=114605 软件工程就像一个国家的农业,是最基础的设施!

从软件工程的角度解读任正非的新年公开信,首发于山西十一选五手机版。

]]>
近日任正非的公开信《全面提升软件工程能力与实践,打造可信的高质量产品》刷屏了,作为一个软件工程专业科班出身的软件开发从业者,自然是引起了我(@宝玉xp)的好奇。仔细阅读之下确实让我大吃一惊,看似八股官方文,但细看之下是作者对于软件工程的理解确实非常深刻,各种专业术语信手拈来,比喻恰到好处。

我对华为的研发其实一直挺好奇的,从传统的硬件公司,到现在软硬件齐头并进,华为手机销量都已经超过了苹果,可见华为的软硬件研发实力早已是全球领先了。公开信中的这一句:

二十年前的 IPD 变革,重构了我们的研发模式,实现了从依赖个人、偶然性推出成功产品,到制度化、持续地推出高质量产品的转变。

也揭示了华为的软件研发能做到领先水平的原因。

华为是在 1999 年开始从 IBM 引进 IPD 的,到今年 2019 年正好 20 年,在过去的 20 年里,IPD 帮助华为从游击队变成了正规军,研发队伍从几千人到几万人,软件产品也覆盖到手机操作系统、应用、云服务。

我对 IPD 是不甚了解的,只知道 IPD(Integrated Product Development,集成产品开发)是一种产品开发方法,但如果说软件产品的开发方法,我是比较熟悉的,那就是软件工程么!

任正非发出的这封信的大背景也很特殊,2018 年中美贸易战开始,中兴、华为首当其冲成为美国开刀的对象,跟风站队的澳大利亚、新西兰、英国也跳出来抵制华为,说华为不安全,可能含有间谍软件,窃听国家机密,这帽子一扣是很难扯清的!这就是为什么整封信从标题开始,一共 17 次提到两个关键字:“可信”。

只有让客户觉得华为的产品“可信”,华为才能尽快走出这场?;?,那么怎么才能做到可信?

如果你是餐厅老板,有人造谣你的厨房脏乱差,员工上完厕所不洗手,你怎么办?最好的办法自然是用先进的管理流程,并且让整个做菜的过程尽可能公开透明。

所以信中有这样一句话:

我们要转变观念,追求打造可信的高质量产品,不仅仅是功能、特性的高质量,也包括产品开发到交付过程的高质量。

要转变观念,不再只认结果的质量,还要追求过程质量了!而如何追求过程质量呢?那就是要:“全面提升软件工程能力和实践

如果信到此为止,也就是个普通官方八股文了。领导们么,可不就是喜欢指个大方向,说你们要用软件工程,要实施软件工程,至于怎么用,那是你们的事情,毕竟做领导的哪有几个真的懂软件工程的,难得的是这封信居然有很多具体怎么做的内容。

软件项目管理金三角

先看这一句:

我们各级管理者和全体员工都不得以进度、功能、特性等为理由来降低可信的要求,确??尚诺囊笤谥葱泄讨胁槐湫?。

振聋发聩呀同志们,热泪盈眶呀!生活中多少次:三个月的项目老板说你一个月就要给我做完;做到一半的项目,PM 说这个功能很重要,我们要加上去。最终怎么办?牺牲质量呗!又想要马儿跑得快又想要马儿不吃草,天底下哪有那么好的事情!

软件工程里面早就告诉我们了:时间、范围、成本这三个要素直接决定了产品的质量!

?希望各位老板别光学乔布斯,也学学任正非!

程序开发

2018年底程序员被裁的不少,很多程序员开始担忧起前景来,其实如果你能做到这下面要求的应该是不担心被裁的!

我们要从最基础的编码质量做起,视高质量代码为尊严和个人声誉。代码就像是高楼大厦的一砖一瓦,没有高质量的代码,可信的产品就是空中楼阁。我们要优化并遵循公司各种编程规范,遵从架构与设计原则,熟练使用各种编程库和API,编写出简洁、规范、可读性强、健壮安全的代码。

这一段是说给我们程序员看的,这其实也是对程序员的基本要求,大家看看自己,看看身边,真能做到的有多少?像我一样觉得自己还做的不够好的,咱还是努力学习吧,多练练,多用点心肯定更没问题的。

架构

说完程序员开始说架构师了:

我们要深刻理解架构的核心要素,基于可信导向来进行架构与设计。

看到没有,又提到可信了,架构设计的时候,别再天马行空,啥新酷用啥,啥流行用啥,一定要“可信导向”,架构设计目标先搞清楚!

再是细节:

在确??尚诺那疤嵯?,要在性能、功能、扩展性等方面做好权衡;慎重地定义我们的??橛虢涌?,真正做到高内聚与低耦合;我们要遵循权限和攻击面最小化等安全设计原则,科学设计??橹涞母衾胗虢涌?,提升安全性;低阶架构与设计要遵循高阶的架构与设计原则,在充分理解原有架构与设计的情况下,持续优化;我们要熟悉各种设计模式,重用公共成熟组件和服务,避免重复劳动。

“高内聚与低耦合”,“权限和攻击面最小化”,“??橹涞母衾胗虢涌凇?,“重用公共成熟组件和服务”……道理我都明白,做到可不容易!

技术债务

华为这些年高速发展,早些年为了追求速度肯定也没少走捷径,这些年下来也肯定没少欠技术债务,现在也是一个从追求速度到追求质量转型的契机。所以信中说完架构开始讲技术债务了:

我们要重构腐化的架构及不符合软件工程规范和质量要求的历史代码。我们知道,再好的架构,其生命力也是有限的。随着时间的推移、环境的变化以及新技术、新功能特性的引入,架构也会腐化。面对腐化了的架构,要毫不犹豫地去重构它。同时主动以可信设计原则为导向,去重构不符合软件工程规范和质量要求的历史代码,提升软件架构的生命力。

我们都知道,没有万能的架构,只有适合当时需求,当时技术条件和人员的架构,时间推移了很多架构就满足不了要求了,就需要重构了!作为80后,小时候其实生活挺艰苦的,那时候我们穿衣服都讲究的是:“新三年,旧三年,缝缝补补又三年”,架构也一样嘛,不满足需求我们先修修补补,真要重构挑战还是不小的,但是不去做它会一直成为发展的一个障碍,这封信也算是推了一把:“面对腐化了的架构,要毫不犹豫地去重构它?!?,当然你重构,也不要忘记“可信”这个根本目标:“同时主动以可信设计原则为导向”。

其实Google在这方面已经走在前面了,一直鼓励重写代码,任何软件每隔几年就重写一遍,这样可以优化代码,采用最新技术,去掉一些没有价值的功能,最重要的是让新员工得到锻炼,保持高昂的斗志。不知道这点是不是华为在像Google学习!

安全

这些年,互联网发展很快,但是安全事故却层出不穷:开房记录被泄漏、密码被泄漏、比特币被盗……这暴露出业界其实对安全是不够重视的,所以信中也不止一次提到安全问题:

公司已经明确,把网络安全和隐私?;ぷ魑镜淖罡吒倭??!?/p>

“我们要深入钻研软件技术,尤其是安全技术?!?/p>

“我们要遵循权限和攻击面最小化等安全设计原则,科学设计??橹涞母衾胗虢涌?,提升安全性”

“编写出简洁、规范、可读性强、健壮安全的代码。

要打造一个“安全”的软件,就是首先要有安全意识,然后要懂安全技术,在整个开发过程中要从架构设计、代码方方面面去注意。

技术是工具

这些年开发界一直有些不好的风气,就是都认为自己的技术是最牛的,写后端的看不上前端的,用angular的看不上vue,写PHP的认为自己的语言是全世界最好的,开发的还看不上测试的。但是信中这一句话不要忽视呀:“软件技术是我们打造产品的基本工具”,技术只是工具,只是我们用来打造产品的工具!

“技术是否先进,技术选择是否合理,将决定我们软件的高度;”,技术的选型,不仅看的是不是先进,还要看是不是适合当前产品项目,并不是什么什么新酷就用什么!

“我们要深入学习架构与设计、编码、测试、安全、可用性、性能、维护性、体验等技术,并科学运用这些技术?!?,既然技术只是工具,那么我们就没必要给自己设置各种技术壁垒障碍。如果开发就只学编码,测试就只学测试,认为安全那应该是搞安全的事,这样的话是非常不利于团体协作的,每个人都在一个领域能有深入的钻研,同时对其他领域有一定了解,对个人,对团队是非常有利的一件事。这样也不需要DevOps这种为了兼顾开发、测试、运维三种角色而存在的工种!

一致性

我们做软件开发的都知道,也看过很多段子:从客户的需求,到最终的实现,总是差别很大;我们在项目初始的时候制定了很多规范,却总是不了了之,难以执行;我们良好的设计,在编码实现的时候,因为赶进度、开发人员偷懒等各种原因绕开设计,抄近路,最后设计和编码无法一致……

一致性在软件开发领域一直都是理想美好而现实却很残酷,信中也提到:

我们要遵守过程的一致性。遵守适用的法律法规、遵循业界共识的标准、规范,确保规范到实现的一致性、代码到二进制的一致性。架构要符合架构原则,设计要遵循设计模式,代码要符合编程规范,最终做到需求与实现一致,达成各项对客户的承诺。我们只有脚踏实地做好每一步,才能真正打造出可信的高质量产品。

无论这个目标有多难,但是从“遵守过程的一致性”开始,在每个阶段都去做到一致性,“脚踏实地做好每一步”,还是有希望做到,“真正打造出可信的高质量产品”。

改变习惯

在实施软件工程的过程中,有两个难题,一个就是转变思想,另一个就是改变习惯了,这种改变的过程也一定是很痛苦的。

为此,我们要改变行为习惯,追求精品。我们要开放透明、积极和勇于揭示问题并主动推动改进。软件开发是一种创造性和艺术性的工作,需要充分发挥我们的聪明才智和潜力。我们要改变只重视功能结果、不重视代码质量的行为习惯,要严格遵守软件工程规范;改变被动的修修补补;改变碎片化知识获取,主动去学习提升并贡献经验、代码,形成共享知识库。我们需要改变的行为和习惯还有很多,对绝大多数人来讲都将是一个痛苦的转变过程,会脱一层皮,但我相信大家能够迎接这种挑战。

从事软件开发工作越久,恐怕养成的坏习惯就越多,信中列的几条都很有代表性:

  • “只重视功能结果、不重视代码质量”
    “功能实现完了就完事了,质量那是QA的事”,这种坏习惯不改质量是很难有保障的
  • “不遵守软件工程规范”
    软件工程的各种规范不是约束,也不是摆设,而是实实在在为了团队整体更好的协作。对于定好的规范,要严格执行,不合理的规范,也要提出来一起改进。
  • “被动的修修补补”
    为了能继续凑合,继续修修补补,而没有考虑重构改进,也是一个不好的习惯。
  • “碎片化知识获取,不主动去学习提升”
    在现在的信息时代,碎片化的知识获取是容易的,但是像软件工程这种知识,仅仅通过碎片化的学习还是不够的,必须的主动的,系统的去学习,虽然这个过程会很辛苦,但是是非常有必要的。
  • “不愿意贡献经验、代码,不去形成共享知识库”
    很多人不愿意去分享知识和经验,有的是因为太懒,有的是觉得没什么好处。但是分享本身就是一个学习和提升的最好手段!知识库这种事不仅是对别人,对自己也是一个特别好的过程。
    想象下你新加入一个团队,如果这个团队有很好的知识库,你可以通过知识库很快的上手工作,同样的,如果你把你的经验写到知识库,后面的新人也可以受益你的贡献!

“软件工程”和“质量工程”需要依靠架构技术

“软件工程”和“质量工程”需要依靠架构技术,而不是依靠CMM和QA管理流程。一切工程问题,首先要思考能否通过技术解决,当前技术无法解决的问题,暂时由管理手段代劳,同时不停止寻找技术手段。

所有的涉及到人的管理最终都要归结到人管理还是制度管理的问题上,软件项目管理也不例外,如果过多的依赖于人的管理,那么项目经理的职责就太重了,优秀的项目经理本身就是稀缺资源,最终会变成一个瓶颈。

所以通过架构技术和工具,把管理流程落实下来是一个非常好的方式。有两个例子可以很好的说明这点。

早些年软件项目团队是非常庞大的,各个服务庞大??榻裘?,所以管理成本很高,后来微服务这种架构提出后,将大的服务拆成小的服务,整个组织也从大项目部门拆分成各个小组,各小组可以独立更新维护。

另一个例子是以前单元测试和代码审查还有自动部署很难执行,后来借助源代码管理工具和CI(Continuous integration,持续集成)工具,就可以很容易的进行代码审查、并且可以确保单元测试测试跑通过后才进行部署。这一点其实信中也有体现:

我们将全面强化以Committer角色为核心的代码审核和提交机制,代码经过更加严格和系统的审核才能合入版本。为此我们将建立一支更高水平的Committer角色群体,负责软件架构的看护、代码的审核和提交,整体保障合入代码的高质量。我们要变革考核机制,要让架构设计好、代码写得好的人脱颖而出,对编程能力不满足要求的人给予帮助和培训。但任何人如果编写的代码长时间不能合入版本,将会被团队抛弃。

软件工程就像一个国家的农业

软件工程就像一个国家的农业,是最基础的设施!

很感动,这些年软件工程被提起的其实不多,大家关注的更多是各种新酷的技术,而对于这种软件开发最基础的理论视而不见?;褂腥艘惶岬饺砑こ?,就马上说软件工程不是银弹。软件工程从来不说自己是银弹,就像现代医学,也不会号称自己包治百病,只会不断改进,对症下药!

希望这封信能带动软件工程在国内的更多发展,也希望我这篇浅显的文章能帮助大家更好的理解一些软件工程的概念。

从软件工程的角度解读任正非的新年公开信,首发于山西十一选五手机版。

]]>
//www.brhi.net/114605/feed/ 0
追思杰出的 Linux 内核开发者李少华 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114630/ //www.brhi.net/114630/#comments Tue, 08 Jan 2019 05:45:47 +0000 //www.brhi.net/?p=114630 李少华是我们这一辈 Linux 内核开发者之中的杰出代表,应该说是目前华人圈最优秀和最重要的 Linux 内核开发者之一,他除了是内核子系统 MD(软raid)的维护者之外,在块设备层、I/O 调度器、NVMe、内存管理、电源管理等领域都做出了非常重要的工作。

追思杰出的 Linux 内核开发者李少华,首发于山西十一选五手机版。

]]>
2018 年最后一天,我在商场溜娃,忽然看到少华妻子的朋友圈提到李少华于 12 月 28 日离开了我们。我知道少华前阵子身体不好但有好转,这心痛的消息来得太突然,眼泪一下就出来了。家人也发现了我忽然情绪低沉,我忍不住眼泪颤抖的说“少华走了”。

李少华是我们这一辈 Linux 内核开发者之中的杰出代表,应该说是目前华人圈最优秀和最重要的 Linux 内核开发者之一,他除了是内核子系统 MD(软raid)的维护者之外,在块设备层、I/O 调度器、NVMe、内存管理、电源管理等领域都做出了非常重要的工作。他的代码和对内核的改进,融合在整个内核在 IO 和存储站的方方面面,真的是通过很具体的工作,贡献了正能量,让这世界变得更美好了些许。

和少华家人联系后得知,在最后的时候,少华还时不时的提起“我有了一个新的想法”,“这段代码路径还可以再优化改进”。这是一个多么纯粹的人,对系统软件技术充满的虔诚的纯真的热爱,我心想恐怕也只有这般专注,才能成为一个改变世界的人。很多人活一辈子,恐怕也没有少华这三十几年来的闪亮和耀眼。一辈子活成这样,从个人来说,没有遗憾,是我们心目中的英雄!

我和少华最直接的工作交集,我印象最深的有两件事情。

第一件事情是我在解决 MD raid1在 NVMe SSD 上的读写性能瓶颈时,少华认为我的修改可能会在多层 MD 设备堆叠的时候引入死锁,而我认为不会。我们在邮件列表里来回讨论了很多次,他指导我来理解MD代码中的一些细节,最终我们发现 MD raid1 在发射 IO 的时候会从另外一个 raid1d() 线程来处理,所以不会在 generic_make_request() 里面发生死锁,少华鼓励我“这是很好的讨论”,然后接受了我的 patch。而后来少华还发现了我的 patch 里的其他问题,直接就修掉了。在少华的帮助下,除了我们将 MD raid1 的读性能提升了好几倍之外,我也对 MD 的基本原理有了更深刻的认识。

第二件事情是有用户报告 MD raid0 在 NVMe SSD 上做 trim 的时候时间非常久,我自己测试在 15T 的 NVMe raid0 设备上格式化 xfs 文件系统(加trim)需要 300 多秒,绝大多数时间都用在 trim 这里了,这是很不正常的。少华分析这是因为 raid0 会将上层发来的 discard bio 按照 stripe 大小做切分,然后再发送到 raid0 组成的不同硬盘上去,所以当 raid0 容量比较大的时候,原本的一个 discard bio 可能会被切分成几百甚至上千万个小 bio 来处理,这性能一下就降低了。

我们的思路是将切分后的 bio 再根据每一个 raid0 的组件设备拼接起来,最后可以组成连续的一个或者极少数几个 bio,然后再将拼接后的bio发送到每一个组件设备上去,这样就可以降低几百万个 bio 了。我先写了一个很复杂的 patch,来准确的将所有切分的 bio 按照每个设备一个 bio 的方式拼接起来。虽然拼接出来的 bio 最少,但是代码非常难读懂。少华后来做了一个实现,大概几十行代码,做了一个次优化版本:

raid0_handle_discard()核心代码片段raid0_handle_discard()核心代码片段

少华的代码,通过一个很优雅的循环,就完成了将分布在不同组件设备上的 bio 的拼接,并且在绝大多数通常情况下,拼接效果和我的复杂代码一样,速度还更快!最后我测试出来,少华的这个代码可以将在 raid0 上格式化 xfs 文件系统的速度从 300 多秒降低到 20 多秒。当我阅读少华的 patch 时,心中充满的欣赏和愉悦,能看到一段更优雅和高效的代码,真的是非常愉悦的事情,而且这种好心情可以持续很久,每每想起都会觉得开心。

再后来我接手了 bcache 子系统的维护工作,和少华一起密切合作的机会就少了很多。但我一直在关注他在内核里的工作,看到他继续在做的很多优秀的工作。非常切合实际的说,少华是为 Linux 内核做贡献的最杰出和最重要的中国人之一,从全球华人的范围来看他的工作重要性也能够进入前 20 位。而他还这么年轻,还不到 40 岁,实在是我们这辈人之中的璀璨明星,能和他一起工作是我的骄傲和荣幸!

在写这些文字的时候,不禁回想起从最初认识少华,那时他在 Intel OTC 我在 SUSE Labs,他在做性能相关,我在做文件系统。然后2009 年我们在 CLSF[1] 会议上第一次见面,然后 2010 年他和其他 Intel 朋友一起帮忙在 Intel 紫竹园区举行第二届 CLSF。再后来他离开了 Intel 去了存储领域创新独角兽公司 Fusion IO,在 PCIe SSD、IO 调度器和块设备层做了大量的优秀工作。在我加入阿里组建淘宝内核组的时候,他已经决定去 Facebook 内核团队,和 Jens Axboe、Chiris Mason、Tanjun Heo 等国际顶级内核黑客一起工作,很遗憾没有机会和他做同事。少华一直是我们这一批人中最耀眼最杰出的极少数几个人。我在 2016 年遇到参加 Kernel Summit 的 Tanjun Heo 的时候,他特意反复几次的提到,少华非常优秀,在块设备层做了很多优秀的工作。能够被顶级黑客这样评价的人,少之又少,而我认识的国人之中大概也就两三个人吧。当时我意识到,经过长期不懈的努力工作,在 Linux 内核开发领域,少华已经跻身全球最好的 IO 栈开发者之列了。

从 git log 里看到少华的大量的贡献,我感觉到少华虽然人离开了我们,但是他的代码,仍然散发着他的才华,继续在为世界做贡献。在今后的工作中,我们还会继续阅读他的代码,就像和他在谈心,对于熟悉他的人而言,这虽然心酸,也是和老朋友对话的最好的方式。而我也知道,他的代码以及他所维护的 MD 子系统,在工业届被广泛应用。譬如现在火热的 PD-1/PD-L1 生物制药领域,通过大数据的方式来分析 DNA 特征,其中的信息处理系统中 Linux 内核和高性能 IO 栈肯定被广泛地使用到了。少华虽然离开了我们,他为这个世界留下的贡献,仍然在为创造更美好的世界发挥价值。

我们都会离开这个世界,或早或晚,命不在长短而在价值,活得有价值是很幸运的。很痛心少华过早离开了我们,但我也很羡慕他的人生活出了精彩,他自己的不懈奋斗为这操蛋的世界带来了更多的光亮。这光亮,对家人对朋友,都是鼓励和激励,让我们继续在这操蛋的世界里,为更好的世界努力不懈,也享受生活中点滴的幸福。感激少华,你短暂的一生所创造的温暖和快乐,让我感受到这世界多一点的善意和希望。

CLSF[1]: China Linux Storage, Memory management & File system Workshp

追思杰出的 Linux 内核开发者李少华,首发于山西十一选五手机版。

]]>
//www.brhi.net/114630/feed/ 1
在 Linux 上使用 tarball - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114628/ //www.brhi.net/114628/#respond Mon, 07 Jan 2019 12:57:28 +0000 //www.brhi.net/?p=114628 Tarball 提供了一种在 Linux 系统上备份和管理一组文件的通用方法。请按照以下提示了解如何创建它们,以及从中提取和删除单个文件。

在 Linux 上使用 tarball,首发于山西十一选五手机版。

]]>

Tarball 提供了一种在 Linux 系统上备份和管理一组文件的通用方法。请按照以下提示了解如何创建它们,以及从中提取和删除单个文件。

“tarball” (LCTT 译注:国内也常称为“tar 包”)一词通常用于描述备份一组选择的文件并将它们打包在一个文件中的一种文件格式。该名称来自 .tar 文件扩展名和 tar 命令,它用于将文件打包到一个文件中,有时还会压缩该文件,使其在移动到其它系统时更小。

tarball 通常用于备份个人或系统文件来创建存档,特别是在进行可能需要撤消的更改之前。例如,Linux 系统管理员通?;嵩诟挠τ弥按唇ò幌盗信渲梦募?tarball,以防必须撤消这些更改。从 tarball 中解压文件通常比在备份中搜索文件快。

如何在 Linux 上创建 tarball

使用如下命令,你可以在单条命令中创建 tarball 并压缩它。

$ tar -cvzf PDFs.tar.gz *.pdf

其结果是一个压缩文件(gzip 压缩的),其中包含了当前目录中的所有 PDF 文件。当然,压缩是可选的。一个稍微简单的只是将 PDF 文件打包成未压缩 tarball 的命令:

$ tar -cvf PDFs.tar *.pdf

注意,选项中的 z 将文件变成压缩的。 c 表明创建文件,v(详细)表示你在命令运行时需要一些反馈。如果你不想查看列出的文件,请忽略 v。

另一个常见的命名约定是给压缩的 tarball 命名成 .tgz 而不是双扩展名 .tar.gz,如下所示:

$ tar cvzf MyPDFs.tgz *.pdf

如何从 tarball 中解压文件

要从 gzip 压缩包中解压所有文件,你可以使用如下命令:

$ tar -xvzf file.tar.gz

如果使用 .tgz 命名约定,该命令将如下所示:

$ tar -xvzf MyPDFs.tgz

要从 gzip 包中解压单个文件,你可以执行几乎相同的操作,只需添加文件名:

$ tar -xvzf PDFs.tar.gz ShenTix.pdf
ShenTix.pdf
ls -l ShenTix.pdf
-rw-rw-r-- 1 shs shs 122057 Dec 14 14:43 ShenTix.pdf

如果未压缩 tarball,你甚至可以从 tarball 中删除文件。例如,如果我们想从 PDFs.tar.gz 中删除我们上面解压过的文件,我们会这样做:

$ gunzip PDFs.tar.gz
$ ls -l PDFs.tar
-rw-rw-r-- 1 shs shs 10700800 Dec 15 11:51 PDFs.tar
$ tar -vf PDFs.tar --delete ShenTix.pdf
$ ls -l PDFs.tar
-rw-rw-r-- 1 shs shs 10577920 Dec 15 11:45 PDFs.tar

请注意,我们在删除 ShenTix.pdf 后,缩小了一点 tarball 文件占用的空间。如果我们想要,我们可以再次压缩文件:

$ gzip -f PDFs.tar
ls -l PDFs.tar.gz
-rw-rw-r-- 1 shs shs 10134499 Dec 15 11:51 PDFs.tar.gzFlickr / James St. John

丰富的命令行选项使得 tarball 使用起来简单方便。

 

在 Linux 上使用 tarball,首发于山西十一选五手机版。

]]>
//www.brhi.net/114628/feed/ 0
微软变了!招程序员的流程完全改了 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114610/ //www.brhi.net/114610/#comments Sat, 05 Jan 2019 11:42:51 +0000 //www.brhi.net/?p=114610 微软真的是变了!

微软变了!招程序员的流程完全改了,首发于山西十一选五手机版。

]]>
【伯乐在线导读】:在微软新 CEO?萨蒂亚·纳德拉的领导下,微软试图改革公司文化,让整个公司朝着同一个方向发展。为此,微软在 2016 年开始研究「替代面试框架(Alternative Interview Framework)」。这是一种在开发部门进行面试的新方式,目的是减少偏见,更好地测试求职者在工作中实际需要的技能。该团队首先在自己身上测试了面试过程,并在过程中不断改进。

本文作者约翰·蒙哥马利(John Montgomery)是微软项目管理合伙人,他也是新面试流程的负责人。本文写于 2018 年 12 月 15 日,原标题:《Rethinking how we interview in Microsoft’s Developer Division | 对微软开发者部门招聘流程的反思》。以下是伯乐在线的译文:


 

几年前,我有了一系列的顿悟。我刚刚和团队讨论了我们将如何改变项目经理的角色。少关注待办事项,多关注业务;少强调“知道”,多强调“学习和质疑”;更多地关注与客户一对一的接触,而不是聚合数据。我们想把能帮助我们改变这种文化的人带到团队中,但我们仍然在问同样的面试问题,使用同样的面试风格。所以反思了我们是如何面试候选人的,并想出了一些适合我们的方法。

新面试流程我们已经用了一段时间了,我认为有必要分享一下我们所做的,以及学到的东西。

第?1 个顿悟:我们仍然在问一些过时(和无效)的面试题

第一个顿悟是我首次从程序员转为项目经理之后,我们注意到,我们仍然在问过去 10 年或更长的时间里一直在问的面试题。如果我们想寻找能够将不同技能和观点的新人带入团队,那些面试题就没有意义了。(补充:我在微软工作时,我们还在问为什么下水道井盖是圆的,一架波音 747 飞机能装满多少个乒乓球,以及如何反转一个链表。在微软工作的 20 年中,我还没有编写代码来反转链表,或用任何类型的球堆满波音 747。)

不仅如此,有时两个面试官会无意中问同样的基本问题。即使在协调面试题的时候,面试官之间也是共享着相同的面试题目录。有些基于行为的面试题并不可怕,但我们并没有特别有效地运用它们。

 

第 2 个顿悟:并不是人人都能在快节奏、高压力的环境中超常发挥

第二次顿悟是在一次会议上。点子冒出来的很快,与会者听着对方的话语,试图把自己的想法融入谈话,我们很快就做出了一些重大决定。至少,我确信这将是一件大事。(我当时被一个客户问题分散了注意力,我正与客户经理和工程团队一起解决客户的问题。这是另一个故事。)不管怎么说,这次会议已经达到了高潮,其中一位与会者,我团队中的一名 PM,他非常聪明,也很安静。他说,“我只是在网上搜索有关我们主题的信息,是的,这个想法行不通?!彼奶缺日庖?。但让我顿悟的是,并不是所有人都能在那些快节奏的头脑风暴会议中出色表现。很多人(包括我)更喜欢坐着喝杯咖啡,看看数据,试着把事情想清楚。更重要的是,在我的职业生涯中,几乎没有任何一次我们做出重大决定的时候,没有不是离开一段时间,然后用新的眼光、新的数据和新的客户研究来处理点子。

但大多数面试都是快节奏的,那些你从未遇到过的问题,你能有多快地解决?

 

第 3 个顿悟:观察新人工作能力的最佳方式,是和他们一起工作

在我和一些工程团队谈论他们是如何将新人带入他们团队的时候,第三个顿悟冒出来了??⒄卟棵抛隽撕芏嗫垂ぷ鳎?NET Core、VS?Code、TypeScript 等等。作为面试过程的一部分,我们开发团队已经开始与应聘者一起解决某个问题或特性。这就是候选人在和团队一起合作,解决实际问题的过程。

把点子写下来,然后迭代

既然“写作就是思考”,我就给自己写了一封电子邮件,讨论我们团队面试流程能有什么样的变化。然后我把自己的想法分享给团队成员,我们开始迭代。 Karen Ng、Amanda Silver、Cindy Alvarez、Nathan Halstead、Anthony Cangialosi、Jeff McAffer、Jessica Rich、Travis Lowdermilk 等成员参与了测试、迭代和测试。

当我们准备推出新面试流程时,我们从很小的地方开始,然后继续学习、迭代,然后扩展。现在,这个新流程(我们称之为“另类面试框架”,因为我们中没有人特别擅长命名事物)是我们的标准实践。我们不断地完善和学习,它对我们非常有效。

下面是我们做的一些不同的事情。

1、提前分享面试

首先,我们要提前几天让候选人知道面试日是什么样的,我们要解决什么样的问题。我们给他们时间去做研究和思考。这并不是说每天上班都是件惊喜的事,那么面试为什么要这样呢?

2、用真实的问题

其次,我们采用了团队正在努力解决的一个实际问题,比如:提高满意度、增加留存率、促进服务或特性的使用。事实上,这是我们正在解决的一个真正的问题,这有助于促进合作对话。

3、候选人可以查阅资料

第三,我们让候选人能拿到与正式员工相同的资料。在面试过程中,他们可以自由上网或搜索更多数据。我们经常向候选人提供我们的客户研究、使用数据、设计和模型——几乎我们所有的一切。

4、让面试更有互动性

我们不是简单地向求职者提问题。面试官和求职者要一起解决问题,所以我们就把求职者当同事看待,一起来解决特定的实际问题。

5、遵循单一场景/问题

第五,我们在面试日都遵循一个单一的场景/问题,带领求职者过一遍类似 PM 会经历的过程,从客户或业务问题开始,理解客户待完成的工作要,设计解决方案,将解决方案交到客户手中,最终让客户使用并爱上它。每次面试都侧重于这一流程中的某一不同的阶段。

6、两个面试官

第六,我们为面试官配对。与一对一的面试不同,我们每次面试都从团队中挑选两个人。我们的最初动机是培养更多的面试官,但有两个面试官在一起还有其他好处。与多个合作者的谈话,不仅更有活力,而且让我们有机会从多个角度观察同一场面试谈话。并不是每个人对同一场面试谈话都有同样的看法,所以,它给了我们一种方法来检查在相同谈话中的无意识偏见。

7、把反馈留到最后

面试官之间的反馈,我们把保留到面试日结束之时。我们希望每位面试官只根据他们谈话的优点来判断候选人,而不是根据他们之前面试官的意见。我们告诉面试官,不要向别人暗示他们是否倾向于推荐我们雇佣某人。他们把候选人交给下一轮面试官,并总结我们在上一环节中学到的东西。在面试日结束的时候,每个面试官同时提出他们的建议,并解释相应理由。

8、总结面试流程的优缺点

第八,在每次面试循环的最后,我们不仅要讨论我们在面试中所学到的东西,还要讨论在面试过程中哪些有用,哪些没用。我们再把这些反馈重新应用到面试流程中,流程会变得更好。

 

关于新的面试流程,我或许忘了一些,但上述 8 个方面是最重要的。

我们学到了什么?

嗯,我们担心候选人会紧张。面试日有两个面试官和候选人在一起,有真实的问题,还有真实的数据。几乎每个候选人都主动反馈说,新面试流程是独一无二的,真正帮助他们了解我们的业务和团队。即使是那些没有收到录用通知的求职者,也喜欢新的面试流程,明白我们为什么没有录用他们。

不断优化面试流程

我们发现还是有不少要改进的地方。例如,我们 PM 团队是非常有技术性的。许多 PM 将代码?check in 生产产品。这对我们来说是有意义的:我们面试流程的「客户」是开发者,因此拥有来自于创建软件的那种客户理解,是很有帮助的。但在这个过程中,我们并没有很好地深入了解应聘者的技术技能。因此,我们增加了一个面试环节,以进行更技术性的互动。

后勤

我们了解到,相对标准面试流程,新流程的一些相应准备工作真心不容易。举个例子,由于求职者正在处理同样的问题,并在白板上写下他们以后需要的东西,我们需要为求职者保留一个专门的空间(会议室或?Focus Room),面试官会去找求职者。

(补注:Focus Room 是一个消除压力的环境,可以帮助你在缓解压力的同时传递信息。)

面试过程的成本不菲

我们了解到面试是“昂贵的”?!懊看瘟礁雒媸怨?,我们的时间和人力投入翻了一番,大大增加了日程安排的复杂性?!比欢?,在最初对费用有些抱怨之后,团队中的每个人都开始看到了好处,就像团队中有更多的人见到了我们潜在的新员工。所以,我们愿意付出这些代价。

最终,招聘流程的目标是把优秀的人员带入团队或公司——确保他们是合适的,会成功,并为他们创造良好的经验,他们才会想要加入。我们和其他几家西雅图的大型科技公司都向一位求职者提供了竞争性的工作机会,她特别选择了我们的团队,因为她非常喜欢这个过程。她碰巧成为了我们在早期实验阶段的候选人之一,她还在这里上班,做的很好。其他很多候选人也类似。所以我说,我们的新面试流程表现的很好。我们仍在学习,但到目前为止,新面试流程的成果已超过我们的预期。

微软变了!招程序员的流程完全改了,首发于山西十一选五手机版。

]]>
//www.brhi.net/114610/feed/ 1
cat 命令的源码进化史 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114591/ //www.brhi.net/114591/#respond Thu, 03 Jan 2019 14:53:08 +0000 //www.brhi.net/?p=114591 在不断变化,日新月异的计算机领域,过往的知识和软件都被淘汰了吗?通过我们最熟悉的 cat 命令行,作者想像你说明的是,现在看来很陈旧的,三四十年前的想法与概念,在很多时候都融入到了你现在计算机上安装了的应用程序里,而很多程序比你想象的要古老的多。

cat 命令的源码进化史,首发于山西十一选五手机版。

]]>
cat-command-linux

有一次,我跟我的亲戚有一场争论,是关于读一个计算机科学的学位是否值得。当时是我在大学里面临是否选择计算机科学专业的时候。我姑姑和一个表哥认为我不该选。他们觉得会编程当然是个既有用又合算的事情,但是他们也坚信,计算机科学更新太快了,当下学到的知识会很快被淘汰掉。所以最好是选一门编程的课程,然后主修经济或者物理这种基本知识一辈子都适用的专业。

我并不相信他们的理论,并且选择了主修计算机专业(抱歉了姑姑和表哥?。┢涫挡荒芽闯?,为什么常人会认为计算机科学,或者软件工程这样的专业,每几年就会更新换代。先是诞生了私人计算机,然后是网络,手机,机器学习……科技永远在变化,那么其潜在的技术原理当然也在变化了。当然,最让人惊讶的是,这些基础技术原理,其实基本没变。我相信大部分人要是知道他们计算机中重要软件到底有多老,肯定会震惊。我并不是说软件的表面,毕竟我自己用的最多的火狐浏览器,两周前才更新过。但是如果你打开帮助手册查看?grep?之类的工具,你会发现它的上一次更新还是在 2010 年(至少 Mac 系统是这样)。grep?的初代诞生于 1974 年,那时候的计算机时代好比侏罗纪。现如今,人们(以及程序)在工作中仍然要依赖 grep 做很多事情。

我姑姑和表哥把计算机科技想象成一系列沙滩上的城堡,涨潮时潮水抹去旧的城堡,更加华丽的新城堡又会被建成。其实在现实中的很多领域,我们都是不断地在现有的程序基础上进行迭代。我们也许会时不时的修改这些程序来避免软件崩溃,但是除此之外这些程序不需要额外的维护。grep?是一个简单的程序,它所解决的问题现在也有意义,所以它至今还存在。很多应用程序的编写都起始于一个很高的角度,就像是在金字塔顶端的基础上构建,而金字塔本身是由曾经解决问题的答案所建成的。现在看来很陈旧的,三四十年前的想法与概念,在很多时候都融入到了你现在计算机上安装了的应用程序里。

我想仔细研究一个这样的老程序,看看它从诞生到现在到底被修改了多少次,这肯定很有趣。我想用?cat?这个最简单的 Unix 工具来作为例子。Ken Thompson 在 1969 年开发了初代?cat。如果我跟别人说我计算机里有个 1969 年的程序,这准确吗?cat?在这几十年里到底经历了几次迭代?我们计算机里的程序到底有多古老?

幸好有这个代码仓库,我们可以清晰地了解到,从 1969 年以来,cat?是如何进化的。我接下来会主要聚焦于我自己 Macbook 上?cat?程序的历史实现方式。你会看到,cat?历史从最初的 Unix 版本,到现在的 Mac 版本,这个程序被重写了比你预想的还要多的次数,但是最终它所实现的功能几乎跟五十年前一模一样。

Unix实验版本

1969 年,Ken Thompson 和 Dennis Ritchie 开始在 PDP 7 上开发 Unix。这是在 C 语言出现之前,所以早期的 Unix 程序都是用 PDP 7 上用汇编语言开发的。他们使用了专门针对于 Unix 的汇编版本,因为 Ken Thompson 开发了自己的汇编编译器,他在 PDP 7 出厂商DEC 提供的编译器基础上添加了新的功能。Thompson 的改进文档在初始?Unix 编程手册中有收录,在?as 编译器条目下面。

cat?的初代实现使用了 PDP 7 汇编语言。我有添加一些注释来解释每行命令,但是除非你明白 Thompson 编写汇编编译器的一些扩展,不然这个程序还是很难理解。这里有两个重要的点。第一,字符?;?可以被用于分隔同一行的声明语句。根据 sys 指令的描述,?;?通常被用于在同一行使用系统调用参数。第二,Thompson 添加了数字 0-9 用于支持“暂存标记”。这些标记可以被整个程序重用,这就像 Unix 编程手册所描述的,“对于程序员思维和汇编语言字符空间的缩减优化”。从手册中,你可以使用?nf?来表示下一个标记?n,用?nb?来表示上一个标记?n。举个例子,如果你有个标记为?1:?的代码块,你可以从相距很远的下方代码中使用?jmp 1b?来往上跳回标记代码。(但是你不能往下跳到标记代码,除非你使用jmp 1f。)

关于初代?cat?最有意思的是,它包含了两个我们熟知的名字,分别是一个标记为是一个标记为?getc,和一个标记为?putc?的代码块,这表示这俩名字要比标准 C 语言库都要历史久远。初代?cat?实际上包含了这两个方法的实现。这样的实现方式使得输入字符可以被写入缓冲区,也就是说,读和写不需要以单个字符为单位完成。

初代?cat?并没有存在很久。Ken Thompson 和 Dennis Ritchie 成功劝说了贝尔实验室帮他们购入了一台 PDP11,以便于他们对 Unix 系统进行扩展与提高。PDP 11 使用的是一种不同的指令集,因此他们不得不重写?cat。对于?第二代?cat?代码我也加了注释。第二代使用了针对于新指令集的新版汇编助记符,也利用了 PDP 11中不同的地址模式。(那些源代码中的括号和 $ 符号,是被用来指代不同的地址模式的。)但是?cat?第二代中也同样使用了初代中的?;?和暂存标记,这些功能一定是在 PDP 11 中移植?as?时被保留了下来。

cat?的第二代源代码远比初代要简洁很多。第二代也更加的”Unix-y”,因为它不再需要一串文件名作为命令参数,而是与如今的?cat一样,在没有参数的情况下,从?stdin?读取输入。对于二代?cat,你也可以使用参数来指定从?stdin 读取输入数据。

1973 年,为了准备发布第四版 Unix,很大一部分 Unix 系统都用 C 语言重写了一遍。但是 C 语言版本的?cat?在 Unix 发布后过了一段时间才出现。第一个 C 语言版本的?cat?只出现在第七版 Unix 系统中。这个实现方法非常值得一读,因为它非常简单明了。与其他版本比较,这一版最能作为代表?cat?的 K&R C 语言教育演示版本。这段程序的核心就是如下两行:

while ((c = getc(fi)) != EOF)
    putchar(c);

当然还有更多的代码,但是除了这两行以外,剩下的逻辑更多的是在确保用户不会同时读写同一个文件。另一个有意思的地方是,这个版本的?cat?只认得一个标记,-u。这个?-u?标记可以被用于关闭输入输出缓冲区,不然?cat?会默认缓存 512 字节。

伯克利软件套件/BSD

在第七版之后,Unix 催生了各种各样的衍生品。MacOS 是基于 Darwin 系统的,而 Darwin 是基于伯克利软件套件(BSD),因此 BSD 是我们最感兴趣的 Unix 分支。BSD 最初是作为Unix附加功能的软件合集,但是它最终成为了一个完整的操作系统。BSD似乎一直在用cat的初代版本,一直到第四版 BSD 发布为止。第四版 BSD 也就是 4BSD,它添加了对于新标记的支持。4BSD 版本的?cat?能明显的看出是初代的衍生品,不过它添加了一些新的函数用来实现用新标记触发的功能。4BSD 文件系统的命名方法是基于?fflg?这个变量的,fflg?用于标记指令的输入是从文件,还是?stdin?读取的。继?fflg?之后,nflg、bflg、vflg、sflg、eflg?和?tflg?也被用于记录程序中的标记是否被用到。这些命令行标记是?cat?添加的最后一批标记;如今至少在 Mac 系统中的?cat?命令行手册有列出来这些标记。4BSD 是在 1980 年发布的,所以这一系列的标记有 38 岁了。

cat?最后一次被重写是为了 BSD Net/2,这主要是为了避免软件许可证问题,因此所有 AT&T Unix 衍生代码都被替换为了新代码。BSD Net/2 在 1991 年发布。最后一次重写是由 Kevin Fall 完成的,Kevin Fall 于 1988 年毕业于伯克利,之后他花了一年的时间在计算机系统研究院(CSRG)工作了一年。Fall 告诉我,用 AT&T 代码写的 Unix 工具集列表被挂在了 CSRG 的一面墙上,员工们被告知可以选择感兴趣的工具重写。Fall 选择了?cat?和?mknod。在如今 Mac 系统的默认?cat?版本中,Fall 的名字排在开发者名单前列。他所编写的?cat,虽然是个很简单的程序,但是直到今年还有数百万的用户在使用。

Fall 所写的?cat?源代码比我们之前看到的版本要长许多。除了支持?-??帮助标记,这一版并没有添加新的功能。理论上来说,这一版代码与 4BSD 版本非常相似。代码之所以长,是因为 Fall 分开了“旧版”和“新版”的逻辑?!熬砂妗笔堑湫偷?cat;它一个字符一个字符的输出?!靶掳妗钡?cat?包括了 4BSD 命令行选项。这样的分割很有道理,但是使得代码在第一眼看上去比实际复杂很多。代码的最后有个华丽的错误处理方程,这也增加了代码长度。

MacOS

2001 年,苹果公司发布了 Mac OS X 系统。这次发布对于苹果公司来说非常重要,因为他们花了很多年,走了不少弯路,为了研发能够取代存在了很多年的旧版 Mac OS 系统。苹果公司内部曾经有过两次研发新系统的尝试,但是最终都没能成功;后来,苹果收购了史蒂夫·乔布斯的公司 NeXT,他们公司开发了一款名为 NeXTSTEP 的,基于面向对象编程框架的操作系统。苹果决定使用 NeXTSTEP 作为Mac OS X 的基础。NeXTSTEP 的一部分是基于 BSD 开发的,所以用 NeXTSTEP 作为 Mac OS X 的基础,同时也给苹果系统带来了 BSD 代码风格。

新发布的第一版 Mac OS X中包含了来自 NetBSD 项目的?cat?代码实现。NetBSD 项目如今仍在不断开发中,它最初是来自 386BSD 的分支。而 386BSD 是直接基于 BSD Net/2 的。所以 Mac OS X 上的?cat?就是 Kevin Fall 所写的?cat。唯一变化的是,Kevin Fall 写的错误处理函数?err()?被替换成了?err.h?中的?err()。err.h?是 BSD 基于 C 语言标准库的扩展。

NetBSD 版本的?cat?在不久之后被 FreeBSD 版本取代了。根据维基百科,苹果从 Mac OS X 10.3 (Panther)开始,使用 FreeBSD 来取代 NetBSD。但是 Mac OS X 版本的?cat,根据苹果的开软发布记录,一直到 2007 年发布 Mac OS X 10.5 (Leopard) 才被取代。苹果为了发布 Leopard 而引进的?FreeBSD 的实现版本一直被沿用到了今天。从 2007 一直到 2018 年,这一版没有做过任何升级或者改变。

所以说 Mac OS 中的?cat?是古老的。实际上?cat?的出现,比 2007 年的正式发布时间还早两年。2005 年的改动,在 FreeBSD 的Github 镜像中可以看到,是?cat?被移植到 Mac OS X 之前 FreeBSD 版的最后一次更新。所以 Mac OS X 中?cat?实际上有 13 年的历史了,它并没有与 FreeBSD 的?cat?进行同步更新。这里有过一个辩论,软件到底被改动过几次才算是一个新的软件呢;就?cat?这个个例来看,它的源代码从 2005 年开始就完全没有改变过了。

如今 Mac OS 系统中的?cat?与 Fall 在 1991 年为 BSD Net/2 所写的版本并没有太多不同。最大的不同是添加了一个新的函数用来支持 Unix 上的套接字。一个 FreeBSD 的开发者认为 Fall 所写的?raw_args()?函数应该与?cook_args()?合并为一个函数?scanfiles()。除此之外,最核心的部分还是 Fall 的代码。

我问过 Fall,有几百万苹果用户在使用你所写的?cat,还有很多程序直接或者间接依赖?cat,对此你有什么感想。如今已经是顾问兼最新版 TCP/IP 协议合作者的 Fall 表示,人们对他开发?cat?的经历如此的感兴趣,让他觉得非常惊讶。Fall 曾经在计算领域工作过很久,并且有过很多有影响力的项目经历。但是似乎人们对于他在 1989 年开发?cat?的那六个月更加感兴趣。

百岁程序

纵观历史上各种伟大的发明,计算机的历史并没有很久。我们仍然在使用有着百年历史的照片和胶卷。但是计算机软件是另外一个类别——目前仍属于高新科技。至少现在的软件是这样。随着计算机产业日渐成熟,我们会不会有一天发现,我们在使用有着百年历史的软件呢?

计算机硬件最终也会更新换代,现在的软件想必是没法跑在一个世纪以后的硬件上。也许高级语言设计的进步,也会导致在将来没有人会使用 C 语言,而?cat?也会被其他的语言重写。(不过 C 语言已经存在了五十年了,估计短期内也不会被取代。)不考虑以上这些的话,不如我们就一直用现在这版?cat?吧。

我认为,cat?的历史告诉我们,在计算机科学领域有一些思想是非常耐用的。实际上,对于?cat,它的代码和思想都是很多年前出现的。要说我计算机中的cat是1969年的其实并不准确。但如果说我计算机中的?cat?是 1989 年 Fall 开发的,就准确多了。很多软件都很古老。也许我们不能单纯的认为计算机科学和软件开发是不断更新换代的领域。我们所开发的系统都是基于历史基础的。在某些时候,我们在开发新代码的同时,也需要去花时间去理解和维护历史代码。

cat 命令的源码进化史,首发于山西十一选五手机版。

]]>
//www.brhi.net/114591/feed/ 0
救命!我的电子邮件发不到 500 英里以外! - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114589/ //www.brhi.net/114589/#respond Sat, 29 Dec 2018 14:09:46 +0000 //www.brhi.net/?p=114589 这是一个听起来几乎不可能的事情……我甚至有点后悔将它发到网上,因为它在一个会议上成了一则著名的酒后故事。

救命!我的电子邮件发不到 500 英里以外!,首发于山西十一选五手机版。

]]>

这是一个听起来几乎不可能的事情……我甚至有点后悔将它发到网上,因为它在一个会议上成了一则著名的酒后故事。这个故事略有改动,以?;す适轮械娜宋?,以及忽略了一些无关的细节使之更有趣一些。

几年前,当我接到统计系主任的电话时,我正在从事维护校园电子邮件系统的工作。

“我们从部门发送电子邮件时遇到了问题?!?/p>

“有什么问题?” 我问。

“我们不能发送超过 500 英里的邮件,”主任解释说。

“咳咳”,我被我喝的拿铁呛了一口,“您再说一遍?”

“我们不能发送距这里超过 500 英里的邮件,”他重复道。 “实际上,更远一点,是 520 英里,但不能更远了?!?/p>

“嗯……电子邮件真的不会这样,通常,”我说,试着让我的声音听起来不那么慌乱。我不能和一个系主任说话时显得慌乱,即使是一个像统计系这样的相对没钱的院系。?“是什么让你觉得你不能发送邮件超过 500 英里?”

“这不是我认为的,”主任有点急躁地回答道。 “我们首先注意到了这种情况是几天前?!?/p>

“你等了几天?” 我打断他,带点颤音说道。 “这段时间你一直你不能发送电子邮件?”

“我们可以发送电子邮件。只是不超过 ——”

“—— 500 英里,我知道,”我接过他的话,“我知道了。但为什么没有你早点打电话呢?”

“好吧,我们没有收集到足够的数据来确定发生了什么,直到现在?!泵淮?,这是统计系的主任?!安还茉趺此?,我请了一位地理统计学家研究它 ——”

“地理统计学家……”

“—— 是的,她制作了一张地图,显示了我们发送电子邮件能够达到的半径略超过 500 英里。在那个半径范围内有零星的几个无法到达的目的地,但我们永远不能发送比这半径更远的电子邮件?!?/p>

“我明白了,”我说,把头埋在我的手中。 “这是什么时候开始的?几天前,你说过,但是那时你的系统做了什么改变?”

“嗯,服务顾问来给我们的服务器打了补丁,并重新启动了它。但我打电话给他,他说他没有碰过邮件系统?!?/p>

“好的,让我来看看,我稍后会给你回电话,”我说。我简直觉得我在做梦,这不是愚人节。我试着回想是不是有人恶作剧报复我。

我登录了他们系的服务器,并发送了一些测试邮件。在北卡罗来纳州的三角研究园( Research Triangle Park),我自己的帐户的测试邮件顺利投递。发往里士满、亚特兰大和华盛顿的也是如此。发往普林斯顿(400 英里)的另一个邮件也正常。

但后来我尝试向孟菲斯(600 英里)发送电子邮件,失败了。波士顿,失败了。底特律,也失败了。我拿出了我的地址簿,开始试图缩小它的范围。纽约(420 英里)工作,但普罗维登斯(580 英里)失败了。

我开始怀疑自己是不是疯了。我试过给住在北卡罗来纳州的朋友发电子邮件,但他的 ISP 在西雅图。谢天谢地,它失败了。如果问题与收件人的地理位置有关,而不是他的邮件服务器,我想我要哭了。

已经确定!虽然令人难以置信,但所报告的问题是真实的、可重复的,我看了一下 sendmail.cf 文件。它看起来很正常。事实上,它看起来很熟悉。

我把它与我主目录中的 sendmail.cf 做了个对比。它没有被改过 —— 这是我写的 sendmail.cf。 而且我相当确定我没有启用某种 “FAIL_MAIL_OVER_500_MILES” 选项。我不知所措,我 telnet 到 SMTP 端口。 服务器愉快地回复了 SunOS sendmail 的横幅消息。

等一下……一个 SunOS sendmail 的横幅消息?当时,即使 Sendmail 8 已经相当成熟,Sun 公司在其操作系统中装的仍然是 Sendmail 5。作为一名优秀的系统管理员,我已经对 Sendmail 8 进行了标准化。并且作为一名优秀的系统管理员,我编写了一个 sendmail.cf,它使用了 Sendmail 8 中提供的很长的、具有自我描述意义的选项和变量,而不是 Sendmail 5 中使用的那种神秘的标点符号式配置选项。

这个细节一下子又回到了起点,我再次被我现在已经冷掉了的拿铁咖啡渣呛了。 当服务顾问“对服务器打补丁”时,他显然升级了 SunOS 的版本,并且这样做降级了 Sendmail。这次升级会将 sendmail.cf 单独留下,即使它现在是错误的版本。

事实上,Sendmail 5 —— 至少是 Sun 所带的版本,是有一些调整的 —— 它可以处理 Sendmail 8 的 sendmail.cf,因为大多数规则在那时保持不变。但新的长配置选项 —— 它被视为垃圾,并跳过。 并且 sendmail 二进制文件编译时没有针对其中大多数设置默认值,因此,在 sendmail.cf 文件中找不到合适的配置,它们被设置为?0。

被设置为 0 的配置之一是连接到远程 SMTP 服务器的超时选项。 一些实验证明,在具有典型负载的特定机器上,0 超时将在稍微超过 3 毫秒的时间内中止连接调用。

当时我们校园网络的一个奇怪的特点是它是 100% 交换的。传出的数据包不会出现路由器延迟,直到命中 POP 服务器并到达远端的路由器。因此,连接到附近网络上的轻负载的远程主机的时间实际上主要取决于到目的地的光速的速度,而不是偶然的路由器延迟。

这让我有点晕,我在我的 shell 中输入:

$ units
1311 units, 63 prefixes

You have: 3 millilightseconds
You want: miles
* 558.84719
/ 0.0017893979

“500 英里,或者稍微多一点点?!?/p>

救命!我的电子邮件发不到 500 英里以外!,首发于山西十一选五手机版。

]]>
//www.brhi.net/114589/feed/ 0
计算机科学自学指南 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114573/ //www.brhi.net/114573/#comments Sat, 22 Dec 2018 14:08:16 +0000 //www.brhi.net/?p=114573 如果你是一名自学工程师或者是一名软件集训课程毕业的学生,有些计算机科学基础课程是你必须要补齐的,本文介绍了几个领域最佳的教材和在线视频

计算机科学自学指南,首发于山西十一选五手机版。

]]>
如果你是一名自学工程师或者是一名软件集训课程毕业的学生,有些计算机科学基础课程是你必须要补齐的。幸好,你通过互联网就能获得世界顶级的CS(计算机科学)课程。

其实网上有很多学习资源但它们良莠不齐,你需要不是什么“200+免费在线课程”列表而是如下问题的答案:

你应该学习哪门课程?为什么?

每门课程最好的书籍或者视频,讲座是什么?

我写这篇文章的目的就是尝试对于这些问题给出的明确答案:

使用建议的书籍或者视频讲座来学习以下的九门科目,最好是书籍和讲座都仔细的研究一下,可以不严格按照列出的顺序来。每一门科目都需要花上100-200小时来研读,然后在你的职业生涯中对于最热爱的方向进行反复重温。

主题 为什么学习它 推荐书籍 推荐视频
编程 别做那些连“递归”是什么都搞不懂的程序员 Programs Brian Harvey’s Berkeley CS 61A
计算机体系结构 如果你基础薄弱,不知道计算机是如何运行的,那些你学会的所谓“高层”技能不过是空中楼阁 计算机组成与设计硬件/软件接口 Berkeley CS 61C
算法和数据结构 如果你不能很好的使用随处可见的数据结构(例如栈、队列、树和图),你是无法解决复杂问题的 Steven Skiena’s lectures
计算机数学 计算机科学实际上是应用数学的一个分支,学好数学让你更具竞争力 《Mathematics for Computer Science》 Tom Leighton’s MIT 6.042J
操作系统 你编写的大部分代码是通过操作系统运行的,所以你需要知道它们是如何交互的 Operating Systems: Three Easy Pieces Berkeley CS 162
计算机网络 互联网是个了不起的发明,只有理解它的原理,才能发挥它的威力
计算机网络(第4版)
Stanford CS 144
数据库 对于很多程序来说,数据是其核心,但是很少有人真正理解数据库系统是如何运行的 Readings in Database Systems Joe Hellerstein’s Berkeley CS 186
编程语言和编译器 如果你理解语言和编译器是如何运行的,你就能编写更好的代码并轻松学会新的语言 编译原理 技术与工具 Alex Aiken’s course on Lagunita
分布式系统 近年来,大部分系统已经发展成为分布式系统 Distributed Systems ?

为什么要学习计算机科学

有两种软件工程师:一种人对于电脑科学有很好的理解从而去从事挑战性的、富有创造力的工作。另外一种人仅仅熟悉一些高级工具,对其原理持得过且过的态度。

两者都叫做软件工程师,而且两者在早期的职业生涯中可能领着同样的薪水。但是第一种工程师,不管他从事的是商业工作,还是突破性的开源工程,都会由于他的技术领导力或者杰出的个人贡献一点一点成长成一名对于编程更加痴迷而且待遇更高的工程师。

第一种工程师可以通过常规手段或者在职业生涯中不断学习来加深对于计算机科学的理解深度。第二种工程师通常停留在表面,学习具体的工具或者技巧而不是其中的基础,当前流行什么技术,他们就仅仅捡起新的技能学习一下。

近些年来,越来越多的人进入软件领域工作,但是本质上计算机科学的毕业生数量是没有改变的。第二种工程师的供应过量开始导致他们的就业机会变少而且导致他们离企业中令人感觉充实的工作更远。不管你是努力要成为第一种工程师或者仅仅是保险起见地想找到更多的工作,学习计算机科学是唯一一种可靠的途径。

课程指南

编程

大多数大学的计算机编程课程通常以“入门类”计算机的课程开始。这些课程最好是不仅仅针对于初学者,而且对于第一次学习编程,基本概念和编程模型不是很熟悉的人也有所启发的。

对于这种介绍的内容的我们给出的标准建议是经典的计算机程序的结构与解释,在网络上能找到很多这样的资料,它们可能是电子书或者是MIT的一系列讲座视频。这些讲座都很不错,但是我们的视频推荐的实际上是伯克利的一门课程:Brian Harvey 的 SICP?讲座?,这个系列的课程比起MIT的讲座更精炼而且对于入门者更具有针对性。

我们推荐观看完至少前三章节的SICP(《计算机程序的构造和解释?》)并且做完相应训练。额外地,可以在 exercism 进行一些编程训练。

image

如果你觉得SICP太难,我们推荐《程序设计方法(中文版)|?How to Design Programs》这本书。如果你觉得它太简单,我们推荐《Concepts, Techniques, and Models of Computer Programming 这本书。

计算机体系结构

硬件是平台 – Mike Acton(Insomniac Games的工程总监) (收看他在 CPP 大会上的演讲)

计算机结构–有的时候被称为“计算机系统”或者“计算机组织”–是了解程序外表下计算机运行的第一步。根据我们的经验,这是自学软件工程师最容易忽略的地方。

《计算机系统要素》The Elements of Computing Systems,也被称为“从与非门到俄罗斯方块”。这是一本让你对于计算机中的每一个零件是怎么工作的有一个整体的理解的雄心勃勃的书。每个章节涉及到建立整体系统中一个小的部分,从写基本的逻辑门到HDL,到CPU和汇编语言,一直到完成一个俄罗斯方块应用程序。

elements-computing-systems

我们推荐阅读书的前六章节并且完成相关的工程。这会提高你对于计算机结构和运行的软件之间关系的理解。

这本书的前半部分(和它的全部工程)在 Nand2Tetris 网站上可以免费获得。在?Coursera 课程网站上你也可以找到它们。

为了保证课程简单并吸引人,Nand2Tetris 舍弃了深度。特别是现代计算机结构中两个很重要的概念:流水线(pipelining)和内存层级(memory hierarchy),在书中都没有提及。

当你觉得看Nand2Tetris已经很简单了,我们下一个建议是Patterson和Hennessy合著的《计算机组成与设计硬件/软件接口》Computer Organization and Design——一本杰出的现代经典书籍。不是书中所有的部分都很重要;我们建议跟随Berkeley的CS61C 课程——(Great Ideas in Computer Architecture),作为特殊读物。讲座的笔记和实验环境都是在线的,而且可以在在这个归档链接回看讲座。

算法和数据结构

只有一个方法是我一直以来广泛推荐的—编码前首先要思考 — Richard Hamming

我们根据几十年的通识来看,熟悉通用的算法和数据结构是计算机科学教育中最重要的方面之一。这是一个训练一个人解决问题的通用能力的方式,而且这种能力还可以迁移到其他领域的学习。

这个领域有很多优秀的书籍,但是我们最喜欢的是Steven Skiena的《算法设计手册》The Algorithm Design Manual 。他显然喜欢这东西而且也迫不及待地想帮助你学习数据结构和算法。这是令人耳目一新的变化,我们认为这本书相对于被更多人所推荐的Cormen, Leiserson, Rivest & Stein 或者 Sedgewick 的书来说更好。后两本书有些太过于引经据典,对于想通过阅读来解决问题的人来说并不是一个好的选择。

skiena

对于那些更喜欢讲座视频的人来说,我们推荐Skiena的讲座. 我们也喜欢Tim Roughgarden的课程,在斯坦福的MOOC平台或者Coursera上面可以获得。你喜欢 Skiena 还是 Roughgarden 的讲课风格就是你的个人喜好问题了。

说到练习,我们倾向于让学生在Leetcode上面解决问题。LeetCode上面的问题都比较有趣而且有答案和讨论。这上面还可以通过解决各大软件公司广泛应用的技术问题来帮助你测试你的进步。我们建议解决你学习的时候解决大约随机100道LeetCode上面的问题。

最后,我们强烈推荐《怎样解题》这本书,它针对如何解题进行了精彩绝伦和独特的讲解,既适用于数学也适用于电脑科学。

polya

计算机科学领域的数学

如果人们不认为数学是简单的,那么他们一定没有体会过人生的艰难 — John von Neumann

在某些方面,计算机科学是应用数学的一个扩展。虽然许多软件工程师忽略了这一点,我们建议你去学习它。好好学习数学会给你比那些不学习它们的人巨大的竞争优势。

和CS最相关的数学领域是“离散数学”,离散是连续对立面。是微积分之外的一系列的有趣的应用数学的主题。从大体上说,尝试学会全部范围的“离散数学”是没有意义的。更现实一点的做法是对于逻辑学,组合学和概率学,集合论,图论和一些数论告知密码学有一个了解。对于计算机图像学和机器学习来说,线性代数也是一门值得学习的课程。

我们建议从László Lovász的讲座学起. 这一系列开始学习离散数学。Lovász 教授让学习的内容变得直观生动,比起拘谨的文字,这更利于你学习。

接下来,我们推荐《Mathematics for Computer Science》, 它是MIT同名课程的讲义。讲座课程的视频也是免费的,而且是我们推荐的离散数学的视频课程。

线性代数,我们建议从 Essence of linear algebra 系列开始学习,接着是Gilbert Strang的书籍视频。

操作系统

《操作系统概念》Operating System Concepts)(恐龙书)和《现代操作系统》Modern Operating Systems?)是经典的操作系统的书籍。这两本书的写作方式都饱受争议,而且为了鼓励你去购买新版,这些长达1000页的书每几年就会添加一些内容。

Operating Systems: Three Easy Pieces》这本书是一本比较好的可供选择的线上免费读物。我们特别喜欢书的结构和它经典的练习题。

ostep

读完这本书,我们推荐你去探索一种特定的操作系统的设计方式,比如那些书名中有系统名字的书籍,比如 《Lion‘s commentary on Unix》、《The Design and Implementation of the FreeBSD Operating System》,还有 Mac OS X Internals.

巩固你对于操作系统的理解很好的方式是去读一个小的内核并且添加功能。xv6 是一个不错的选择,它是 Unix V6 和 ANSI C 和 X86 的接口,MIT专门有一门课程就是讲这个的。OSTEP(之前提到的)这本书有一个 XV6 的实验附录,里面都是充满潜力项目的好点子。

计算机网络

你不能够通过凝视水晶球来预见未来。因特网未来会变成什么样,取决于如今人类如何去塑造它 — Bob Kahn

考虑到很多软件项目都是基于web服务器和客户端的,计算机网络变成计算机科学中一门有实用价值的学科。系统学习过该课程的自学学生发现他们终于理解了围绕了伴随它们很多年的术语,概念,协议等等。

关于这个主题我们最推荐的书是:《计算机网络》Computer Networking: A Top-Down Approach)。书中的小工程和实验都很好,值得一做。我们非常喜欢它们提供的?Wireshark labs。

对于那些喜欢视频课程的人,我们推荐斯坦福MOOC平台上的《Introduction to Computer Networking course》。

学习网络的好处不仅仅在于做小的实验而且对于工程来说也有很大的好处??赡苌婕暗降挠校阂桓鯤TTP的服务器,一个UDP协议的聊天软件,一个迷你的 TCP 协议栈,一个代理或者负载平衡器,还有分布式的哈希表等等。

数据库

对于自学者来说,学习数据库系统会比学习其他花费更多的时间。这是一个相对较新的(即1970年代后期)的研究领域。比起写书,许多潜在的杰出教科书作者更愿意去加入或者创办一家公司。

在这种情况下,我们建议自学者放弃教科书而去学习伯克利的Joe Hellerstein的数据库课程,看完课程再去阅读论文。

对于初学者有一篇论文比较推荐的是:《Architecture of a Database System》,它高屋建瓴地讲解了关系数据库管理系统是如果工作的这一问题。它会为你未来的学习提供一个有用的纲要。

Readings in Database Systems这本书,又被称为数据库红皮书、是一本Peter Bailis、Joe Hellerstein和Michael Stonebraker编辑地论文集。对于那些理解了CS 186内容的人来说,红皮书是你的不二之选。

如果你坚持要使用一本引导性的教科书,我们推荐Ramakrishnan 和Gehrke的《数据库管理系统》Database Management Systems,对于更优秀的学生,Jim Gray的传统课程《Transaction Processing: Concepts and Techniques值得一看,但是我们不建议把它当成入门书。

不编大量的代码是不能很好的巩固数据库的理论的,CS 186的学生往Spark中添加功能,这是一个很有意义的工程。但是我们建议仅仅是从头写一个简单的关系数据库管理系统。功能可能不是很丰富,但是即使每一个部分都涉及到一些基本功能也很有启发性。

最后,数据模型是一个数据库使用中被忽略和没有被重点学习的方面。我们对于这个课题建议的书籍是:《Data and Reality: A Timeless Perspective on Perceiving and Managing Information in Our Imprecise World

语言和编译器

Don’t be a boilerplate programmer. Instead, build tools for users and other programmers. Take historical note of textile and steel industries: do you want to build machines and tools, or do you want to operate those machines? — Ras Bodik at the start of his compilers course

大部分程序员学习如何使用一门编程语言,然而大部分的计算机科学家则学习这门语言本身。这给了计算机科学家比起程序员很明显的优势。他们的知识能够更好的泛化,他们能比简简单单地掌握一门语言的更加深入和快速的理解一门新语言的操作。

经典的教科书《编译原理 技术与工具》(?Compilers: Principles, Techniques & Tools)通常又被称为“龙书”。不幸的是,这本书并不适合自学者,它比较适合教师从中选出1-2个章节并在课堂上讲授。这本书是有必要看的,你可以挑选里面的主题,最好再有个师傅指导你。

如果你选择在自学中使用龙书,我们推荐你一系列门视频讲座,然后再沉浸在对于龙书的研究中。我们推荐的在线课程是:Alex Aiken 的讲座,你可以在斯坦福大学的幕课平台上观看。

也有可以替代龙书的教材:Terence Parr写的《编程语言实现模式》Language Implementation Patterns,它更适合那些工作中使用类似特定领域语言的小众语言的有经验的编程者,它显得更加实用。当然,为了达到这个目的它也删去了一些有价值的理论。

对于工程实践,我们推荐你写一个编译器,你可以选择像COOL这种简单的教学语言或者你感兴趣的一门语言。如果你觉得太难,你可以参考Make a Lisp,你可以参考它作为开始。

分布式系统

计算机的数量增长了,它们的分布也更广了。企业之前会购买越来越大型的主机,但是现在大家更倾向于在很多机器上分布式的运行多个小型的应用程序。分布式系统研究的就是这样的技术,这一技术变得越来越重要了。

我们建议的自学教科书是 Maarten van Steen 和 Andrew Tanenbaum 的《Distributed Systems, 3rd Edition》。 针对于之前的版本做了很大的改进,而且作者慷慨地把书放在了网上共享。由于分布式计算是一门变化很快的领域,所以没有教科书可以很好的涵盖所有的内容。但是Maarten van Steen的书是我们读过的所有书中最好的书。

研究生在线课程?MIT’s 6.824 也是一个不错的选择,但可惜视频中的音质不太好,而且不清楚这些视频是不是都被授权过。

尽管有参考书或者其它的资源,但学习分布式系统是绝对要读论文的。链接中有一个很好的清单,而且我们十分推荐你从Papers We Love 上面下载论文到本地学习。

 

译注:本文提到的大部分书籍,中英文版已归档至该豆列

计算机科学自学指南,首发于山西十一选五手机版。

]]>
//www.brhi.net/114573/feed/ 1
在 Linux 命令行上拥有一头奶牛 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114570/ //www.brhi.net/114570/#respond Fri, 21 Dec 2018 14:55:20 +0000 //www.brhi.net/?p=114570 使用 cowsay 实用程序将牛的话语带到你的终端输出。

在 Linux 命令行上拥有一头奶牛,首发于山西十一选五手机版。

]]>

使用 cowsay 实用程序将牛的话语带到你的终端输出。

欢迎来到 Linux 命令行玩具第四天。如果这是你第一次访问这个系列,你可能会问自己,什么是命令行玩具。我们也在考虑这一点,但是一般来说,这可能是一个游戏,或者任何简单的娱乐,可以帮助你在终端玩得开心。

你们中的一些人会见过我们之前的选中的各种玩具,但是我们希望至少有一个对每个人来说都是新的。因为几乎所有我告诉他这个系列的人都已经问过它了,所以今天的选中的玩具是必须提及的。

你也不会认为我们会在不提及 cowsay 的情况下完成这个系列,对吧?

cowsay 是一个神奇的实用程序,它将文本作为 ASCII 艺术牛的讲话文本输出。

你可能会发现 cowsay?打包在你的默认存储库中,甚至可能已经安装了。对我来说,在 Fedora,像这样安装:

$ sudo dnf install -y cowsay

然后,用 cowsay 调用它,然后是你的消息。也许你想到昨天我们谈到的 fortune 应用 连接起来。

$ fortune | cowsay
 _________________________________________
/ If at first you do succeed, try to hide \
\ your astonishment.                      /
 -----------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

就这样!cowsay 还有点小变体,称为 cow 文件,通??梢栽?/usr/share/cowsay 找到 ,要查看系统上可用的 cow 文件,请在 cowsay 之后使用 -l 。然后,用 -f 试试其中之一。

$ cowsay -f dragon "Run for cover, I feel a sneeze coming on."
 _______________________________________
/ Run for cover, I feel a sneeze coming \
\ on.                                   /
 ---------------------------------------
      \                    / \  //\
       \    |\___/|      /   \//  \\
            /0  0  \__  /    //  | \ \    
           /     /  \/_/    //   |  \  \  
           @_^_@'/   \/_   //    |   \   \ 
           //_^_/     \/_ //     |    \    \
        ( //) |        \///      |     \     \
      ( / /) _|_ /   )  //       |      \     _\
    ( // /) '/,_ _ _/  ( ; -.    |    _ _\.-~        .-~~~^-.
  (( / / )) ,-{        _      `-.|.-~-.           .~         `.
 (( // / ))  '/\      /                 ~-. _ .-~      .-~^-.  \
 (( /// ))      `.   {            }                   /      \  \
  (( / ))     .----~-.\        \-'                 .~         \  `. \^-.
             ///.----..>        \             _ -~             `.  ^-`  ^-_
               ///-._ _ _ _ _ _ _}^ - - - - ~                     ~-- ,.-~
                                                                  /.-~

我对 cowsay 的真正不满是,我今天没有足够的时间来为牛的挤奶 —— 一语双关。牛排价格太高了,我只是开个玩笑。

更严重的是,我已经完全忘记了 cowsay 直到我在学习 Ansible 的剧本时再次遇到它。如果你碰巧安装了 cowsay,当你运行Ansible 的剧本时,你会从一队奶牛那里获得输出。例如,运行这个剧本:

- hosts:
? ? - localhost
? tasks:
? ? - action: ping

可能会给你以下信息:

$ ansible-playbook playbook.yml
 __________________
< PLAY [localhost] >
 ------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

 ________________________
< TASK [Gathering Facts] >
 ------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

ok: [localhost]
 _____________
< TASK [ping] >
 -------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

ok: [localhost]
 ____________
< PLAY RECAP >
 ------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

localhost                  : ok=2    changed=0    unreachable=0    failed=0

cowsay 在 GPLv3 许可证下可用,您可以在 GitHub 上找到 它的 Perl 源代码。我也见过其他语言的版本,所以可以看看其他变体;例如,这是 R 语言版。用你选择的语言实现你自己的版本可能是一项有趣的编程学习任务。

既然讲完了 cowsay,我们可以去更绿色的牧场了。

你有希望我来介绍的喜欢的命令行玩具吗?这个系列的排期大部分都填好了,但我还有一些空位方。在下面的评论中让我知道,我会来看看。如果有空间,我会尝试把它包括进去。如果没有,但是我收到了一些好的意见,我在结尾提及。

看看昨天的玩具,如何给你的 Linux 终端带来好运,明天再来看看另一个!

 

在 Linux 命令行上拥有一头奶牛,首发于山西十一选五手机版。

]]>
//www.brhi.net/114570/feed/ 0
Linux 搜索文件和文件夹的 4 种简单方法 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114561/ //www.brhi.net/114561/#respond Wed, 19 Dec 2018 12:49:07 +0000 //www.brhi.net/?p=114561 Linux 管理员一天都不能离开搜索文件,因为这是他们的日?;疃?。

Linux 搜索文件和文件夹的 4 种简单方法,首发于山西十一选五手机版。

]]>

Linux 管理员一天都不能离开搜索文件,因为这是他们的日?;疃?。了解一些搜索的东西是不错的,因为这能帮助你在命令行服务器中工作。这些命令记忆起来不复杂,因为它们使用的是标准语法。

可以通过四个 Linux 命令啦执行此操作,每个命令都有自己独特的功能。

方法 1:使用 find 命令在 Linux 中搜索文件和文件夹

find 命令被广泛使用,并且是在 Linux 中搜索文件和文件夹的著名命令。它搜索当前目录中的给定文件,并根据搜索条件递归遍历其子目录。

它允许用户根据大小、名称、所有者、组、类型、权限、日期和其他条件执行所有类型的文件搜索。

运行以下命令以在系统中查找给定文件。

# find / -iname "sshd_config"
/etc/ssh/sshd_config

运行以下命令以查找系统中的给定文件夹。要在 Linux 中搜索文件夹,我们需要使用 -type 参数。

# find / -type d -iname "ssh"
/usr/lib/ssh
/usr/lib/go/src/cmd/vendor/golang.org/x/crypto/ssh
/usr/lib/go/pkg/linux_amd64/cmd/vendor/golang.org/x/crypto/ssh
/etc/ssh

使用通配符搜索系统上的所有文件。我们将搜索系统中所有以 .config 为扩展名的文件。

# find / -name "*.config"
/usr/lib/mono/gac/avahi-sharp/1.0.0.0__4d116c78973743f5/avahi-sharp.dll.config
/usr/lib/mono/gac/avahi-ui-sharp/0.0.0.0__4d116c78973743f5/avahi-ui-sharp.dll.config
/usr/lib/python2.7/config/Setup.config
/usr/share/git/mw-to-git/t/test.config
/var/lib/lightdm/.config
/home/daygeek/.config
/root/.config
/etc/skel/.config

使用以下命令格式在系统中查找空文件和文件夹。

# find / -empty

使用以下命令组合查找 Linux 上包含特定文本的所有文件。

# find / -type f -exec grep "Port 22" '{}' \; -print
# find / -type f -print | xargs grep "Port 22"
# find / -type f | xargs grep 'Port 22'
# find / -type f -exec grep -H 'Port 22' {} \;

方法 2:使用 locate 命令在 Linux 中搜索文件和文件夹

locate 命令比 find 命令运行得更快,因为它使用 updatedb 数据库,而 find 命令在真实系统中搜索。

它使用数据库而不是搜索单个目录路径来获取给定文件。

locate 命令未在大多数发行版中预安装,因此,请使用你的包管理器进行安装。

数据库通过 cron 任务定期更新,但我们可以通过运行以下命令手动更新它。

$ sudo updatedb

只需运行以下命令即可列出给定的文件或文件夹。在 locate 命令中不需要指定特定选项来打印文件或文件夹。

在系统中搜索 ssh 文件夹。

# locate --basename '\ssh'
/etc/ssh
/usr/bin/ssh
/usr/lib/ssh
/usr/lib/go/pkg/linux_amd64/cmd/vendor/golang.org/x/crypto/ssh
/usr/lib/go/src/cmd/go/testdata/failssh/ssh
/usr/lib/go/src/cmd/vendor/golang.org/x/crypto/ssh

在系统中搜索 ssh_config 文件。

# locate --basename '\sshd_config'
/etc/ssh/sshd_config

方法 3:在 Linux 中搜索文件使用 which 命令

which 返回在终端输入命令时执行的可执行文件的完整路径。

当你想要为可执行文件创建桌面快捷方式或符号链接时,它非常有用。

which 命令搜索当前用户而不是所有用户的 $PATH 环境变量中列出的目录。我的意思是,当你登录自己的帐户时,你无法搜索 root 用户文件或目录。

运行以下命令以打印 vim 可执行文件的完整路径。

# which vi
/usr/bin/vi

或者,它允许用户一次执行多个文件搜索。

# which -a vi sudo
/usr/bin/vi
/bin/vi
/usr/bin/sudo
/bin/sudo

方法 4:使用 whereis 命令在 Linux 中搜索文件

whereis 命令用于搜索给定命令的二进制、源码和手册页文件。

# whereis vi
vi: /usr/bin/vi /usr/share/man/man1/vi.1p.gz /usr/share/man/man1/vi.1.gz

 

Linux 搜索文件和文件夹的 4 种简单方法,首发于山西十一选五手机版。

]]>
//www.brhi.net/114561/feed/ 0
学会这两件事,让你成为 Git 老司机 - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114551/ //www.brhi.net/114551/#comments Tue, 18 Dec 2018 15:34:25 +0000 //www.brhi.net/?p=114551 “我在提交中犯了个错误,我如何修正它?”,“我的提交历史一团糟,我该如何让它更整洁?”如果你曾经有上述问题,那么这篇文章很适合你。这篇文章介绍了一个让你成为 Git 老司机的清单。

学会这两件事,让你成为 Git 老司机,首发于山西十一选五手机版。

]]>

  • 我在提交中犯了个错误,我如何修正它?
  • 我的提交历史一团糟,我该如何让它更整洁?

如果你曾经有上述问题,那么这篇文章很适合你。这篇文章介绍了一个让你成为 Git 老司机的清单。

我在提交中犯了个错误,我该怎么办?

情景 1

假设你已经提交了一堆文件,并发现输入的提交信息实际上并不清晰。现在你要更改提交消息。为此,你可以使用 git commit --amend

git commit --amend -m “New commit message”

场景 2

假设你要提交六个文件,但你最终错误地只提交了五个文件。你可能认为可以创建新提交并将第六个文件添加到该提交。

这种方法没错。但是,为了保持整洁的提交历史,如果你可以以某种方式将此文件加入到你之前的提交本身,那岂不是更好?这也可以通过 git commit --amend 完成:

git add file6
git commit --amend --no-edit

--no-edit 表示提交信息不会更改。

场景 3

无论你何时在 Git 进行提交,提交都会附上作者名称和作者电子邮箱。通常,当你第一次配置 Git 时,就需要设置作者和电子邮箱。你无需担心每次提交的作者详细信息。

也就是说,对于特定项目,你可能希望使用不同的电子邮箱 ID。你需要使用以下命令为该项目配置电子邮箱 ID:

git config user.email “your email id”

假设你忘记配置电子邮箱,并且已经完成了第一次提交。amend 命令也可以用于更改先前提交的作者消息??梢允褂靡韵旅罡奶峤坏淖髡咝畔ⅲ?/p>

git commit --amend --author "Author Name <Author Email>"

注意事项

应该仅在本地仓库使用 amend 命令。在远端仓库使用 amend 命令会制造大量混乱。

我的提交历史一团糟,我该如何处理?

假设你正在处理一段代码。你知道代码大约需要十天完成。在这十天内,其他开发人员也将提交代码到远程仓库。

将本地仓库代码与远程仓库代码保持同步是个很好的做法。这在你拉取请求时会避免许多合并冲突的操作。因此,你应该每两天从远程仓库中拉取一个变更。

每次将代码从远程仓库拉取到本地仓库时,都会在本地操作中创建新的合并提交。这意味着你的本地历史提交记录会有大量的合并提交,这会让审阅人员头大。

上面是历史提交记录在本地仓库中的显示方式。

如何让历史提交记录看起来更整洁?

这就需要用到 rebase 了。

什么是变基(rebase)?

举个🌰。

此图显示了发布(release)分支和功能(feature)分支中的提交。

  1. 发布分支有三个提交:Rcommit1、Rcommit2 和 Rcommit3。
  2. 你在发布分支中仅有一个提交(即 Rcommit1)时,创建了功能分支。
  3. 你已向功能分支添加了两个提交。它们是 Fcommit1 和 Fcommit2。
  4. 你希望从发布分支提交到功能分支中。
  5. 你可以使用变基来完成该操作。
  6. 让发布分支命名为 release,让功能分支命名为 feature。
  7. 可以使用以下命令进行变基:

git checkout feature
git rebase release

变基

当执行变基时,你的目标是确保功能分支从发布分支获取最新代码。

变基命令尝试逐个添加每个提交,并检查冲突。这听起来是不是有点头大?

让我画个图帮助理解。

这显示了变基内部实际做的事情:

第 1 步

  1. 运行该命令的那一刻,功能分支指向发布分支的头部。
  2. 现在,功能分支有三个提交,Rcommit1、Rcommit2 和 Rcommit3。
  3. 你可能想知道 Rcommit1 和 Rcommit2 发生了什么?
  4. 提交仍然存在,将在下面步骤中使用。

第 2 步

  1. 现在 Git 尝试将 Fcommit1 添加到功能分支上。
  2. 如果没有冲突,则在 Rcommit3 之后添加 Fcommit1;
  3. 如果存在冲突,Git 会通知你,你必须手动解决冲突。解决冲突后,使用以下命令继续变基:

git add fixedfile
git rebase --continue

第 3 步

  1. 一旦添加了 Fcommit1,Git 将尝试添加 Fcommit2。
  2. 同样,如果没有冲突,则在 Fcommit1 之后添加 Fcommit2,并且变基成功。
  3. 如果存在冲突,Git 会通知你,你必须手动解决。解决冲突后,使用第 2 步提到的相同命令。
  4. 整个变基完成后,你会发现功能分支有 Rcommit1、Rcommit2、Rcommit3、Fcommit1 和 Fcommit2。

注意事项

  1. 变基和合并(merge)在 Git 中都很有用。两种并无优劣之分。
  2. 在合并的情况下,你将有个合并提交。在变基的情况下,不会像合并提交那样有额外的提交。
  3. 一种最佳的实践是一分为二。使用远端仓库中的最新代码更新本地仓库时,请使用变基。在处理拉取请求,以将功能分支和发布分支或主分支合并时,请使用合并。
  4. 使用变基会更改历史提交记录(使其更整洁)。但话虽如此,改变历史提交存在风险。因此,请确保永远不要对远程存储仓库的代码使用变基。始终仅对本地仓库代码使用变基,来更改历史提交记录。
  5. 如果对远端仓库进行变基,会制造许多混乱,因为其他开发人员无法识别新的历史记录。
  6. 此外,如果在远端仓库上完成变基,则当其他开发人员尝试从远端仓库中拉取最新代码时,就可能会出问题。所以,我再重申一遍,变基总是仅在本地仓库中进行。😃

恭喜

你现在是个 Git 老司机了。😃

在这篇文章中,你了解到:

  • 修改提交记录
  • 变基

这两个都是非常实用的概念。探索 Git 的世界,继续学习吧。

学会这两件事,让你成为 Git 老司机,首发于山西十一选五手机版。

]]>
//www.brhi.net/114551/feed/ 2
神奇的 Linux 命令行字符形状工具 boxes - 山西十一选五手机版▁山西11选五前三组遗漏▁山西十一选五任七遗漏▁山西11选五遗漏top10▁山西体彩十一选5一定牛 //www.brhi.net/114549/ //www.brhi.net/114549/#respond Sun, 16 Dec 2018 13:08:05 +0000 //www.brhi.net/?p=114549 本文将教你如何在 Linux 命令行终端中使用 boxes 工具绘制字符形状图形来包装你的文字让其更突出。

神奇的 Linux 命令行字符形状工具 boxes,首发于山西十一选五手机版。

]]>

本文将教你如何在 Linux 命令行终端中使用 boxes 工具绘制字符形状图形来包装你的文字让其更突出。

现在正值假期,每个 Linux 终端用户都该得到一点礼物。无论你是庆祝圣诞节还是庆祝其他节日,或者什么节日也没有,都没有关系。我将在接下来的几周内介绍 24 个 Linux 命令行小玩具,供你把玩或者与朋友分享。让我们享受乐趣,让这个月过得快乐一点,因为对于北半球来说,这个月有点冷并且沉闷。

对于我要讲述的内容,可能你之前就有些了解。但是,我还是希望我们都有机会学到一些新的东西(我做了一点研究,确??梢苑窒?24 个小玩具)。

24 个 Linux 终端小玩具中的第一个是叫做 boxes 的小程序。为何从 boxes 说起呢?因为在没有它的情况下很难将所有其他命令礼物包装起来!

在我的 Fedora 机器上,默认没有安装 boxes 程序,但它在我的普通仓库中可以获取到,所以用如下命令就可安装:

$ sudo dnf install boxes -y

如果你在使用其他 Linux 发行版,一般也都可以在默认仓库中找到 boxes。

boxes 是我真正希望在高中和大学计算机课程中就使用的实用程序,因为善意的老师要求我在每个源文件、函数、代码块等开头添加一些特定外观的备注信息。

/***************/
/* Hello World */
/***************/

事实证明,一旦你需要在框内添加几行文字,并且格式化的将它们统一风格就会变得很乏味。而 boxes 是一个简单实用程序,它使用 ASCII 艺术风格的字符形状框来包围文本。其字符形状默认风格是源代码注释风格,但也提供了一些其他选项。

它真的很容易使用。使用管道,便可以将一个简短问候语塞进字符形状盒子里。

$ cat greeting.txt | boxes -d diamonds -a c

上面的命令输出结果如下:

? ? ? ?/ ? ? ? ? ?/ ? ? ? ? ?/
? ? ///\/ ? ?///\/ ? ?///\/
?///\///\///\///\///\///\/
//\///\///\///\///\///\///\
\/// ? ? ? ? ? ? ? ? ? ? ? ? ? ?/\//
?/ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/
?/ ? ? ?I'm wishing you all a ? ? ? /
//\ ? ? joyous holiday season ? ? ?//\
\// ? ? and a Happy Gnu Year! ? ? ?\//
?/ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/
?/ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?/
//\/ ? ? ? ? ? ? ? ? ? ? ? ? ? ?///\
\///\///\///\///\///\///\//
?/\///\///\///\///\///\///
? ? /\/// ? ?/\/// ? ?/\///
? ? ? ?/ ? ? ? ? ?/ ? ? ? ? ?/

或者玩点更有趣的,比如:

echo "I am a dog" | boxes -d dog -a c

不要惊讶,它将会输出如下:

? ? ? ? ? __ ? _,--="=--,_ ? __
? ? ? ? ?/ ?." ? ?.-. ? ?"./ ?
? ? ? ? / ?,/ ?_ ? : : ? _ ?/` 
? ? ? ?  ?`| /o ?:_: ?/o |__/
? ? ? ? ?`-'| :="~` _ `~"=: |
? ? ? ? ? ? ` ? ? (_) ? ? `/
? ? ?.-"-. ?  ? ? ?| ? ? ?/ ? .-"-.
.---{ ? ? }--| ?/,.-'-., ?|--{ ? ? }---.
?) ?(_)_)_) ?_/`~-===-~`_/ ?(_(_(_) ?(
( ? ? ? ? ? ? ?I am a dog ? ? ? ? ? ? ? )
?) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (
'---------------------------------------'

boxes 程序提供了很多选项 用于填充、定位甚至处理正则表达式。你可以在其 项目主页 上了解更多有关 boxes 的信息,或者转到 GitHub 去下载源代码或者贡献你自己的盒子形状。说到此,如果你想给你的提交找个好点子,我已经有了一个想法:为什么不能是一个节日礼物盒子?

? ? ? ? ?_ ?_
? ? ? ? /_/_
?________/_/_______
| ? ? ? ///\ ? ? ? |
| ? ? ?/// ?\ ? ? ?|
| ? ? ? ? ? ? ? ? ? ?|
| ? ? "Happy pull ? ?|
| ? ? ? request!" ? ?|
|____________________|

boxes 是基于 GPLv2 许可证的开源项目。

你有特别喜欢的命令行小玩具需要我介绍的吗?这个系列要介绍的小玩具大部分已经落实,但还预留了几个空位置。如果你有特别想了解的可以评论留言,我会查看的。如果还有空位置,我会考虑介绍它的。即使要介绍的小玩具已经有 24 个了,但如果我得到了一些很好的意见,我会在最后做一些有价值的提及。

你可以通过 Drive a locomotive through your Linux terminal 来查看明天会介绍的命令行小玩具。


 

山西十一选五手机版,首发于山西十一选五手机版。

]]>
//www.brhi.net/114549/feed/ 0
  • 回复@看着就想笑:历史至今的客观事实是奴隶主剥削奴隶、封建地主剥削农奴、资本家剥削雇佣劳动者、师傅剥削徒弟都是建立在私有制基础上的,而且小私有和他人联合顾工生 2019-04-24
  • 青岛欢迎你,上合  2019-04-22
  • 端午节西安南湖赛龙舟 传承文化祈福安康 2019-04-22
  • 世界文化遗产旅行地中国国家地理网 2019-04-17
  • 我省进入毒蘑菇中毒高发季 省疾控中心发布安全警示 2019-04-17
  • 《人民日报》与红旗渠 2019-04-14
  • 世界杯八大热门悉数亮相 西班牙巴西最有冠军相 2019-04-14
  • 宣酒李健:酒业营销六段证明,必须抓住消费者这个“一”营销 阶段 2019-04-01
  • 巨力索具利润七连降 高管套现26亿分红不足3亿 2019-03-23
  • 澳大利亚主帅:勇气和信念在对阵法国队时至关重要 2019-03-21
  • 抚州发现“一代布王”李璟禧故居 2019-03-11