分类 技术 下的文章

Firefox 浏览器内置了截屏工具,你可以用它对整个网页进行截屏。Chrome 浏览器也有同样的功能。

截屏来捕获信息是很常见的。

但你知道你可以在 Firefox 中截取整个网页的截图吗?Firefox 附带一个内置的截图工具,允许你截取选定区域、可见屏幕区域甚至整个网页的截图。

这意味着如果你想保存网页供以后参考,你可以快速捕获整个网页。

Chrome 也有截图功能,但稍微复杂一些。

在本教程中,我将引导你完成以下内容:

  • 如何在 Firefox 中截图
  • 如何在 Chrome 中截图
  • 使用 Nimbus 扩展获得比内置的截屏更多的功能

那么让我们从第一个开始。

在 Firefox 中截取网页截图

Firefox 的内置工具可让你通过单击选择整个屏幕、整个页面,甚至特定段落。

步骤 1:访问截图工具

要启动截图程序,请在使用 Firefox 时按 Ctrl + Shift + s

如果你不总是能记住快捷方式,也可以从右键单击菜单访问该工具。

Screenshot tool can also be accessed from right-click context menu

如果你经常截图,那么将该程序添加到工具栏将是一个好主意。为此,你只需执行三个简单步骤:

  1. 首先,右键单击工具栏并选择 “ 自定义工具栏 Customize Toolbar ” 选项
  2. 找到 “ 截图 Screenshot ” 程序并将其拖至工具栏
  3. 点击 “ 完成 Done ” 按钮即可

还困惑吗? 操作方法如下:

启用后,你可以单击刚刚拖动到工具栏的截图图标。

步骤 2:在 Firefox 中截图

当你启动截图工具时,它会提示两个选项:“ 保存整页 Save full page ” 和 “ 保存可见 Save visible ”。这里:

  • 保存整页将捕获整个网页
  • 保存可见只会捕获当前帧中可见的内容

但如果你想捕获特定部分,你可以使用鼠标光标选择该部分并保存:

如你所见,有两个选项: 下载 Download 复制 Copy (到剪贴板,以便你可以将其粘贴到文档或编辑工具中)。你可以根据你的场景使用其中之一。

在 Chrome 中截取网页截图

在 Chrome 中截取全部网页截图比在 Firefox 中要复杂一些,因为它隐藏在开发人员选项下。

不用担心!你将通过以下步骤做到:

  • 打开菜单,进入 “ 更多工具 More Tools -> 开发者工具 Developer tools ”。或者,你可以按 Ctrl + Shift + l 进入开发者工具目录。
  • Ctrl + Shift + p 并输入 screenshot(LCTT 译注:在中文环境中请输入 “屏幕截图”)
  • 选择区域或整个页面,然后回下载截图。

让我向你展示如何做到这一点:

Chrome 基本提供的就是这些。

如何使用扩展程序截图

✋ 非自由和开源软件警告!这里讨论的 Nimbus 扩展不是开源的。

如果你想要更多功能,例如添加延迟、水印或符号,那么你必须使用扩展程序。

为此,我建议使用 Nimbus,它几乎可以让你执行任何本地安装的截图工具可以执行的所有操作。

下载 Firefox 版 Nimbus:

Firefox 版 Nimbus

下载 Chrome 版 Nimbus:

Chrome 版 Nimbus
? 只有 Nimbus 的 Chrome 扩展具有视频录制功能。

完成安装后,请务必注册 Nimbus 以启用所有功能。

单击 Nimbus 扩展图标,你会看到多个选项:

你可以选择任何显示的功能,完成后,根据捕获后进行的操作(我选择编辑),它将直接下载截图,打开编辑器或将其发送到任何选定的云提供商。

如果你也将 “ 编辑 Edit ” 作为捕获后的操作,那么它将打开一个编辑器,你可以在其中对捕获的截图进行编辑:

如果你想添加水印、了解/更改快捷方式、更改截图的格式等,请打开 Nimbus 并点击小齿轮按钮:

只是一个扩展却有非常酷的功能。不是吗?

? 如果你经常截屏,你可能需要将 Nimbus 扩展固定到任务栏。

想要更多功能吗?使用截图工具

如果你不想受到扩展功能的束缚,那么需要尝试具有更多功能的截图工具,这些工具可以在整个系统的任何地方使用。

如果你是 Linux 用户,那么我们有一份关于 Linux 中截取和编辑截图的最佳工具 的专门指南:

Linux 中截取和编辑截图的最佳工具

我希望你喜欢这个快速技巧。

(题图:MJ/76cc0d02-fb37-4bd0-94ec-fc249c1f537e)


via: https://itsfoss.com/firefox-screenshot/

作者:Sagar Sharma 选题:lujun9972 译者:geekpi 校对:wxy

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

今天我在想 —— 当你在 Linux 上运行一个简单的 “Hello World” Python 程序时,发生了什么,就像下面这个?

print("hello world")

这就是在命令行下的情况:

$ python3 hello.py
hello world

但是在幕后,实际上有更多的事情在发生。我将描述一些发生的情况,并且(更重要的是)解释一些你可以用来查看幕后情况的工具。我们将用 readelfstraceldddebugfs/procltraceddstat。我不会讨论任何只针对 Python 的部分 —— 只研究一下当你运行任何动态链接的可执行文件时发生的事情。

0、在执行 execve 之前

要启动 Python 解释器,很多步骤都需要先行完成。那么,我们究竟在运行哪一个可执行文件呢?它在何处呢?

1、解析 python3 hello.py

Shell 将 python3 hello.py 解析成一条命令和一组参数:python3['hello.py']

在此过程中,可能会进行一些如全局扩展等操作。举例来说,如果你执行 python3 *.py ,Shell 会将其扩展到 python3 hello.py

2、确认 python3 的完整路径

现在,我们了解到需要执行 python3。但是,这个二进制文件的完整路径是什么呢?解决办法是使用一个名为 PATH 的特殊环境变量。

自行验证:在你的 Shell 中执行 echo $PATH。对我来说,它的输出如下:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

当执行一个命令时,Shell 将会依序在 PATH 列表中的每个目录里搜索匹配的文件。

对于 fish(我的 Shell),你可以在 这里 查看路径解析的逻辑。它使用 stat 系统调用去检验是否存在文件。

自行验证:执行 strace -e stat bash,然后运行像 python3 这样的命令。你应该会看到如下输出:

stat("/usr/local/sbin/python3", 0x7ffcdd871f40) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/python3", 0x7ffcdd871f40) = -1 ENOENT (No such file or directory)
stat("/usr/sbin/python3", 0x7ffcdd871f40) = -1 ENOENT (No such file or directory)
stat("/usr/bin/python3", {st_mode=S_IFREG|0755, st_size=5479736, ...}) = 0

你可以观察到,一旦在 /usr/bin/python3 找到了二进制文件,搜索就会立即终止:它不会继续去 /sbin/bin 中查找。

对 execvp 的补充说明

如果你想要不用自己重新实现,而运行和 Shell 同样的 PATH 搜索逻辑,你可以使用 libc 函数 execvp(或其它一些函数名中含有 pexec* 函数)。

3、stat 的背后运作机制

你可能在思考,Julia,stat 到底做了什么?当你的操作系统要打开一个文件时,主要分为两个步骤:

  1. 它将 文件名 映射到一个包含该文件元数据的 inode
  2. 它利用这个 inode 来获取文件的实际内容

stat 系统调用只是返回文件的 inode 内容 —— 它并不读取任何的文件内容。好处在于这样做速度非常快。接下来让我们一起来快速了解一下 inode。(在 Dmitry Mazin 的这篇精彩文章 《磁盘就是一堆比特》中有更多的详细内容)

$ stat /usr/bin/python3
  File: /usr/bin/python3 -> python3.9
  Size: 9           Blocks: 0          IO Block: 4096   symbolic link
Device: fe01h/65025d    Inode: 6206        Links: 1
Access: (0777/lrwxrwxrwx)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2023-08-03 14:17:28.890364214 +0000
Modify: 2021-04-05 12:00:48.000000000 +0000
Change: 2021-06-22 04:22:50.936969560 +0000
 Birth: 2021-06-22 04:22:50.924969237 +0000

自行验证:我们来实际查看一下硬盘上 inode 的确切位置。

首先,我们需要找出硬盘的设备名称:

$ df
...
tmpfs             100016      604     99412   1% /run
/dev/vda1       25630792 14488736  10062712  60% /
...

看起来它是 /dev/vda1。接着,让我们寻找 /usr/bin/python3 的 inode 在我们硬盘上的确切位置(在 debugfs 提示符下输入 imap 命令):

$ sudo debugfs /dev/vda1
debugfs 1.46.2 (28-Feb-2021)
debugfs:  imap /usr/bin/python3
Inode 6206 is part of block group 0
    located at block 658, offset 0x0d00

我不清楚 debugfs 是如何确定文件名对应的 inode 的位置,但我们暂时不需要深入研究这个。

现在,我们需要计算硬盘中 “块 658,偏移量 0x0d00” 处是多少个字节,这个大的字节数组就是你的硬盘。每个块有 4096 个字节,所以我们需要到 4096 * 658 + 0x0d00 字节。使用计算器可以得到,这个值是 2698496

$ sudo dd if=/dev/vda1 bs=1 skip=2698496 count=256 2>/dev/null | hexdump -C
00000000  ff a1 00 00 09 00 00 00  f8 b6 cb 64 9a 65 d1 60  |...........d.e.`|
00000010  f0 fb 6a 60 00 00 00 00  00 00 01 00 00 00 00 00  |..j`............|
00000020  00 00 00 00 01 00 00 00  70 79 74 68 6f 6e 33 2e  |........python3.|
00000030  39 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |9...............|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000060  00 00 00 00 12 4a 95 8c  00 00 00 00 00 00 00 00  |.....J..........|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 2d cb 00 00  |............-...|
00000080  20 00 bd e7 60 15 64 df  00 00 00 00 d8 84 47 d4  | ...`.d.......G.|
00000090  9a 65 d1 60 54 a4 87 dc  00 00 00 00 00 00 00 00  |.e.`T...........|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

好极了!我们找到了 inode!你可以在里面看到 python3,这是一个很好的迹象。我们并不打算深入了解所有这些,但是 Linux 内核的 ext4 inode 结构 指出,前 16 位是 “模式”,即权限。所以现在我们将看一下 ffa1 如何对应到文件权限。

  • ffa1 对应的数字是 0xa1ff,或者 41471(因为 x86 是小端表示)
  • 41471 用八进制表示就是 0120777
  • 这有些奇怪 - 那个文件的权限肯定可以是 777,但前三位是什么呢?我以前没见过这些!你可以在 inode 手册页 中找到 012 的含义(向下滚动到“文件类型和模式”)。这里有一个小的表格说 012 表示 “符号链接”。

我们查看一下这个文件,确实是一个权限为 777 的符号链接:

$ ls -l /usr/bin/python3
lrwxrwxrwx 1 root root 9 Apr  5  2021 /usr/bin/python3 -> python3.9

它确实是!耶,我们正确地解码了它。

4、准备复刻

我们尚未准备好启动 python3。首先,Shell 需要创建一个新的子进程来进行运行。在 Unix 上,新的进程启动的方式有些特殊 - 首先进程克隆自己,然后运行 execve,这会将克隆的进程替换为新的进程。

自行验证: 运行 strace -e clone bash,然后运行 python3。你应该会看到类似下面的输出:

clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f03788f1a10) = 3708100

3708100 是新进程的 PID,这是 Shell 进程的子进程。

这里有些工具可以查看进程的相关信息:

  • pstree 会展示你的系统中所有进程的树状图
  • cat /proc/PID/stat 会显示一些关于该进程的信息。你可以在 man proc 中找到这个文件的内容说明。例如,第四个字段是父进程的PID。

新进程的继承

新的进程(即将变为 python3 的)从 Shell 中继承了很多内容。例如,它继承了:

  1. 环境变量:你可以通过 cat /proc/PID/environ | tr '\0' '\n' 查看
  2. 标准输出和标准错误的文件描述符:通过 ls -l /proc/PID/fd 查看
  3. 工作目录(也就是当前目录)
  4. 命名空间和控制组(如果它在一个容器内)
  5. 运行它的用户以及群组
  6. 还有可能是我此刻未能列举出来的更多东西

5、Shell 调用 execve

现在我们准备好启动 Python 解释器了!

自行验证:运行 strace -f -e execve bash,接着运行 python3。其中的 -f 参数非常重要,因为我们想要跟踪任何可能产生的子进程。你应该可以看到如下的输出:

[pid 3708381] execve("/usr/bin/python3", ["python3"], 0x560397748300 /* 21 vars */) = 0

第一个参数是这个二进制文件,而第二个参数是命令行参数列表。这些命令行参数被放置在程序内存的特定位置,以便在运行时可以访问。

那么,execve 内部到底发生了什么呢?

6、获取该二进制文件的内容

我们首先需要打开 python3 的二进制文件并读取其内容。直到目前为止,我们只使用了 stat 系统调用来获取其元数据,但现在我们需要获取它的内容。

让我们再次查看 stat 的输出:

$ stat /usr/bin/python3
  File: /usr/bin/python3 -> python3.9
  Size: 9           Blocks: 0          IO Block: 4096   symbolic link
Device: fe01h/65025d    Inode: 6206        Links: 1
...

该文件在磁盘上占用 0 个块的空间。这是因为符号链接(python3.9)的内容实际上是存储在 inode 自身中:在下面显示你可以看到(来自上述 inode 的二进制内容,以 hexdump 格式分为两行输出)。

00000020  00 00 00 00 01 00 00 00  70 79 74 68 6f 6e 33 2e  |........python3.|
00000030  39 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |9...............|

因此,我们将需要打开 /usr/bin/python3.9 。所有这些操作都在内核内部进行,所以你并不会看到其他的系统调用。

每个文件都由硬盘上的一系列的 构成。我知道我系统中的每个块是 4096 字节,所以一个文件的最小大小是 4096 字节 —— 甚至如果文件只有 5 字节,它在磁盘上仍然占用 4KB。

自行验证:我们可以通过 debugfs 找到块号,如下所示:(再次说明,我从 Dmitry Mazin 的《磁盘就是一堆比特》文章中得知这些步骤)。

$ debugfs /dev/vda1
debugfs:  blocks /usr/bin/python3.9
145408 145409 145410 145411 145412 145413 145414 145415 145416 145417 145418 145419 145420 145421 145422 145423 145424 145425 145426 145427 145428 145429 145430 145431 145432 145433 145434 145435 145436 145437

接下来,我们可以使用 dd 来读取文件的第一个块。我们将块大小设定为 4096 字节,跳过 145408 个块,然后读取 1 个块。

$ dd if=/dev/vda1 bs=4096 skip=145408 count=1 2>/dev/null | hexdump -C | head
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  c0 a5 5e 00 00 00 00 00  |..>.......^.....|
00000020  40 00 00 00 00 00 00 00  b8 95 53 00 00 00 00 00  |@.........S.....|
00000030  00 00 00 00 40 00 38 00  0b 00 40 00 1e 00 1d 00  |[email protected]...@.....|
00000040  06 00 00 00 04 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000050  40 00 40 00 00 00 00 00  40 00 40 00 00 00 00 00  |@.@.....@.@.....|
00000060  68 02 00 00 00 00 00 00  68 02 00 00 00 00 00 00  |h.......h.......|
00000070  08 00 00 00 00 00 00 00  03 00 00 00 04 00 00 00  |................|
00000080  a8 02 00 00 00 00 00 00  a8 02 40 00 00 00 00 00  |..........@.....|
00000090  a8 02 40 00 00 00 00 00  1c 00 00 00 00 00 00 00  |..@.............|

你会发现,这样我们得到的输出结果与直接使用 cat 读取文件所获得的结果完全一致。

$ cat /usr/bin/python3.9 | hexdump -C | head
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  c0 a5 5e 00 00 00 00 00  |..>.......^.....|
00000020  40 00 00 00 00 00 00 00  b8 95 53 00 00 00 00 00  |@.........S.....|
00000030  00 00 00 00 40 00 38 00  0b 00 40 00 1e 00 1d 00  |[email protected]...@.....|
00000040  06 00 00 00 04 00 00 00  40 00 00 00 00 00 00 00  |........@.......|
00000050  40 00 40 00 00 00 00 00  40 00 40 00 00 00 00 00  |@.@.....@.@.....|
00000060  68 02 00 00 00 00 00 00  68 02 00 00 00 00 00 00  |h.......h.......|
00000070  08 00 00 00 00 00 00 00  03 00 00 00 04 00 00 00  |................|
00000080  a8 02 00 00 00 00 00 00  a8 02 40 00 00 00 00 00  |..........@.....|
00000090  a8 02 40 00 00 00 00 00  1c 00 00 00 00 00 00 00  |..@.............|

关于魔术数字的额外说明

这个文件以 ELF 开头,这是一个被称为“ 魔术数字 magic number ”的标识符,它是一种字节序列,告诉我们这是一个 ELF 文件。在 Linux 上,ELF 是二进制文件的格式。

不同的文件格式有不同的魔术数字。例如,gzip 的魔数是 1f8b。文件开头的魔术数字就是 file blah.gz 如何识别出它是一个 gzip 文件的方式。

我认为 file 命令使用了各种启发式方法来确定文件的类型,而其中,魔术数字是一个重要的特征。

7、寻找解释器

我们来解析这个 ELF 文件,看看里面都有什么内容。

自行验证:运行 readelf -a /usr/bin/python3.9。我得到的结果是这样的(但是我删减了大量的内容):

$ readelf -a /usr/bin/python3.9
ELF Header:
    Class:                             ELF64
    Machine:                           Advanced Micro Devices X86-64
...
->  Entry point address:               0x5ea5c0
...
Program Headers:
  Type           Offset             VirtAddr           PhysAddr
  INTERP         0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
                 0x000000000000001c 0x000000000000001c  R      0x1
->      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
        ...
->        1238: 00000000005ea5c0    43 FUNC    GLOBAL DEFAULT   13 _start

从这段内容中,我理解到:

  1. 请求内核运行 /lib64/ld-linux-x86-64.so.2 来启动这个程序。这就是所谓的动态链接器,我们将在随后的部分对其进行讨论。
  2. 该程序制定了一个入口点(位于 0x5ea5c0),那里是这个程序代码开始的地方。

接下来,让我们一起来聊聊动态链接器。

8、动态链接

好的!我们已从磁盘读取了字节数据,并启动了这个“解释器”。那么,接下来会发生什么呢?如果你执行 strace -o out.strace python3,你会在 execve 系统调用之后观察到一系列的信息:

execve("/usr/bin/python3", ["python3"], 0x560af13472f0 /* 21 vars */) = 0
brk(NULL)                       = 0xfcc000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=32091, ...}) = 0
mmap(NULL, 32091, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f718a1e3000
close(3)                        = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 l\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=149520, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f718a1e1000
...
close(3)                        = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3

这些内容初看可能让人望而生畏,但我希望你能重点关注这一部分:openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0" ...。这里正在打开一个被称为 pthread 的 C 语言线程库,运行 Python 解释器时需要这个库。

自行验证:如果你想知道一个二进制文件在运行时需要加载哪些库,你可以使用 ldd 命令。下面展示的是我运行后的效果:

$ ldd /usr/bin/python3.9
    linux-vdso.so.1 (0x00007ffc2aad7000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f2fd6554000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f2fd654e000)
    libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007f2fd6549000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2fd6405000)
    libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f2fd63d6000)
    libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f2fd63b9000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2fd61e3000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f2fd6580000)

你可以看到,第一个列出的库就是 /lib/x86_64-linux-gnu/libpthread.so.0,这就是它被第一个加载的原因。

关于 LD\_LIBRARY\_PATH

说实话,我关于动态链接的理解还有些模糊,以下是我所了解的一些内容:

  • 动态链接发生在用户空间,我的系统上的动态链接器位于 /lib64/ld-linux-x86-64.so.2. 如果你缺少动态链接器,可能会遇到一些奇怪的问题,比如这种 奇怪的“文件未找到”错误
  • 动态链接器使用 LD_LIBRARY_PATH 环境变量来查找库
  • 动态链接器也会使用 LD_PRELOAD 环境变量来覆盖你想要的任何动态链接函数(你可以使用它来进行 有趣的魔改,或者使用像 jemalloc 这样的替代品来替换默认内存分配器)
  • strace 的输出中有一些 mprotect,因为安全原因将库代码标记为只读
  • 在 Mac 上,不是使用 LD_LIBRARY_PATH(Linux),而是 DYLD_LIBRARY_PATH

你可能会有疑问,如果动态链接发生在用户空间,我们为什么没有看到大量的 stat 系统调用在 LD_LIBRARY_PATH 中搜索这些库,就像 Bash 在 PATH 中搜索那样?

这是因为 ld/etc/ld.so.cache 中有一个缓存,因此所有之前已经找到的库都会被记录在这里。你可以在 strace 的输出中看到它正在打开缓存 - openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

完整的 strace 输出 中,我仍然对动态链接之后出现的一些系统调用感到困惑(什么是 prlimit64?本地环境的内容是如何介入的?gconv-modules.cache 是什么?rt_sigaction 做了什么?arch_prctl 是什么?以及 set_tid_addressset_robust_list 是什么?)。尽管如此,我觉得已经有了一个不错的开头。

旁注:ldd 实际上是一个简单的 Shell 脚本!

在 Mastodon 上,有人 指出ldd 实际上是一个 Shell 脚本,它设置了 LD_TRACE_LOADED_OBJECTS=1 环境变量,然后启动程序。因此,你也可以通过以下方式实现相同的功能:

$ LD_TRACE_LOADED_OBJECTS=1 python3
    linux-vdso.so.1 (0x00007ffe13b0a000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f01a5a47000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f01a5a41000)
    libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007f2fd6549000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f2fd6405000)
    libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007f2fd63d6000)
    libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f2fd63b9000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2fd61e3000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f2fd6580000)

事实上,ld 也是一个可以直接运行的二进制文件,所以你也可以通过 /lib64/ld-linux-x86-64.so.2 --list /usr/bin/python3.9 来达到相同的效果。

关于 init 和 fini

让我们来谈谈这行 strace 输出中的内容:

set_tid_address(0x7f58880dca10) = 3709103

这似乎与线程有关,我认为这可能是因为 pthread 库(以及所有其他动态加载的库)在加载时得以运行初始化代码。在库加载时运行的代码位于 init 区域(或者也可能是 .ctors 区域)。

自行验证:让我们使用 readelf 来看看这个:

$ readelf -a /lib/x86_64-linux-gnu/libpthread.so.0
...
  [10] .rela.plt         RELA             00000000000051f0  000051f0
       00000000000007f8  0000000000000018  AI       4    26     8
  [11] .init             PROGBITS         0000000000006000  00006000
       000000000000000e  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000006010  00006010
       0000000000000560  0000000000000010  AX       0     0     16
...

这个库没有 .ctors 区域,只有一个 .init。但是,.init 区域都有些什么内容呢?我们可以使用 objdump 来反汇编这段代码:

$ objdump -d /lib/x86_64-linux-gnu/libpthread.so.0
Disassembly of section .init:

0000000000006000 <_init>:
    6000:       48 83 ec 08             sub    $0x8,%rsp
    6004:       e8 57 08 00 00          callq  6860 <__pthread_initialize_minimal>
    6009:       48 83 c4 08             add    $0x8,%rsp
    600d:       c3

所以它在调用 __pthread_initialize_minimal。我在 glibc 中找到了 这个函数的代码,尽管我不得不找到一个较早版本的 glibc,因为在更近的版本中,libpthread 不再是一个独立的库

我不确定这个 set_tid_address 系统调用是否实际上来自 __pthread_initialize_minimal,但至少我们知道了库可以通过 .init 区域在启动时运行代码。

这里有一份关于 .init 区域的 elf 手册的笔记:

$ man elf

.init 这个区域保存着对进程初始化代码有贡献的可执行指令。当程序开始运行时,系统会安排在调用主程序入口点之前执行该区域中的代码。

在 ELF 文件中也有一个在结束时运行的 .fini 区域,以及其他可以存在的区域 .ctors / .dtors(构造器和析构器)。

好的,关于动态链接就说这么多。

9、转到 \_start

在动态链接完成后,我们进入到 Python 解释器中的 _start。然后,它将执行所有正常的 Python 解析器会做的事情。

我不打算深入讨论这个,因为我在这里关心的是关于如何在 Linux 上运行二进制文件的一般性知识,而不是特别针对 Python 解释器。

10、写入字符串

不过,我们仍然需要打印出 “hello world”。在底层,Python 的 print 函数调用了 libc 中的某个函数。但是,它调用了哪一个呢?让我们来找出答案!

自行验证:运行 ltrace -o out python3 hello.py

$ ltrace -o out python3 hello.py
$ grep hello out
write(1, "hello world\n", 12) = 12

看起来它确实在调用 write 函数。

我必须承认,我对 ltrace 总是有一些疑虑 —— 与我深信不疑的 strace 不同,我总是不完全确定 ltrace 是否准确地报告了库调用。但在这个情况下,它似乎有效。并且,如果我们查阅 cpython 的源代码,它似乎在一些地方确实调用了 write() 函数,所以我倾向于相信这个结果。

什么是 libc?

我们刚刚提到,Python 调用了 libc 中的 write 函数。那么,libc 是什么呢?它是 C 的标准库,负责许多基本操作,例如:

  • malloc 分配内存
  • 文件 I/O(打开/关闭文件)
  • 执行程序(像我们之前提到的 execvp
  • 使用 getaddrinfo 查找 DNS 记录
  • 使用 pthread 管理线程

在 Linux 上,程序不一定需要使用 libc(例如 Go 就广为人知地未使用它,而是直接调用了 Linux 系统调用),但是我常用的大多数其他编程语言(如 node、Python、Ruby、Rust)都使用了 libc。我不确定 Java 是否也使用了。

你能通过在你的二进制文件上执行 ldd 命令,检查你是否正在使用 libc:如果你看到了 libc.so.6 这样的信息,那么你就在使用 libc。

为什么 libc 重要?

你也许在思考 —— 为何重要的是 Python 调用 libc 的 write 函数,然后 libc 再调用 write 系统调用?为何我要着重提及 libc 是调用过程的一环?

我认为,在这个案例中,这并不真的很重要(根据我所知,libc 的 write 函数与 write 系统调用的映射相当直接)。

然而,存在不同的 libc 实现,有时它们的行为会有所不同。两个主要的实现是 glibc(GNU libc)和 musl libc。

例如,直到最近,musl 的 getaddrinfo 并不支持 TCP DNS这是一篇关于这个问题引发的错误的博客文章

关于 stdout 和终端的小插曲

在我们的程序中,stdout(1 文件描述符)是一个终端。你可以在终端上做一些有趣的事情!例如:

  1. 在终端中运行 ls -l /proc/self/fd/1。我得到了 /dev/pts/2 的结果。
  2. 在另一个终端窗口中,运行 echo hello > /dev/pts/2
  3. 返回到原始终端窗口。你应会看到 hello 被打印出来了!

暂时就到这儿吧!

希望通过上文,你对 hello world 是如何打印出来的有了更深的了解!我暂时不再添加更多的细节,因为这篇文章已经足够长了,但显然还有更多的细节可以探讨,如果大家能提供更多的细节,我可能会添加更多的内容。如果你有关于我在这里没提到的程序内部调用过程的任何工具推荐,我会特别高兴。

我很期待看到一份 Mac 版的解析

我对 Mac OS 的一个懊恼是,我不知道如何在这个级别上解读我的系统——当我打印 “hello world”,我无法像在 Linux 上那样,窥视背后的运作机制。我很希望看到一个深度的解析。

我所知道的一些在 Mac 下的对应工具:

  • ldd -> otool -L
  • readelf -> otool
  • 有人说你可以在 Mac 上使用 dtrussdtrace 来代替 strace,但我尚未有足够的勇气关闭系统完整性保护来让它工作。
  • strace -> sc_usage 似乎能够收集关于系统调用使用情况的统计信息,fs_usage 则可以收集文件使用情况的信息。

延伸阅读

一些附加的链接:

(题图:MJ/b87ed0a2-80d6-49cd-b2bf-1ef822485e3f)


via: https://jvns.ca/blog/2023/08/03/behind--hello-world/

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

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

网络 绑定 Bonding 成组 Teaming ) 是 Linux 的一项内核特性,它让我们可以将多个网络接口(例如 ens192ens224)聚合为一个专有的虚拟网络接口,被称为通道绑定(bond0)。这样做能够提升吞吐量并增加冗余备份。

网络绑定一共支持 7 种模式,你可以根据实际需求进行设置。 链接聚合控制协议 Link Aggregation Control Protocol (LACP), 即模式 4(802.3ad)因其支持链接聚合与冗余而被广泛应用。

在本篇文章中,我们将引导你学习如何在 RHEL 系统中配置网卡(网络)绑定。

LACP 绑定的前置条件

  • 网络团队需要在网络交换机的端口上开启 LACP(802.3ad)来实现链接的聚合。
  • 一个 Linux 系统应该配备至少两个网络接口。
  • 对于物理服务器,我们推荐在板载接口与 PCI 接口间进行绑定配置,以避免在主机端的网络卡出现单点故障。

Bonding 模块

你可以使用 lsmod 命令来确认你的 Linux 系统是否已经加载了 bonding 模块。

lsmod | grep -i bonding
bonding               12451  0

系统应该默认已加载。如果未看到,可以运用 modprobe 命令进行加载。

modprobe bonding

创建绑定接口

/etc/sysconfig/network-scripts/ 路径下,创建一个名为 ifcfg-bond0 的绑定接口文件。依据你的网络情况,你可能需要修改诸如 IPMASK 以及 GATEWAY 等值。

vi /etc/sysconfig/network-scripts/ifcfg-bond0
TYPE=Bond
DEVICE=bond0
NAME=bond0
BONDING_MASTER=yes
BOOTPROTO=none
ONBOOT=yes
IPADDR=192.168.1.100
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
BONDING_OPTS="mode=4 miimon=100 lacp_rate=1"
参数描述
BONDING_MASTER=yes表示设备是一个绑定主设备。
mode=4绑定模式是 IEEE 802.3ad 动态链接聚合(LACP)。
miimon=100定义以毫秒单位的 MII 链路监测频率,这决定了多久检查每个从属链路的状态一次以寻找失败的链路。0 值将禁用 MII 链路监测。100 是个不错的初始值。
lacp_rate=1一个设置项,规定我们将以何种频率要求我们的链路伙伴每秒钟发送 LACPDU。默认为慢,即 0

配置第一个子接口

修改你希望添加到绑定中的第一个子接口。请根据你的实际环境使用合适的接口名。

vi /etc/sysconfig/network-scripts/ifcfg-ens192
TYPE=Ethernet
BOOTPROTO=none
DEVICE=ens192
ONBOOT=yes
MASTER=bond0
SLAVE=yes

配置第二个子接口

修改你希望添加到绑定中的第二个子接口。请根据你的实际环境使用合适的接口名。

vi /etc/sysconfig/network-scripts/ifcfg-ens224
TYPE=Ethernet
BOOTPROTO=none
DEVICE=ens224
ONBOOT=yes
MASTER=bond0
SLAVE=yes

重启网络服务

重启网络服务以激活绑定接口。

systemctl restart network

验证绑定配置

你可以借助 ip 命令 来查看绑定接口以及其子接口的情况。可以看到,bond0 现在已启动并在运行。

查阅绑定接口状态

检查以下文件,你可以看到绑定接口及其子接口的详细信息。输出结果应该看起来很不错,我们能看到诸如绑定模式,MII 状态,MII 轮询间隔,LACP 速率,端口数量等信息。

cat /proc/net/bonding/bond0
Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011)

Bonding Mode: IEEE 802.3ad Dynamic link aggregation
Transmit Hash Policy: layer (0)
MII Status: up
MII Polling Interval (ms): 100
Up Delay (ms): 0
Down Delay (ms): 0

802.3ad info
LACP rate: fast
Min links: 0
Aggregator selection policy (ad_select): stable
System priority: 65535
System MAC address: c8:5b:76:4d:d4:5c
Active Aggregator Info:
        Aggregator ID: 1
        Number of ports: 2
        Actor Key: 15
        Partner Key: 32773
        Partner Mac Address: e4:a7:a0:32:fc:e9

Slave Interface: ens192
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: c8:5b:76:4d:d4:5c
Slave queue ID: 0
Aggregator ID: 1
Actor Churn State: none
Partner Churn State: none
Actor Churned State: 0
Partner Churned State: 0
details actor lacp pdu:
    system priority: 65535
    system mac address: c8:5b:76:4d:d4:5c
    port key: 15
    port priority: 255
    port number: 1
    port state: 63
details Partner lacp pdu:
    system priority: 32667
    system mac address: e4:a7:a0:32:fc:e9
    oper key: 32773
    port priority: 32768
    port number: 290
    port state: 61

Slave Interface: ens224
MII Status: up
Speed: 10000 Mbps
Duplex: full
Link Failure Count: 0
Permanent HW addr: e4:a7:a0:32:fc:e9
Slave queue ID: 0
Aggregator ID: 1
Actor Churn State: none
Partner Churn State: none
Actor Churned State: 0
Partner Churned State: 0
details actor lacp pdu:
    system priority: 65535
    system mac address: e4:a7:a0:32:fc:e9
    port key: 15
    port priority: 255
    port number: 2
    port state: 63
details Partner lacp pdu:
    system priority: 32667
    system mac address: c8:5b:76:4d:d4:5c
    oper key: 32773
    port priority: 32768
    port number: 16674
    port state: 61

容错/冗余测试

为了验证容错性和连接速度,你可以逐个断开接口,然后检查服务器是否仍旧可达。

  • 测试用例-1:当两个子接口都启动并运行时,使用 ethtool 命令 检查链路速度。
  • 测试用例-2:断开第一个子接口,然后尝试访问系统。
  • 测试用例-3:断开第二个子接口,然后尝试访问系统。

测试用例-1:

如果你想检测下连接速度:没错,我在 bond0 上看到了 20 Gbps 的速度,因为每个子接口支持 10 Gbps。

ethtool bond0
Settings for bond0:
        Supported ports: [ ]
        Supported link modes:   Not reported
        Supported pause frame use: No
        Supports auto-negotiation: No
        Supported FEC modes: Not reported
        Advertised link modes:  Not reported
        Advertised pause frame use: No
        Advertised auto-negotiation: No
        Advertised FEC modes: Not reported
        Speed: 20000Mb/s
        Duplex: Full
        Port: Other
        PHYAD: 0
        Transceiver: internal
        Auto-negotiation: off
        Link detected: yes

测试用例-2:

现在我们将关闭第一个子接口。

ifdown ens192
Device 'ens192' successfully disconnected.

通过 ssh 尝试访问系统。没问题,系统现在是可以访问的。

ssh [email protected]

由于已经有一个子接口关闭,你现在在 bond0 上只能看到 10 Gbps 的速度。

ethtool bond0 | grep -i speed
            Speed: 10000Mb/s

现在,我们再次查看绑定接口的状态。可以看出,只有一个子接口处于活跃状态。

cat /proc/net/bonding/bond0

测试用例-3:

我们来关闭第二个子接口,并进行类似测试用例-2 的测试:

ifdown ens224
Device 'ens224' successfully disconnected.

结语

我希望你已经掌握了在 RHEL 上配置 LACP 绑定的方法。

在本教程中,我们为你展示了在 RHEL 系统配置网络绑定或网卡聚合的最简单方式。

如果你有任何疑问或者反馈,欢迎在下面留言。

(题图:MJ/939f6ba6-eb46-480d-8879-3a422c7425d2)


via: https://www.2daygeek.com/configure-network-bonding-nic-teaming-rhel/

作者:Jayabal Thiyagarajan 选题:lujun9972 译者:ChatGPT 校对:wxy

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

你不需要专门的 PDF 编辑器来添加注释和高亮文本。下面介绍如何在 Linux 中使用 GNOME 的文档查看器来注释 PDF。

阅读一些 PDF 格式的有趣内容,并觉得需要添加评论或高亮显示某些文本?也许你在 PDF 文档上留一些反馈?

Linux 用户可以使用多种 PDF 编辑器。但你不需要专门的 PDF 编辑器来完成这个简单的任务。

大多数 PDF 阅读器都具有内置注释功能,你可以使用它来快速轻松地高亮显示文本并添加注释。

我最近在审阅 O'Reilly 即将出版的第四版 《Linux Pocket Guide》一书时“发现”了它。出版商请求对 PDF 文件本身提出建议。

让我分享一下如何在 Linux 中使用 Evince(GNOME 中的默认 PDF 阅读器)对 PDF 进行注释。使用 Okular(KDE 中默认的 PDF 阅读器)也可以实现同样的效果。

大多数 Linux 发行版都应该附带上述工具之一。如果没有,你可以轻松安装它们。我不会介绍安装过程。请在你的发行版的软件管理器中查找它们。

使用 Evince 文档查看器注释 PDF

使用 Evince(在 GNOME 中也称为“ 文档查看器 Document Viewer ”)打开所需的 PDF 文件。

你将在文档查看器的左上角看到一个“编辑”选项。点击它会出现以下两个选项:

  • 备注文本(用于添加评论)
  • 高亮显示文本(用于高亮显示选定的文本)

让我详细介绍这是如何工作的。

在 PDF 中添加注释

要添加评论,单击 “ 注释文本 Note text ” 选项。

你会注意到光标变成了 “+” 号。你可以单击文档中的任意位置,它会立即添加注释图标并打开另一个窗口以添加注释。

我建议单击该行的末尾,以便注释图标位于空白区域,并且不会遮挡文件的实际文本。

添加所需注释后,你可以 单击注释区域的 “X” 按钮来关闭 注释文本窗口。

? 你可以通过在屏幕上拖动注释图标来移动注释图标。

在 PDF 中高亮显示文本

同样,你可以选择 “ 高亮显示文本 Highlight text ” 选项来高亮显示 PDF 文件中的特定文本。

之后,只需选择要高亮显示的文本即可。当你开始移动光标,它就会开始高亮显示。

? 你还可以在高亮显示的文本上添加注释,以提供有关高亮显示的一些上下文。要添加文本,请单击高亮显示的文本(现在它可点击了)。

保存带注释的文件

你可能已在 PDF 上添加注释,但更改尚未保存。

保存更改很简单。按 Ctrl+S 键,你可以选择保存文件的位置。

你可以覆盖现有 PDF 文件或将其另存为新文件。

? 注释、评论和高亮被附加到 PDF 文件中,即使你使用其他可以读取注释的工具打开 PDF 文件,它们也应该可以看到。

阅读注释

要阅读文本注释或评论,只需将鼠标悬停在注释图标或高亮显示的文本上即可。它将立即显示注释。

不要单击图标来阅读文本。单击将打开它进行编辑。

编辑现有注释

假设你注意到需要向现有笔记添加更多详细信息。你所要做的就是单击“注释”图标。

它将打开添加的文本。你可以阅读它,如果你愿意,也可以编辑它。对于高亮显示的文本部分中的注释也是如此。

但是,编辑高亮显示的文本时它不是很完善。如果你认为必须高亮显示现有文本周围的更多文本,那么它会起作用。但如果你想缩短高亮显示的文本,那就不行了。

为此,你必须删除高亮显示并再次添加。

删除现有注释

删除注释非常简单,只需右键单击注释并选择 “ 删除注释 Remove Annotation ” 选项即可。

这对于高亮显示的文本也同样有效。

修改注释的外观

不喜欢默认的黄色或注释图标?这一切都是可以改变的。

右键单击现有注释并选择 “ 注释属性 Annotation Properties... ” 选项。

你可以更改评论的作者、注释的颜色和不透明度。你还可以更改注释的图标和高亮显示的标记类型(删除线、下划线等)。

这仅适用于现有注释。我找不到一种方法来更改所有未来注释的默认属性。

更多 PDF 编辑选项

如果你需要的不仅仅是简单的文本注释和高亮显示,可以使用专门的 PDF 编辑器。

11 最好的 Linux 上的 PDF 编辑器

这些工具可能允许你重新排列或合并 PDF 文件。

如何在 Linux 上合并 PDF 文件

如果需要,你还可以 压缩 PDF 文件

如何在 Linux 上压缩 PDF 文件

PDF 编辑永无止境。我希望你喜欢这篇有关 Linux 中 PDF 注释的初学者技巧。

KDE 的 Okular 还提供 PDF 注释选项。也许我可以写一篇关于 Okul 的类似文章。

请在评论栏留下你的反馈。

(题图:MJ/a5318540-0b82-4ef6-a0bb-532505a17458)


via: https://itsfoss.com/annotate-pdf-linux/

作者:Abhishek Prakash 选题:lujun9972 译者:geekpi 校对:wxy

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

今天群里一朋友遇到这样一个问题,明明硬盘只用了 30% 左右的空间,但是却无法写入文件。使用 df -iT 命令查看文件系统使用情况时,发现根目录的 inode 使用率竟然是 100%。后来通过聊天得知,原来他的服务器主要用于存储 1KB 左右的小文件,这一下就破案了。

inode 主要用来记录文件的属性,及此文件的数据所在的块编号。每一个文件会占用一个 inode,因此如果都是小文件的话,那么就会出现 inode 已经耗尽,但文件系统还有很大的空闲空间,从而无法写入新文件。

如何获得更多的 inode

其实在创建 ext4 文件系统时,我们可以使用 -T small 参数来获得更多的 inode,从而优化对小文件的存储。接下来我们通过一个示例来看看效果。

这是两块相同大小的硬盘:

root@debian:~# lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sdb      8:16   0    1G  0 disk
└─sdb1   8:17   0 1023M  0 part
sdc      8:32   0    1G  0 disk
└─sdc1   8:33   0 1023M  0 part

首先使用默认参数给 /dev/sdb1 创建文件系统:

root@debian:~# /sbin/mkfs.ext4 /dev/sdb1
mke2fs 1.47.0 (5-Feb-2023)
Creating filesystem with 261888 4k blocks and 65536 inodes
Filesystem UUID: 8935c902-df71-4808-b547-c85b6fd37a46
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376

Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

从输出中可见,该文件系统有 261888 个 4KB 大小的块和 65536 个 inode。

然后使用 -T 参数对 /dev/sdc1 创建文件系统:

root@debian:~# /sbin/mkfs.ext4 -T small /dev/sdc1
mke2fs 1.47.0 (5-Feb-2023)
Creating filesystem with 1047552 1k blocks and 262144 inodes
Filesystem UUID: f521096d-a5a1-41c9-bbf7-e6102e74e87a
Superblock backups stored on blocks:
        8193, 24577, 40961, 57345, 73729, 204801, 221185, 401409, 663553,
        1024001

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

从输出中可见,该文件系统有 1047552 个 1KB 大小的块和 262144 个 inode。

也可以通过以下方式对比两个文件系统的 inode 数量:

root@debian:~# mkdir default small
root@debian:~# mount /dev/sdb1 default/
root@debian:~# mount /dev/sdc1 small/
root@debian:~# df -iT
Filesystem     Type      Inodes IUsed   IFree IUse% Mounted on
/dev/sdb1      ext4       65536    11   65525    1% /root/default
/dev/sdc1      ext4      262144    11  262133    1% /root/small

从以上示例中我们可以看出,在使用 -T small 参数后,inode 数量多了近 20 万个!

注意: 这样做也是有代价的。在使用默认参数创建 ext4 文件系统时,默认数据块大小为 4KB,而使用 -T small 参数后,数据块大小为 1KB。这就意味着我们存储一个同样大小的文件,使用 -T small 参数创建的文件系统存储该数据时,占用的数据块更多,数据更分散,如果文件较大,会直接影响文件的读取速度

mke2fsmkfs.ext4)的 -T 参数指定了如何使用该文件系统,以便 mke2fs 可以为该用途选择最佳的文件系统参数,其支持的使用类型在配置文件 /etc/mke2fs.conf 中定义,可以使用逗号分隔指定一个或多个使用类型

inode 不足的解决方法

当你已有的文件系统上出现 inode 不足的问题时,除了备份数据、重建分区并恢复分区数据外,还有两种临时解决方案:

1、删除文件大小为 0 的空文件,可以使用如下命令查找:

find PATH -name "*" -type f -size 0c 

注意: 使用 -size 参数时,不要用 -size 1k,这个表示占用空间为 1KB,而不是文件大小为 1KB,应该使用 -size 1024c 才表示文件大小为 1KB。

2、可以定期对历史小文件进行打包、归档,以减少文件数量。


作者简介:一个喜欢瞎折腾的 IT 技术人员,懂得不多,但是喜欢和有共同兴趣爱好的朋友交流、学习

(题图:MJ/9a66155a-772e-41f1-b29d-c3d4161f7853)


via: http://www.yanjun.pro/?p=128 作者:老颜随笔 编辑:wxy

本文由贡献者投稿至 Linux 中国公开投稿计划,采用 CC-BY-SA 协议 发布,Linux中国 荣誉推出

Thunar 是 Xfce 提供的一款优秀文件管理器,通过这些技巧和调整,你的使用体验可以得到提升。

Thunar 是 Xfce 桌面环境的默认文件管理器,它实现了轻量级与优秀的用户体验的完美平衡。

但是,像其他任何尚未深入探索的工具一样,你对 Thunar 的掌握会在你深入了解它之后变得更多。

我大胆地说,一系列的特性将会改善你的 Linux 体验。

在这个指南中,我会分享可以让 Thunar 体验更上一层楼的以下技巧:

  • Thunar 的通用快捷键
  • 添加 Thunar 插件
  • 从你上次离开的地方继续
  • 使用图标进行更好的识别
  • 高亮显示文件/文件夹
  • 在双窗口模式下轻松复制或移动文件
  • 自定义操作

那就让我们从第一条开始吧。

1、利用键盘快捷键

毫无疑问,利用键盘快捷键可以提升你的工作效率。

以下是一些在 Thunar 中使用的简单快捷键,可以帮助减少鼠标点击操作:

快捷键描述
Ctrl + T新加标签页
Ctrl + N新建窗口
F3切换至双窗格模式
Alt + ←后退
Alt + →前进
Ctrl + F搜索文件
Ctrl + S按模式选取文件
Ctrl + H显示隐藏文件
Ctrl + E侧边栏启用树视图
Alt + ↑打开父目录
Alt + Home前往主目录

虽然看似简单,但请相信我,这些快捷键一旦融入你的日常工作流,你就会离不开它。

2、从你离开的地方重新打开 Thunar

? 此功能只在 Thunar 4.18 或更高版本中可用。

打开文件管理器时,默认打开上次关闭时的所有标签页,这是一个令人振奋的功能。

我就是那种喜欢深入 Linux 目录进行探索的人,所以从我离开的地方重新打开 Thunar 是一项重要功能。

若要启用此功能,只需遵循以下三个简单的步骤:

  • 点击 “ 编辑 Edit ” 菜单并选择 “ 偏好设定 Preferences
  • 切换至 “ 行为 Behavior ” 标签页
  • 勾选 “ 启动时还原标签 Restore tabs on startup ” 选项

3、为文件或文件夹添加徽章

你可以把徽章看作是一个特殊标记,它可以帮助你更快地识别出特定的文件或文件夹。

另外,你也可以通过添加徽章使目录名更加直观。例如,将一个 “▶️” 徽章添加到“视频”目录上。

要添加徽章,遵循下面的步骤:

  • 右键点击需要添加徽章的文件/文件夹,从上下文菜单中选择 “ 属性 Properties
  • 进入 “ 徽章 Emblem ” 标签页,为选定项选择一个或多个徽章

我添加徽章后的样子就像这样:

4、使用突出颜色高亮文件/文件夹

? 这项功能只在 Thunar 4.18 及更高版本中可用。

如果仅通过添加徽章仍无法使文件/文件夹在众多文件中突显出来,你可以改变它的背景或前景颜色。

注意:改变前景颜色只会改变选定项目的字体颜色。

要突出高亮一个文件或文件夹,你需要按照以下步骤操作:

  • 将鼠标悬停在需要高亮的项目上,右键并选择 “属性”。
  • 进入 “ 高亮 Highlight ” 部分,选择改变前景或背景(或两者都选,但要一次进行一项操作)。
  • 接着,选择你想要的颜色,它会展示结果。如果满意,点击 “ 应用 Apply ” 按钮,不满意就通过 “ 重置 Reset ” 按钮恢复默认设置。

5、探索 Thunar 插件

不同于那些知名的文件管理器,Thunar 的插件相对较少,因为很多操作都可以通过自定义动作实现,其他的功能如内置一样与生俱来。

但是,也有一些实用的插件供你选择。

如要了解更多,你可以 访问官方网站 查看详情。

在这里,我将向你展示如何添加一个归档插件,让你可直接在右键菜单中创建和解压归档文件:

在 Ubuntu 中安装 Thunar 归档插件,可以使用以下命令:

sudo apt install thunar-archive-plugin

在 Fedora 中安装:

sudo dnf install thunar-archive-plugin

在 Arch Linux 中安装:

sudo pacman -S thunar-archive-plugin

6、利用双窗格特性进行文件传输

这是几乎所有的文件管理器都具备、而又被大多数人所忽视的一个重要特性。

那为什么我要将此特性列为 Thunar 的优化建议呢?答案很简单。因为 Thunar 的双窗格特性非常好用,它使得文件的复制和移动变得轻而易举。

移动文件

接下来我们看一下如何将文件从一个目录动态一个目录:

  • 首先,按下 F3 键开启双窗格模式。
  • 打开两边窗格中的目录。一边导航到需要被移动的文件所在,另一边则打开目标位置。
  • 接着,选中需要移动的项目。
  • 移动文件很简单,只需选中它们,并拖动到另一侧窗格中(即你已打开的目标位置)。

移动文件

复制文件

复制文件的操作非常类似移动文件,只是在拖曳并释放文件时需要按下 Ctrl 键。

  • 使用 F3 键切换到双窗格模式。
  • 打开两边窗格中的源文件和目标位置。
  • 选中需要复制的文件,然后按下 Ctrl 键,将它们拖到第二窗格中,释放鼠标后再放开 Ctrl 键。

复制文件

如果你仔细看,会发现在移动文件时,鼠标指针呈现一个箭头 “↗”,而在复制文件时,它会显示一个加号 “➕”。

7、在 Thunar 中使用自定义操作(针对进阶用户)

到现在为止,Thunar 中最实用的功能无疑是你能创建属于自己的行为,比如使选定的文件变为可执行文件,以 root 用户身份打开 Thunar 等等。

因此,这也意味着这将是本教程中最详细的一节!

首先,点击 “编辑” 菜单,你会找到一个 “ 配置自定义操作 configure custom actions ” 的选项,这是倒数第二个:

如你所见,所有可用的操作都在列表中显示。

要添加操作,点击加号 “➕” 按钮,你会看到多个选项:

这些选项包括:

  • 名称 Name ”:为自定义操作命名。
  • 描述 Description ”:该操作的信息说明(以帮助用户理解该操作的功能)。
  • 子菜单 Submenu ”:用于将多个操作整合到一起。
  • 命令 Command ”:关键的一步,你需要为这个操作分配一条命令使其可以工作。

如果你仔细看,你会发现还有另一个名为 “ 出现条件 Appearance Conditions ” 的选项卡,在这里你可以指定在哪些文件(以及目录)类型的右键菜单中这个动作会出现。

例如,如果一个动作只应在所选文件是文本文件时才显示,那么你就需要勾选 “文本文件Text Files” 选项:

接下来,我要向你展示如何设置一个以 root 身份打开 Thunar 的自定义动作。

创建自定义操作以 root 身份打开 Thunar

你一定经历过这种情况:你想操作一个文件,但是只有 root 用户能够访问。于是你打开终端,执行命令以 root 身份启动文件管理器。

但如果用这个自定义操作,只需轻点一下鼠标就可以了。

具体方法是,在 “ 基本 Basic ” 选项卡的数据区域输入以下内容:

  • 名称:以 root 身份打开 Thunar
  • 命令:pkexec thunar %F

你能选择任何相关的图标,我在这里选择了一个简单的终端图标。

接着,在 “出现条件” 区域,输入如下内容:

  • 如果所选内容包含: 目录 Directories

完成后,数据区域会如下所示:

看一下最后的结果:

以 root 身份打开 Thunar

看起来很酷,对吧?

以下是一些其他可能有用的自定义操作:

创建创建符号链接的自定义操作

创建符号链接 是访问深度嵌套在文件系统中的文件的一种简便手段。它同时也被用来重定向到特定的包的路径。

要创建一个创建符号链接的自定义操作,你可以输入以下指令:

  • 名称:创建链接
  • 描述:创建一个符号链接
  • 命令:ln -s %f 'Link to %n'
  • 若选择包含:框全部都要勾选

直接查找文件

点击一个目录,选择搜索,输入你想要查找的内容。

这是在目录内搜索文件最为手边的方式了。

首先,在你的系统中安装 catfish,如果你是 Ubuntu/Debian 系的用户,可以使用如下命令:

sudo apt install catfish

然后,创建一个自定义操作,输入以下信息:

  • 名称:在该目录中查找文件
  • 描述:在选中的目录中搜索文件
  • 命令:catfish %f
  • 若选择包含:仅勾选 “目录”。

一键安装多个图像转换的自定义操作

在我写作的过程中,我找到了一款令人惊艳的包,着包中包含了以下几种自定义操作:

  • 旋转图像
  • 缩小图像
  • 图像灰度处理
  • 将图像转换为 PDF
  • 将图像转换为任意格式
  • 合并 PDF 文件
  • 缩小 PDF 文件的大小
  • 创建符号链接
  • 显示校验和
  • 往图像的透明区域填充颜色
  • 加密/解密文件

首先,在你的系统中安装以下软件包:

sudo apt install make imagemagick gnupg2 m4 build-essential pandoc gettext python3-lxml ubuntu-dev-tools git

接着,克隆这个库并切换到这个新的目录:

git clone https://gitlab.com/nobodyinperson/thunar-custom-actions.git && cd thunar-custom-actions

然后,运行以下命令来确认所有的依赖项都已就绪:

./configure --prefix=$HOME/.local PASSWDFILE=/etc/passwd

如果提示你缺少某个包,那你可能需要手动安装它(但大部分情况下不需要)。

接着,运行以下命令从源代码建立这个包:

make

然后,运行以下命令安装这个包:

sudo make install

要将更改合并进 Thunar,使用以下命令:

uca-apply update

完成以上操作后,记得登出并重新登录以使改变生效。

这样你就会看到系统中增加了多种新的自定义操作:

如果你觉得这些太多了,你也可以通过选择它并点击减号按钮来去除不需要的操作。

只要你有足够的想象力,你就可以创造出无数的自定义操作。欢迎你分享你最喜欢的(和命令),这样新用户也能受益。

进一步自定义 Xfce

有人可能觉得 Xfce 显示得有些传统。实际上,你完全可以为它带来现代化的感受。

使 Xfce 看起来现代化和漂亮的四种方法

进行主题设计是最主要的自定义方式。这里有一些 Xfce 主题建议 你可以参考。

使 Xfce 看起来现代化和漂亮的 11 个主题

我希望你觉得这些关于 Thunar 的改进很有用。

你能在 Linux 桌面上尝试到更多的乐趣,不妨开始你的探索之旅吧 ?

(题图:MJ/0bd19051-a95f-41f8-839a-47c1ce84ac83)


via: https://itsfoss.com/thunar-tweaks/

作者:Sagar Sharma 选题:lujun9972 译者:ChatGPT 校对:wxy

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