diff --git a/published/20180429 Passwordless Auth- Client.md b/published/20180429 Passwordless Auth- Client.md new file mode 100644 index 0000000000..0a14e7a871 --- /dev/null +++ b/published/20180429 Passwordless Auth- Client.md @@ -0,0 +1,359 @@ +无密码验证:客户端 +====== + +我们继续 [无密码验证][1] 的文章。上一篇文章中,我们用 Go 写了一个 HTTP 服务,用这个服务来做无密码验证 API。今天,我们为它再写一个 JavaScript 客户端。 + +我们将使用 [这里的][2] 这个单页面应用程序(SPA)来展示使用的技术。如果你还没有读过它,请先读它。 + +记住流程: + +- 用户输入其 email。 +- 用户收到一个带有魔法链接的邮件。 +- 用户点击该链接、 +- 用户验证成功。 + +对于根 URL(`/`),我们将根据验证的状态分别使用两个不同的页面:一个是带有访问表单的页面,或者是已验证通过的用户的欢迎页面。另一个页面是验证回调的重定向页面。 + +### 伺服 + +我们将使用相同的 Go 服务器来为客户端提供服务,因此,在我们前面的 `main.go` 中添加一些路由: + +``` +router.Handle("GET", "/...", http.FileServer(SPAFileSystem{http.Dir("static")})) +``` + +``` +type SPAFileSystem struct { + fs http.FileSystem +} + +func (spa SPAFileSystem) Open(name string) (http.File, error) { + f, err := spa.fs.Open(name) + if err != nil { + return spa.fs.Open("index.html") + } + return f, nil +} +``` + +这个伺服文件放在 `static` 下,配合 `static/index.html` 作为回调。 + +你可以使用你自己的服务器,但是你得在服务器上启用 [CORS][3]。 + +### HTML + +我们来看一下那个 `static/index.html` 文件。 + +``` + + + + + + Passwordless Demo + + + + + +``` + +单页面应用程序的所有渲染由 JavaScript 来完成,因此,我们使用了一个空的 body 部分和一个 `main.js` 文件。 + +我们将使用 [上篇文章][2] 中的 Router。 + +### 渲染 + +现在,我们使用下面的内容来创建一个 `static/js/main.js` 文件: + +``` +import Router from 'https://unpkg.com/@nicolasparada/router' +import { isAuthenticated } from './auth.js' + +const router = new Router() + +router.handle('/', guard(view('home'))) +router.handle('/callback', view('callback')) +router.handle(/^\//, view('not-found')) + +router.install(async resultPromise => { + document.body.innerHTML = '' + document.body.appendChild(await resultPromise) +}) + +function view(name) { + return (...args) => import(`/js/pages/${name}-page.js`) + .then(m => m.default(...args)) +} + +function guard(fn1, fn2 = view('welcome')) { + return (...args) => isAuthenticated() + ? fn1(...args) + : fn2(...args) +} +``` + +与上篇文章不同的是,我们实现了一个 `isAuthenticated()` 函数和一个 `guard()` 函数,使用它去渲染两种验证状态的页面。因此,当用户访问 `/` 时,它将根据用户是否通过了验证来展示主页或者是欢迎页面。 + +### 验证 + +现在,我们来编写 `isAuthenticated()` 函数。使用下面的内容来创建一个 `static/js/auth.js` 文件: + +``` +export function getAuthUser() { + const authUserItem = localStorage.getItem('auth_user') + const expiresAtItem = localStorage.getItem('expires_at') + + if (authUserItem !== null && expiresAtItem !== null) { + const expiresAt = new Date(expiresAtItem) + + if (!isNaN(expiresAt.valueOf()) && expiresAt > new Date()) { + try { + return JSON.parse(authUserItem) + } catch (_) { } + } + } + + return null +} + +export function isAuthenticated() { + return localStorage.getItem('jwt') !== null && getAuthUser() !== null +} +``` + +当有人登入时,我们将保存 JSON 格式的 web 令牌、它的过期日期,以及在 `localStorage` 上的当前已验证用户。这个模块就是这个用处。 + + * `getAuthUser()` 用于从 `localStorage` 获取已认证的用户,以确认 JSON 格式的 Web 令牌没有过期。 + * `isAuthenticated()` 在前面的函数中用于去检查它是否没有返回 `null`。 + +### 获取 + +在继续这个页面之前,我将写一些与服务器 API 一起使用的 HTTP 工具。 + +我们使用以下的内容去创建一个 `static/js/http.js` 文件: + +``` +import { isAuthenticated } from './auth.js' + +function get(url, headers) { + return fetch(url, { + headers: Object.assign(getAuthHeader(), headers), + }).then(handleResponse) +} + +function post(url, body, headers) { + return fetch(url, { + method: 'POST', + headers: Object.assign(getAuthHeader(), { 'content-type': 'application/json' }, headers), + body: JSON.stringify(body), + }).then(handleResponse) +} + +function getAuthHeader() { + return isAuthenticated() + ? { authorization: `Bearer ${localStorage.getItem('jwt')}` } + : {} +} + +export async function handleResponse(res) { + const body = await res.clone().json().catch(() => res.text()) + const response = { + statusCode: res.status, + statusText: res.statusText, + headers: res.headers, + body, + } + if (!res.ok) { + const message = typeof body === 'object' && body !== null && 'message' in body + ? body.message + : typeof body === 'string' && body !== '' + ? body + : res.statusText + const err = new Error(message) + throw Object.assign(err, response) + } + return response +} + +export default { + get, + post, +} +``` + +这个模块导出了 `get()` 和 `post()` 函数。它们是 `fetch` API 的封装。当用户是已验证的,这二个函数注入一个 `Authorization: Bearer ` 头到请求中;这样服务器就能对我们进行身份验证。 + +### 欢迎页 + +我们现在来到欢迎页面。用如下的内容创建一个 `static/js/pages/welcome-page.js` 文件: + +``` +const template = document.createElement('template') +template.innerHTML = ` +

Passwordless Demo

+

Access

+
+ + +
+` + +export default function welcomePage() { + const page = template.content.cloneNode(true) + + page.getElementById('access-form') + .addEventListener('submit', onAccessFormSubmit) + + return page +} +``` + +这个页面使用一个 `HTMLTemplateElement` 作为视图。这只是一个输入用户 email 的简单表单。 + +为了避免干扰,我将跳过错误处理部分,只是将它们输出到控制台上。 + +现在,我们来写 `onAccessFormSubmit()` 函数。 + +``` +import http from '../http.js' + +function onAccessFormSubmit(ev) { + ev.preventDefault() + + const form = ev.currentTarget + const input = form.querySelector('input') + const email = input.value + + sendMagicLink(email).catch(err => { + console.error(err) + if (err.statusCode === 404 && wantToCreateAccount()) { + runCreateUserProgram(email) + } + }) +} + +function sendMagicLink(email) { + return http.post('/api/passwordless/start', { + email, + redirectUri: location.origin + '/callback', + }).then(() => { + alert('Magic link sent. Go check your email inbox.') + }) +} + +function wantToCreateAccount() { + return prompt('No user found. Do you want to create an account?') +} +``` + +它对 `/api/passwordless/start` 发起了 POST 请求,请求体中包含 `email` 和 `redirectUri`。在本例中它返回 `404 Not Found` 状态码时,我们将创建一个用户。 + +``` +function runCreateUserProgram(email) { + const username = prompt("Enter username") + if (username === null) return + + http.post('/api/users', { email, username }) + .then(res => res.body) + .then(user => sendMagicLink(user.email)) + .catch(console.error) +} +``` + +这个用户创建程序,首先询问用户名,然后使用 email 和用户名做一个 `POST` 请求到 `/api/users`。成功之后,给创建的用户发送一个魔法链接。 + +### 回调页 + +这是访问表单的全部功能,现在我们来做回调页面。使用如下的内容来创建一个 `static/js/pages/callback-page.js` 文件: + +``` +import http from '../http.js' + +const template = document.createElement('template') +template.innerHTML = ` +

Authenticating you

+` + +export default function callbackPage() { + const page = template.content.cloneNode(true) + + const hash = location.hash.substr(1) + const fragment = new URLSearchParams(hash) + for (const [k, v] of fragment.entries()) { + fragment.set(decodeURIComponent(k), decodeURIComponent(v)) + } + const jwt = fragment.get('jwt') + const expiresAt = fragment.get('expires_at') + + http.get('/api/auth_user', { authorization: `Bearer ${jwt}` }) + .then(res => res.body) + .then(authUser => { + localStorage.setItem('jwt', jwt) + localStorage.setItem('auth_user', JSON.stringify(authUser)) + localStorage.setItem('expires_at', expiresAt) + + location.replace('/') + }) + .catch(console.error) + + return page +} +``` + +请记住……当点击魔法链接时,我们会来到 `/api/passwordless/verify_redirect`,它将把我们重定向到重定向 URI,我们将放在哈希中的 JWT 和过期日期传递给 `/callback`。 + +回调页面解码 URL 中的哈希,提取这些参数去做一个 `GET` 请求到 `/api/auth_user`,用 JWT 保存所有数据到 `localStorage` 中。最后,重定向到主页面。 + +### 主页 + +创建如下内容的 `static/pages/home-page.js` 文件: + +``` +import { getAuthUser } from '../auth.js' + +export default function homePage() { + const authUser = getAuthUser() + + const template = document.createElement('template') + template.innerHTML = ` +

Passwordless Demo

+

Welcome back, ${authUser.username} 👋

+ + ` + + const page = template.content + + page.getElementById('logout-button') + .addEventListener('click', logout) + + return page +} + +function logout() { + localStorage.clear() + location.reload() +} +``` + +这个页面用于欢迎已验证用户,同时也有一个登出按钮。`logout()` 函数的功能只是清理掉 `localStorage` 并重载这个页面。 + +这就是全部内容了。我猜你在此之前已经看过这个 [demo][4] 了。当然,这些源代码也在同一个 [仓库][5] 中。 + +-------------------------------------------------------------------------------- + +via: https://nicolasparada.netlify.com/posts/passwordless-auth-client/ + +作者:[Nicolás Parada][a] +选题:[lujun9972](https://github.com/lujun9972) +译者:[qhwdw](https://github.com/qhwdw) +校对:[wxy](https://github.com/wxy) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]:https://nicolasparada.netlify.com/ +[1]:https://linux.cn/article-9748-1.html +[2]:https://linux.cn/article-9815-1.html +[3]:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS +[4]:https://go-passwordless-demo.herokuapp.com/ +[5]:https://github.com/nicolasparada/go-passwordless-demo diff --git a/translated/tech/20180502 Customizing your text colors on the Linux command line.md b/published/20180502 Customizing your text colors on the Linux command line.md similarity index 73% rename from translated/tech/20180502 Customizing your text colors on the Linux command line.md rename to published/20180502 Customizing your text colors on the Linux command line.md index 1ea94c1d46..393e857ed2 100644 --- a/translated/tech/20180502 Customizing your text colors on the Linux command line.md +++ b/published/20180502 Customizing your text colors on the Linux command line.md @@ -1,13 +1,15 @@ 在 Linux 命令行中自定义文本颜色 ====== +> 在 Linux 命令行当中使用不同颜色以期提供一种根据文件类型来识别文件的简单方式。你可以修改这些颜色,但是在做之前应该对你做的事情有充分的理由。 + ![](https://images.idgesg.net/images/article/2018/05/numbers-100756457-large.jpg) 如果你在 Linux 命令行上花费了大量的时间(如果没有,那么你可能不会读这篇文章),你无疑注意到了 `ls` 以多种不同的颜色显示文件。你可能也注意到了一些区别 —— 目录是一种颜色,可执行文件是另一种颜色等等。 -这一切是如何发生的呢?而且,你可以选择哪些选项来改变颜色分配可能不是那么明显。 +这一切是如何发生的呢?以及,你可以选择哪些选项来改变颜色分配可能就不是很多人都知道的。 -获取大量数据显示如何指定这些颜色的一种方法是运行 `dircolors` 命令。它会显示以下这些东西: +一种方法是运行 `dircolors` 命令得到一大堆展示了如何指定这些颜色的数据。它会显示以下这些东西: ``` $ dircolors @@ -39,7 +41,7 @@ mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35: export LS_COLORS ``` -如果你擅长解析文件,那么你可能会注意到这个列表有一个模式patten。加上冒号,你会看到这样的东西: +如果你擅长解析文件,那么你可能会注意到这个列表有一种模式patten。用冒号分隔开,你会看到这样的东西: ``` $ dircolors | tr ":" "\n" | head -10 @@ -61,7 +63,7 @@ OK,这里有一个模式 —— 一系列定义,有一到三个数字组件 pi=40;33 ``` -有些人可能会问的第一个问题是“pi 是什么?”我们在这里处理颜色和文件类型,所以这显然不是以 3.14 开头的有趣数字。当然不是,这个“pi” 代表“pipe (管道)” —— Linux 系统上的一种特殊类型的文件,它可以将数据从一个程序传递给另一个程序。所以,让我们建立一个管道。 +有些人可能会问的第一个问题是“pi 是什么?”在这里,我们研究的是颜色和文件类型,所以这显然不是以 3.14 开头的那个有趣的数字。当然不是,这个 “pi” 代表 “pipe(管道)” —— Linux 系统上的一种特殊类型的文件,它可以将数据从一个程序传递给另一个程序。所以,让我们建立一个管道。 ``` $ mknod /tmp/mypipe p @@ -71,13 +73,13 @@ prw-rw-r-- 1 shs shs 0 May 1 14:00 /tmp/mypipe 当我们在终端窗口中查看我们的管道和其他几个文件时,颜色差异非常明显。 -![font colors][1] Sandra Henry-Stocker +![font colors][1] -在 `pi` 的定义中(如上所示),“40”是文件显示在带有黑色背景的终端(或 PuTTY)窗口中,31 使字体颜色变红。管道是特殊的文件,这种特殊的处理使它们在目录列表中突出显示。 +在 `pi` 的定义中(如上所示),“40” 使文件在终端(或 PuTTY)窗口中使用黑色背景显示,31 使字体颜色变红。管道是特殊的文件,这种特殊的处理使它们在目录列表中突出显示。 -`bd` 和 `cd` 定义是相同的 —— `40;33;01` 并且有一个额外的设置。设置会导致 块设备block device(bd)和 字符设备character device(cd)以黑色背景,橙色字体和另一种效果显示 —— 字符将以粗体显示。 +`bd` 和 `cd` 定义是相同的 —— `40;33;01`,它有一个额外的设置。这个设置会导致 块设备block device(bd)和 字符设备character device(cd)以黑色背景,橙色字体和另一种效果显示 —— 字符将以粗体显示。 -以下列表显示由文件类型file type创建的颜色和字体分配: +以下列表显示由文件类型file type所指定的颜色和字体分配: ``` setting file type @@ -118,9 +120,9 @@ $ dircolors | tr ":" "\n" | tail -10 export LS_COLORS ``` -这些设置(上面列表中所有的 `00;36`)将使这些文件名以青色显示。可用颜色如下所示。 +这些设置(上面列表中所有的 `00;36`)将使这些文件名以青色显示。可用的颜色如下所示。 -![all colors][2] Sandra Henry-Stocker +![all colors][2] ### 如何改变设置 @@ -138,9 +140,9 @@ alias ls='ls --color=auto' $ export LS_COLORS='rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;... ``` -注意:上面的命令被截断了。 +注意:上面的命令由于太长被截断了。 -如果希望修改后的文本颜色是永久性的,则需要将修改后的 `$LS_COLORS` 定义添加到一个启动文件中,例如 `.bashrc`。 +如果希望文本颜色的修改是永久性的,则需要将修改后的 `$LS_COLORS` 定义添加到一个启动文件中,例如 `.bashrc`。 ### 更多关于命令行文本 diff --git a/published/20180606 6 Open Source AI Tools to Know.md b/published/20180606 6 Open Source AI Tools to Know.md new file mode 100644 index 0000000000..8c26e7308c --- /dev/null +++ b/published/20180606 6 Open Source AI Tools to Know.md @@ -0,0 +1,71 @@ +你应该了解的 6 个开源 AI 工具 +====== + +> 让我们来看看几个任何人都能用的自由开源的 AI 工具。 + +![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/artificial-intelligence-3382507_1920.jpg?itok=HarDnwVX) + +在开源领域,不管你的想法是多少的新颖独到,先去看一下别人是否已经做成了这个概念,总是一个很明智的做法。对于有兴趣借助不断成长的人工智能Artificial Intelligence(AI)的力量的组织和个人来说,许多优秀的工具不仅是自由开源的,而且在很多的情况下,它们都已经过测试和久经考验的。 + +在领先的公司和非盈利组织中,AI 的优先级都非常高,并且这些公司和组织都开源了很有价值的工具。下面的举例是任何人都可以使用的自由开源的 AI 工具。 + +### Acumos + +[Acumos AI][1] 是一个平台和开源框架,使用它可以很容易地去构建、共享和分发 AI 应用。它规范了运行一个“开箱即用的”通用 AI 环境所需要的基础设施栈infrastructure stack和组件。这使得数据科学家和模型训练者可以专注于它们的核心竞争力,而不用在无止境的定制、建模,以及训练一个 AI 实现上浪费时间。 + +Acumos 是 [LF 深度学习基金会][2] 的一部分,它是 Linux 基金会中的一个组织,它支持在人工智能、机器学习machine learning、以及深度学习deep learning方面的开源创新。它的目标是让这些重大的新技术可用于开发者和数据科学家,包括那些在深度学习和 AI 上经验有限的人。LF 深度学习基金会 [最近批准了一个项目生命周期和贡献流程][3],并且它现在正接受项目贡献的建议。 + +### Facebook 的框架 + +Facebook [开源了][4] 其中心机器学习系统,它设计用于做一些大规模的人工智能任务,以及一系列其它的 AI 技术。这个工具是经过他们公司验证使用的平台的一部分。Facebook 也开源了一个叫 [Caffe2][5] 的深度学习和人工智能的框架。 + +### CaffeOnSpark + +**说到 Caffe**。 Yahoo 也在开源许可证下发布了它自己的关键的 AI 软件。[CaffeOnSpark 工具][6] 是基于深度学习的,它是人工智能的一个分支,在帮助机器识别人类语言,或者照片、视频的内容方面非常有用。同样地,IBM 的机器学习程序 [SystemML][7] 可以通过 Apache 软件基金会自由地共享和修改。 + +### Google 的工具 + +Google 花费了几年的时间开发了它自己的 [TensorFlow][8] 软件框架,用于去支持它的 AI 软件和其它预测和分析程序。TensorFlow 是你可能都已经在使用的一些 Google 工具背后的引擎,包括 Google Photos 和在 Google app 中使用的语言识别。 + +Google 开源了两个 [AIY 套件][9],它可以让个人很容易地使用人工智能,它们专注于计算机视觉和语音助理。这两个套件将用到的所有组件封装到一个盒子中。该套件目前在美国的 Target 中有售,并且它是基于开源的树莓派平台的 —— 有越来越多的证据表明,在开源和 AI 交集中将发生非常多的事情。 + +### H2O.ai + +我 [以前介绍过][10] H2O.ai,它在机器学习和人工智能领域中占有一席之地,因为它的主要工具是自由开源的。你可以获取主要的 H2O 平台和 Sparkling Water,它与 Apache Spark 一起工作,只需要去 [下载][11] 它们即可。这些工具遵循 Apache 2.0 许可证,它是一个非常灵活的开源许可证,你甚至可以在 Amazon Web 服务(AWS)和其它的集群上运行它们,而这仅需要几百美元而已。 + +### Microsoft 入局 + +“我们的目标是让 AI 大众化,让每个人和组织获得更大的成就,“ Microsoft CEO 萨提亚·纳德拉 [说][12]。因此,微软持续迭代它的 [Microsoft Cognitive Toolkit][13](CNTK)。它是一个能够与 TensorFlow 和 Caffe 去竞争的一个开源软件框架。Cognitive Toolkit 可以工作在 64 位的 Windows 和 Linux 平台上。 + +Cognitive Toolkit 团队的报告称,“Cognitive Toolkit 通过允许用户去创建、训练,以及评估他们自己的神经网络,以使企业级的、生产系统级的 AI 成为可能,这些神经网络可能跨多个 GPU 以及多个机器在大量的数据集中高效伸缩。” + +--- + +从来自 Linux 基金会的新电子书中学习更多的有关 AI 知识。Ibrahim Haddad 的 [开源 AI:项目、洞察和趋势][14] 调查了 16 个流行的开源 AI 项目—— 深入研究了他们的历史、代码库、以及 GitHub 的贡献。 [现在可以免费下载这个电子书][14]。 + +-------------------------------------------------------------------------------- + +via: https://www.linux.com/blog/2018/6/6-open-source-ai-tools-know + +作者:[Sam Dean][a] +选题:[lujun9972](https://github.com/lujun9972) +译者:[qhwdw](https://github.com/qhwdw) +校对:[pityonline](https://github.com/pityonline), [wxy](https://github.com/wxy) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]:https://www.linux.com/users/sam-dean +[1]:https://www.acumos.org/ +[2]:https://www.linuxfoundation.org/projects/deep-learning/ +[3]:https://www.linuxfoundation.org/blog/lf-deep-learning-foundation-announces-project-contribution-process/ +[4]:https://code.facebook.com/posts/1687861518126048/facebook-to-open-source-ai-hardware-design/ +[5]:https://venturebeat.com/2017/04/18/facebook-open-sources-caffe2-a-new-deep-learning-framework/ +[6]:http://yahoohadoop.tumblr.com/post/139916563586/caffeonspark-open-sourced-for-distributed-deep +[7]:https://systemml.apache.org/ +[8]:https://www.tensorflow.org/ +[9]:https://www.techradar.com/news/google-assistant-sweetens-raspberry-pi-with-ai-voice-control +[10]:https://www.linux.com/news/sparkling-water-bridging-open-source-machine-learning-and-apache-spark +[11]:http://www.h2o.ai/download +[12]:https://blogs.msdn.microsoft.com/uk_faculty_connection/2017/02/10/microsoft-cognitive-toolkit-cntk/ +[13]:https://www.microsoft.com/en-us/cognitive-toolkit/ +[14]:https://www.linuxfoundation.org/publications/open-source-ai-projects-insights-and-trends/ diff --git a/sources/tech/20141127 Keeping (financial) score with Ledger .md b/sources/tech/20141127 Keeping (financial) score with Ledger .md deleted file mode 100644 index 17b36ea1dc..0000000000 --- a/sources/tech/20141127 Keeping (financial) score with Ledger .md +++ /dev/null @@ -1,77 +0,0 @@ -translating---geekpi - -Keeping (financial) score with Ledger – -====== -I’ve used [Ledger CLI][1] to keep track of my finances since 2005, when I moved to Canada. I like the plain-text approach, and its support for virtual envelopes means that I can reconcile both my bank account balances and my virtual allocations to different categories. Here’s how we use those virtual envelopes to manage our finances separately. - -Every month, I have an entry that moves things from my buffer of living expenses to various categories, including an allocation for household expenses. W- doesn’t ask for a lot, so I take care to be frugal with the difference between that and the cost of, say, living on my own. The way we handle it is that I cover a fixed amount, and this is credited by whatever I pay for groceries. Since our grocery total is usually less than the amount I budget for household expenses, any difference just stays on the tab. I used to write him cheques to even it out, but lately I just pay for the occasional additional large expense. - -Here’s a sample envelope allocation: -``` -2014.10.01 * Budget - [Envelopes:Living] - [Envelopes:Household] $500 - ;; More lines go here - -``` - -Here’s one of the envelope rules set up. This one encourages me to classify expenses properly. All expenses are taken out of my “Play” envelope. -``` -= /^Expenses/ - (Envelopes:Play) -1.0 - -``` - -This one reimburses the “Play” envelope for household expenses, moving the amount from the “Household” envelope into the “Play” one. -``` -= /^Expenses:House$/ - (Envelopes:Play) 1.0 - (Envelopes:Household) -1.0 - -``` - -I have a regular set of expenses that simulate the household expenses coming out of my budget. For example, here’s the one for October. -``` -2014.10.1 * House - Expenses:House - Assets:Household $-500 - -``` - -And this is what a grocery transaction looks like: -``` -2014.09.28 * No Frills - Assets:Household:Groceries $70.45 - Liabilities:MBNA:September $-70.45 - -``` - -Then `ledger bal Assets:Household` will tell me if I owe him money (negative balance) or not. If I pay for something large (ex: plane tickets, plumbing), the regular household expense budget gradually reduces that balance. - -I picked up the trick of adding a month label to my credit card transactions from W-, who also uses Ledger to track his transactions. It lets me doublecheck the balance of a statement and see if the previous statement has been properly cleared. - -It’s a bit of a weird use of the assets category, but it works out for me mentally. - -Using Ledger to track it in this way lets me keep track of our grocery expenses and the difference between what I’ve actually paid and what I’ve budgeted for. If I end up spending more than I expected, I can move virtual money from more discretionary envelopes, so my budget always stays balanced. - -Ledger’s a powerful tool. Pretty geeky, but maybe more descriptions of workflow might help people who are figuring things out! - -More posts about: [finance][2] Tags: [ledger][3] | [See in index][4] // **[5 Comments »][5]** - --------------------------------------------------------------------------------- - -via: http://sachachua.com/blog/2014/11/keeping-financial-score-ledger/ - -作者:[Sacha Chua][a] -选题:[lujun9972](https://github.com/lujun9972) -译者:[译者ID](https://github.com/译者ID) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]:http://sachachua.com -[1]:http://www.ledger-cli.org/ -[2]:http://sachachua.com/blog/category/finance/ -[3]:http://sachachua.com/blog/tag/ledger/ -[4]:http://pages.sachachua.com/sharing/blog.html?url=http://sachachua.com/blog/2014/11/keeping-financial-score-ledger/ -[5]:http://sachachua.com/blog/2014/11/keeping-financial-score-ledger/#comments diff --git a/sources/tech/20180428 How to get a core dump for a segfault on Linux.md b/sources/tech/20180428 How to get a core dump for a segfault on Linux.md deleted file mode 100644 index 1fe70221ac..0000000000 --- a/sources/tech/20180428 How to get a core dump for a segfault on Linux.md +++ /dev/null @@ -1,195 +0,0 @@ -translating by stenphenxs -How to get a core dump for a segfault on Linux -============================================================ - -This week at work I spent all week trying to debug a segfault. I’d never done this before, and some of the basic things involved (get a core dump! find the line number that segfaulted!) took me a long time to figure out. So here’s a blog post explaining how to do those things! - -At the end of this blog post, you should know how to go from “oh no my program is segfaulting and I have no idea what is happening” to “well I know what its stack / line number was when it segfaulted at at least!“. - -### what’s a segfault? - -A “segmentation fault” is when your program tries to access memory that it’s not allowed to access, or tries to . This can be caused by: - -* trying to dereference a null pointer (you’re not allowed to access the memory address `0`) - -* trying to dereference some other pointer that isn’t in your memory - -* a C++ vtable pointer that got corrupted and is pointing to the wrong place, which causes the program to try to execute some memory that isn’t executable - -* some other things that I don’t understand, like I think misaligned memory accesses can also segfault - -This “C++ vtable pointer” thing is what was happening to my segfaulting program. I might explain that in a future blog post because I didn’t know any C++ at the beginning of this week and this vtable lookup thing was a new way for a program to segfault that I didn’t know about. - -But! This blog post isn’t about C++ bugs. Let’s talk about the basics, like, how do we even get a core dump? - -### step 1: run valgrind - -I found the easiest way to figure out why my program is segfaulting was to use valgrind: I ran - -``` -valgrind -v your-program - -``` - -and this gave me a stack trace of what happened. Neat! - -But I wanted also wanted to do a more in-depth investigation and find out more than just what valgrind was telling me! So I wanted to get a core dump and explore it. - -### How to get a core dump - -A core dump is a copy of your program’s memory, and it’s useful when you’re trying to debug what went wrong with your problematic program. - -When your program segfaults, the Linux kernel will sometimes write a core dump to disk. When I originally tried to get a core dump, I was pretty frustrated for a long time because – Linux wasn’t writing a core dump!! Where was my core dump???? - -Here’s what I ended up doing: - -1. Run `ulimit -c unlimited` before starting my program - -2. Run `sudo sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t` - -### ulimit: set the max size of a core dump - -`ulimit -c` sets the maximum size of a core dump. It’s often set to 0, which means that the kernel won’t write core dumps at all. It’s in kilobytes. ulimits are per process – you can see a process’s limits by running `cat /proc/PID/limit` - -For example these are the limits for a random Firefox process on my system: - -``` -$ cat /proc/6309/limits -Limit Soft Limit Hard Limit Units -Max cpu time unlimited unlimited seconds -Max file size unlimited unlimited bytes -Max data size unlimited unlimited bytes -Max stack size 8388608 unlimited bytes -Max core file size 0 unlimited bytes -Max resident set unlimited unlimited bytes -Max processes 30571 30571 processes -Max open files 1024 1048576 files -Max locked memory 65536 65536 bytes -Max address space unlimited unlimited bytes -Max file locks unlimited unlimited locks -Max pending signals 30571 30571 signals -Max msgqueue size 819200 819200 bytes -Max nice priority 0 0 -Max realtime priority 0 0 -Max realtime timeout unlimited unlimited us - -``` - -The kernel uses the soft limit (in this case, “max core file size = 0”) when deciding how big of a core file to write. You can increase the soft limit up to the hard limit using the `ulimit` shell builtin (`ulimit -c unlimited`!) - -### kernel.core_pattern: where core dumps are written - -`kernel.core_pattern` is a kernel parameter or a “sysctl setting” that controls where the Linux kernel writes core dumps to disk. - -Kernel parameters are a way to set global settings on your system. You can get a list of every kernel parameter by running `sysctl -a`, or use `sysctl kernel.core_pattern` to look at the `kernel.core_pattern` setting specifically. - -So `sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t` will write core dumps to `/tmp/core-` - -If you want to know more about what these `%e`, `%p` parameters read, see [man core][1]. - -It’s important to know that `kernel.core_pattern` is a global settings – it’s good to be a little careful about changing it because it’s possible that other systems depend on it being set a certain way. - -### kernel.core_pattern & Ubuntu - -By default on Ubuntu systems, this is what `kernel.core_pattern` is set to - -``` -$ sysctl kernel.core_pattern -kernel.core_pattern = |/usr/share/apport/apport %p %s %c %d %P - -``` - -This caused me a lot of confusion (what is this apport thing and what is it doing with my core dumps??) so here’s what I learned about this: - -* Ubuntu uses a system called “apport” to report crashes in apt packages - -* Setting `kernel.core_pattern=|/usr/share/apport/apport %p %s %c %d %P`means that core dumps will be piped to `apport` - -* apport has logs in /var/log/apport.log - -* apport by default will ignore crashes from binaries that aren’t part of an Ubuntu packages - -I ended up just overriding this Apport business and setting `kernel.core_pattern` to `sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t` because I was on a dev machine, I didn’t care whether Apport was working on not, and I didn’t feel like trying to convince Apport to give me my core dumps. - -### So you have a core dump. Now what? - -Okay, now we know about ulimits and `kernel.core_pattern` and you have actually have a core dump file on disk in `/tmp`. Amazing! Now what??? We still don’t know why the program segfaulted! - -The next step is to open the core file with `gdb` and get a backtrace. - -### Getting a backtrace from gdb - -You can open a core file with gdb like this: - -``` -$ gdb -c my_core_file - -``` - -Next, we want to know what the stack was when the program crashed. Running `bt` at the gdb prompt will give you a backtrace. In my case gdb hadn’t loaded symbols for the binary, so it was just like `??????`. Luckily, loading symbols fixed it. - -Here’s how to load debugging symbols. - -``` -symbol-file /path/to/my/binary -sharedlibrary - -``` - -This loads symbols from the binary and from any shared libraries the binary uses. Once I did that, gdb gave me a beautiful stack trace with line numbers when I ran `bt`!!! - -If you want this to work, the binary should be compiled with debugging symbols. Having line numbers in your stack traces is extremely helpful when trying to figure out why a program crashed :) - -### look at the stack for every thread - -Here’s how to get the stack for every thread in gdb! - -``` -thread apply all bt full - -``` - -### gdb + core dumps = amazing - -If you have a core dump & debugging symbols and gdb, you are in an amazing situation!! You can go up and down the call stack, print out variables, and poke around in memory to see what happened. It’s the best. - -If you are still working on being a gdb wizard, you can also just print out the stack trace with `bt` and that’s okay :) - -### ASAN - -Another path to figuring out your segfault is to do one compile the program with AddressSanitizer (“ASAN”) (`$CC -fsanitize=address`) and run it. I’m not going to discuss that in this post because this is already pretty long and anyway in my case the segfault disappeared with ASAN turned on for some reason, possibly because the ASAN build used a different memory allocator (system malloc instead of tcmalloc). - -I might write about ASAN more in the future if I ever get it to work :) - -### getting a stack trace from a core dump is pretty approachable! - -This blog post sounds like a lot and I was pretty confused when I was doing it but really there aren’t all that many steps to getting a stack trace out of a segfaulting program: - -1. try valgrind - -if that doesn’t work, or if you want to have a core dump to investigate: - -1. make sure the binary is compiled with debugging symbols - -2. set `ulimit` and `kernel.core_pattern` correctly - -3. run the program - -4. open your core dump with `gdb`, load the symbols, and run `bt` - -5. try to figure out what happened!! - -I was able using gdb to figure out that there was a C++ vtable entry that is pointing to some corrupt memory, which was somewhat helpful and helped me feel like I understood C++ a bit better. Maybe we’ll talk more about how to use gdb to figure things out another day! - --------------------------------------------------------------------------------- - -via: https://jvns.ca/blog/2018/04/28/debugging-a-segfault-on-linux/ - -作者:[Julia Evans ][a] -译者:[译者ID](https://github.com/译者ID) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]:https://jvns.ca/about/ -[1]:http://man7.org/linux/man-pages/man5/core.5.html diff --git a/sources/tech/20180625 How To Upgrade Everything Using A Single Command In Linux.md b/sources/tech/20180625 How To Upgrade Everything Using A Single Command In Linux.md index 741cd82f0d..599185befb 100644 --- a/sources/tech/20180625 How To Upgrade Everything Using A Single Command In Linux.md +++ b/sources/tech/20180625 How To Upgrade Everything Using A Single Command In Linux.md @@ -1,3 +1,5 @@ +translating-----geekpi + How To Upgrade Everything Using A Single Command In Linux ====== diff --git a/translated/tech/20141127 Keeping (financial) score with Ledger .md b/translated/tech/20141127 Keeping (financial) score with Ledger .md new file mode 100644 index 0000000000..9cea9eed77 --- /dev/null +++ b/translated/tech/20141127 Keeping (financial) score with Ledger .md @@ -0,0 +1,73 @@ +使用 Ledger 记录(财务)情况 +====== +自 2005 年搬到加拿大以来,我使用 [Ledger CLI][1] 来跟踪我的财务状况。我喜欢纯文本的方式,它支持虚拟信封意味着我可以同时将我的银行帐户余额和我的虚拟分配到不同的目录下。以下是我们如何使用这些虚拟信封分别管理我们的财务状况。 + +每个月,我都有一个条目将我生活开支分配到不同的目录中,包括家庭开支的分配。W- 不要求太多, 所以我要谨慎地处理这两者之间的差别和我自己的生活费用。我们处理它的方式是我支付固定金额,这是贷记我支付的杂货。由于我们的杂货总额通常低于我预算的家庭开支,因此任何差异都会留在标签上。我过去常常给他写支票,但最近我只是支付偶尔额外的大笔费用。 + +这是个示例信封分配: +``` +2014.10.01 * Budget + [Envelopes:Living] + [Envelopes:Household] $500 + ;; More lines go here + +``` + +这是设置的信封规则之一。它鼓励我正确地分类支出。所有支出都从我的 “Play” 信封中取出。 +``` += /^Expenses/ + (Envelopes:Play) -1.0 + +``` + +这个为家庭支出报销 “Play” 信封,将金额从 “Household” 信封转移到 “Play” 信封。 +``` += /^Expenses:House$/ + (Envelopes:Play) 1.0 + (Envelopes:Household) -1.0 + +``` + +我有一套定期的支出来模拟我的预算中的家庭开支。例如,这是 10 月份的。 +``` +2014.10.1 * House + Expenses:House + Assets:Household $-500 + +``` + +这是杂货交易的形式: +``` +2014.09.28 * No Frills + Assets:Household:Groceries $70.45 + Liabilities:MBNA:September $-70.45 + +``` + +接着 `ledger bal Assets:Household` 就会告诉我是否欠他钱(负余额)。如果我支付大笔费用(例如:机票、通管道),那么正常家庭开支预算会逐渐减少余额。 + +我从 W- 那找到了一个为我的信用卡交易添加一个月标签的技巧,他还使用 Ledger 跟踪他的交易。它允许我再次检查条目的余额,看看前一个条目是否已被正确清除。 + +这个资产分类使用有点奇怪,但它在精神上对我有用。 + +使用 Ledger 以这种方式跟踪它可以让我跟踪我们的杂货费用以及我实际支付费用和我预算费用之间的差额。如果我最终支出超出预期,我可以从更多可自由支配的信封中移动虚拟货币,因此我的预算始终保持平衡。 + +Ledger 是一个强大的工具。相当极客,但也许更多的工作流描述可能会帮助那些正在搞清楚它的人! + +-------------------------------------------------------------------------------- + +via: http://sachachua.com/blog/2014/11/keeping-financial-score-ledger/ + +作者:[Sacha Chua][a] +选题:[lujun9972](https://github.com/lujun9972) +译者:[geekpi](https://github.com/geekpi) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]:http://sachachua.com +[1]:http://www.ledger-cli.org/ +[2]:http://sachachua.com/blog/category/finance/ +[3]:http://sachachua.com/blog/tag/ledger/ +[4]:http://pages.sachachua.com/sharing/blog.html?url=http://sachachua.com/blog/2014/11/keeping-financial-score-ledger/ +[5]:http://sachachua.com/blog/2014/11/keeping-financial-score-ledger/#comments diff --git a/translated/tech/20180428 How to get a core dump for a segfault on Linux.md b/translated/tech/20180428 How to get a core dump for a segfault on Linux.md new file mode 100644 index 0000000000..6cba5f8c0a --- /dev/null +++ b/translated/tech/20180428 How to get a core dump for a segfault on Linux.md @@ -0,0 +1,197 @@ +在 Linux 上如何得到一个段错误的核心转储 +============================================================ + +本周工作中,我花了整整一周的时间来尝试调试一个段错误。我以前从来没有这样做过,我花了很长时间才弄清楚其中涉及的一些基本事情(获得核心转储、找到导致段错误的行号)。于是便有了这篇博客来解释如何做那些事情! + + +在看完这篇博客后,你应该知道如何从“哦,我的程序出现段错误,但我不知道正在发生什么”到“我知道它出现段错误时的堆栈、行号了! “。 + +### 什么是段错误? + +一个“段错误”是指你的程序尝试访问不允许访问的内存地址的情况。这可能是由于: + +* 试图解引用空指针(你不被允许访问内存地址 `0`); + +* 试图解引用其他一些不在你内存(译者注:指不在合法的内存地址区间内)中的指针; + +* 一个已被破坏并且指向错误的地方的 C++ 虚表指针,这导致程序尝试执行没有执行权限的内存地址; + +* 其他一些我不明白的事情,比如我认为访问未对齐的内存地址也可能会导致段错误(译者注:在要求自然边界对齐的体系结构,如MIPS、ARM中更容易因非对齐访问产生段错误)。 + +这个“C++ 虚表指针”是我的程序发生段错误的情况。我可能会在未来的博客中解释这个,因为我最初并不知道任何关于 C++ 的知识,并且这种虚表查找导致程序段错误的情况也是我所不了解的。 + +但是!这篇博客后不是关于 C++ 问题的。让我们谈论的基本的东西,比如,我们如何得到一个核心转储? + +### 步骤1:运行 valgrind + +我发现找出为什么我的程序出现段错误的最简单的方式是使用valgrind:我运行 + +``` +valgrind -v your-program + +``` + +这给了我一个故障时的堆栈调用序列。 简洁! + +但我想也希望做一个更深入调查,并找出些valgrind没告诉我的信息! 所以我想获得一个核心转储并探索它。 + +### 如何获得一个核心转储 + +一个核心转储是您的程序内存的一个副本,并且当您试图调试您的有问题的程序哪里出错的时候它非常有用。 + +当您的程序出现段错误,Linux 的内核有时会把一个核心转储写到磁盘。 当我最初试图获得一个核心转储时,我很长一段时间非常沮丧,因为 - Linux 没有生成核心转储!我的核心转储在哪里? + +这就是我最后做了什么: + +1. 在启动我的程序之前运行 `ulimit -c unlimited` + +2. 运行 `sudo sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t` + +### ulimit:设置核心转储的最大尺寸 + +`ulimit -c` 设置核心转储的最大尺寸。 它往往设置为 0,这意味着内核根本不会写核心转储。 它以千字节为单位。 分别为每个进程设置 ulimit - 你可以通过运行 `cat /proc/PID/limit` 看到一个进程的各种资源限制。 + +例如这些是我的系统上一个任意Firefox进程的资源限制: + +``` +$ cat /proc/6309/limits +Limit Soft Limit Hard Limit Units +Max cpu time unlimited unlimited seconds +Max file size unlimited unlimited bytes +Max data size unlimited unlimited bytes +Max stack size 8388608 unlimited bytes +Max core file size 0 unlimited bytes +Max resident set unlimited unlimited bytes +Max processes 30571 30571 processes +Max open files 1024 1048576 files +Max locked memory 65536 65536 bytes +Max address space unlimited unlimited bytes +Max file locks unlimited unlimited locks +Max pending signals 30571 30571 signals +Max msgqueue size 819200 819200 bytes +Max nice priority 0 0 +Max realtime priority 0 0 +Max realtime timeout unlimited unlimited us + +``` + +内核在决定写入多大的核心转储文件时使用 soft limit(在这种情况下,“最大的核心文件大小= 0”)。 您可以使用 shell 内置命令 `ulimit`(`ulimit -c unlimited`) 将 soft limit 增加到 hard limit。 + +### kernel.core_pattern:核心转储保存在哪里 + +`kernel.core_pattern` 是一个内核参数,或者叫“sysctl 设置”,它控制 Linux 内核将核心转储文件写到磁盘的哪里。 + +内核参数是一种设定您的系统全局设置的方法。您可以通过运行 `sysctl -a` 得到一个包含每个内核参数的列表,或使用 `sysctl kernel.core_pattern` 来专门查看 `kernel.core_pattern` 设置。 + +所以 `sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t` 将核心转储保存到目录 `/tmp` 下并以 `core` 加上一系列能够标识(出故障的)进程的参数构成的后缀为文件名。 + +如果你想知道这些形如 `%e`、`%p` 的参数都表示什么,请参考 [man core][1]。 + +有一点很重要,`kernel.core_pattern` 是一个全局设置 - 修改它的时候最好小心一点,因为有可能其它系统功能依赖于把它被设置为一个特定的方式(才能正常工作)。 + +### kernel.core_pattern 和 Ubuntu + +默认情况下在ubuntu系统中,`kernel.core_pattern` 被设置为下面的值 + +``` +$ sysctl kernel.core_pattern +kernel.core_pattern = |/usr/share/apport/apport %p %s %c %d %P + +``` + +这引起了我的迷惑(这apport是干什么的,它对我的核心转储做了什么?)。以下关于这个我了解到的: + +* Ubuntu 使用一种叫做 apport 的系统来报告apt包有关的崩溃信息。 + +* 设定 `kernel.core_pattern=|/usr/share/apport/apport %p %s %c %d %P` 意味着核心转储将被通过管道送给 `apport` 程序。 + +* apport 的日志保存在文件 /var/log/apport.log 中。 + +* apport 默认会忽略来自不属于Ubuntu软件包一部分的二进制文件的崩溃信息 + +我最终只是覆盖了 apport,并把 `kernel.core_pattern` 重新设置为 `sysctl -w kernel.core_pattern=/tmp/core-%e.%p.%h.%t`,因为我在一台开发机上,我不在乎 apport 是否工作,我也不想尝试让 apport 把我的核心转储留在磁盘上。 + +### 现在你有了核心转储,接下来干什么? + +好的,现在我们了解了 ulimit 和 `kernel.core_pattern` ,并且实际上在磁盘的 `/tmp` 目录中有了一个核心转储文件。太好了!接下来干什么?我们仍然不知道该程序为什么会出现段错误! + +下一步将使用 `gdb` 打开核心转储文件并获取堆栈调用序列。 + +### 从gdb中得到堆栈调用序列 + +你可以像这样用 `gdb` 打开一个核心转储文件 + +``` +$ gdb -c my_core_file + +``` + +接下来,我们想知道程序崩溃时的堆栈是什么样的。在 gdb 提示符下运行 `bt` 会给你一个调用序列。在我的例子里,gdb 没有为二进制文件加载符号信息,所以这些函数名就像“??????”。幸运的是,(我们通过)加载符号修复了它。 + +下面是如何加载调试符号。 + +``` +symbol-file /path/to/my/binary +sharedlibrary + +``` + +这从二进制文件及其引用的任何共享库中加载符号。一旦我这样做了,当我执行 `bt` 时,gdb 给了我一个带有行号的漂亮的堆栈跟踪! + +如果你想它能工作,二进制文件应该以带有调试符号信息的方式被编译。在试图找出程序崩溃的原因时,堆栈跟踪中的行号非常有帮助。:) + +### 查看每个线程的堆栈 + +通过以下方式在 gdb 中获取每个线程的调用栈! + +``` +thread apply all bt full + +``` + +### gdb + 核心转储 = 惊喜 + +If you have a core dump & debugging symbols and gdb, you are in an amazing situation!! You can go up and down the call stack, print out variables, and poke around in memory to see what happened. It’s the best. + +如果你有一个带调试符号的核心转储以及 gdb,那太棒了!您可以上下查看调用堆栈(译注:指跳进调用序列不同的函数中以便于查看局部变量),打印变量,并查看内存来得知发生了什么。这是最好的。 + +如果您仍然正在基于gdb向导来工作上,只打印出栈跟踪与bt也可以:) + +### ASAN + +另一种搞清楚您的段错误的方法是使用 AddressSanitizer 选项编译程序(“ASAN”,即 `$CC -fsanitize=address`)然后运行它。 本文中我不准备讨论那个,因为本文已经相当长了,并且在我的例子中打开 ASAN 后段错误消失了,可能是因为 ASAN 使用了一个不同的内存分配器(系统内存分配器,而不是tcmalloc)。 + +在未来如果我能让ASAN工作,我可能会多写点有关它的东西。(译注:这里指使用ASAN也能复现段错误) + +### 从一个核心转储得到一个堆栈跟踪真的很亲切! + +这个博客后听起来很多,但当我做这些的时候很困惑,但说真的,从一个段错误的程序中获得一个堆栈调用序列不需要那么多步骤: + +1. 试试用valgrind + +如果那没用,或者你想要拿到一个核心转储来调查: + +1. 确保二进制文件编译时带有调试符号信息; + +2. 正确的设置 `ulimit` 和 `kernel.core_pattern`; + +3. 运行程序; + +4. 一旦你用 `gdb` 调试核心转储了,加载符号并运行 `bt`; + +5. 尝试找出发生了什么! + +我可以使用 gdb 弄清楚有个 C++ 的 vtable 条目指向一些被破坏的内存,这有点帮助,并且使我感觉好像理解 C++ 更好一点。也许有一天我们会更多地讨论如何使用 gdb 来查找问题! + +-------------------------------------------------------------------------------- + +via: https://jvns.ca/blog/2018/04/28/debugging-a-segfault-on-linux/ + +作者:[Julia Evans ][a] +译者:[stephenxs](https://github.com/stephenxs) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]:https://jvns.ca/about/ +[1]:http://man7.org/linux/man-pages/man5/core.5.html diff --git a/translated/tech/20180429 Passwordless Auth- Client.md b/translated/tech/20180429 Passwordless Auth- Client.md deleted file mode 100644 index e2faf2cb6e..0000000000 --- a/translated/tech/20180429 Passwordless Auth- Client.md +++ /dev/null @@ -1,338 +0,0 @@ -无密码验证:客户端 -====== -我们继续 [无密码验证][1] 的文章。上一篇文章中,我们用 Go 写了一个 HTTP 服务,用这个服务来做无密码验证 API。今天,我们为它再写一个 JavaScript 客户端。 - -我们将使用 [这里的][2] 这个单页面应用程序(SPA)来展示使用的技术。如果你还没有读过它,请先读它。 - -我们将根据验证的状态分别使用两个不同的根 URL(`/`):一个是访问状态的页面或者是欢迎已验证用户的页面。另一个页面是验证失败后重定向到验证页面。 - -### Serving - -我们将使用相同的 Go 服务器来为客户端提供服务,因此,在我们前面的 `main.go` 中添加一些路由: -``` -router.Handle("GET", "/js/", http.FileServer(http.Dir("static"))) -router.HandleFunc("GET", "/...", serveFile("static/index.html")) - -``` - -这个伺服文件在 `static/js` 下,而 `static/index.html` 文件是为所有的访问提供服务的。 - -你可以使用你自己的服务器,但是你得在服务器上启用 [CORS][3]。 - -### HTML - -我们来看一下那个 `static/index.html` 文件。 -``` - - - - - - Passwordless Demo - - - - - - -``` - -单页面应用程序剩余的渲染由 JavaScript 来完成,因此,我们使用了一个空的 body 部分和一个 `main.js` 文件。 - -我们将使用 [上篇文章][2] 中的 Router。 - -### Rendering - -现在,我们使用下面的内容来创建一个 `static/js/main.js` 文件: -``` -import Router from 'https://unpkg.com/@nicolasparada/router' -import { isAuthenticated } from './auth.js' - -const router = new Router() - -router.handle('/', guard(view('home'))) -router.handle('/callback', view('callback')) -router.handle(/^\//, view('not-found')) - -router.install(async resultPromise => { - document.body.innerHTML = '' - document.body.appendChild(await resultPromise) -}) - -function view(name) { - return (...args) => import(`/js/pages/${name}-page.js`) - .then(m => m.default(...args)) -} - -function guard(fn1, fn2 = view('welcome')) { - return (...args) => isAuthenticated() - ? fn1(...args) - : fn2(...args) -} - -``` - -与上篇文章不同的是,我们实现了一个 `isAuthenticated()` 函数和一个 `guard()` 函数,使用它去渲染两种验证状态的页面。因此,当用户访问 `/` 时,它将根据用户是否通过了验证来展示 home 页面或者是欢迎页面。 - -### Auth - -现在,我们来编写 `isAuthenticated()` 函数。使用下面的内容来创建一个 `static/js/auth.js` 文件: -``` -export function getAuthUser() { - const authUserItem = localStorage.getItem('auth_user') - const expiresAtItem = localStorage.getItem('expires_at') - - if (authUserItem !== null && expiresAtItem !== null) { - const expiresAt = new Date(expiresAtItem) - - if (!isNaN(expiresAt.valueOf()) && expiresAt > new Date()) { - try { - return JSON.parse(authUserItem) - } catch (_) { } - } - } - - return null -} - -export function isAuthenticated() { - return localStorage.getItem('jwt') !== null && getAuthUser() !== null -} - -``` - -当有人登入时,我们将保存 JSON 格式的 web 令牌、过期日期、以及在 `localStorage` 上的当前已验证用户。这个模块就是这个用处。 - - * `getAuthUser()` 用于从 `localStorage` 获取已认证的用户,以确认 JSON 格式的 Web 令牌没有过期。 - * `isAuthenticated()` 在前面的函数中用于去检查它是否返回了 `null`。 - - - -### Fetch - -在继续这个页面之前,我将写一些与服务器 API 一起使用的 HTTP 工具。 - -我们使用以下的内容去创建一个 `static/js/http.js` 文件: -``` -import { isAuthenticated } from './auth.js' - -function get(url, headers) { - return fetch(url, { - headers: Object.assign(getAuthHeader(), headers), - }).then(handleResponse) -} - -function post(url, body, headers) { - return fetch(url, { - method: 'POST', - headers: Object.assign(getAuthHeader(), { 'content-type': 'application/json' }, headers), - body: JSON.stringify(body), - }).then(handleResponse) -} - -function getAuthHeader() { - return isAuthenticated() - ? { authorization: `Bearer ${localStorage.getItem('jwt')}` } - : {} -} - -export async function handleResponse(res) { - const body = await res.clone().json().catch(() => res.text()) - const response = { - url: res.url, - statusCode: res.status, - statusText: res.statusText, - headers: res.headers, - body, - } - if (!res.ok) throw Object.assign( - new Error(body.message || body || res.statusText), - response - ) - return response -} - -export default { - get, - post, -} - -``` - -这个模块导出了 `get()` 和 `post()` 函数。它们是 `fetch` API 的封装。当用户是已验证的,这二个函数注入一个 `Authorization: Bearer ` 头到请求中;这样服务器就能对我们进行身份验证。 - -### Welcome Page - -我们现在来到欢迎页面。用如下的内容创建一个 `static/js/pages/welcome-page.js` 文件: -``` -const template = document.createElement('template') -template.innerHTML = ` -

Passwordless Demo

-

Access

-
- - -
-` - -export default function welcomePage() { - const page = template.content.cloneNode(true) - - page.getElementById('access-form') - .addEventListener('submit', onAccessFormSubmit) - - return page -} - -``` - -正如你所见,这个页面使用一个 `HTMLTemplateElement`。这是一个只输入用户 email 的简单表格。 - -为了不让代码太乏味,我将跳过错误处理部分,只是将它们输出到控制台上。 - -现在,我们来写 `onAccessFormSubmit()` 函数。 -``` -import http from '../http.js' - -function onAccessFormSubmit(ev) { - ev.preventDefault() - - const form = ev.currentTarget - const input = form.querySelector('input') - const email = input.value - - sendMagicLink(email).catch(err => { - console.error(err) - if (err.statusCode === 404 && wantToCreateAccount()) { - runCreateUserProgram(email) - } - }) -} - -function sendMagicLink(email) { - return http.post('/api/passwordless/start', { - email, - redirectUri: location.origin + '/callback', - }).then(() => { - alert('Magic link sent. Go check your email inbox.') - }) -} - -function wantToCreateAccount() { - return prompt('No user found. Do you want to create an account?') -} - -``` - -它使用 email 做了一个 `POST` 请求到 `/api/passwordless/start`,然后在 body 中做了 URI 转向。在本例中使用 `404 Not Found` 状态码返回,我们将创建一个用户。 -``` -function runCreateUserProgram(email) { - const username = prompt("Enter username") - if (username === null) return - - http.post('/api/users', { email, username }) - .then(res => res.body) - .then(user => sendMagicLink(user.email)) - .catch(console.error) -} - -``` - -这个用户创建程序,首先询问用户名,然后使用 email 和用户名,在 body 中做一个 `POST` 请求到 `/api/users`。成功之后,给创建的用户发送一个魔法链接。 - -### Callback Page - -这就是访问表格的所有功能,现在我们来做回调页面。使用如下的内容来创建一个 `static/js/pages/callback-page.js` 文件: -``` -import http from '../http.js' - -const template = document.createElement('template') -template.innerHTML = ` -

Authenticating you 👀

-` - -export default function callbackPage() { - const page = template.content.cloneNode(true) - - const hash = location.hash.substr(1) - const fragment = new URLSearchParams(hash) - for (const [k, v] of fragment.entries()) { - fragment.set(decodeURIComponent(k), decodeURIComponent(v)) - } - const jwt = fragment.get('jwt') - const expiresAt = fragment.get('expires_at') - - http.get('/api/auth_user', { authorization: `Bearer ${jwt}` }) - .then(res => res.body) - .then(authUser => { - localStorage.setItem('jwt', jwt) - localStorage.setItem('auth_user', JSON.stringify(authUser)) - localStorage.setItem('expires_at', expiresAt) - - location.replace('/') - }) - .catch(console.error) - - return page -} - -``` - -请记住 … 当点击魔法链接时,我们来到 `/api/passwordless/verify_redirect`,我们通过 (`/callback`)在 URL 的哈希中传递 JWT 和过期日期,将我们转向到重定向 URI。 - -回调页面解码 URL 中的哈希,提取这些参数去做一个 `GET` 请求到 `/api/auth_user`,用 JWT 保存所有数据到 `localStorage` 中。最后,重定向到主页面。 - -### Home Page - -创建如下内容的 `static/pages/home-page.js` 文件: -``` -import { getAuthUser } from '../auth.js' - -export default function homePage() { - const authUser = getAuthUser() - - const template = document.createElement('template') - template.innerHTML = ` -

Passwordless Demo

-

Welcome back, ${authUser.username} 👋

- - ` - - const page = template.content - - page.getElementById('logout-button') - .addEventListener('click', logout) - - return page -} - -function logout() { - localStorage.clear() - location.reload() -} - -``` - -这个页面欢迎已验证用户,同时也有一个登出按钮。`logout()` 函数的功能只是清理掉 `localStorage` 并重载这个页面。 - -这就是全部内容了。我敢说你在此之前已经看过这个 [demo][4] 了。当然,这些源代码也在同一个 [仓库][5] 中。 - -👋👋👋 - --------------------------------------------------------------------------------- - -via: https://nicolasparada.netlify.com/posts/passwordless-auth-client/ - -作者:[Nicolás Parada][a] -选题:[lujun9972](https://github.com/lujun9972) -译者:[qhwdw](https://github.com/qhwdw) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]:https://nicolasparada.netlify.com/ -[1]:https://nicolasparada.netlify.com/posts/passwordless-auth-server/ -[2]:https://nicolasparada.netlify.com/posts/javascript-client-router/ -[3]:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS -[4]:https://go-passwordless-demo.herokuapp.com/ -[5]:https://github.com/nicolasparada/go-passwordless-demo diff --git a/translated/tech/20180606 6 Open Source AI Tools to Know.md b/translated/tech/20180606 6 Open Source AI Tools to Know.md deleted file mode 100644 index 02395c016e..0000000000 --- a/translated/tech/20180606 6 Open Source AI Tools to Know.md +++ /dev/null @@ -1,67 +0,0 @@ -你应该知道的 6 个开源 AI 工具 -====== - -![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/artificial-intelligence-3382507_1920.jpg?itok=HarDnwVX) - -在开源领域,不管你的想法是多少的新颖独到,先去看一下别人是否已经做成了这个概念总是一个很明智的做法。对于有兴趣借助不断成长的人工智能Artificial Intelligence(AI)的力量的组织和个人来说,许多优秀的工具不仅是免费和开源的,而且在很多的情况下,它们都已经过测试和久经考验的。 - -在领先的公司和非盈利组织中,AI 的优先级都非常高,并且这些公司和组织都开源了很有价值的工具。下面的举例是任何人都可以使用的免费的、开源的 AI 工具。 - -### Acumos - -[Acumos AI][1] 是一个平台和开源框架,使用它可以很容易地去构建、共享和分发 AI 应用。它规范了需要的基础设施栈infrastructure stack和组件,使其可以在一个“开箱即用的”通用 AI 环境中运行。这令数据工程师和模型训练人员可以专注于它们的核心竞争力,而不用在无止境的定制、建模、以及训练一个 AI 实现上浪费时间。 - -Acumos 是 [LF 深度学习基金会][2] 的一部分,它是 Linux 基金会中的一个组织,它支持在人工智能、机器学习machine learning、以及深度学习deep learning方面的开源创新。它的目标是让这些重大创新的技术可用于开发者和数据工程师,包括那些在深度学习和 AI 经验有限的人。LF 深度学习基金会 [最近批准了一个项目生命周期和贡献流程][3],并且它现在正接受项目贡献的建议。 - -### Facebook 的框架 - -Facebook 它自己 [有开源的][4] 中央机器学习系统,它设计用于做一些大规模的人工智能任务,以及一系列其它的 AI 技术。这个工具是经过他们公司验证的平台的一部分。Facebook 也开源了一个叫 [Caffe2][5] 的深度学习和人工智能的框架。 - -### CaffeOnSpark - -**说到 Caffe**,Yahoo 也在开源许可证下发布了它自己的核心 AI 软件。[CaffeOnSpark 工具][6] 是基于深度学习的,它是人工智能的一个分支,在帮助机器识别人类语言、或者照片、视频的内容方面非常有用。同样,IBM 的机器学习程序 [SystemML][7] 可以通过 Apache 软件基金会免费共享和修改。 - -### Google 的工具 - -Google 花费了几年的时间开发了它自己的 [TensorFlow][8] 软件框架,用于去支持它的 AI 软件和其它预测和分析程序。TensorFlow 是你可能都已经在使用的一些 Google 工具背后的引擎,包括 Google Photos 和在 Google app 中使用的语言识别。 - -Google 开源了两个 [AIY kits][9],它可以让个人很容易上手人工智能。它们专注于计算机视觉和语音助理,这两个工具包将用到的所有组件封装到一个盒子中。这个工具包目前在美国的 Target 中有售,并且它是基于开源的树莓派平台的 —— 有越来越多的证据表明,开源和 AI 将有更多的交集。 - -### H2O.ai - -我 [以前介绍过][10] H2O.ai,它在机器学习和人工智能领域中占有一席之地,因为它的主要工具是免费和开源的。你可以获取主要的 H2O 平台和 Sparkling Water,它与 Apache Spark 一起工作,只需要去 [下载][11] 它们即可。这些工具遵循 Apache 2.0 许可证,它是一个非常灵活的开源许可证,你甚至可以在 Amazon Web 服务(AWS)和其它的集群cluster上运行它们,而这仅需要几百美元而已。 - -### Microsoft Onboard - -“我们的目标是让 AI 大众化,让每个人和组织获得更大的成就。”微软 CEO Satya Nadella [曾说过][12]。因此,微软持续迭代它的 [Microsoft Cognitive Toolkit][13]。它是一个能够与 TensorFlow 和 Caffe 竞争的一个开源软件框架。Cognitive 套件可以工作在 64 位的 Windows 和 Linux 平台上。 - -Cognitive 套件团队的报告称,“Cognitive 套件通过允许用户去创建、训练、以及评估他们自己的神经网络neural networks,以使企业级的、生产系统级的 AI 成为可能,这些神经网络可能跨多个 GPU 以及多个机器在大量的数据集中高效伸缩。” - -更多的有关 AI 的信息可在来自 Linux 基金会的新电子书中学习。Ibrahim Haddad 的 [《开源 AI:项目、洞察和趋势》][14] 调查了 16 个流行的开源 AI 项目 —— 深入研究了他们的历史、代码库、以及 GitHub 的贡献。[现在可以免费下载这本电子书][14]。 - ---- - -via: https://www.linux.com/blog/2018/6/6-open-source-ai-tools-know - -作者:[Sam Dean][a] -选题:[lujun9972](https://github.com/lujun9972) -译者:[qhwdw](https://github.com/qhwdw) -校对:[pityonline](https://github.com/pityonline) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]: https://www.linux.com/users/sam-dean -[1]: https://www.acumos.org/ -[2]: https://www.linuxfoundation.org/projects/deep-learning/ -[3]: https://www.linuxfoundation.org/blog/lf-deep-learning-foundation-announces-project-contribution-process/ -[4]: https://code.facebook.com/posts/1687861518126048/facebook-to-open-source-ai-hardware-design/ -[5]: https://venturebeat.com/2017/04/18/facebook-open-sources-caffe2-a-new-deep-learning-framework/ -[6]: http://yahoohadoop.tumblr.com/post/139916563586/caffeonspark-open-sourced-for-distributed-deep -[7]: https://systemml.apache.org/ -[8]: https://www.tensorflow.org/ -[9]: https://www.techradar.com/news/google-assistant-sweetens-raspberry-pi-with-ai-voice-control -[10]: https://www.linux.com/news/sparkling-water-bridging-open-source-machine-learning-and-apache-spark -[11]: http://www.h2o.ai/download -[12]: https://blogs.msdn.microsoft.com/uk_faculty_connection/2017/02/10/microsoft-cognitive-toolkit-cntk/ -[13]: https://www.microsoft.com/en-us/cognitive-toolkit/ -[14]: https://www.linuxfoundation.org/publications/open-source-ai-projects-insights-and-trends/