Julia Evans 发布的文章

当我开始在个人计算机上使用 Linux 时,首先令我兴奋的就是轻巧的窗口管理器,这主要是因为当时我的笔记本电脑只有有 32MB 的内存,其它的都运行不了了。

接着我开始接触 xmonad 之类的平铺窗口管理器!我可以用键盘管理窗口了!它们是如此之快!我可以通过编写 Haskell 程序来配置 xmonad!我可以用各种有趣的方式自定义所有内容(例如使用 dmenu 作为启动器)!这些年来,我用过 3、4 个不同的平铺窗口管理器,它们都很有趣。

大约 6 年前,我觉得配置平铺窗口管理器对我来说不再是一件有趣的事情,因此转而使用 Ubuntu 桌面环境 Gnome。(现在,我的笔记本电脑中的内存增加了 500 倍,这要快得多 :) )

我使用 Gnome 已有很长时间了,但是我仍然有点想念平铺窗口管理器。六个月前,一个朋友告诉我有关 PaperWM 的消息,它使你可以在 Gnome 中平铺窗口!我立即安装了它,并从那时起我一直在使用它。

PaperWM:Gnome 下的平铺窗口管理

PaperWM 的基本思想是:你想继续使用 Gnome(因为在 Gnome 中各种任务都能完成),但是你也希望使用平铺窗口管理器。

它是一个 Gnome 扩展程序(而不是一个独立的窗口管理器),并且使用 Javascript。

“Paper” 表示你的所有窗户都在一行中

PaperWM 的主要想法是将所有窗口排成一行,这实际上与传统的平铺窗口管理器大不相同,在传统的平铺窗口管理器中,你可以按任意方式平铺窗口。这是我写这篇博客时在几个窗口之间切换/调整大小的 gif 图像(有一个浏览器和两个终端窗口):

PaperWM 的 Github README 链接了此视频:http://10gui.com/video/,它描述为一个类似的”线性窗口管理器“。

我以前从未听说过这种组织窗口的方式,但是我喜欢它的简单性。如果要查找特定的窗口,只需向左/向右移动,直到找到它。

我在 PaperWM 中所做的一切

还有很多其他功能,但这是我使用的功能:

  • 在窗口之间左右移动(Super + ,Super + .
  • 按顺序向左/向右移动窗口(Super+Shift+,Super+Shift+.
  • 全屏显示窗口(Super + f
  • 缩小窗口(Super + r

我喜欢不需要配置的工具

我在笔记本上使用 PaperWM 已经 6 个月了,我真的很喜欢它!即使它是可配置(通过编写 Javascript 配置文件),我也非常欣赏它,它自带我想要的功能,我无需研究如何去配置。

fish shell 是另一个类似的令人愉悦的工具,我基本上没有配置 fish(除了设置环境变量等),我真的很喜欢它的默认功能。


via: https://jvns.ca/blog/2020/01/05/paperwm/

作者:Julia Evans 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

昨天,我和一名记者谈到了一个问题:广告商是如何在互联网上对人们进行追踪的?我们津津有味地查看了 Firefox 的开发者工具(虽然我不是一个互联网隐私专家,但至少还会使用开发者工具中的“network”标签页),从中我终于弄明白 像素追踪 tracking pixels 在实际中是如何工作的了。

问题:Facebook 怎么知道你逛了 Old Navy?

我时常听人们说起这种有些诡异的上网经历:你在线上浏览了一个商品,一天之后,竟然看到了同一款靴子(或者是别的什么你当时浏览的商品)的广告。这就是所谓的“再营销”,但它到底是如何实现的呢?

在本文中,我们来进行一个小实验,看看 Facebook 究竟是怎么知道你在线上浏览了什么商品的。这里使用 Facebook 作为示例,只是因为很容易找到使用了 Facebook 像素追踪技术的网站;其实,几乎所有互联网广告公司都会使用类似的追踪技术。

准备:允许第三方追踪器,同时关闭广告拦截器

我使用的浏览器是 Firefox,但是 Firefox 默认拦截了很多这种类型的追踪,所以需要修改 Firefox 的隐私设置,才能让这种追踪生效。

首先,我将隐私设置从默认设置(截图)修改为允许第三方追踪器的个性化设置(截图),然后禁用了一些平时运行的隐私保护扩展。

截图

截图

像素追踪:关键不在于 gif,而在于请求参数

像素追踪是网站用来追踪你的一个 1x1 大小的 gif。就其本身而言,一个小小的 1x1 gif 显然起不到什么作用。那么,像素追踪到底是如何进行追踪的?其中涉及两个方面:

  1. 通过使用像素追踪上的请求参数,网站可以添加额外的信息,比如你正在访问的页面。这样一来,请求的就不是 https://www.facebook.com/tr/(这个链接是一个 44 字节大小的 1x1 gif),而是 https://www.facebook.com/tr/?the_website_you're_on。(邮件营销人员会使用类似的技巧,通过为像素追踪指定一个独特的 URL,弄清楚你是否打开了某一封邮件。)
  2. 在发送该请求的同时,还发送了相应的 cookie。这样一来广告商就可以知道,访问 oldnavy.com 的这个人和在同一台电脑上使用 Facebook 的是同一个人。

Old Navy 网站上的 Facebook 像素追踪

为了对此进行验证,我在 Old Navy(GAP 旗下的一个服装品牌)网站上浏览了一个商品,相应的 URL 是 https://oldnavy.gap.com/browse/product.do?pid=504753002&cid=1125694&pcid=1135640&vid=1&grid=pds_0_109_1(这是一件“男款短绒格子花呢大衣”)。

在我浏览这个商品的同时,页面上运行的 Javascript(用的应该是这段代码)向 facebook.com 发送了一个请求。在开发者工具中,该请求看上去是这样的:(我屏蔽了大部分 cookie 值,因为其中有一些是我的登录 cookie)

下面对其进行拆解分析:

  1. 我的浏览器向如下 URL 发送了一个请求;
https://www.facebook.com/tr/?id=937725046402747&ev=PageView&dl=https%3A%2F%2Foldnavy.gap.com%2Fbrowse%2Fproduct.do%3Fpid%3D504753002%26cid%3D1125694%26pcid%3Dxxxxxx0%26vid%3D1%26grid%3Dpds_0_109_1%23pdp-page-content&rl=https%3A%2F%2Foldnavy.gap.com%2Fbrowse%2Fcategory.do%3Fcid%3D1135640%26mlink%3D5155%2Cm_mts_a&if=false&ts=1576684838096&sw=1920&sh=1080&v=2.9.15&r=stable&a=tmtealium&ec=0&o=30&fbp=fb.1.1576684798512.1946041422&it=15xxxxxxxxxx4&coo=false&rqm=GET
  1. 与该请求同时发送的,还有一个名为 fr 的 cookie,取值为
10oGXEcKfGekg67iy.AWVdJq5MG3VLYaNjz4MTNRaU1zg.Bd-kxt.KU.F36.0.0.Bd-kx6.

(估计是我的 Facebook 广告追踪 ID)

在所发送的像素追踪查询字符串里,有三个值得注意的地方:

  • 我当前访问的页面:https://oldnavy.gap.com/browse/product.do?pid=504753002&cid=1125694&pcid=1135640&vid=1&grid=pds_0_109_1#pdp-page-content
  • 引导我来到当前页面的上一级页面:https://oldnavy.gap.com/browse/category.do?cid=1135640&mlink=5155,m_mts_a
  • 作为我的身份标识的 cookie:10oGXEcKfGekg67iy.AWVdJq5MG3VLYaNjz4MTNRaU1zg.Bd-kxt.KU.F36.0.0.Bd-kx6.

下面来逛逛 Facebook!

下面来逛逛 Facebook 吧。我之前已经登入了 Facebook,猜猜看,我的浏览器发送给 Facebook 的 cookie 是什么?

不出所料,正是之前见过的 fr cookie:10oGXEcKfGekg67iy.AWVdJq5MG3VLYaNjz4MTNRaU1zg.Bd-kxt.KU.F36.0.0.Bd-kx6.。Facebook 现在一定知道我(Julia Evans,这个 Facebook 账号所关联的人)在几分钟之前访问了 Old Navy 网站,并且浏览了“男款短绒格子花呢大衣”,因为他们可以使用这个 cookie 将数据串联起来。

这里涉及到的是第三方 cookie

Facebook 用来追踪我访问了哪些网站的 cookie,属于所谓的“第三方 cookie”,因为 Old Navy 的网站使用它为一个第三方(即 facebook.com)确认我的身份。这和用来维持登录状态的“第一方 cookie”有所不同。

Safari 和 Firefox 默认都会拦截许多第三方 cookie(所以需要更改 Firefox 的隐私设置,才能够进行这个实验),而 Chrome 目前并不进行拦截(很可能是因为 Chrome 的所有者正是一个广告公司)。(LCTT 译注:Chrome 可以设置阻拦)

网站上的像素追踪有很多

如我所料,网站上的像素追踪有 很多。比如,wrangler.com 在我的浏览器里加载了来自不同域的 19 个不同的像素追踪。wrangler.com 上的像素追踪分别来自:ct.pinterest.comaf.monetate.netcsm.va.us.criteo.netgoogle-analytics.comdpm.demdex.netgoogle.caa.tribalfusion.comdata.photorank.mestats.g.doubleclick.netvfcorp.dl.sc.omtrdc.netib.adnxs.comidsync.rlcdn.comp.brsrvr.com,以及 adservice.google.com

Firefox 贴心地指出,如果使用 Firefox 的标准隐私设置,其中的大部分追踪器都会被拦截:

浏览器的重要性

浏览器之所以如此重要,是因为你的浏览器最终决定了发送你的什么信息、发送到哪些网站。Old Navy 网站上的 Javascript 可以请求你的浏览器向 Facebook 发送关于你的追踪信息,但浏览器可以拒绝执行。浏览器的决定可以是:“哈,我知道 facebook.com/tr/ 是一个像素追踪,我不想让我的用户被追踪,所以我不会发送这个请求”。

浏览器还可以允许用户对上述行为进行配置,方法包括更改浏览器设置,以及安装浏览器扩展(所以才会有如此多的隐私保护扩展)。

摸清其中原理,实为一件趣事

在我看来,弄清楚 cookie/像素追踪是怎么用于对你进行追踪的,实在是一件趣事(尽管有点吓人)。我之前大概明白其中的道理,但是并没有亲自查看过像素追踪上的 cookie,也没有看过发送的查询参数上究竟包含什么样的信息。

当然,明白了其中的原理,也就更容易降低被追踪的概率了。

可以采取的措施

为了尽量避免在互联网上被追踪,我采取了几种简单的措施:

  • 安装一个广告拦截器(比如 ublock origin 之类)。广告拦截器可以针对许多追踪器的域进行拦截。
  • 使用目前默认隐私保护强度更高的 Firefox/Safari,而不是 Chrome。
  • 使用 Facebook Container 这个 Firefox 扩展。该扩展针对 Facebook 进一步采取了防止追踪的措施。

虽然在互联网上被追踪的方式还有很多(尤其是在使用手机应用的时候,因为在这种情况下,你没有和像对浏览器一样的控制程度),但是能够理解这种追踪方法的工作原理,稍微减少一些被追踪的可能性,也总归是一件好事。


via: https://jvns.ca/blog/how-tracking-pixels-work/

作者:Julia Evans 选题:lujun9972 译者:chen-ni 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

最近,我对人们如何学习新事物感兴趣。我正在读 Kathy Sierra 的好书《Badass: Making Users Awesome》,它探讨了有关“刻意练习”的想法。这个想法是,你找到一个可以用三节 45 分钟课程内能够学会的小技能,并专注于学习这项小技能。因此,作为一项练习,我尝试考虑一项能够在三节 45 分钟课程内学会的计算机技能。

我认为使用 curl 构造 HTTP 请求也许就是这样的一项技能,所以这里有一些 curl 练习作为实验!

什么是 curl ?

curl 是用于构造 HTTP 请求的命令行工具。我喜欢使用 curl,因为它能够很轻松地测试服务器或 API 的行为是否符合预期,但是刚开始接触它的时候会让你感到一些困惑!

下面是一幅解释 curl 常用命令行参数的漫画 (在我的 Bite Size Networking 杂志的第 6 页)。

熟能生巧

对于任何命令行工具,我认为熟练使用是很有帮助的,能够做到只输入必要的命令真是太好了。例如,最近我在测试 Gumroad API,我只需要输入:

curl https://api.gumroad.com/v2/sales \
                         -d "access_token=<SECRET>" \
                         -X GET  -d "before=2016-09-03"

就能从命令行中得到想要的结果。

21 个 curl 练习

这些练习是用来理解如何使用 curl 构造不同种类的 HTTP 请求的,它们是故意有点重复的,基本上包含了我需要 curl 做的任何事情。

为了简单起见,我们将对 https://httpbin.org 发起一系列 HTTP 请求,httpbin 接受 HTTP 请求,然后在响应中回显你所发起的 HTTP 请求。

  1. 请求 https://httpbin.org
  2. 请求 https://httpbin.org/anything,它将会解析你发起的请求,并且在响应中回显。curl 默认发起的是 GET 请求
  3. https://httpbin.org/anything 发起 GET 请求
  4. https://httpbin.org/anything 发起 GET 请求,但是这次需要添加一些查询参数(设置 value=panda
  5. 请求 Google 的 robots.txt 文件 (www.google.com/robots.txt)
  6. https://httpbin.org/anything 发起 GET 请求,并且设置请求头为 User-Agent: elephant
  7. https://httpbin.org/anything 发起 DELETE 请求
  8. 请求 https://httpbin.org/anything 并获取响应头信息
  9. https://httpbin.com/anything 发起请求体为 JSON {"value": "panda"} 的 POST 请求
  10. 发起与上一次相同的 POST 请求,但是这次要把请求头中的 Content-Type 字段设置成 application/json(因为 POST 请求需要一个与请求体相匹配的 Content-Type 请求头字段)。查看响应体中的 json 字段,对比上一次得到的响应体
  11. https://httpbin.org/anything 发起 GET 请求,并且在请求头中设置 Accept-Encoding: gzip(将会发生什么?为什么会这样?)
  12. 将一些 JSON 放在文件中,然后向 https://httpbin.org/anything 发起请求体为该文件的 POST 请求
  13. 设置请求头为 Accept: image/png 并且向 https://httpbin.org/image 发起请求,将输出保存为 PNG 文件,然后使用图片浏览器打开。尝试使用不同的 Accept: 字段去请求此 URL
  14. https://httpbin.org/anything 发起 PUT 请求
  15. 请求 https://httpbin.org/image/jpeg 并保存为文件,然后使用你的图片编辑器打开这个文件
  16. 请求 https://www.twitter.com,你将会得到空的响应。让 curl 显示出响应头信息,并尝试找出响应内容为空的原因
  17. https://httpbin.org/anything 发起任意的请求,同时设置一些无意义的请求头(例如:panda: elephant
  18. 请求 https://httpbin.org/status/404和https://httpbin.org/status/200,然后再次请求它们并且让 curl 显示响应头信息
  19. 请求 https://httpbin.org/anything 并且设置用户名和密码(使用 -u username:password
  20. 设置 Accept-Language: es-ES 的请求头用以下载 Twitter 的西班牙语主页 (https://twitter.com)
  21. 使用 curl 向 Stripe API 发起请求(请查看 https://stripe.com/docs/development了解如何使用,他们会给你一个测试用的APIkey)。尝试向https://httpbin.org/anything 发起相同的请求

via: https://jvns.ca/blog/2019/08/27/curl-exercises/

作者:Julia Evans 选题:lujun9972 译者:hanwckf 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

今天早上,我为未来潜在容器杂志画了一幅 OverlayFS 的漫画,我对这个主题感到兴奋,想写一篇关于它的博客来提供更多详细信息。

容器镜像很大

容器镜像可能会很大(尽管有些很小,例如 alpine linux 才 2.5MB)。Ubuntu 16.04 约为 27 MB,Anaconda Python 发行版为 800MB 至 1.5GB

你以镜像启动的每个容器都是原始空白状态,仿佛它只是为使用容器而复制的一份镜像拷贝一样。但是对于大的容器镜像,像 800MB 的 Anaconda 镜像,复制一份拷贝既浪费磁盘空间也很慢。因此 Docker 不会复制,而是采用叠加

叠加如何工作

OverlayFS,也被称为 联合文件系统联合挂载,它可让你使用 2 个目录挂载文件系统:“下层”目录和“上层”目录。

基本上:

  • 文件系统的下层目录是只读的
  • 文件系统的上层目录可以读写

当进程“读取”文件时,OverlayFS 文件系统驱动将在上层目录中查找并从该目录中读取文件(如果存在)。否则,它将在下层目录中查找。

当进程“写入”文件时,OverlayFS 会将其写入上层目录。

让我们使用 mount 制造一个叠加层!

这有点抽象,所以让我们制作一个 OverlayFS 并尝试一下!这将只包含一些文件:我将创建上、下层目录,以及用来挂载合并的文件系统的 merged 目录:

$ mkdir upper lower merged work
$ echo "I'm from lower!" > lower/in_lower.txt
$ echo "I'm from upper!" > upper/in_upper.txt
$ # `in_both` is in both directories
$ echo "I'm from lower!" > lower/in_both.txt
$ echo "I'm from upper!" > upper/in_both.txt

合并上层目录和下层目录非常容易:我们可以通过 mount 来完成!

$ sudo mount -t overlay overlay
    -o lowerdir=/home/bork/test/lower,upperdir=/home/bork/test/upper,workdir=/home/bork/test/work
    /home/bork/test/merged

在执行此操作时,我不断收到一条非常烦人的错误消息,内容为:mount: /home/bork/test/merged: special device overlay does not exist.。这条消息是错误的,实际上只是意味着我指定的一个目录缺失(我写成了 ~/test/merged,但它没有被展开)。

让我们尝试从 OverlayFS 中读取其中一个文件!文件 in_both.txt 同时存在于 lower/upper/ 中,因此应从 upper/ 目录中读取该文件。

$ cat merged/in_both.txt
"I'm from upper!

可以成功!

目录的内容就是我们所期望的:

find lower/ upper/ merged/
lower/
lower/in_lower.txt
lower/in_both.txt
upper/
upper/in_upper.txt
upper/in_both.txt
merged/
merged/in_lower.txt
merged/in_both.txt
merged/in_upper.txt

创建新文件时会发生什么?

$ echo 'new file' > merged/new_file
$ ls -l */new_file
-rw-r--r-- 1 bork bork 9 Nov 18 14:24 merged/new_file
-rw-r--r-- 1 bork bork 9 Nov 18 14:24 upper/new_file

这是有作用的,新文件会在 upper 目录创建。

删除文件时会发生什么?

读写似乎很简单。但是删除会发生什么?开始试试!

$ rm merged/in_both.txt

发生了什么?让我们用 ls 看下:

ls -l upper/in_both.txt  lower/lower1.txt  merged/lower1.txt
ls: cannot access 'merged/in_both.txt': No such file or directory
-rw-r--r-- 1 bork bork    6 Nov 18 14:09 lower/in_both.txt
c--------- 1 root root 0, 0 Nov 18 14:19 upper/in_both.txt

所以:

  • in_both.txt 仍在 lower 目录中,并且保持不变
  • 它不在 merged 目录中。到目前为止,这就是我们所期望的。
  • 但是在 upper 中发生的事情有点奇怪:有一个名为 upper/in_both.txt 的文件,但是它是字符设备?我想这就是 overlayfs 驱动表示删除的文件的方式。

如果我们尝试复制这个奇怪的字符设备文件,会发生什么?

$ sudo cp upper/in_both.txt upper/in_lower.txt
cp: cannot open 'upper/in_both.txt' for reading: No such device or address

好吧,这似乎很合理,复制这个奇怪的删除信号文件并没有任何意义。

你可以挂载多个“下层”目录

Docker 镜像通常由 25 个“层”组成。OverlayFS 支持具有多个下层目录,因此你可以运行:

mount -t overlay overlay
      -o lowerdir:/dir1:/dir2:/dir3:...:/dir25,upperdir=...

因此,我假设这是有多个 Docker 层的容器的工作方式,它只是将每个层解压缩到一个单独的目录中,然后要求 OverlayFS 将它们全部合并在一起,并使用一个空的上层目录,容器将对其进行更改。

Docker 也可以使用 btrfs 快照

现在,我使用的是 ext4,而 Docker 使用 OverlayFS 快照来运行容器。但是我曾经用过 btrfs,接着 Docker 将改为使用 btrfs 的写时复制快照。(这是 Docker 何时使用哪种存储驱动的列表)

以这种方式使用 btrfs 快照会产生一些有趣的结果:去年某个时候,我在笔记本上运行了数百个临时的 Docker 容器,这导致我用尽了 btrfs 元数据空间(像这个人一样)。这真的很令人困惑,因为我以前从未听说过 btrfs 元数据,而且弄清楚如何清理文件系统以便再次运行 Docker 容器非常棘手。(这个 docker github 上的提案描述了 Docker 和 btrfs 的类似问题)

以简单的方式尝试容器功能很有趣!

我认为容器通常看起来像是在做“复杂的”事情,我认为将它们分解成这样很有趣。你可以运行一条 mount 咒语,而实际上并没有做任何与容器相关的其他事情,看看叠加层是如何工作的!


via: https://jvns.ca/blog/2019/11/18/how-containers-work–overlayfs/

作者:Julia Evans 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

上周,我一直在做一个 SQL 网站(https://sql-steps.wizardzines.com/,一个 SQL 示例列表)。我使用 sqlite 运行网站上的所有查询,并且我想在其中一个例子(这个)中使用窗口函数。

但是我使用的是 Ubuntu 18.04 中的 sqlite 版本,它太旧了,不支持窗口函数。所以我需要升级 sqlite!

事实证明,这个过程超麻烦(如通常一样),但是非常有趣!我想起了一些有关可执行文件和共享库如何工作的信息,结论令人满意。所以我想在这里写下来。

(剧透:https://www.sqlite.org/howtocompile.html 中解释了如何编译 SQLite,它只需花费 5 秒左右,这比我平时从源码编译的体验容易了许多。)

尝试 1:从它的网站下载 SQLite 二进制文件

SQLite 的下载页面有一个用于 Linux 的 SQLite 命令行工具的二进制文件的链接。我下载了它,它可以在笔记本电脑上运行,我以为这就完成了。

但是后来我尝试在构建服务器(Netlify) 上运行它,得到了这个极其奇怪的错误消息:“File not found”。我进行了追踪,并确定 execve 返回错误代码 ENOENT,这意味着 “File not found”。这有点令人发狂,因为该文件确实存在,并且有正确的权限。

我搜索了这个问题(通过搜索 “execve enoen”),找到了这个 stackoverflow 中的答案,它指出要运行二进制文件,你不仅需要二进制文件存在!你还需要它的加载程序才能存在。(加载程序的路径在二进制文件内部)

要查看加载程序的路径,可以使用 ldd,如下所示:

$ ldd sqlite3
    linux-gate.so.1 (0xf7f9d000)
    libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0xf7f70000)
    libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0xf7e6e000)
    libz.so.1 => /lib/i386-linux-gnu/libz.so.1 (0xf7e4f000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7c73000)
    /lib/ld-linux.so.2

所以 /lib/ld-linux.so.2 是加载程序,而该文件在构建服务器上不存在,可能是因为 Xenial(Xenial 是 Ubuntu 16.04,本文应该使用的是 18.04 “Bionic Beaver”)安装程序不支持 32 位二进制文​​件(?),因此我需要尝试一些不同的东西。

尝试 2:安装 Debian sqlite3 软件包

好吧,我想我也许可以安装来自 debian testing 的 sqlite 软件包。尝试从另一个我不使用的 Debian 版本安装软件包并不是一个好主意,但是出于某种原因,我还是决定尝试一下。

这次毫不意外地破坏了我计算机上的 sqlite(这也破坏了 git),但我设法通过 sudo dpkg --purge --force-all libsqlite3-0 恢复了,并使所有依赖于 sqlite 的软件再次工作。

尝试 3:提取 Debian sqlite3 软件包

我还尝试仅从 Debian sqlite 软件包中提取 sqlite3 二进制文件并运行它。毫不意外,这也行不通,但这个更容易理解:我有旧版本的 libreadline(.so.7),但它需要 .so.8

$ ./usr/bin/sqlite3
./usr/bin/sqlite3: error while loading shared libraries: libreadline.so.8: cannot open shared object file: No such file or directory

尝试 4:从源代码进行编译

我花费这么多时间尝试下载 sqlite 二进制的原因是我认为从源代码编译 sqlite 既烦人又耗时。但是显然,下载随便一个 sqlite 二进制文件根本不适合我,因此我最终决定尝试自己编译它。

这有指导:如何编译 SQLite。它是宇宙中最简单的东西。通常,编译的感觉是类似这样的:

  • 运行 ./configure
  • 意识到我缺少依赖
  • 再次运行 ./configure
  • 运行 make
  • 编译失败,因为我安装了错误版本的依赖
  • 去做其他事,之后找到二进制文件

编译 SQLite 的方式如下:

所有代码都在一个文件(sqlite.c)中,并且没有奇怪的依赖项!太奇妙了。

对我而言,我实际上并不需要线程支持或 readline 支持,因此我用编译页面上的说明来创建了一个非常简单的二进制文件,它仅使用了 libc 而没有其他共享库。

$ ldd sqlite3
    linux-vdso.so.1 (0x00007ffe8e7e9000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbea4988000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fbea4d79000)

这很好,因为它使体验 sqlite 变得容易

我认为 SQLite 的构建过程如此简单很酷,因为过去我很乐于编辑 sqlite 的源码来了解其 B 树的实现方式。

鉴于我对 SQLite 的了解,这并不令人感到意外(它在受限环境/嵌入式中确实可以很好地工作,因此可以以一种非常简单/最小的方式进行编译是有意义的)。 但这真是太好了!


via: https://jvns.ca/blog/2019/10/28/sqlite-is-really-easy-to-compile/

作者:Julia Evans 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我觉得前几天的 curl 练习进展顺利,所以今天我醒来后,想尝试编写一些 Git 练习。Git 是一大块需要学习的技能,可能要花几个小时才能学会,所以我分解练习的第一个思路是从“导航”一个存储库开始的。

我本来打算使用一个玩具测试库,但后来我想,为什么不使用真正的存储库呢?这样更有趣!因此,我们将浏览 Ruby 编程语言的存储库。你无需了解任何 C 即可完成此练习,只需熟悉一下存储库中的文件随时间变化的方式即可。

克隆存储库

开始之前,需要克隆存储库:

git clone https://github.com/ruby/ruby

与实际使用的大多数存储库相比,该存储库的最大不同之处在于它没有分支,但是它有很多标签,它们与分支相似,因为它们都只是指向一个提交的指针而已。因此,我们将使用标签而不是分支进行练习。改变标签的方式和分支非常不同,但查看标签和分支的方式完全相同。

Git SHA 总是引用同一个代码

执行这些练习时要记住的最重要的一点是,如本页面所述,像9e3d9a2a009d2a0281802a84e1c5cc1c887edc71 这样的 Git SHA 始终引用同一个的代码。下图摘自我与凯蒂·西勒·米勒撰写的一本杂志,名为《Oh shit, git!》。(她还有一个名为 https://ohshitgit.com/ 的很棒的网站,启发了该杂志。)

我们将在练习中大量使用 Git SHA,以使你习惯于使用它们,并帮助你了解它们与标签和分支的对应关系。

我们将要使用的 Git 子命令

所有这些练习仅使用这 5 个 Git 子命令:

git checkout
git log (--oneline, --author, and -S will be useful)
git diff (--stat will be useful)
git show
git status

练习

  1. 查看 matz 从 1998 年开始的 Ruby 提交。提交 ID 为 3db12e8b236ac8f88db8eb4690d10e4a3b8dbcd4。找出当时 Ruby 的代码行数。
  2. 检出当前的 master 分支。
  3. 查看文件 hash.c 的历史记录。更改该文件的最后一个提交 ID 是什么?
  4. 了解最近 20 年来 hash.c 的变化:将 master 分支上的文件与提交 3db12e8b236ac8f88db8eb4690d10e4a3b8dbcd4 的文件进行比较。
  5. 查找最近更改了 hash.c 的提交,并查看该提交的差异。
  6. 对于每个 Ruby 版本,该存储库都有一堆标签。获取所有标签的列表。
  7. 找出在标签 v1_8_6_187 和标签 v1_8_6_188 之间更改了多少文件。
  8. 查找 2015 年的提交(任何一个提交)并将其检出,简单地查看一下文件,然后返回 master 分支。
  9. 找出标签 v1_8_6_187 对应的提交。
  10. 列出目录 .git/refs/tags。运行 cat .git/refs/tags/v1_8_6_187 来查看其中一个文件的内容。
  11. 找出当前 HEAD 对应的提交 ID。
  12. 找出已经对 test/ 目录进行了多少次提交。
  13. 提交 65a5162550f58047974793cdc8067a970b2435c09e3d9a2a009d2a0281802a84e1c5cc1c887edc71 之间的 lib/telnet.rb 的差异。该文件更改了几行?
  14. 在 Ruby 2.5.1 和 2.5.2 之间进行了多少次提交(标记为 v2_5_1v2_5_3)(这一步有点棘手,步骤不只一步)
  15. “matz”(Ruby 的创建者)作了多少提交?
  16. 最近包含 “tkutil” 一词的提交是什么?
  17. 检出提交 e51dca2596db9567bd4d698b18b4d300575d3881 并创建一个指向该提交的新分支。
  18. 运行 git reflog 以查看你到目前为止完成的所有存储库导航操作。 ——————————————————————————–

via: https://jvns.ca/blog/2019/08/30/git-exercises–navigate-a-repository/

作者:Julia Evans 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出