diff --git a/published/20210120 Learn JavaScript by writing a guessing game.md b/published/20210120 Learn JavaScript by writing a guessing game.md new file mode 100644 index 0000000000..e457842c51 --- /dev/null +++ b/published/20210120 Learn JavaScript by writing a guessing game.md @@ -0,0 +1,286 @@ +[#]: collector: "lujun9972" +[#]: translator: "amwps290" +[#]: reviewer: "wxy" +[#]: publisher: "wxy" +[#]: url: "https://linux.cn/article-13087-1.html" +[#]: subject: "Learn JavaScript by writing a guessing game" +[#]: via: "https://opensource.com/article/21/1/learn-javascript" +[#]: author: "Mandy Kendall https://opensource.com/users/mkendall" + +通过编写一个简单的游戏学习 JavaScript +====== + +> 通过使用一个简单的游戏来练习一些基本的 JavaScript 概念,迈出创建交互性动态 Web 内容的第一步。 + +![](https://img.linux.net.cn/data/attachment/album/202102/05/151531z6fzqx8vk8tdan81.jpg) + +可以肯定地说,没有 [JavaScript][2],大多数现代 Web 都将不存在。它是三种标准 Web 技术(以及 HTML 和 CSS )之一,它使任何人都可以创建我们在万维网体验中所期待的交互式、动态内容。从 [React][3] 这样的框架到 [D3][4] 这样的数据可视化库,很难想象没有它的 Web。 + +现在有很多东西要学习,开始学习这种流行语言的好方法是编写一个简单的应用程序以熟悉某些概念。 最近,一些人写了关于如何通过编写简单的猜谜游戏来学习自己喜欢的语言的文章,因此这是一个很好的起点! + +### 现在开始吧 + +JavaScript 有许多种风格,但我将从最基本的开始,通常称为 “普通 JavaScript”。 JavaScript 主要是一种客户端脚本语言,因此它可以在任何标准浏览器中运行,而无需安装任何程序。你只需要一个代码编辑器([Brackets][5] 就是一个不错的选择)和一个 Web 浏览器。 + +### HTML 用户界面 + +JavaScript 在 Web 浏览器中运行,并与其他标准 Web 技术 HTML 和 CSS 交互。要创建此游戏,你首先需要使用 HTML(超文本标记语言)来创建供玩家使用的简单界面。如果你不清楚,HTML 是一种标记语言,用于为 Web 内容提供结构。 + +首先,先创建一个 HTML 文件。该文件应具有 `.html` 扩展名,以使浏览器知道它是 HTML 文档。你可以将文件命名为 `guessingGame.html`。 + +在此文件中使用一些基本的 HTML 标签来显示游戏的标题、玩法说明,供玩家用来输入和提交其猜测的交互式元素以及用于向玩家提供反馈的占位符: + +``` + + + + + JavaScript Guessing Game + + +

Guess the Number!

+

I am thinking of a number between 1 and 100. Can you guess what it is?

+ + + + + +

+ + +``` + +`

` 和 `

` 元素使浏览器知道在页面上显示什么类型的文本。标签对 `

` 表示标签之间的文本(`Guess the Number!`)是标题。后面的一组 `

` 标签表示带有说明的短文本是一个段落。此代码块末尾的空 `

` 标签用作占位符,用于根据用户的输入提供一些反馈。 + +### ` + + + + +``` + +要在浏览器中运行此文件,请双击文件或打开你喜欢的浏览器,点击菜单,然后选择**文件->打开文件**。(如果使用 Brackets 软件,也可以使用角落处的闪电图标在浏览器中打开文件)。 + +### 生成伪随机数 + +猜谜游戏的第一步是为玩家生成一个数字供玩家猜测。JavaScript 包含几个内置的全局对象,可帮助你编写代码。要生成随机数,请使用 `Math` 对象。 + +JavaScript中的 [Math][17] 具有处理和数学相关的属性和功能。你将使用两个数学函数来生成随机数,供你的玩家猜测。 + +[Math.random()][18],会将生成一个介于 0 和 1 之间的伪随机数。(`Math.random` 包含 0 但不包含 1。这意味着该函数可以生成 0 ,永远不会产生 1) + +对于此游戏,请将随机数设置在 1 到 100 之间以缩小玩家的选择范围。取刚刚生成的小数,然后乘以 100,以产生一个介于 0 到……甚至不是 100 之间的小数。至此,你将需要其他步骤来解决这个问题。 + +现在,你的数字仍然是小数,但你希望它是一个整数。为此,你可以使用属于 `Math` 对象的另一个函数 [Math.floor()][19]。`Math.floor()` 的目的是返回小于或等于你作为参数指定的数字的最大整数,这意味着它会四舍五入为最接近的整数: + +``` +Math.floor(Math.random() * 100) +``` + +这样你将得到 0 到 99 之间的整数,这不是你想要的范围。你可以在最后一步修复该问题,即在结果中加 1。瞧!现在,你有一个(有点)随机生成的数字,介于 1 到 100 之间: + +``` +Math.floor(Math.random() * 100) + 1 +``` + +### 变量 + +现在,你需要存储随机生成的数字,以便可以将其与玩家的猜测进行比较。为此,你可以将其存储到一个 **变量**。 + +JavaScript 具有不同类型的变量,你可以选择这些类型,具体取决于你要如何使用该变量。对于此游戏,请使用 `const` 和 `let`。 + + * `let` 用于指示变量在整个程序中可以改变。 + * `const` 用于指示变量不应该被修改。 + +`const` 和 `let` 还有很多要说的,但现在知道这些就足够了。 + +随机数在游戏中仅生成一次,因此你将使用 `const` 变量来保存该值。你想给变量起一个清楚地表明要存储什么值的名称,因此将其命名为 `randomNumber`: + +``` +const randomNumber +``` + +有关命名的注意事项:JavaScript 中的变量和函数名称以驼峰形式编写。如果只有一个单词,则全部以小写形式书写。如果有多个单词,则第一个单词均为小写,其他任何单词均以大写字母开头,且单词之间没有空格。 + +### 打印到控制台 + +通常,你不想向任何人显示随机数,但是开发人员可能想知道生成的数字以使用它来帮助调试代码。 使用 JavaScript,你可以使用另一个内置函数 [console.log()][20] 将数字输出到浏览器的控制台。 + +大多数浏览器都包含开发人员工具,你可以通过按键盘上的 `F12` 键来打开它们。从那里,你应该看到一个 **控制台** 标签。打印到控制台的所有信息都将显示在此处。由于到目前为止编写的代码将在浏览器加载后立即运行,因此,如果你查看控制台,你应该会看到刚刚生成的随机数! + +![Javascript game with console][21] + +### 函数 + +接下来,你需要一种方法来从数字输入字段中获得玩家的猜测,将其与你刚刚生成的随机数进行比较,并向玩家提供反馈,让他们知道他们是否正确猜到了。为此,编写一个函数。 **函数** 是执行一定任务的代码块。函数是可以重用的,这意味着如果你需要多次运行相同的代码,则可以调用函数,而不必重写执行任务所需的所有步骤。 + +根据你使用的 JavaScript 版本,有许多不同的方法来编写或声明函数。由于这是该语言的基础入门,因此请使用基本函数语法声明函数。 + +以关键字 `function` 开头,然后起一个函数名。好的做法是使用一个描述该函数的功能的名称。在这个例子中,你正在检查玩家的猜测的数,因此此函数的名字可以是 `checkGuess`。在函数名称之后,写上一组小括号,然后写上一组花括号。 你将在以下花括号之间编写函数的主体: + +``` +function checkGuess() {} +``` + +### 使用 DOM + +JavaScript 的目的之一是与网页上的 HTML 交互。它通过文档对象模型(DOM)进行此操作,DOM 是 JavaScript 用于访问和更改网页信息的对象。现在,你需要从 HTML 中获取数字输入字段中玩家的猜测。你可以使用分配给 HTML 元素的 `id` 属性(在这种情况下为 `guess`)来做到这一点: + +``` + +``` + +JavaScript 可以通过访问玩家输入到数字输入字段中的数来获取其值。你可以通过引用元素的 ID 并在末尾添加 `.value` 来实现。这次,使用 `let` 定义的变量来保存用户的猜测值: + +``` +let myGuess = guess.value +``` + +玩家在数字输入字段中输入的任何数字都将被分配给 `checkGuess` 函数中的 `myGuess` 变量。 + +### 条件语句 + +下一步是将玩家的猜测与游戏产生的随机数进行比较。你还想给玩家反馈,让他们知道他们的猜测是太高,太低还是正确。 + +你可以使用一系列条件语句来决定玩家将收到的反馈。**条件语句** 在运行代码块之前检查是否满足条件。如果不满足条件,则代码停止,继续检查下一个条件,或者继续执行其余代码,而无需执行条件块中的代码: + +``` +if (myGuess === randomNumber){ +  feedback.textContent = "You got it right!" +} +else if(myGuess > randomNumber) { +  feedback.textContent = "Your guess was " + myGuess + ". That's too high. Try Again!" +} +else if(myGuess < randomNumber) { +  feedback.textContent = "Your guess was " + myGuess + ". That's too low. Try Again!" +} +``` + +第一个条件块使用比较运算符 `===` 将玩家的猜测与游戏生成的随机数进行比较。比较运算符检查右侧的值,将其与左侧的值进行比较,如果匹配则返回布尔值 `true`,否则返回布尔值 `false`。 + +如果数字匹配(猜对了!),为了让玩家知道。通过将文本添加到具有 `id` 属性 `feedback` 的 `

` 标记中来操作 DOM。就像上面的 `guess.value` 一样,除了不是从 DOM 获取信息,而是更改其中的信息。`

` 元素没有像 `` 元素那样的值,而是具有文本,因此请使用 `.textContent` 访问元素并设置要显示的文本: + +``` +feedback.textContent = "You got it right!" +``` + +当然,玩家很有可能在第一次尝试时就猜错了,因此,如果 `myGuess` 和 `randomNumber` 不匹配,请给玩家一个线索,以帮助他们缩小猜测范围。如果第一个条件失败,则代码将跳过该 `if` 语句中的代码块,并检查下一个条件是否为 `true`。 这使你进入 `else if` 块: + +``` +else if(myGuess > randomNumber) { +  feedback.textContent = "Your guess was " + myGuess + ". That's too high. Try Again!" +} +``` + +如果你将其作为句子阅读,则可能是这样的:“如果玩家的猜测等于随机数,请让他们知道他们猜对了。否则,请检查玩家的猜测是否大于 `randomNumber`,如果是,则显示玩家的猜测并告诉他们太高了。” + +最后一种可能性是玩家的猜测低于随机数。 要检查这一点,再添加一个 `else if` 块: + +``` +else if(myGuess < randomNumber) { +  feedback.textContent = "Your guess was " + myGuess + ". That's too low. Try Again!" +} +``` + +### 用户事件和事件监听器 + +如果你看上面的代码,则会看到某些代码在页面加载时自动运行,但有些则不会。你想在玩游戏之前生成随机数,但是你不想在玩家将数字输入到数字输入字段并准备检查它之前检查其猜测。 + +生成随机数并将其打印到控制台的代码不在函数的范围内,因此它将在浏览器加载脚本时自动运行。 但是,要使函数内部的代码运行,你必须对其进行调用。 + +调用函数有几种方法。在此,你希望该函数在用户单击 “Check My Guess” 按钮时运行。单击按钮将创建一个用户事件,然后 JavaScript 可以 “监听” 这个事件,以便知道何时需要运行函数。 + +代码的最后一行将事件侦听器添加到按钮上,以在单击按钮时调用函数。当它“听到”该事件时,它将运行分配给事件侦听器的函数: + +``` +submitGuess.addEventListener('click', checkGuess) +``` + +就像访问 DOM 元素的其他实例一样,你可以使用按钮的 ID 告诉 JavaScript 与哪个元素进行交互。 然后,你可以使用内置的 `addEventListener` 函数来告诉 JavaScript 要监听的事件。 + +你已经看到了带有参数的函数,但花点时间看一下它是如何工作的。参数是函数执行其任务所需的信息。并非所有函数都需要参数,但是 `addEventListener` 函数需要两个参数。它采用的第一个参数是将为其监听的用户事件的名称。用户可以通过多种方式与 DOM 交互,例如键入、移动鼠标,键盘上的 `TAB` 键和粘贴文本。在这种情况下,你正在监听的用户事件是单击按钮,因此第一个参数将是 `click`。 + +`addEventListener`的第二个所需的信息是用户单击按钮时要运行的函数的名称。 这里我们需要 `checkGuess` 函数。 + +现在,当玩家按下 “Check My Guess” 按钮时,`checkGuess` 函数将获得他们在数字输入字段中输入的值,将其与随机数进行比较,并在浏览器中显示反馈,以使玩家知道他们猜的怎么样。 太棒了!你的游戏已准备就绪。 + +### 学习 JavaScript 以获取乐趣和收益 + +这一点点的平凡无奇的 JavaScript 只是这个庞大的生态系统所提供功能的一小部分。这是一种值得花时间投入学习的语言,我鼓励你继续挖掘并学习更多。 + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/21/1/learn-javascript + +作者:[Mandy Kendall][a] +选题:[lujun9972][b] +译者:[amwps290](https://github.com/amwps290) +校对:[wxy](https://github.com/wxy) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/mkendall +[b]: https://github.com/lujun9972 +[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/code_javascript.jpg?itok=60evKmGl "Javascript code close-up with neon graphic overlay" +[2]: https://opensource.com/tags/javascript +[3]: https://opensource.com/article/20/11/reactjs-tutorial +[4]: https://opensource.com/article/18/9/open-source-javascript-chart-libraries +[5]: https://opensource.com/article/20/12/brackets +[6]: http://december.com/html/4/element/html.html +[7]: http://december.com/html/4/element/head.html +[8]: http://december.com/html/4/element/meta.html +[9]: http://december.com/html/4/element/title.html +[10]: http://december.com/html/4/element/body.html +[11]: http://december.com/html/4/element/h1.html +[12]: http://december.com/html/4/element/p.html +[13]: http://december.com/html/4/element/label.html +[14]: http://december.com/html/4/element/input.html +[15]: http://december.com/html/4/element/form.html +[16]: http://december.com/html/4/element/script.html +[17]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math +[18]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random +[19]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor +[20]: https://developer.mozilla.org/en-US/docs/Web/API/Console/log +[21]: https://opensource.com/sites/default/files/javascript-game-with-console.png "Javascript game with console" diff --git a/translated/tech/20210126 Use your Raspberry Pi as a productivity powerhouse.md b/published/20210126 Use your Raspberry Pi as a productivity powerhouse.md similarity index 82% rename from translated/tech/20210126 Use your Raspberry Pi as a productivity powerhouse.md rename to published/20210126 Use your Raspberry Pi as a productivity powerhouse.md index 5ee3f52a43..d7deca1036 100644 --- a/translated/tech/20210126 Use your Raspberry Pi as a productivity powerhouse.md +++ b/published/20210126 Use your Raspberry Pi as a productivity powerhouse.md @@ -1,16 +1,18 @@ [#]: collector: (lujun9972) [#]: translator: (geekpi) -[#]: reviewer: ( ) -[#]: publisher: ( ) -[#]: url: ( ) +[#]: reviewer: (wxy) +[#]: publisher: (wxy) +[#]: url: (https://linux.cn/article-13084-1.html) [#]: subject: (Use your Raspberry Pi as a productivity powerhouse) [#]: via: (https://opensource.com/article/21/1/raspberry-pi-productivity) [#]: author: (Kevin Sonney https://opensource.com/users/ksonney) 将你的树莓派用作生产力源泉 ====== -树莓派已经从主要为 hack 和业余爱好者服务,成为了小型生产力工作站的可靠选择。 -![Team checklist and to dos][1] + +> 树莓派已经从主要为黑客和业余爱好者服务,成为了小型生产力工作站的可靠选择。 + +![](https://img.linux.net.cn/data/attachment/album/202102/04/103826pjbxb7j1m8ok6ezf.jpg) 在前几年,这个年度系列涵盖了单个的应用。今年,我们除了关注 2021 年的策略外,还将关注一体化解决方案。欢迎来到 2021 年 21 天生产力的第十六天。 @@ -20,17 +22,17 @@ ![Geary and Calendar apps on the Raspberry Pi][3] -Geary 和 Calendar 应用 (Kevin Sonney, [CC BY-SA 4.0][4]) +*Geary 和 Calendar 应用 (Kevin Sonney, [CC BY-SA 4.0][4])* 基本的 [Raspbian][5] 安装包括 [Claw Mail][6],这是一个轻量级的邮件客户端。它的用户界面有点过时了,而且非常的简陋。如果你是一个 [Mutt 用户][7],它可能会满足你的需求。 我更喜欢安装 [Geary][8],因为它也是轻量级的,而且有一个现代化的界面。另外,与 Claws 不同的是,Geary 默认支持富文本 (HTML) 邮件。我不喜欢富文本电子邮件,但它已经成为必要的,所以对它有良好的支持是至关重要的。 -默认的 Raspbian 安装不包含日历,所以我添加了 [GNOME Calendar][9] ,因为它可以与远程服务通信(因为我的几乎所有日历都在云提供商那里)。 +默认的 Raspbian 安装不包含日历,所以我添加了 [GNOME 日历][9] ,因为它可以与远程服务通信(因为我的几乎所有日历都在云提供商那里)。 ![GTG and GNote open on Raspberry Pi][10] -GTG 和 GNote(Kevin Sonney, [CC BY-SA 4.0][4]) +*GTG 和 GNote(Kevin Sonney, [CC BY-SA 4.0][4])* 那笔记和待办事项清单呢?有很多选择,但我喜欢用 [GNote][11] 来做笔记,用 [Getting-Things-GNOME!][12] 来做待办事项。两者都相当轻量级,并且可以相互同步,也可以同步到其他服务。 @@ -40,9 +42,9 @@ GNOME 中包含了 [Evolution][13],它将邮件、日历、笔记、待办事 ![Evolution on Raspbian][14] -Raspbian 上的 Evolution (Kevin Sonney, [CC BY-SA 4.0][4]) +*Raspbian 上的 Evolution (Kevin Sonney, [CC BY-SA 4.0][4])* -树莓派在过去的几年里走过了很长的路,已经从主要为 hack 和业余爱好者服务,成为了小型生产力工作站的可靠选择。 +树莓派在过去的几年里进步很快,已经从主要为黑客和业余爱好者服务,成为了小型生产力工作站的可靠选择。 -------------------------------------------------------------------------------- @@ -51,7 +53,7 @@ via: https://opensource.com/article/21/1/raspberry-pi-productivity 作者:[Kevin Sonney][a] 选题:[lujun9972][b] 译者:[geekpi](https://github.com/geekpi) -校对:[校对者ID](https://github.com/校对者ID) +校对:[wxy](https://github.com/wxy) 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 diff --git a/translated/tech/20210127 3 email mistakes and how to avoid them.md b/published/20210127 3 email mistakes and how to avoid them.md similarity index 65% rename from translated/tech/20210127 3 email mistakes and how to avoid them.md rename to published/20210127 3 email mistakes and how to avoid them.md index a065a4e917..4e0b3c8cc9 100644 --- a/translated/tech/20210127 3 email mistakes and how to avoid them.md +++ b/published/20210127 3 email mistakes and how to avoid them.md @@ -1,30 +1,30 @@ [#]: collector: (lujun9972) [#]: translator: (geekpi) -[#]: reviewer: ( ) -[#]: publisher: ( ) -[#]: url: ( ) +[#]: reviewer: (wxy) +[#]: publisher: (wxy) +[#]: url: (https://linux.cn/article-13086-1.html) [#]: subject: (3 email mistakes and how to avoid them) [#]: via: (https://opensource.com/article/21/1/email-mistakes) [#]: author: (Kevin Sonney https://opensource.com/users/ksonney) 3 个电子邮件错误以及如何避免它们 ====== -自动化是美好的,除非不是那样。确保你的电子邮件自动回复和抄送配置正确,这样你就不会浪费大家的时间。 -![Computer screen with files or windows open][1] + +> 自动化是美好的,但也不总是那样。确保你的电子邮件自动回复和抄送配置正确,这样你就不会浪费大家的时间。 + +![](https://img.linux.net.cn/data/attachment/album/202102/05/090335a888nqn7pcolblzn.jpg) 在前几年,这个年度系列涵盖了单个的应用。今年,我们除了关注 2021 年的策略外,还将关注一体化解决方案。欢迎来到 2021 年 21 天生产力的第十七天。 好了,我们已经谈到了一些我们应该对电子邮件做的事情:[不要再把它当作即时通讯工具][2]、[优先处理事情][3]、[努力达到收件箱 0 新邮件][4],以及[有效过滤][5]。但哪些事情是我们不应该做的呢? -  - ![Automated email reply][6] -你真幸运 (Kevin Sonney, [CC BY-SA 4.0][7]) +*你真幸运 (Kevin Sonney, [CC BY-SA 4.0][7])* -### 1\. 请不要对_所有事情_自动回复 +### 1、请不要对所有事情自动回复 -任何邮件列表中总有些人,他们去度假了,并设置了一个“我在度假”的自动回复信息。然而,他们没有正确地设置,所以它对列表上的每一封邮件都会回复“我在度假”,直到管理员将其屏蔽或取消订阅。 +邮件列表中总有些人,他们去度假了,并设置了一个“我在度假”的自动回复信息。然而,他们没有正确地设置,所以它对列表上的每一封邮件都会回复“我在度假”,直到管理员将其屏蔽或取消订阅。 我们都感受到了这种痛苦,我承认过去至少有一次,我就是那个人。 @@ -32,25 +32,25 @@ ![An actual email with lots of CC'd recipients][8] -这是一封真实的电子邮件 (Kevin Sonney, [CC BY-SA 4.0][7]) +*这是一封真实的电子邮件 (Kevin Sonney, [CC BY-SA 4.0][7])* -### 2\. 请不要抄送给所有人 +### 2、请不要抄送给所有人 -我们都至少做过一次。我们需要发送邮件的人员众多,因此我们只需抄送他们_所有人_。有时这是有必要的,但大多数时候,它并不是。当然,你邀请每个人在庭院吃生日蛋糕,或者你的表姐要结婚了,或者公司刚拿到一个大客户,这都是好事。如果你有邮件列表的话,请用邮件列表,如果没有的话,请给每个人密送。说真的,密送是你的朋友。 +我们都至少做过一次。我们需要发送邮件的人员众多,因此我们只需抄送他们*所有人*。有时这是有必要的,但大多数时候,它并不是。当然,你邀请每个人在庭院吃生日蛋糕,或者你的表姐要结婚了,或者公司刚拿到一个大客户,这都是好事。如果你有邮件列表的话,请用邮件列表,如果没有的话,请给每个人密送。说真的,密送是你的朋友。 -### 3\. 回复所有人不是你的朋友 +### 3、回复所有人不是你的朋友 ![Reply options in Kmail][9] 这一条与上一条是相辅相成的。我不知道有多少次看到有人向一个名单(或者是一个大群的人)发送信息,而这个信息本来是要发给一个人的。我见过这样发送的相对好的邮件,以及随之而来的纪律处分邮件。 -认真地说,除非必须,不要使用“回复全部”按钮。即使是这样,也要确保你_真的_需要这样做。 +认真地说,除非必须,不要使用“回复全部”按钮。即使是这样,也要确保你*真的*需要这样做。 -有些电子邮件应用比其他应用管理得更好。Kmail,[KDE Kontact][10] 的电子邮件组件,在**回复**工具栏按钮的子菜单中,有几个不同的回复选项。你可以选择只回复给**发件人**字段中的任何实体(通常是一个人,但有时是一个邮件列表),或者回复给作者(在抄送或密送中去除任何一个人),只回复给一个邮件列表,或者回复_所有人_(不要这样做)。看到明确列出的选项,的确可以帮助你了解谁会收到你要发送的邮件副本,这有时比你想象的更发人深省。我曾经发现,当我意识到一个评论并不一定会对一个复杂的讨论的最终目标有所帮助时,我就会把邮件的收件人改为只收作者,而不是整个列表。 +有些电子邮件应用比其他应用管理得更好。Kmail,[KDE Kontact][10] 的电子邮件组件,在**回复**工具栏按钮的子菜单中,有几个不同的回复选项。你可以选择只回复给**发件人**字段中的任何实体(通常是一个人,但有时是一个邮件列表),或者回复给作者(在抄送或密送中去除每一个人),或者只回复给一个邮件列表,或者回复*所有人*(不要这样做)。看到明确列出的选项,的确可以帮助你了解谁会收到你要发送的邮件副本,这有时比你想象的更发人深省。我曾经发现,当我意识到一个评论并不一定会对一个复杂的讨论的最终目标有所帮助时,我就会把邮件的收件人改为只是作者,而不是整个列表。 -(另外,如果你写的邮件可能会给你的 HR 或公司带来麻烦,请在点击发送之前多考虑下。) +(另外,如果你写的邮件可能会给你的 HR 或公司带来麻烦,请在点击发送之前多考虑下。—— -希望你已经_不再_电子邮件中这么做了。如果你认识的人是这样的呢?欢迎与他们分享。 +希望你已经*不再*在电子邮件中这么做了。如果你认识的人是这样的呢?欢迎与他们分享。 -------------------------------------------------------------------------------- @@ -59,7 +59,7 @@ via: https://opensource.com/article/21/1/email-mistakes 作者:[Kevin Sonney][a] 选题:[lujun9972][b] 译者:[geekpi](https://github.com/geekpi) -校对:[校对者ID](https://github.com/校对者ID) +校对:[wxy](https://github.com/wxy) 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 diff --git a/sources/tech/20200129 Ansible Playbooks Quick Start Guide with Examples.md b/sources/tech/20200129 Ansible Playbooks Quick Start Guide with Examples.md index 93b17b0fd3..8d47286681 100644 --- a/sources/tech/20200129 Ansible Playbooks Quick Start Guide with Examples.md +++ b/sources/tech/20200129 Ansible Playbooks Quick Start Guide with Examples.md @@ -1,5 +1,5 @@ [#]: collector: (lujun9972) -[#]: translator: ( ) +[#]: translator: (MjSeven) [#]: reviewer: ( ) [#]: publisher: ( ) [#]: url: ( ) diff --git a/sources/tech/20210128 How to Run a Shell Script in Linux -Essentials Explained for Beginners.md b/sources/tech/20210128 How to Run a Shell Script in Linux -Essentials Explained for Beginners.md deleted file mode 100644 index 6acc9e50e2..0000000000 --- a/sources/tech/20210128 How to Run a Shell Script in Linux -Essentials Explained for Beginners.md +++ /dev/null @@ -1,174 +0,0 @@ -[#]: collector: (lujun9972) -[#]: translator: (robsean) -[#]: reviewer: ( ) -[#]: publisher: ( ) -[#]: url: ( ) -[#]: subject: (How to Run a Shell Script in Linux [Essentials Explained for Beginners]) -[#]: via: (https://itsfoss.com/run-shell-script-linux/) -[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/) - -How to Run a Shell Script in Linux [Essentials Explained for Beginners] -====== - -There are two ways to run a shell script in Linux. You can use: - -``` -bash script.sh -``` - -Or you can execute the shell script like this: - -``` -./script.sh -``` - -That maybe simple, but it doesn’t explain a lot. Don’t worry, I’ll do the necessary explaining with examples so that you understand why a particular syntax is used in the given format while running a shell script. - -I am going to use this one line shell script to make things as uncomplicated as possible: - -``` -[email protected]:~/Scripts$ cat hello.sh - -echo "Hello World!" -``` - -### Method 1: Running a shell script by passing the file as argument to shell - -The first method involves passing the script file name as an argument to the shell. - -Considering that bash is the default shell, you can run a script like this: - -``` -bash hello.sh -``` - -Do you know the advantage of this approach? **Your script doesn’t need to have the execute permission**. Pretty handy for quick and simple tasks. - -![Running a Shell Script Linux][1] - -If you are not familiar already, I advise you to [read my detailed guide on file permission in Linux][2]. - -Keep in mind that it needs to be a shell script that you pass as argument. A shell script is composed of commands. If you use a normal text file, it will complain about incorrect commands. - -![Running a Text File As Script][3] - -In this approach, **you explicitly specified that you want to use bash as the interpreter** for the script. - -Shell is just a program and bash is an implementation of that. There are other such shells program like ksh, [zsh][4], etc. If you have other shells installed, you can use that as well instead of bash. - -For example, I installed zsh and used it to run the same script: - -![Execute Shell Script With Zsh][5] - -**Recommended Read:** - -![][6] - -#### [How to Run Multiple Linux Commands at Once in Linux Terminal [Essential Beginners Tip]][7] - -### Method 2: Execute shell script by specifying its path - -The other method to run a shell script is by providing its path. But for that to be possible, your file must be executable. Otherwise, you’ll have “permission denied” error when you try to execute the script. - -So first you need to make sure that your script has the execute permission. You can [use the chmod command][8] to give yourself this permission like this: - -``` -chmod u+x script.sh -``` - -Once your script is executable, all you need to do is to type the file name along with its absolute or relative path. Most often you are in the same directory so you just use it like this: - -``` -./script.sh -``` - -If you are not in the same directory as your script, you can specify it the absolute or relative path to the script: - -![Running Shell Script In Other Directory][9] - -#### That ./ before the script is important (when you are in the same directory as the script) - -![][10] - -Why can you not use the script name when you are in the same directory? That is because your Linux systems looks for the executables to run in a few selected directories that are specified in the PATH variable. - -Here’s the value of PATH variable for my system: - -``` -[email protected]:~$ echo $PATH -/home/abhishek/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin -``` - -This means that any file with execute permissions in one of the following directories can be executed from anywhere in the system: - - * /home/abhishek/.local/bin - * /usr/local/sbin - * /usr/local/bin - * /usr/sbin - * /usr/bin - * /sbin - * /bin - * /usr/games - * /usr/local/games - * /snap/bin - - - -The binaries or executable files for Linux commands like ls, cat etc are located in one of those directories. This is why you are able to run these commands from anywhere on your system just by using their names. See, the ls command is located in /usr/bin directory. - -![][11] - -When you specify the script WITHOUT the absolute or relative path, it cannot find it in the directories mentioned in the PATH variable. - -#### Why most shell scripts contain #! /bin/bash at the beginning of the shell scripts? - -Remember how I mentioned that shell is just a program and there are different implementations of shells. - -When you use the #! /bin/bash, you are specifying that the script is to run with bash as interpreter. If you don’t do that and run a script in ./script.sh manner, it is usually run with whatever shell you are running. - -Does it matter? It could. See, most of the shell syntax is common in all kind of shell but some might differ. - -For example, the array behavior is different in bash and zsh shells. In zsh, the array index starts at 1 instead of 0. - -![Bash Vs Zsh][12] - -Using #! /bin/bash indicates that the script is bash shell script and should be run with bash as interpreter irrespective of the shell which is being used on the system. If you are using zsh specific syntax, you can indicate that it is zsh script by adding #! /bin/zsh as the first line of the script. - -The space between #! /bin/bash doesn’t matter. You can also use #!/bin/bash. - -### Was it helpful? - -I hope this article added to your Linux knowledge. If you still have questions or suggestions, please leave a comment. - -Expert users can still nitpick this article about things I missed out. But the problem with such beginner topics is that it is not easy to find the right balance of information and avoid having too much or too few details. - -If you are interested in learning bash script, we have an [entire Bash Beginner Series][13] on our sysadmin focused website [Linux Handbook][14]. If you want, you may also [purchase the ebook with additional exercises to support Linux Handbook][15]. - --------------------------------------------------------------------------------- - -via: https://itsfoss.com/run-shell-script-linux/ - -作者:[Abhishek Prakash][a] -选题:[lujun9972][b] -译者:[译者ID](https://github.com/译者ID) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]: https://itsfoss.com/author/abhishek/ -[b]: https://github.com/lujun9972 -[1]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2021/01/run-a-shell-script-linux.png?resize=741%2C329&ssl=1 -[2]: https://linuxhandbook.com/linux-file-permissions/ -[3]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2021/01/running-text-file-as-script.png?resize=741%2C329&ssl=1 -[4]: https://www.zsh.org -[5]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2021/01/execute-shell-script-with-zsh.png?resize=741%2C253&ssl=1 -[6]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2020/09/run-multiple-commands-in-linux.png?fit=800%2C450&ssl=1 -[7]: https://itsfoss.com/run-multiple-commands-linux/ -[8]: https://linuxhandbook.com/chmod-command/ -[9]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2021/01/running-shell-script-in-other-directory.png?resize=795%2C272&ssl=1 -[10]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2021/01/executing-shell-scripts-linux.png?resize=800%2C450&ssl=1 -[11]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2021/01/locating-command-linux.png?resize=795%2C272&ssl=1 -[12]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2021/01/bash-vs-zsh.png?resize=795%2C386&ssl=1 -[13]: https://linuxhandbook.com/tag/bash-beginner/ -[14]: https://linuxhandbook.com -[15]: https://www.buymeacoffee.com/linuxhandbook diff --git a/sources/tech/20210201 Generate QR codes with this open source tool.md b/sources/tech/20210201 Generate QR codes with this open source tool.md deleted file mode 100644 index f33601d7d0..0000000000 --- a/sources/tech/20210201 Generate QR codes with this open source tool.md +++ /dev/null @@ -1,85 +0,0 @@ -[#]: collector: (lujun9972) -[#]: translator: (geekpi) -[#]: reviewer: ( ) -[#]: publisher: ( ) -[#]: url: ( ) -[#]: subject: (Generate QR codes with this open source tool) -[#]: via: (https://opensource.com/article/21/2/zint-barcode-generator) -[#]: author: (Don Watkins https://opensource.com/users/don-watkins) - -Generate QR codes with this open source tool -====== -Zint makes it easy to generate more than 50 types of custom barcodes. -![Working from home at a laptop][1] - -QR codes are an excellent way to provide information to people without the trouble and expense of printing it. Most people have smartphones that support QR code scanning, regardless of the operating system. - -There are many reasons you might want to use QR codes. Maybe you're a teacher looking to challenge your students with supplemental material to enhance learning or a restaurant that needs to provide menus while complying with social-distancing guidelines. I often walk on nature trails where trees and other flora are labeled. Supplementing those small labels with QR codes is a great way to provide additional information about the park's exhibits without the expense and maintenance of signage. In these cases and others, QR codes are very useful. - -In searching the internet for an easy, open source way to create QR codes, I discovered [Zint][2]. Zint is an excellent open source (GPLv3.0) solution for generating barcodes. According to the project's [GitHub repository][3], "Zint is a suite of programs to allow easy encoding of data in any of a wide range of public domain barcode standards and to allow integration of this capability into your own programs." - -Zint supports more than 50 types of barcodes, including QR codes (ISO 18004), that you can easily create, then copy and paste into word processing documents, blogs, wikis, and other digital media. People can scan these QR codes with their smartphones to quickly link to information. - -### Install Zint - -Zint is available for Linux, macOS, and Windows. - -You can install the Zint command with Apt on Ubuntu-based Linux distributions: - - -``` -`$ sudo apt install zint` -``` - -I also wanted a graphical user interface (GUI), so I installed Zint-QT: - - -``` -`$ sudo apt install zint-qt` -``` - -Consult the manual's [installation section][4] for macOS and Windows instructions. - -### Generate QR codes with Zint - -Once I installed the application, I launched it and created my first QR code, which is a link to Opensource.com. - -![Generating QR code with Zint][5] - -(Don Watkins, [CC BY-SA 4.0][6]) - -Zint's 50-plus other barcode options include postal codes for many countries, DotCode, EAN, EAN-14, and Universal Product Code (UPC). The [project documentation][2] contains a complete list of all the codes it can render. - -You can copy any barcode as a BMP or SVG or save the output as an image file in any size you need for your application. This is my QR code at 77x77 pixels. - -![QR code][7] - -(Don Watkins, [CC BY-SA 4.0][6]) - -The project maintains an excellent user manual with instructions for using Zint on the [command line][8] and in the [GUI][9]. You can even try Zint [online][10]. For feature requests or bug reports, [visit the site][11] or [send an email][12]. - --------------------------------------------------------------------------------- - -via: https://opensource.com/article/21/2/zint-barcode-generator - -作者:[Don Watkins][a] -选题:[lujun9972][b] -译者:[译者ID](https://github.com/译者ID) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]: https://opensource.com/users/don-watkins -[b]: https://github.com/lujun9972 -[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/wfh_work_home_laptop_work.png?itok=VFwToeMy (Working from home at a laptop) -[2]: http://www.zint.org.uk/ -[3]: https://github.com/zint/zint -[4]: http://www.zint.org.uk/Manual.aspx?type=p&page=2 -[5]: https://opensource.com/sites/default/files/uploads/zintqrcode_generation.png (Generating QR code with Zint) -[6]: https://creativecommons.org/licenses/by-sa/4.0/ -[7]: https://opensource.com/sites/default/files/uploads/zintqrcode_77px.png (QR code) -[8]: http://zint.org.uk/Manual.aspx?type=p&page=4 -[9]: http://zint.org.uk/Manual.aspx?type=p&page=3 -[10]: http://www.barcode-generator.org/ -[11]: https://lists.sourceforge.net/lists/listinfo/zint-barcode -[12]: mailto:zint-barcode@lists.sourceforge.net diff --git a/sources/tech/20210203 Paru - A New AUR Helper and Pacman Wrapper Based on Yay.md b/sources/tech/20210203 Paru - A New AUR Helper and Pacman Wrapper Based on Yay.md index ebbc04f3a9..7be70e4de5 100644 --- a/sources/tech/20210203 Paru - A New AUR Helper and Pacman Wrapper Based on Yay.md +++ b/sources/tech/20210203 Paru - A New AUR Helper and Pacman Wrapper Based on Yay.md @@ -1,5 +1,5 @@ [#]: collector: (lujun9972) -[#]: translator: ( ) +[#]: translator: (geekpi) [#]: reviewer: ( ) [#]: publisher: ( ) [#]: url: ( ) diff --git a/sources/tech/20210204 A guide to understanding Linux software libraries in C.md b/sources/tech/20210204 A guide to understanding Linux software libraries in C.md new file mode 100644 index 0000000000..68e8c4a4d3 --- /dev/null +++ b/sources/tech/20210204 A guide to understanding Linux software libraries in C.md @@ -0,0 +1,507 @@ +[#]: collector: (lujun9972) +[#]: translator: ( ) +[#]: reviewer: ( ) +[#]: publisher: ( ) +[#]: url: ( ) +[#]: subject: (A guide to understanding Linux software libraries in C) +[#]: via: (https://opensource.com/article/21/2/linux-software-libraries) +[#]: author: (Marty Kalin https://opensource.com/users/mkalindepauledu) + +A guide to understanding Linux software libraries in C +====== +Software libraries are an easy and sensible way to reuse code. +![5 pengiuns floating on iceburg][1] + +Software libraries are a longstanding, easy, and sensible way to reuse code. This article explains how to build libraries from scratch and make them available to clients. Although the two sample libraries target Linux, the steps for creating, publishing, and using these libraries apply to other Unix-like systems. + +The sample libraries are written in C, which is well suited to the task. The Linux kernel is written mostly in C with the rest in assembly language. (The same goes for Windows and Linux cousins such as macOS.) The standard system libraries for input/output, networking, string processing, mathematics, security, data encoding, and so on are likewise written mainly in C. To write a library in C is therefore to write in Linux's native language. Moreover, C sets the mark for performance among high-level languages. + +There are also two sample clients (one in C, the other in Python) to access the libraries. It's no surprise that a C client can access a library written in C, but the Python client illustrates that a library written in C can serve clients from other languages. + +### Static versus dynamic libraries + +Linux systems have two types of libraries: + + * A **static library (aka library archive)** is baked into a statically compiled client (e.g., one in C or Rust) during the compilation process' link phase. In effect, each client gets its own copy of the library. A significant downside of a static library comes to the fore if the library needs to be revised (for example, to fix a bug), as each library client must be relinked to the static library. A dynamic library, described next, avoids this shortcoming. + * A **dynamic (aka shared) library** is flagged during a statically compiled client program's link phase, but the client program and the library code remain otherwise unconnected until runtime—the library code is not baked into the client. At runtime, the system's dynamic loader connects a shared library with an executing client, regardless of whether the client comes from a statically compiled language, such as C, or a dynamically compiled language, such as Python. As a result, a dynamic library can be updated without inconveniencing clients. Finally, multiple clients can share a single copy of a dynamic library. + + + +In general, dynamic libraries are preferred over static ones, although there is a cost in complexity and performance. Here's how each library type is created and published: + + 1. The source code for the library is compiled into one or more object modules, which are binary files that can be included in a library and linked to executable clients. + 2. The object modules are packaged into a single file. For a static library, the standard extension is `.a` for "archive." For a dynamic library, the extension is `.so` for "shared object." The two sample libraries, which have the same functionality, are published as the files `libprimes.a` (static) and `libshprimes.so` (dynamic). The prefix `lib` is used for both types of library. + 3. The library file is copied to a standard directory so that client programs, without fuss, can access the library. A typical location for the library, whether static or dynamic, is `/usr/lib` or `/usr/local/lib`; other locations are possible. + + + +Detailed steps for building and publishing each type of library are coming shortly. First, however, I will introduce the C functions in the two libraries. + +### The sample library functions + +The two sample libraries are built from the same five C functions, four of which are accessible to client programs. The fifth function, which is a utility for one of the other four, shows how C supports hiding information. The source code for each function is short enough that the functions can be housed in a single source file, although multiple source files (e.g., one per each of the four published functions) is an option. + +The library functions are elementary and deal, in various ways, with prime numbers. All of the functions expect unsigned (i.e., non-negative) integer values as arguments: + + * The `is_prime` function tests whether its single argument is a prime. + * The `are_coprimes` function checks whether its two arguments have a greatest common divisor (gcd) of 1, which defines co-primes. + * The `prime_factors` function lists the prime factors of its argument. + * The `goldbach` function expects an even integer value of 4 or more, listing whichever two primes sum to this argument; there may be multiple summing pairs. The function is named after the 18th-century mathematician [Christian Goldbach][2], whose conjecture that every even integer greater than two is the sum of two primes remains one of the oldest unsolved problems in number theory. + + + +The utility function `gcd` resides in the deployed library files, but this function is not accessible outside of its containing file; hence, a library client cannot directly invoke the `gcd` function. A closer look at C functions clarifies the point. + +### More on C functions + +Every function in C has a storage class, which determines the function's scope. For functions there are two options: + + * The default storage class for functions is `extern`, which gives a function global scope. A client program can call any `extern` function in the sample libraries. Here's the definition for the function `are_coprimes` with an explicit `extern`: [code] extern unsigned are_coprimes(unsigned n1, unsigned n2) { +  ... +} +``` + * The storage class `static` limits a function's scope to the file in which the function is defined. In the sample libraries, the utility function `gcd` is `static`: [code] static unsigned gcd(unsigned n1, unsigned n2) { +  ... +} +``` + + + +Only functions within the `primes.c` file can invoke `gcd`, and only the function `are_coprimes` does so. When the static and dynamic libraries are built and published, other programs can call an `extern` function such as `are_coprimes` but not the `static` function `gcd`. The `static` storage class thus hides the `gcd` function from library clients by limiting the function's scope to the other library functions. + +The functions other than `gcd` in the `primes.c` file need not specify a storage class, which would default to `extern`. However, it is common in libraries to make the `extern` explicit. + +C distinguishes between function definitions and declarations, which is important for libraries. Let's start with definitions. C has named functions only, and every function is defined with: + + * A unique name. No two functions in a program can have the same name. + * An argument list, which may be empty. The arguments are typed. + * Either a return value type (e.g., `int` for a 32-bit signed integer) or `void` if there is no value returned. + * A body enclosed in curly braces. In a contrived example, the body could be empty. + + + +Every function in a program must be defined exactly once. + +Here's the full definition for the library function `are_coprimes`: + + +``` +extern unsigned are_coprimes(unsigned n1, unsigned n2) { /* definition */ +  return 1 == gcd(n1, n2); /* greatest common divisor of 1? */ +} +``` + +The function returns a boolean value (either 0 for false or 1 for true), depending on whether the two integer arguments have a greatest common divisor of 1. The utility function `gcd` computes the greatest common divisor of integer arguments `n1` and `n2`. + +A function declaration, unlike a definition, does not have a body: + + +``` +`extern unsigned are_coprimes(unsigned n1, unsigned n2); /* declaration */` +``` + +The declaration ends with a semicolon after the argument list; there are no curly braces enclosing a body. A function may be declared multiple times within a program. + +Why are declarations needed at all? In C, a called function must be visible to its caller. There are various ways to provide such visibility, depending on how fussy the compiler is. One sure way is to define the called function above its caller when both reside in the same file: + + +``` +void f() {...}     /* f is defined before being called */ +void g() { f(); }  /* ok */ +``` + +The definition of function `f` could be moved below the call from function `g` if `f` were declared above the call: + + +``` +void f();         /* declaration makes f visible to caller */ +void g() { f(); } /* ok */ +void f() {...}    /* easier to put this above the call from g */ +``` + +But what if the called function resides in a different file than its caller? How are functions defined in one file made visible in another file, given that each function must be defined exactly once in a program? + +This issue impacts libraries, whether static or dynamic. For example, the functions in the two primes libraries are defined in the source file `primes.c`, binary copies of which are in each library; but these defined functions must be visible to a library client in C, which is a separate program with its own source file(s). + +Providing visibility across files is what function declarations can do. For the "primes" examples, there is a header file named `primes.h` that declares the four functions to be made visible to library clients in C: + + +``` +/** header file primes.h: function declarations **/ +extern unsigned is_prime(unsigned); +extern void prime_factors(unsigned); +extern unsigned are_coprimes(unsigned, unsigned); +extern void goldbach(unsigned); +``` + +These declarations serve as an interface by specifying the invocation syntax for each function. + +For client convenience, the text file `primes.h` could be stored in a directory on the C compiler's search path. Typical locations are `/usr/include` and `/usr/local/include`. A C client would `#include` this header file near the top of the client's source code. (A header file is thus imported into the "head" of another source file.) C header files also serve as input to utilities (e.g., Rust's `bindgen`) that enable clients in other languages to access a C library. + +In summary, a library function is defined exactly once but declared wherever needed; any library client in C needs the declaration. A header file should contain function declarations—but not function definitions. If a header file did contain definitions, the file might be included multiple times in a C program, thereby breaking the rule that a function must be defined exactly once in a C program. + +### The library source code + +Below is the source code for two libraries. This code, the header file, and the two sample clients are available on my [website][3]. + +**The library functions** + + +``` +#include <stdio.h> +#include <math.h> + +extern unsigned is_prime(unsigned n) { +  if (n <= 3) return n > 1;                   /* 2 and 3 are prime */ +  if (0 == (n % 2) || 0 == (n % 3)) return 0; /* multiples of 2 or 3 aren't */ + +  /* check that n is not a multiple of other values < n */ +  unsigned i; +  for (i = 5; (i * i) <= n; i += 6) +    if (0 == (n % i) || 0 == (n % (i + 2))) return 0; /* not prime */ + +  return 1; /* a prime other than 2 or 3 */ +} + +extern void prime_factors(unsigned n) { +  /* list 2s in n's prime factorization */ +  while (0 == (n % 2)) {   +    [printf][4]("%i ", 2); +    n /= 2; +  } + +  /* 2s are done, the divisor is now odd */ +  unsigned i; +  for (i = 3; i <= [sqrt][5](n); i += 2) { +    while (0 == (n % i)) { +      [printf][4]("%i ", i); +      n /= i; +    } +  } + +  /* one more prime factor? */ +  if (n > 2) [printf][4]("%i", n); +} + +/* utility function: greatest common divisor */ +static unsigned gcd(unsigned n1, unsigned n2) { +  while (n1 != 0) { +    unsigned n3 = n1; +    n1 = n2 % n1; +    n2 = n3; +  } +  return n2; +} + +extern unsigned are_coprimes(unsigned n1, unsigned n2) { +  return 1 == gcd(n1, n2); +} + +extern void goldbach(unsigned n) { +  /* input errors */ +  if ((n <= 2) || ((n & 0x01) > 0)) { +    [printf][4]("Number must be > 2 and even: %i is not.\n", n); +    return; +  } + +  /* two simple cases: 4 and 6 */ +  if ((4 == n) || (6 == n)) { +    [printf][4]("%i = %i + %i\n", n, n / 2, n / 2); +    return; +  } +  +  /* for n >= 8: multiple possibilities for many */ +  unsigned i; +  for (i = 3; i < (n / 2); i++) { +    if (is_prime(i) && is_prime(n - i)) { +      [printf][4]("%i = %i + %i\n", n, i, n - i); +      /* if one pair is enough, replace this with break */ +    } +  } +} +``` + +These functions serve as grist for the library mill. The two libraries derive from exactly the same source code, and the header file `primes.h` is the C interface for both libraries. + +### Building the libraries + +The steps for building and publishing static and dynamic libraries differ in a few details. Only three steps are required for the static library and just two more for the dynamic library. The additional steps in building the dynamic library reflect the added flexibility of the dynamic approach. Let's start with the static library. + +The library source file `primes.c` is compiled into an object module. Here's the command, with the percent sign as the system prompt (double sharp signs introduce my comments): + + +``` +`% gcc -c primes.c ## step 1 static` +``` + +This produces the binary file `primes.o`, the object module. The flag `-c` means compile only. + +The next step is to archive the object module(s) by using the Linux `ar` utility: + + +``` +`% ar -cvq libprimes.a primes.o ## step 2 static` +``` + +The three flags `-cvq` are short for "create," "verbose," and "quick append" (in case new files must be added to an archive). Recall that the prefix `lib` is standard, but the library name is arbitrary. Of course, the file name for a library must be unique to avoid conflicts. + +The archive is ready to be published: + + +``` +`% sudo cp libprimes.a /usr/local/lib ## step 3 static` +``` + +The static library is now accessible to clients, examples of which are forthcoming. (The `sudo` is included to ensure the correct access rights for copying a file into `/usr/local/lib`.) + +The dynamic library also requires one or more object modules for packaging: + + +``` +`% gcc primes.c -c -fpic ## step 1 dynamic` +``` + +The added flag `-fpic` directs the compiler to generate position-independent code, which is a binary module that need not be loaded into a fixed memory location. Such flexibility is critical in a system of multiple dynamic libraries. The resulting object module is slightly larger than the one generated for the static library. + +Here's the command to create the single library file from the object module(s): + + +``` +`% gcc -shared -Wl,-soname,libshprimes.so -o libshprimes.so.1 primes.o ## step 2 dynamic` +``` + +The flag `-shared` indicates that the library is shared (dynamic) rather than static. The `-Wl` flag introduces a list of compiler options, the first of which sets the dynamic library's `soname`, which is required. The `soname` first specifies the library's logical name (`libshprimes.so`) and then, following the `-o` flag, the library's physical file name (`libshprimes.so.1`). The goal to is keep the logical name constant while allowing the physical file name to change with new versions. In this example, the 1 at the end of the physical file name `libshprimes.so.1` represents the first version of the library. The logical and physical file names could be the same, but best practice is to have separate names. A client accesses the library through its logical name (in this case, `libshprimes.so`), as I will clarify shortly. + +The next step is to make the shared library easily accessible to clients by copying it to the appropriate directory; for example, `/usr/local/lib again:` + + +``` +`% sudo cp libshprimes.so.1 /usr/local/lib ## step 3 dynamic` +``` + +A symbolic link is now set up between the shared library's logical name (`libshprimes.so`) and its full physical file name (`/usr/local/lib/libshprimes.so.1`). It's easiest to give the command with `/usr/local/lib` as the working directory: + + +``` +`% sudo ln --symbolic libshprimes.so.1 libshprimes.so ## step 4 dynamic` +``` + +The logical name `libshprimes.so` should not change, but the target of the symbolic link (`libshrimes.so.1`) can be updated as needed for new library implementations that fix bugs, boost performance, and so on. + +The final step (a precautionary one) is to invoke the `ldconfig` utility, which configures the system's dynamic loader. This configuration ensures that the loader will find the newly published library: + + +``` +`% sudo ldconfig ## step 5 dynamic` +``` + +The dynamic library is now ready for clients, including the two sample ones that follow. + +### A C library client + +The sample C client is the program tester, whose source code begins with two `#include` directives: + + +``` +#include <stdio.h>  /* standard input/output functions */ +#include <primes.h> /* my library functions */ +``` + +The angle brackets around the file names indicate that these header files are to be found on the compiler's search path (in the case of `primes.h`, the directory `/usr/local/include`). Without this `#include`, the compiler would complain about missing declarations for functions such as `is_prime` and `prime_factors`, which are published in both libraries. By the way, the source code for the tester program need not change at all to test each of the two libraries. + +By contrast, the source file for the library (`primes.c`) opens with these `#include` directives: + + +``` +#include <stdio.h> +#include <math.h> +``` + +The header file `math.h` is required because the library function `prime_factors` calls the mathematics function `sqrt` in the standard library `libm.so`. + +For reference, here is the source code for the tester program: + +**The tester program** + + +``` +#include <stdio.h> +#include <primes.h> + +int main() { +  /* is_prime */ +  [printf][4]("\nis_prime\n"); +  unsigned i, count = 0, n = 1000; +  for (i = 1; i <= n; i++) { +    if (is_prime(i)) { +      count++; +      if (1 == (i % 100)) [printf][4]("Sample prime ending in 1: %i\n", i); +    } +  } +  [printf][4]("%i primes in range of 1 to a thousand.\n", count); + +  /* prime_factors */ +  [printf][4]("\nprime_factors\n"); +  [printf][4]("prime factors of 12: "); +  prime_factors(12); +  [printf][4]("\n"); +  +  [printf][4]("prime factors of 13: "); +  prime_factors(13); +  [printf][4]("\n"); +  +  [printf][4]("prime factors of 876,512,779: "); +  prime_factors(876512779); +  [printf][4]("\n"); + +  /* are_coprimes */ +  [printf][4]("\nare_coprime\n"); +  [printf][4]("Are %i and %i coprime? %s\n", +         21, 22, are_coprimes(21, 22) ? "yes" : "no"); +  [printf][4]("Are %i and %i coprime? %s\n", +         21, 24, are_coprimes(21, 24) ? "yes" : "no"); + +  /* goldbach */ +  [printf][4]("\ngoldbach\n"); +  goldbach(11);    /* error */ +  goldbach(4);     /* small one */ +  goldbach(6);     /* another */ +  for (i = 100; i <= 150; i += 2) goldbach(i); + +  return 0; +} +``` + +In compiling `tester.c` into an executable, the tricky part is the order of the link flags. Recall that the two sample libraries begin with the prefix `lib`, and each has the usual extension: `.a` for the static library `libprimes.a` and `.so` for the dynamic library `libshprimes.so`. In a links specification, the prefix `lib` and the extension fall away. A link flag begins with `-l` (lowercase L), and a compilation command may contain many link flags. Here is the full compilation command for the tester program, using the dynamic library as the example: + + +``` +`% gcc -o tester tester.c -lshprimes -lm` +``` + +The first link flag identifies the library `libshprimes.so` and the second link flag identifies the standard mathematics library `libm.so`. + +The linker is lazy, which means that the order of the link flags matters. For example, reversing the order of the link specifications generates a compile-time error: + + +``` +`% gcc -o tester tester.c -lm -lshprimes ## danger!` +``` + +The flag that links to `libm.so` comes first, but no function from this library is invoked explicitly in the tester program; hence, the linker does not link to the `math.so` library. The call to the `sqrt` library function occurs only in the `prime_factors` function that is now contained in the `libshprimes.so` library. The resulting error in compiling the tester program is: + + +``` +`primes.c: undefined reference to 'sqrt'` +``` + +Accordingly, the order of the link flags should notify the linker that the `sqrt` function is needed: + + +``` +`% gcc -o tester tester.c -lshprimes -lm ## -lshprimes 1st` +``` + +The linker picks up the call to the library function `sqrt` in the `libshprimes.so` library and, therefore, does the appropriate link to the mathematics library `libm.so`. There is a more complicated option for linking that supports either link-flag order; in this case, however, the easy way is to arrange the link flags appropriately. + +Here is some output from a run of the tester client: + + +``` +is_prime +Sample prime ending in 1: 101 +Sample prime ending in 1: 401 +... +168 primes in range of 1 to a thousand. + +prime_factors +prime factors of 12: 2 2 3 +prime factors of 13: 13 +prime factors of 876,512,779: 211 4154089 + +are_coprime +Are 21 and 22 coprime? yes +Are 21 and 24 coprime? no + +goldbach +Number must be > 2 and even: 11 is not. +4 = 2 + 2 +6 = 3 + 3 +... +32 =  3 + 29 +32 = 13 + 19 +... +100 =  3 + 97 +100 = 11 + 89 +... +``` + +For the `goldbach` function, even a relatively small even value (e.g., 18) may have multiple pairs of primes that sum to it (in this case, 5+13 and 7+11). Such multiple prime pairs are among the factors that complicate an attempted proof of Goldbach's conjecture. + +### Wrapping up with a Python client + +Python, unlike C, is not a statically compiled language, which means that the sample Python client must access the dynamic rather than the static version of the primes library. To do so, Python has various modules (standard and third-party) that support a foreign function interface (FFI), which allows a program written in one language to invoke functions written in another. Python `ctypes` is a standard and relatively simple FFI that enables Python code to call C functions. + +Any FFI has challenges because the interfacing languages are unlikely to have exactly the same data types. For example, the primes library uses the C type `unsigned int`, which Python does not have; the `ctypes` FFI maps a C `unsigned int` to a Python `int`. Of the four `extern` C functions published in the primes library, two behave better in Python with explicit `ctypes` configuration. + +The C functions `prime_factors` and `goldbach` have `void` instead of a return type, but `ctypes` by default replaces the C `void` with the Python `int`. When called from Python code, the two C functions then return a random (hence, meaningless) integer value from the stack. However, `ctypes` can be configured to have the functions return `None` (Python's null type) instead. Here's the configuration for the `prime_factors` function: + + +``` +`primes.prime_factors.restype = None` +``` + +A similar statement handles the `goldbach` function. + +The interactive session below (in Python 3) shows that the interface between a Python client and the primes library is straightforward: + + +``` +>>> from ctypes import cdll + +>>> primes = cdll.LoadLibrary("libshprimes.so") ## logical name + +>>> primes.is_prime(13) +1 +>>> primes.is_prime(12) +0 + +>>> primes.are_coprimes(8, 24) +0 +>>> primes.are_coprimes(8, 25) +1 + +>>> primes.prime_factors.restype = None +>>> primes.goldbach.restype = None + +>>> primes.prime_factors(72) +2 2 2 3 3 + +>>> primes.goldbach(32) +32 = 3 + 29 +32 = 13 + 19 +``` + +The functions in the primes library use only a simple data type, `unsigned int`. If this C library used complicated types such as structures, and if pointers to structures were passed to and returned from library functions, then an FFI more powerful than `ctypes` might be better for a smooth interface between Python and C. Nonetheless, the `ctypes` example shows that a Python client can use a library written in C. Indeed, the popular NumPy library for scientific computing is written in C and then exposed in a high-level Python API. + +The simple primes library and the advanced NumPy library underscore that C remains the lingua franca among programming languages. Almost every language can talk to C—and, through C, to any other language that talks to C. Python talks easily to C and, as another example, Java may do the same when [Project Panama][6] becomes an alternative to Java Native Interface (JNI). + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/21/2/linux-software-libraries + +作者:[Marty Kalin][a] +选题:[lujun9972][b] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/mkalindepauledu +[b]: https://github.com/lujun9972 +[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003499_01_linux31x_cc.png?itok=Pvim4U-B (5 pengiuns floating on iceburg) +[2]: https://en.wikipedia.org/wiki/Christian_Goldbach +[3]: https://condor.depaul.edu/mkalin +[4]: http://www.opengroup.org/onlinepubs/009695399/functions/printf.html +[5]: http://www.opengroup.org/onlinepubs/009695399/functions/sqrt.html +[6]: https://openjdk.java.net/projects/panama diff --git a/sources/tech/20210204 A hands-on tutorial of SQLite3.md b/sources/tech/20210204 A hands-on tutorial of SQLite3.md new file mode 100644 index 0000000000..a2c73e3461 --- /dev/null +++ b/sources/tech/20210204 A hands-on tutorial of SQLite3.md @@ -0,0 +1,272 @@ +[#]: collector: (lujun9972) +[#]: translator: ( ) +[#]: reviewer: ( ) +[#]: publisher: ( ) +[#]: url: ( ) +[#]: subject: (A hands-on tutorial of SQLite3) +[#]: via: (https://opensource.com/article/21/2/sqlite3-cheat-sheet) +[#]: author: (Klaatu https://opensource.com/users/klaatu) + +A hands-on tutorial of SQLite3 +====== +Get started with this incredibly powerful and common database. Download +the SQLite cheat sheet. +![Cheat Sheet cover image][1] + +Applications very often save data. Whether your users create simple text documents, complex graphic layouts, game progress, or an intricate list of customers and order numbers, software usually implies that data is being generated. There are many ways to store data for repeated use. You can dump text to configuration formats such as INI, [YAML][2], XML, or JSON, you can write out raw binary data, or you can store data in a structured database. SQLite is a self-contained, lightweight database that makes it easy to create, parse, query, modify, and transport data. + +**[Download our [SQLite3 cheat sheet][3]]** + +SQLite has been dedicated to the [public domain][4], which [technically means it is not copyrighted and therefore requires no license][5]. Should you require a license, you can [purchase a Warranty of Title][6]. SQLite is immensely common, with an estimated 1 _trillion_ SQLite databases in active use. That's counting multiple databases on every Android and iOS device, every macOS and Windows 10 computer, most Linux systems, within every Webkit-based web browser, modern TV sets, automotive multimedia systems, and countless other software applications. + +In summary, it's a reliable and simple system to use for storing and organizing data. + +### Installing + +You probably already have SQLite libraries on your system, but you need its command-line tools installed to use it directly. On Linux, you probably already have these tools installed. The command provided by the tools is **sqlite3** (not just **sqlite**). + +If you don't have SQLite installed on Linux or BSD, you can install it from your software repository or ports tree, or [download and install it][7] from source code or as a compiled binary. + +On macOS or Windows, you can download and install SQLite tools from [sqlite.org][7]. + +### Using SQLite + +It's common to interact with a database through a programming language. For this reason, there are SQLite interfaces (or "bindings") for Java, Python, Lua, PHP, Ruby, C++, and many many others. However, before using these libraries, it helps to understand what's actually happening with the database engine and why your choice of a database is significant. This article introduces you to SQLite and the **sqlite3** command so you can get familiar with the basics of how this database handles data. + +### Interacting with SQLite + +You can interact with SQLite using the **sqlite3** command. This command provides an interactive shell so you can view and update your databases. + + +``` +$ sqlite3 +SQLite version 3.34.0 2020-12-01 16:14:00 +Enter ".help" for usage hints. +Connected to a transient in-memory database. +Use ".open FILENAME" to reopen on a persistent database. +sqlite> +``` + +The command places you in an SQLite subshell, and so your prompt is now an SQLite prompt. Your usual Bash commands don't work here. You must use SQLite commands. To see a list of SQLite commands, type **.help**: + + +``` +sqlite> .help +.archive ...             Manage SQL archives +.auth ON|OFF             SHOW authorizer callbacks +.backup ?DB? FILE        Backup DB (DEFAULT "main") TO FILE +.bail ON|off             Stop after hitting an error.  DEFAULT OFF +.binary ON|off           Turn BINARY output ON OR off.  DEFAULT OFF +.cd DIRECTORY            CHANGE the working directory TO DIRECTORY +[...] +``` + +Some of these commands are binary, while others require unique arguments (like filenames, paths, etc.). These are administrative commands for your SQLite shell and are not database queries. Databases take queries in Structured Query Language (SQL), and many SQLite queries are the same as what you may already know from the [MySQL][8] and [MariaDB][9] databases. However, data types and functions differ, so pay close attention to minor differences if you're familiar with another database. + +### Creating a database + +When launching SQLite, you can either open a prompt in memory, or you can select a database to open: + + +``` +`$ sqlite3 mydatabase.db` +``` + +If you have no database yet, you can create one at the SQLite prompt: + + +``` +`sqlite> .open mydatabase.db` +``` + +You now have an empty file on your hard drive, ready to be used as an SQLite database. The file extension **.db** is arbitrary. You can also use **.sqlite**, or whatever you want. + +### Creating a table + +Databases contain _tables_, which can be visualized as a spreadsheet. There's a series of rows (called _records_ in a database) and columns. The intersection of a row and a column is called a _field_. + +The Structured Query Language (SQL) is named after what it provides: A method to inquire about the contents of a database in a predictable and consistent syntax to receive useful results. SQL reads a lot like an ordinary English sentence, if a little robotic. Currently, your database is empty, devoid of any tables. + +You can create a table with the **CREATE** query. It's useful to combine this with the **IF NOT EXISTS** statement, which prevents SQLite from clobbering an existing table. + +You can't create an empty table in SQLite, so before trying a **CREATE** statement, you must think about what kind of data you anticipate the table will store. In this example, I'll create a table called _member_ with these columns: + + * A unique identifier + * A person's name + * The date and time of data entry + + + +#### Unique ID + +It's always good to refer to a record by a unique number, and luckily SQLite recognizes this and does it automatically for you in a column called **rowid**. + +No SQL statement is required to create this field. + +#### Data types + +For my example table, I'm creating a _name_ column to hold **TEXT** data. To prevent a record from being created without data in a specified field, you can add the **NOT NULL** directive. + +The SQL to create this field is: **name TEXT NOT NULL** + +There are five data types (actually _storage classes_) in SQLite: + + * TEXT: a text string + * INTEGER: a whole number + * REAL: a floating point (unlimited decimal places) number + * BLOB: binary data (for instance, a .jpeg or .webp image) + * NULL: a null value + + + +#### Date and time stamp + +SQLite includes a convenient date and timestamp function. It is not a data type itself but a function in SQLite that generates either a string or integer, depending on your desired format. In this example, I left it as the default. + +The SQL to create this field is: **datestamp DATETIME DEFAULT CURRENT_TIMESTAMP** + +### Table creation SQL + +The full SQL for creating this example table in SQLite: + + +``` +sqlite> CREATE TABLE +...> IF NOT EXISTS +...> member (name TEXT NOT NULL, +...> datestamp DATETIME DEFAULT CURRENT_TIMESTAMP +``` + +In this code sample, I pressed **Return** after the logical clauses of the statement to make it easier to read. SQLite won't run your command unless it terminates with a semi-colon (**;**). + +You can verify that the table has been created with the SQLite command **.tables**: + + +``` +sqlite> .tables +member +``` + +### View all columns in a table + +You can verify what columns and rows a table contains with the **PRAGMA** statement: + + +``` +sqlite> PRAGMA table_info(member); +0|name|TEXT|1||0 +1|datestamp|CURRENT_TIMESTAMP|0||0 +``` + +### Data entry + +You can populate your new table with some sample data by using the **INSERT** SQL keyword: + + +``` +> INSERT INTO member (name) VALUES ('Alice'); +> INSERT INTO member (name) VALUES ('Bob'); +> INSERT INTO member (name) VALUES ('Carol'); +> INSERT INTO member (name) VALUES ('David'); +``` + +Verify the data in the table: + + +``` +> SELECT * FROM member; +Alice|2020-12-15 22:39:00 +Bob|2020-12-15 22:39:02 +Carol|2020-12-15 22:39:05 +David|2020-12-15 22:39:07 +``` + +#### Adding multiple rows at once + +Now create a second table: + + +``` +> CREATE TABLE IF NOT EXISTS linux ( +...> distro TEXT NOT NULL) +``` + +Populate it with some sample data, this time using a little **VALUES** shortcut so you can add multiple rows in just one command. The **VALUES** keyword expects a list in parentheses but can take multiple lists separated by commas: + + +``` +> INSERT INTO linux (distro) +...> VALUES ('Slackware'), ('RHEL'), +...> ('Fedora'),('Debian'); +``` + +### Altering a table + +You now have two tables, but as yet, there's no relationship between the two. They each contain independent data, but possibly you might need to associate a member of the first table to a specific item listed in the second. + +To do that, you can create a new column for the first table that corresponds to something in the second. Because both tables were designed with unique identifiers (automatically, thanks to SQLite), the easiest way to connect them is to use the **rowid** field of one as a selector for the other. + +Create a new column in the first table to represent a value in the second table: + + +``` +`> ALTER TABLE member ADD os INT;` +``` + +Using the unique IDs of the **linux** table, assign a distribution to each member. Because the records already exist, you use the **UPDATE** SQL keyword rather than **INSERT**. Specifically, you want to select one row and then update the value of one column. Syntactically, this is expressed a little in reverse, with the update happening first and the selection matching last: + + +``` +`> UPDATE member SET os=1 WHERE name='Alice';` +``` + +Repeat this process for the other names in the **member** table, just to populate it with data. For variety, assign three different distributions across the four rows (doubling up on one). + +### Joining tables + +Now that these two tables relate to one another, you can use SQL to display the associated data. There are many kinds of _joins_ in databases, but you can try them all once you know the basics. Here's a basic join to correlate the values found in the **os** field of the **member** table to the **id** field of the **linux** table: + + +``` +> SELECT * FROM member INNER JOIN linux ON member.os=linux.rowid; +Alice|2020-12-15 22:39:00|1|Slackware +Bob|2020-12-15 22:39:02|3|Fedora +Carol|2020-12-15 22:39:05|3|Fedora +David|2020-12-15 22:39:07|4|Debian +``` + +The **os** and **id** fields form the join. + +In a graphical application, you can imagine that the **os** field might be set by a drop-down menu, the values for which are drawn from the contents of the **distro** field of the **linux** table. By using separate tables for unique but related sets of data, you ensure the consistency and validity of data, and thanks to SQL, you can associate them dynamically later. + +### Learning more + +SQLite is an infinitely useful self-contained, portable, open source database. Learning to use it interactively is a great first step toward managing it for web applications or using it through programming language libraries. + +If you enjoy SQLite, you might also try [Fossil][10] by the same author, Dr. Richard Hipp. + +As you learn and use SQLite, it may help to have a list of common commands nearby, so download our **[SQLite3 cheat sheet][3]** today! + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/21/2/sqlite3-cheat-sheet + +作者:[Klaatu][a] +选题:[lujun9972][b] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/klaatu +[b]: https://github.com/lujun9972 +[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/coverimage_cheat_sheet.png?itok=lYkNKieP (Cheat Sheet cover image) +[2]: https://www.redhat.com/sysadmin/yaml-beginners +[3]: https://opensource.com/downloads/sqlite-cheat-sheet +[4]: https://sqlite.org/copyright.html +[5]: https://directory.fsf.org/wiki/License:PublicDomain +[6]: https://www.sqlite.org/purchase/license? +[7]: https://www.sqlite.org/download.html +[8]: https://www.mysql.com/ +[9]: https://mariadb.org/ +[10]: https://opensource.com/article/20/11/fossil diff --git a/sources/tech/20210204 Get started with distributed tracing using Grafana Tempo.md b/sources/tech/20210204 Get started with distributed tracing using Grafana Tempo.md new file mode 100644 index 0000000000..6b0efbd218 --- /dev/null +++ b/sources/tech/20210204 Get started with distributed tracing using Grafana Tempo.md @@ -0,0 +1,106 @@ +[#]: collector: (lujun9972) +[#]: translator: ( ) +[#]: reviewer: ( ) +[#]: publisher: ( ) +[#]: url: ( ) +[#]: subject: (Get started with distributed tracing using Grafana Tempo) +[#]: via: (https://opensource.com/article/21/2/tempo-distributed-tracing) +[#]: author: (Annanay Agarwal https://opensource.com/users/annanayagarwal) + +Get started with distributed tracing using Grafana Tempo +====== +Grafana Tempo is a new open source, high-volume distributed tracing +backend. +![Computer laptop in space][1] + +Grafana's [Tempo][2] is an easy-to-use, high-scale, distributed tracing backend from Grafana Labs. Tempo has integrations with [Grafana][3], [Prometheus][4], and [Loki][5] and requires only object storage to operate, making it cost-efficient and easy to operate. + +I've been involved with this open source project since its inception, so I'll go over some of the basics about Tempo and show why the cloud-native community has taken notice of it. + +### Distributed tracing + +It's common to want to gather telemetry on requests made to an application. But in the modern server world, a single application is regularly split across many microservices, potentially running on several different nodes. + +Distributed tracing is a way to get fine-grained information about the performance of an application that may consist of discreet services. It provides a consolidated view of the request's lifecycle as it passes through an application. Tempo's distributed tracing can be used with monolithic or microservice applications, and it gives you [request-scoped information][6], making it the third pillar of observability (alongside metrics and logs). + +The following is an example of a Gantt chart that distributed tracing systems can produce about applications. It uses the Jaeger [HotROD][7] demo application to generate traces and stores them in Grafana Cloud's hosted Tempo. This chart shows the processing time for the request, broken down by service and function. + +![Gantt chart from Grafana Tempo][8] + +(Annanay Agarwal, [CC BY-SA 4.0][9]) + +### Reducing index size + +Traces have a ton of information in a rich and well-defined data model. Usually, there are two interactions with a tracing backend: filtering for traces using metadata selectors like the service name or duration, and visualizing a trace once it's been filtered. + +To enhance search, most open source distributed tracing frameworks index a number of fields from the trace, including the service name, operation name, tags, and duration. This results in a large index and pushes you to use a database like Elasticsearch or [Cassandra][10]. However, these can be tough to manage and costly to operate at scale, so my team at Grafana Labs set out to come up with a better solution. + +At Grafana, our on-call debugging workflows start with drilling down for the problem using a metrics dashboard (we use [Cortex][11], a Cloud Native Computing Foundation incubating project for scaling Prometheus, to store metrics from our application), sifting through the logs for the problematic service (we store our logs in Loki, which is like Prometheus, but for logs), and then viewing traces for a given request. We realized that all the indexing information we need for the filtering step is available in Cortex and Loki. However, we needed a strong integration for trace discoverability through these tools and a complimentary store for key-value lookup by trace ID. + +This was the start of the [Grafana Tempo][12] project. By focusing on retrieving traces given a trace ID, we designed Tempo to be a minimal-dependency, high-volume, cost-effective distributed tracing backend. + +### Easy to operate and cost-effective + +Tempo uses an object storage backend, which is its only dependency. It can be used in either single binary or microservices mode (check out the [examples][13] in the repo on how to get started easily). Using object storage also means you can store a high volume of traces from applications without any sampling. This ensures that you never throw away traces for those one-in-a-million requests that errored out or had higher latencies. + +### Strong integration with open source tools + +[Grafana 7.3 includes a Tempo data source][14], which means you can visualize traces from Tempo in the Grafana UI. Also, [Loki 2.0's new query features][15] make trace discovery in Tempo easy. And to integrate with Prometheus, the team is working on adding support for exemplars, which are high-cardinality metadata information you can add to time-series data. The metric storage backends do not index these, but you can retrieve and display them alongside the metric value in the Grafana UI. While exemplars can store various metadata, trace-IDs are stored to integrate strongly with Tempo in this use case. + +This example shows using exemplars with a request latency histogram where each exemplar data point links to a trace in Tempo. + +![Using exemplars in Tempo][16] + +(Annanay Agarwal, [CC BY-SA 4.0][9]) + +### Consistent metadata + +Telemetry data emitted from applications running as containerized applications generally has some metadata associated with it. This can include information like the cluster ID, namespace, pod IP, etc. This is great for providing on-demand information, but it's even better if you can use the information contained in metadata for something productive.  + +For instance, you can use the [Grafana Cloud Agent to ingest traces into Tempo][17], and the agent leverages the Prometheus Service Discovery mechanism to poll the Kubernetes API for metadata information and adds these as tags to spans emitted by the application. Since this metadata is also indexed in Loki, it makes it easy for you to jump from traces to view logs for a given service by translating metadata into Loki label selectors. + +The following is an example of consistent metadata that can be used to view the logs for a given span in a trace in Tempo. + +### ![][18] + +### Cloud-native + +Grafana Tempo is available as a containerized application, and you can run it on any orchestration engine like Kubernetes, Mesos, etc. The various services can be horizontally scaled depending on the workload on the ingest/query path. You can also use cloud-native object storage, such as Google Cloud Storage, Amazon S3, or Azure Blog Storage with Tempo. For further information, read the [architecture section][19] in Tempo's documentation. + +### Try Tempo + +If this sounds like it might be as useful for you as it has been for us, [clone the Tempo repo][20] and give it a try. + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/21/2/tempo-distributed-tracing + +作者:[Annanay Agarwal][a] +选题:[lujun9972][b] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/annanayagarwal +[b]: https://github.com/lujun9972 +[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/computer_space_graphic_cosmic.png?itok=wu493YbB (Computer laptop in space) +[2]: https://grafana.com/oss/tempo/ +[3]: http://grafana.com/oss/grafana +[4]: https://prometheus.io/ +[5]: https://grafana.com/oss/loki/ +[6]: https://peter.bourgon.org/blog/2017/02/21/metrics-tracing-and-logging.html +[7]: https://github.com/jaegertracing/jaeger/tree/master/examples/hotrod +[8]: https://opensource.com/sites/default/files/uploads/tempo_gantt.png (Gantt chart from Grafana Tempo) +[9]: https://creativecommons.org/licenses/by-sa/4.0/ +[10]: https://opensource.com/article/19/8/how-set-apache-cassandra-cluster +[11]: https://cortexmetrics.io/ +[12]: http://github.com/grafana/tempo +[13]: https://grafana.com/docs/tempo/latest/getting-started/example-demo-app/ +[14]: https://grafana.com/blog/2020/10/29/grafana-7.3-released-support-for-the-grafana-tempo-tracing-system-new-color-palettes-live-updates-for-dashboard-viewers-and-more/ +[15]: https://grafana.com/blog/2020/11/09/trace-discovery-in-grafana-tempo-using-prometheus-exemplars-loki-2.0-queries-and-more/ +[16]: https://opensource.com/sites/default/files/uploads/tempo_exemplar.png (Using exemplars in Tempo) +[17]: https://grafana.com/blog/2020/11/17/tracing-with-the-grafana-cloud-agent-and-grafana-tempo/ +[18]: https://lh5.googleusercontent.com/vNqk-ygBOLjKJnCbTbf2P5iyU5Wjv2joR7W-oD7myaP73Mx0KArBI2CTrEDVi04GQHXAXecTUXdkMqKRq8icnXFJ7yWUEpaswB1AOU4wfUuADpRV8pttVtXvTpVVv8_OfnDINgfN +[19]: https://grafana.com/docs/tempo/latest/architecture/architecture/ +[20]: https://github.com/grafana/tempo diff --git a/sources/tech/20210204 How to implement business requirements in software development.md b/sources/tech/20210204 How to implement business requirements in software development.md new file mode 100644 index 0000000000..1527f6babe --- /dev/null +++ b/sources/tech/20210204 How to implement business requirements in software development.md @@ -0,0 +1,128 @@ +[#]: collector: (lujun9972) +[#]: translator: ( ) +[#]: reviewer: ( ) +[#]: publisher: ( ) +[#]: url: ( ) +[#]: subject: (How to implement business requirements in software development) +[#]: via: (https://opensource.com/article/21/2/exceptional-behavior) +[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzic) + +How to implement business requirements in software development +====== +Increment your e-commerce app to ensure it implements required business +process rules correctly. +![Working on a team, busy worklife][1] + +In my previous articles in this series, I explained why tackling coding problems all at once, as if they were hordes of zombies, is a mistake. I'm using a helpful acronym to explain why it's better to approach problems incrementally. **ZOMBIES** stands for: + +**Z** – Zero +**O** – One +**M** – Many (or more complex) +**B** – Boundary behaviors +**I** – Interface definition +**E** – Exercise exceptional behavior +**S** – Simple scenarios, simple solutions + +In the first three articles in this series, I demonstrated the first five **ZOMBIES** principles. The first article [implemented **Z**ero][2], which provides the simplest possible path through your code. The second article performed [tests with **O**ne and **M**any][3] samples, and the third article looked at [**B**oundaries and **I**nterfaces][4]. In this article, I'll take a look at the penultimate letter in our acronym: **E**, which stands for "exercise exceptional behavior." + +### Exceptional behavior in action + +When you write an app like the e-commerce tool in this example, you need to contact product owners or business sponsors to learn if there are any specific business policy rules that need to be implemented. + +Sure enough, as with any e-commerce operation, you want to put business policy rules in place to entice customers to keep buying. Suppose a business policy rule has been communicated that any order with a grand total greater than $500 gets a percentage discount. + +OK, time to roll up your sleeves and craft the executable expectation for this business policy rule: + + +``` +[Fact] +public void Add2ItemsTotal600GrandTotal540() { +        var expectedGrandTotal = 540.00; +        var actualGrandTotal = 0.00; +        Assert.Equal(expectedGrandTotal, actualGrandTotal); +} +``` + +The confirmation example that encodes the business policy rule states that if the order total is $600.00, the `shoppingAPI` will calculate the grand total to discount it to $540.00. The script above fakes the expectation just to see it fail. Now, make it pass: + + +``` +[Fact] +public void Add2ItemsTotal600GrandTotal540() { +        var expectedGrandTotal = 540.00; +        Hashtable item = [new][5] Hashtable(); +        item.Add("00000001", 200.00); +        shoppingAPI.AddItem(item); +        Hashtable item2 = [new][5] Hashtable(); +        item2.Add("00000002", 400.00); +        shoppingAPI.AddItem(item2); +        var actualGrandTotal = shoppingAPI.CalculateGrandTotal(); +        Assert.Equal(expectedGrandTotal, actualGrandTotal); +} +``` + +In the confirmation example, you are adding one item priced at $200 and another item priced at $400 for a total of $600 for the order. When you call the `CalculateGrandTotal()` method, you expect to get a total of $540. + +Will this microtest pass? + + +``` +[xUnit.net 00:00:00.57] tests.UnitTest1.Add2ItemsTotal600GrandTotal540 [FAIL] +  X tests.UnitTest1.Add2ItemsTotal600GrandTotal540 [2ms] +  Error Message: +   Assert.Equal() Failure +Expected: 540 +Actual: 600 +[...] +``` + +Well, it fails miserably. You were expecting $540, but the system calculates $600. Why the error? It's because you haven't taught the system how to calculate the discount on order totals larger than $500 and then subtract that discount from the grand total. + +Implement that processing logic. Judging from the confirmation example above, when the order total is $600.00 (which is greater than the business rule threshold of an order totaling $500), the expected grand total is $540. This means the system needs to subtract $60 from the grand total. And $60 is precisely 10% of $600. So the business policy rule that deals with discounts expects a 10% discount on all order totals greater than $500. + +Implement this processing logic in the `ShippingAPI` class: + + +``` +private double Calculate10PercentDiscount(double total) { +        double discount = 0.00; +        if(total > 500.00) { +                discount = (total/100) * 10; +        } +        return discount; +} +``` + +First, check to see if the order total is greater than $500. If it is, then calculate 10% of the order total. + +You also need to teach the system how to subtract the calculated 10% from the order grand total. That's a very straightforward change: + + +``` +`return grandTotal - Calculate10PercentDiscount(grandTotal);` +``` + +Now all tests pass, and you're again enjoying steady success. Your script **Exercises exceptional behavior** to implement the required business policy rules. + +### One more to go + +I've taken us to **ZOMBIE** now, so there's just **S** remaining. I'll cover that in the exciting series finale. + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/21/2/exceptional-behavior + +作者:[Alex Bunardzic][a] +选题:[lujun9972][b] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/alex-bunardzic +[b]: https://github.com/lujun9972 +[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/team_dev_email_chat_video_work_wfm_desk_520.png?itok=6YtME4Hj (Working on a team, busy worklife) +[2]: https://opensource.com/article/21/1/zombies-zero +[3]: https://opensource.com/article/21/1/zombies-2-one-many +[4]: https://opensource.com/article/21/1/zombies-3-boundaries-interface +[5]: http://www.google.com/search?q=new+msdn.microsoft.com diff --git a/translated/tech/20210120 Learn JavaScript by writing a guessing game.md b/translated/tech/20210120 Learn JavaScript by writing a guessing game.md deleted file mode 100644 index c4d659243b..0000000000 --- a/translated/tech/20210120 Learn JavaScript by writing a guessing game.md +++ /dev/null @@ -1,291 +0,0 @@ -[#]: collector: "lujun9972" -[#]: translator: "amwps290" -[#]: reviewer: " " -[#]: publisher: " " -[#]: url: " " -[#]: subject: "Learn JavaScript by writing a guessing game" -[#]: via: "https://opensource.com/article/21/1/learn-javascript" -[#]: author: "Mandy Kendall https://opensource.com/users/mkendall" - -通过编写猜数游戏学习 JavaScript -====== - -通过使用一个简单的游戏来练习一些基本的 JavaScript 概念,迈出创建交互,动态 Web 内容的第一步。 - -![Javascript code close-up with neon graphic overlay][1] - -可以肯定地说,没有 [JavaScript][2],大多数现代 web 都将不存在。 它是三种标准 Web 技术(以及 HTML 和 CSS )之一,它使任何人都可以创建许多交互式的动态内容,这是我们在使用万维网时所期望的。 从 [React][3] 这样的框架到 [D3][4] 这样的数据可视化库,很难想象没有它的网络。 - -现在有很多东西要学习,开始学习这种流行语言的好方法是编写一个简单的应用程序以熟悉某些概念。 最近,Opensource.com 通讯员写了一篇关于如何通过编写简单的猜谜游戏来学习自己喜欢的语言的文章,因此这是一个很好的起点! - -### 现在开始吧 - -JavaScript 有许多种风格,但我将从最基本的开始,通常称为 “ Vanilla JavaScript”。 JavaScript 主要是一种客户端脚本语言,因此它可以在任何标准浏览器中运行,而无需安装任何程序。 您只需要一个代码编辑器([Brackets ][5] 就是一个不错的选择)和一个 Web 浏览器。 - -### HTML 用户界面 - -JavaScript 在 Web 浏览器中运行,并与其他标准 Web 技术 HTML 和 CSS 交互。 要创建此游戏,您首先需要使用 HTML(超文本标记语言)来创建供玩家使用的简单界面。 如果您不熟悉,HTML 是一种标记语言,用于为 Web 内容提供结构。 - -首先,先创建一个 HTML 文件。 该文件应具有`.html `扩展名,以使浏览器知道它是 HTML 文档。 您可以将文件命名为 `guessingGame.html`。 - -在此文件中使用一些基本的 HTML 标签来显示游戏的标题,玩法说明,供玩家用来输入和提交其猜测的交互式元素以及用于向玩家提供反馈的占位符: - - -``` -<!DOCTYPE> -  <[html][6]> -    <[head][7]> -      <[meta][8] charset="UTF-8" /> -      <[title][9]> JavaScript Guessing Game </[title][9]> -    </[head][7]> -    <[body][10]> -      <[h1][11]>Guess the Number!</[h1][11]> -      <[p][12]>I am thinking of a number between 1 and 100. Can you guess what it is?</[p][12]> -    -      <[label][13] for="guess">My Guess</[label][13]> -      <[input][14] type="number" id="guess"> -      <[input][14] type="submit" id="submitGuess" value="Check My Guess"> -    -      <[p][12] id="feedback"></[p][12]> -    </[body][10]> -  </[html][6]> -``` - -<h1> 和 <p> 元素使浏览器知道在页面上显示什么类型的文本。 标签对 `

` 表示标签(`Guess the Number!`)之间的文本是标题。 后面的一组`

` 标签表示带有说明的短文本是一个段落。 此代码块末尾的空 `

` 标签用作占位符,用于根据用户的输入提供一些反馈。 - -### <script> 标签 - -在网页中包含 JavaScript 的方法有很多种,但是对于像这样的简短脚本,可以使用一组 `