ASLR 是如何保护 Linux 系统免受缓冲区溢出攻击的

# ASLR 是如何保护 Linux 系统免受缓冲区溢出攻击的

>
> 地址空间随机化（ASLR）是一种内存攻击缓解技术，可以用于 Linux 和 Windows 系统。了解一下如何运行它、启用/禁用它，以及它是如何工作的。
>
>
>

<img src="content_image/album_201903_05_224044mgokbgytvhhldo6i.jpg" alt="" title="">

<ruby> 地址空间随机化 <rt>  Address Space Layout Randomization </rt></ruby>（ASLR）是一种操作系统用来抵御缓冲区溢出攻击的内存保护机制。这种技术使得系统上运行的进程的内存地址无法被预测，使得与这些进程有关的漏洞变得更加难以利用。

ASLR 目前在 Linux、Windows 以及 MacOS 系统上都有使用。其最早出现在 2005 的 Linux 系统上。2007 年，这项技术被 Windows 和 MacOS 部署使用。尽管 ASLR 在各个系统上都提供相同的功能，却有着不同的实现。

ASLR 的有效性依赖于整个地址空间布局是否对于攻击者保持未知。此外，只有编译时作为<ruby> 位置无关可执行文件 <rt>  Position Independent Executable </rt></ruby>（PIE）的可执行程序才能得到 ASLR 技术的最大保护，因为只有这样，可执行文件的所有代码节区才会被加载在随机地址。PIE 机器码不管绝对地址是多少都可以正确执行。

### ASLR 的局限性

尽管 ASLR 使得对系统漏洞的利用更加困难了，但其保护系统的能力是有限的。理解关于 ASLR 的以下几点是很重要的：

* 它不能*解决*漏洞，而是增加利用漏洞的难度
* 并不追踪或报告漏洞
* 不能对编译时没有开启 ASLR 支持的二进制文件提供保护
* 不能避免被绕过

### ASLR 是如何工作的

通过对攻击者在进行缓冲区溢出攻击时所要用到的内存布局中的偏移做了随机化，ASLR 加大了攻击成功的难度，从而增强了系统的控制流完整性。

通常认为 ASLR 在 64 位系统上效果更好，因为 64 位系统提供了更大的熵（可随机的地址范围）。

### ASLR 是否正在你的 Linux 系统上运行？

下面展示的两条命令都可以告诉你的系统是否启用了 ASLR 功能：

```
$ cat /proc/sys/kernel/randomize_va_space
2
$ sysctl -a --pattern randomize
kernel.randomize_va_space = 2
```

上方指令结果中的数值（`2`）表示 ASLR 工作在全随机化模式。其可能为下面的几个数值之一：

```
0 = Disabled
1 = Conservative Randomization
2 = Full Randomization
```

如果你关闭了 ASLR 并且执行下面的指令，你将会注意到前后两条 `ldd` 的输出是完全一样的。`ldd` 命令会加载共享对象并显示它们在内存中的地址。

```
$ sudo sysctl -w kernel.randomize_va_space=0    <== disable
[sudo] password for shs:
kernel.randomize_va_space = 0
$ ldd /bin/bash
        linux-vdso.so.1 (0x00007ffff7fd1000) <== same addresses
        libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007ffff7c69000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ffff7c63000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7a79000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ffff7fd3000)
$ ldd /bin/bash
        linux-vdso.so.1 (0x00007ffff7fd1000) <== same addresses
        libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007ffff7c69000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ffff7c63000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7a79000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ffff7fd3000)
```

如果将其重新设置为 `2` 来启用 ASLR，你将会看到每次运行 `ldd`，得到的内存地址都不相同。

```
$ sudo sysctl -w kernel.randomize_va_space=2    <== enable
[sudo] password for shs:
kernel.randomize_va_space = 2
$ ldd /bin/bash
        linux-vdso.so.1 (0x00007fff47d0e000) <== first set of addresses
        libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f1cb7ce0000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f1cb7cda000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1cb7af0000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f1cb8045000)
$ ldd /bin/bash
        linux-vdso.so.1 (0x00007ffe1cbd7000) <== second set of addresses
        libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007fed59742000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fed5973c000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fed59552000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fed59aa7000)
```

### 尝试绕过 ASLR

尽管这项技术有很多优点，但绕过 ASLR 的攻击并不罕见，主要有以下几类：

* 利用地址泄露
* 访问与特定地址关联的数据
* 针对 ASLR 实现的缺陷来猜测地址，常见于系统熵过低或 ASLR 实现不完善。
* 利用侧信道攻击

### 总结

ASLR 有很大的价值，尤其是在 64 位系统上运行并被正确实现时。虽然不能避免被绕过，但这项技术的确使得利用系统漏洞变得更加困难了。这份参考资料可以提供 [在 64 位 Linux 系统上的完全 ASLR 的有效性](https://cybersecurity.upv.es/attacks/offset2lib/offset2lib-paper.pdf) 的更多有关细节，这篇论文介绍了一种利用分支预测 [绕过 ASLR](http://www.cs.ucr.edu/%7Enael/pubs/micro16.pdf) 的技术。

---

via: <https://www.networkworld.com/article/3331199/linux/what-does-aslr-do-for-linux.html>

作者：[Sandra Henry-Stocker](https://www.networkworld.com/author/Sandra-Henry_Stocker/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[leommxj](https://github.com/leommxj) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Bash-Insulter：一个在输入错误命令时嘲讽用户的脚本

Summary: 这是一个非常有趣的脚本，每当用户在终端输入错误的命令时，它都会责骂用户。

这是一个非常有趣的脚本，每当用户在终端输入错误的命令时，它都会责骂用户。

# Bash-Insulter：一个在输入错误命令时嘲讽用户的脚本

这是一个非常有趣的脚本，每当用户在终端输入错误的命令时，它都会嘲讽用户。

它让你在解决一些问题时会感到快乐。有的人在受到终端嘲讽的时候感到不愉快。但是，当我受到终端的批评时，我真的很开心。

这是一个有趣的 CLI 工具，在你弄错的时候，会用随机短语嘲讽你。此外，它允许你添加自己的短语。

### 如何在 Linux 上安装 Bash-Insulter?

在安装 Bash-Insulter 之前，请确保你的系统上安装了 git。如果没有，请使用以下命令安装它。

对于 Fedora 系统, 请使用 [DNF 命令](https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/) 安装 git。

```
$ sudo dnf install git
```

对于 Debian/Ubuntu 系统，请使用 [APT-GET 命令](https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/) 或者 [APT 命令](https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/) 安装 git。

```
$ sudo apt install git
```

对于基于 Arch Linux 的系统，请使用 [Pacman 命令](https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/) 安装 git。

```
$ sudo pacman -S git
```

对于 RHEL/CentOS 系统，请使用 [YUM 命令](https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/) 安装 git。

```
$ sudo yum install git
```

对于 openSUSE Leap 系统，请使用 [Zypper 命令](https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/) 安装 git。

```
$ sudo zypper install git
```

我们可以通过<ruby> 克隆 <rt>  clone </rt></ruby>开发人员的 GitHub 存储库轻松地安装它。

首先克隆 Bash-insulter 存储库。

```
$ git clone https://github.com/hkbakke/bash-insulter.git bash-insulter
```

将下载的文件移动到文件夹 `/etc` 下。

```
$ sudo cp bash-insulter/src/bash.command-not-found /etc/
```

将下面的代码添加到 `/etc/bash.bashrc` 文件中。

```
$ vi /etc/bash.bashrc

#Bash Insulter
if [ -f /etc/bash.command-not-found ]; then
 . /etc/bash.command-not-found
fi
```

运行以下命令使更改生效。

```
$ sudo source /etc/bash.bashrc
```

你想测试一下安装是否生效吗？你可以试试在终端上输入一些错误的命令，看看它如何嘲讽你。

```
$ unam -a

$ pin 2daygeek.com
```

如果你想附加你自己的短语，则导航到以下文件并更新它。你可以在 `messages` 部分中添加短语。

```
# vi /etc/bash.command-not-found

print_message () {

    local messages
    local message

    messages=(
        "Boooo!"
        "Don't you know anything?"
        "RTFM!"
        "Haha, n00b!"
        "Wow! That was impressively wrong!"
        "Pathetic"
        "The worst one today!"
        "n00b alert!"
        "Your application for reduced salary has been sent!"
        "lol"
        "u suk"
        "lol... plz"
        "plz uninstall"
        "And the Darwin Award goes to.... ${USER}!"
        "ERROR_INCOMPETENT_USER"
        "Incompetence is also a form of competence"
        "Bad."
        "Fake it till you make it!"
        "What is this...? Amateur hour!?"
        "Come on! You can do it!"
        "Nice try."
        "What if... you type an actual command the next time!"
        "What if I told you... it is possible to type valid commands."
        "Y u no speak computer???"
        "This is not Windows"
        "Perhaps you should leave the command line alone..."
        "Please step away from the keyboard!"
        "error code: 1D10T"
        "ACHTUNG! ALLES TURISTEN UND NONTEKNISCHEN LOOKENPEEPERS! DAS KOMPUTERMASCHINE IST NICHT FÜR DER GEFINGERPOKEN UND MITTENGRABEN! ODERWISE IST EASY TO SCHNAPPEN DER SPRINGENWERK, BLOWENFUSEN UND POPPENCORKEN MIT SPITZENSPARKEN. IST NICHT FÜR GEWERKEN BEI DUMMKOPFEN. DER RUBBERNECKEN SIGHTSEEREN KEEPEN DAS COTTONPICKEN HÄNDER IN DAS POCKETS MUSS. ZO RELAXEN UND WATSCHEN DER BLINKENLICHTEN."
        "Pro tip: type a valid command!"
        "Go outside."
        "This is not a search engine."
        "(╯°□°）╯︵ ┻━┻"
        "¯\_(ツ)_/¯"
        "So, I'm just going to go ahead and run rm -rf / for you."
        "Why are you so stupid?!"
        "Perhaps computers is not for you..."
        "Why are you doing this to me?!"
        "Don't you have anything better to do?!"
        "I am _seriously_ considering 'rm -rf /'-ing myself..."
        "This is why you get to see your children only once a month."
        "This is why nobody likes you."
        "Are you even trying?!"
        "Try using your brain the next time!"
        "My keyboard is not a touch screen!"
        "Commands, random gibberish, who cares!"
        "Typing incorrect commands, eh?"
        "Are you always this stupid or are you making a special effort today?!"
        "Dropped on your head as a baby, eh?"
        "Brains aren't everything. In your case they're nothing."
        "I don't know what makes you so stupid, but it really works."
        "You are not as bad as people say, you are much, much worse."
        "Two wrongs don't make a right, take your parents as an example."
        "You must have been born on a highway because that's where most accidents happen."
        "If what you don't know can't hurt you, you're invulnerable."
        "If ignorance is bliss, you must be the happiest person on earth."
        "You're proof that god has a sense of humor."
        "Keep trying, someday you'll do something intelligent!"
        "If shit was music, you'd be an orchestra."
        "How many times do I have to flush before you go away?"
    )
```

---

via: <https://www.2daygeek.com/bash-insulter-insults-the-user-when-typing-wrong-command/>

作者：[Magesh Maruthamuthu](https://www.2daygeek.com/author/magesh/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[zero-mk](https://github.com/zero-mk) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


正则表达式的分组和数字

# 正则表达式的分组和数字

大约一周前，我在编辑一个程序时想要更改一些变量名。我之前认为这将是一个简单的正则表达式查找/替换。只是这没有我想象的那么简单。

变量名为 `a10`、`v10` 和 `x10`，我想分别将它们改为 `a30`、`v30` 和 `x30`。我想到使用 BBEdit 的查找窗口并输入：

我不能简单地 `30` 替换为 `10`，因为代码中有一些与变量无关的数字 `10`。我认为我很聪明，所以我不想写三个非正则表达式替换，`a10`、`v10` 和 `x10`，每个一个。但是我却没有注意到替换模式中的蓝色。如果我这样做了，我会看到 BBEdit 将我的替换模式解释为“匹配组 13，后面跟着 `0`，而不是”匹配组 1，后面跟着 `30`，后者是我想要的。由于匹配组 13 是空白的，因此所有变量名都会被替换为 `0`。

你看，BBEdit 可以在搜索模式中匹配多达 99 个分组，严格来说，我们应该在替换模式中引用它们时使用两位数字。但在大多数情况下，我们可以使用 `\1` 到 `\9` 而不是 `\01` 到 `\09`，因为这没有歧义。换句话说，如果我尝试将 `a10`、`v10` 和 `x10` 更改为 `az`、`vz` 和 `xz`，那么使用 `\1z`的替换模式就可以了。因为后面的 `z` 意味着不会误解释该模式中 `\1`。

因此，在撤消替换后，我将模式更改为这样：

它可以正常工作。

还有另一个选择：命名组。这是使用 `var` 作为模式名称：

我从来都没有使用过命名组，无论正则表达式是在文本编辑器还是在脚本中。我的总体感觉是，如果模式复杂到我必须使用变量来跟踪所有组，那么我应该停下来并将问题分解为更小的部分。

顺便说一下，你可能已经听说 BBEdit 正在庆祝它诞生[25周年](https://merch.barebones.com/)。当一个有良好文档的应用有如此长的历史时，手册的积累能让人愉快地回到过去的日子。当我在 BBEdit 手册中查找命名组的表示法时，我遇到了这个说明：

BBEdit 目前的版本是 12.5。第一次出现于 2001 年的 V6.5。但手册希望确保长期客户（我记得是在 V4 的时候第一次购买）不会因行为变化而感到困惑，即使这些变化几乎发生在二十年前。

---

via: <https://leancrew.com/all-this/2019/02/regex-groups-and-numerals/>

作者：[Dr.Drang](https://leancrew.com) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何查看 Linux 下 CPU、内存和交换分区的占用率？

# 如何查看 Linux 下 CPU、内存和交换分区的占用率？

<img src="content_image/album_201903_06_210413dczuxbzypax5h7au.jpg" alt="" title="">

在 Linux 下有很多可以用来查看内存占用情况的命令和选项，但是我并没有看见关于内存占用率的更多的信息。

在大多数情况下我们只想查看内存使用情况，并没有考虑占用的百分比究竟是多少。如果你想要了解这些信息，那你看这篇文章就对了。我们将会详细地在这里帮助你解决这个问题。

这篇教程将会帮助你在面对 Linux 服务器下频繁的内存高占用情况时，确定内存使用情况。

而在同时，如果你使用的是 `free -m` 或者 `free -g`，占用情况描述地也并不是十分清楚。

这些格式化命令属于 Linux 高级命令。它将会对 Linux 专家和中等水平 Linux 使用者非常有用。

### 方法-1：如何查看 Linux 下内存占用率？

我们可以使用下面命令的组合来达到此目的。在该方法中，我们使用的是 `free` 和 `awk` 命令的组合来获取内存占用率。

如果你正在寻找其他有关于内存的文章，你可以导航到如下链接。这些文章有 [free 命令](https://www.2daygeek.com/free-command-to-check-memory-usage-statistics-in-linux/)、[smem 命令](https://www.2daygeek.com/smem-linux-memory-usage-statistics-reporting-tool/)、[ps\_mem 命令](https://www.2daygeek.com/ps_mem-report-core-memory-usage-accurately-in-linux/)、[vmstat 命令](https://www.2daygeek.com/linux-vmstat-command-examples-tool-report-virtual-memory-statistics/) 及 [查看物理内存大小的多种方式](https://www.2daygeek.com/easy-ways-to-check-size-of-physical-memory-ram-in-linux/)。

要获取不包含百分比符号的内存占用率：

```
$ free -t | awk 'NR == 2 {print "Current Memory Utilization is : " $3/$2*100}'
或
$ free -t | awk 'FNR == 2 {print "Current Memory Utilization is : " $3/$2*100}'

Current Memory Utilization is : 20.4194
```

要获取不包含百分比符号的交换分区占用率：

```
$ free -t | awk 'NR == 3 {print "Current Swap Utilization is : " $3/$2*100}'
或
$ free -t | awk 'FNR == 3 {print "Current Swap Utilization is : " $3/$2*100}'

Current Swap Utilization is : 0
```

要获取包含百分比符号及保留两位小数的内存占用率：

```
$ free -t | awk 'NR == 2 {printf("Current Memory Utilization is : %.2f%"), $3/$2*100}'
或
$ free -t | awk 'FNR == 2 {printf("Current Memory Utilization is : %.2f%"), $3/$2*100}'

Current Memory Utilization is : 20.42%
```

要获取包含百分比符号及保留两位小数的交换分区占用率：

```
$ free -t | awk 'NR == 3 {printf("Current Swap Utilization is : %.2f%"), $3/$2*100}'
或
$ free -t | awk 'FNR == 3 {printf("Current Swap Utilization is : %.2f%"), $3/$2*100}'

Current Swap Utilization is : 0.00%
```

如果你正在寻找有关于交换分区的其他文章，你可以导航至如下链接。这些链接有 [使用 LVM（逻辑盘卷管理）创建和扩展交换分区](https://www.2daygeek.com/how-to-create-extend-swap-partition-in-linux-using-lvm/)，[创建或扩展交换分区的多种方式](https://www.2daygeek.com/add-extend-increase-swap-space-memory-file-partition-linux/) 和 [创建/删除和挂载交换分区文件的多种方式](https://www.2daygeek.com/shell-script-create-add-extend-swap-space-linux/)。

键入 `free` 命令会更好地作出阐释：

```
$ free
              total        used        free      shared  buff/cache   available
Mem:          15867        3730        9868        1189        2269       10640
Swap:         17454           0       17454
Total:        33322        3730       27322
```

细节如下：

* `free`：是一个标准命令，用于在 Linux 下查看内存使用情况。
* `awk`：是一个专门用来做文本数据处理的强大命令。
* `FNR == 2`：该命令给出了每一个输入文件的行数。其基本上用于挑选出给定的行（针对于这里，它选择的是行号为 2 的行）
* `NR == 2`：该命令给出了处理的行总数。其基本上用于过滤给出的行（针对于这里，它选择的是行号为 2 的行）
* `$3/$2*100`：该命令将列 3 除以列 2 并将结果乘以 100。
* `printf`：该命令用于格式化和打印数据。
* `%.2f%`：默认情况下，其打印小数点后保留 6 位的浮点数。使用后跟的格式来约束小数位。

### 方法-2：如何查看 Linux 下内存占用率？

我们可以使用下面命令的组合来达到此目的。在这种方法中，我们使用 `free`、`grep` 和 `awk` 命令的组合来获取内存占用率。

要获取不包含百分比符号的内存占用率：

```
$ free -t | grep Mem | awk '{print "Current Memory Utilization is : " $3/$2*100}'
Current Memory Utilization is : 20.4228
```

要获取不包含百分比符号的交换分区占用率：

```
$ free -t | grep Swap | awk '{print "Current Swap Utilization is : " $3/$2*100}'
Current Swap Utilization is : 0
```

要获取包含百分比符号及保留两位小数的内存占用率：

```
$ free -t | grep Mem | awk '{printf("Current Memory Utilization is : %.2f%"), $3/$2*100}'
Current Memory Utilization is : 20.43%
```

要获取包含百分比符号及保留两位小数的交换空间占用率：

```
$ free -t | grep Swap | awk '{printf("Current Swap Utilization is : %.2f%"), $3/$2*100}'
Current Swap Utilization is : 0.00%
```

### 方法-1：如何查看 Linux 下 CPU 的占用率？

我们可以使用如下命令的组合来达到此目的。在这种方法中，我们使用 `top`、`print` 和 `awk` 命令的组合来获取 CPU 的占用率。

如果你正在寻找其他有关于 CPU（LCTT 译注：原文误为 memory）的文章，你可以导航至如下链接。这些文章有 [top 命令](https://www.2daygeek.com/linux-top-command-linux-system-performance-monitoring-tool/)、[htop 命令](https://www.2daygeek.com/linux-htop-command-linux-system-performance-resource-monitoring-tool/)、[atop 命令](https://www.2daygeek.com/atop-system-process-performance-monitoring-tool/) 及 [Glances 命令](https://www.2daygeek.com/install-glances-advanced-real-time-linux-system-performance-monitoring-tool-on-centos-fedora-ubuntu-debian-opensuse-arch-linux/)。

如果在输出中展示的是多个 CPU 的情况，那么你需要使用下面的方法。

```
$ top -b -n1 | grep ^%Cpu
%Cpu0  :  5.3 us,  0.0 sy,  0.0 ni, 94.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :  0.0 us,  0.0 sy,  0.0 ni, 94.7 id,  0.0 wa,  0.0 hi,  5.3 si,  0.0 st
%Cpu3  :  5.3 us,  0.0 sy,  0.0 ni, 94.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu4  : 10.5 us, 15.8 sy,  0.0 ni, 73.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu5  :  0.0 us,  5.0 sy,  0.0 ni, 95.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu6  :  5.3 us,  0.0 sy,  0.0 ni, 94.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu7  :  5.3 us,  0.0 sy,  0.0 ni, 94.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
```

要获取不包含百分比符号的 CPU 占用率：

```
$ top -b -n1 | grep ^%Cpu | awk '{cpu+=$9}END{print "Current CPU Utilization is : " 100-cpu/NR}'
Current CPU Utilization is : 21.05
```

要获取包含百分比符号及保留两位小数的 CPU 占用率：

```
$ top -b -n1 | grep ^%Cpu | awk '{cpu+=$9}END{printf("Current CPU Utilization is : %.2f%"), 100-cpu/NR}'
Current CPU Utilization is : 14.81%
```

### 方法-2：如何查看 Linux 下 CPU 的占用率？

我们可以使用如下命令的组合来达到此目的。在这种方法中，我们使用的是 `top`、`print`/`printf` 和 `awk` 命令的组合来获取 CPU 的占用率。

如果在单个输出中一起展示了所有的 CPU 的情况，那么你需要使用下面的方法。

```
$ top -b -n1 | grep ^%Cpu
%Cpu(s): 15.3 us, 7.2 sy, 0.8 ni, 69.0 id, 6.7 wa, 0.0 hi, 1.0 si, 0.0 st
```

要获取不包含百分比符号的 CPU 占用率：

```
$ top -b -n1 | grep ^%Cpu | awk '{print "Current CPU Utilization is : " 100-$8}'
Current CPU Utilization is : 5.6
```

要获取包含百分比符号及保留两位小数的 CPU 占用率：

```
$ top -b -n1 | grep ^%Cpu | awk '{printf("Current CPU Utilization is : %.2f%"), 100-$8}'
Current CPU Utilization is : 5.40%
```

如下是一些细节：

* `top`：是一种用于查看当前 Linux 系统下正在运行的进程的非常好的命令。
* `-b`：选项允许 `top` 命令切换至批处理的模式。当你从本地系统运行 `top` 命令至远程系统时，它将会非常有用。
* `-n1`：迭代次数。
* `^%Cpu`：过滤以 `%CPU` 开头的行。
* `awk`：是一种专门用来做文本数据处理的强大命令。
* `cpu+=$9`：对于每一行，将第 9 列添加至变量 `cpu`。
* `printf`：该命令用于格式化和打印数据。
* `%.2f%`：默认情况下，它打印小数点后保留 6 位的浮点数。使用后跟的格式来限制小数位数。
* `100-cpu/NR`：最终打印出 CPU 平均占用率，即用 100 减去其并除以行数。

---

via: <https://www.2daygeek.com/linux-check-cpu-memory-swap-utilization-percentage/>

作者：[Vinoth Kumar](https://www.2daygeek.com/author/vinoth/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[An-DJ](https://github.com/An-DJ) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Bash 中的逻辑和（&）

Summary: 在 Bash 中，你可以使用 & 作为 AND（逻辑和）操作符。

在 Bash 中，你可以使用 & 作为 AND（逻辑和）操作符。

# Bash 中的逻辑和（&）

>
> 在 Bash 中，你可以使用 & 作为 AND（逻辑和）操作符。
>
>
>

<img src="content_image/album_201903_07_214019pennq1inqe8wwzi2.jpg" alt="" title="">

有人可能会认为两篇文章中的 `&` 意思差不多，但实际上并不是。虽然 [第一篇文章讨论了如何在命令末尾使用 & 来将命令转到后台运行](/article-10587-1.html)，在之后剖析了流程管理，第二篇文章将  [& 看作引用文件描述符的方法](/article-10591-1.html)，这些文章让我们知道了，与 `<` 和 `>` 结合使用后，你可以将输入或输出引导到别的地方。

但我们还没接触过作为 AND 操作符使用的 `&`。所以，让我们来看看。

### & 是一个按位运算符

如果你十分熟悉二进制数操作，你肯定听说过 AND 和 OR 。这些是按位操作，对二进制数的各个位进行操作。在 Bash 中，使用 `&` 作为 AND 运算符，使用 `|` 作为 OR 运算符：

**AND**：

```
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
```

**OR**：

```
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
```

你可以通过对任何两个数字进行 AND 运算并使用 `echo` 输出结果：

```
$ echo $(( 2 & 3 )) # 00000010 AND 00000011 = 00000010
2
$ echo $(( 120 & 97 )) # 01111000 AND 01100001 = 01100000
96
```

OR（`|`）也是如此：

```
$ echo $(( 2 | 3 )) # 00000010 OR 00000011 = 00000011
3
$ echo $(( 120 | 97 )) # 01111000 OR 01100001 = 01111001
121
```

说明：

1. 使用 `(( ... ))` 告诉 Bash 双括号之间的内容是某种算术或逻辑运算。`(( 2 + 2 ))`、 `(( 5 % 2 ))` （`%` 是[求模](https://en.wikipedia.org/wiki/Modulo_operation)运算符）和 `((( 5 % 2 ) + 1))`（等于 3）都可以工作。
2. [像变量一样](https://www.linux.com/blog/learn/2018/12/bash-variables-environmental-and-otherwise)，使用 `$` 提取值，以便你可以使用它。
3. 空格并没有影响：`((2+3))` 等价于 `(( 2+3 ))` 和 `(( 2 + 3 ))`。
4. Bash 只能对整数进行操作。试试这样做： `(( 5 / 2 ))` ，你会得到 `2`；或者这样 `(( 2.5 & 7 ))` ，但会得到一个错误。然后，在按位操作中使用除了整数之外的任何东西（这就是我们现在所讨论的）通常是你不应该做的事情。

**提示：** 如果你想看看十进制数字在二进制下会是什么样子，你可以使用 `bc` ，这是一个大多数 Linux 发行版都预装了的命令行计算器。比如：

```
bc <<< "obase=2; 97"
```

这个操作将会把 `97` 转换成十二进制（`obase` 中的 `o` 代表 “output” ，也即，“输出”）。

```
bc <<< "ibase=2; 11001011"
```

这个操作将会把 `11001011` 转换成十进制（`ibase` 中的 `i` 代表 “input”，也即，“输入”）。

### && 是一个逻辑运算符

虽然它使用与其按位表达相同的逻辑原理，但 Bash 的 `&&` 运算符只能呈现两个结果：`1`（“真值”）和`0`（“假值”）。对于 Bash 来说，任何不是 `0` 的数字都是 “真值”，任何等于 `0` 的数字都是 “假值”。什么也是 “假值”同时也不是数字呢：

```
$ echo $(( 4 && 5 )) # 两个非零数字，两个为 true = true
1
$ echo $(( 0 && 5 )) # 有一个为零，一个为 false = false
0
$ echo $(( b && 5 )) # 其中一个不是数字，一个为 false = false
0
```

与 `&&` 类似， OR 对应着 `||` ，用法正如你想的那样。

以上这些都很简单……直到它用在命令的退出状态时。

### && 是命令退出状态的逻辑运算符

[正如我们在之前的文章中看到的](/article-10591-1.html)，当命令运行时，它会输出错误消息。更重要的是，对于今天的讨论，它在结束时也会输出一个数字。此数字称为“返回码”，如果为 0，则表示该命令在执行期间未遇到任何问题。如果是任何其他数字，即使命令完成，也意味着某些地方出错了。

所以 0 意味着是好的，任何其他数字都说明有问题发生，并且，在返回码的上下文中，0 意味着“真”，其他任何数字都意味着“假”。对！这 **与你所熟知的逻辑操作完全相反** ，但是你能用这个做什么？ 不同的背景，不同的规则。这种用处很快就会显现出来。

让我们继续！

返回码 *临时* 储存在 [特殊变量](https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html) `?` 中 —— 是的，我知道：这又是一个令人迷惑的选择。但不管怎样，[别忘了我们在讨论变量的文章中说过](https://www.linux.com/blog/learn/2018/12/bash-variables-environmental-and-otherwise)，那时我们说你要用 `$` 符号来读取变量中的值，在这里也一样。所以，如果你想知道一个命令是否顺利运行，你需要在命令结束后，在运行别的命令之前马上用 `$?` 来读取 `?` 变量的值。

试试下面的命令：

```
$ find /etc -iname "*.service"
find: '/etc/audisp/plugins.d': Permission denied
/etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service
/etc/systemd/system/dbus-org.freedesktop.ModemManager1.service
[......]
```

[正如你在上一篇文章中看到的一样](/article-10591-1.html)，普通用户权限在 `/etc` 下运行 `find` 通常将抛出错误，因为它试图读取你没有权限访问的子目录。

所以，如果你在执行 `find` 后立马执行……

```
echo $?
```

……，它将打印 `1`，表明存在错误。

（注意：当你在一行中运行两遍 `echo $?` ，你将得到一个 `0` 。这是因为 `$?` 将包含第一个 `echo $?` 的返回码，而这条命令按理说一定会执行成功。所以学习如何使用 `$?` 的第一课就是： **单独执行 `$?`** 或者将它保存在别的安全的地方 —— 比如保存在一个变量里，不然你会很快丢失它。）

一个直接使用 `?` 变量的用法是将它并入一串链式命令列表，这样 Bash 运行这串命令时若有任何操作失败，后面命令将终止。例如，你可能熟悉构建和编译应用程序源代码的过程。你可以像这样手动一个接一个地运行它们：

```
$ configure
.
.
.
$ make
.
.
.
$ make install
.
.
.
```

你也可以把这三行合并成一行……

```
$ configure; make; make install
```

…… 但你要希望上天保佑。

为什么这样说呢？因为你这样做是有缺点的，比方说 `configure` 执行失败了， Bash 将仍会尝试执行 `make` 和 `sudo make install`——就算没东西可 `make` ，实际上，是没东西会安装。

聪明一点的做法是：

```
$ configure && make && make install
```

这将从每个命令中获取退出码，并将其用作链式 `&&` 操作的操作数。

但是，没什么好抱怨的，Bash 知道如果 `configure` 返回非零结果，整个过程都会失败。如果发生这种情况，不必运行 `make` 来检查它的退出代码，因为无论如何都会失败的。因此，它放弃运行 `make`，只是将非零结果传递给下一步操作。并且，由于 `configure && make` 传递了错误，Bash 也不必运行`make install`。这意味着，在一长串命令中，你可以使用 `&&` 连接它们，并且一旦失败，你可以节省时间，因为其他命令会立即被取消运行。

你可以类似地使用 `||`，OR 逻辑操作符，这样就算只有一部分命令成功执行，Bash 也能运行接下来链接在一起的命令。

鉴于所有这些（以及我们之前介绍过的内容），你现在应该更清楚地了解我们在 [这篇文章开头](/article-10587-1.html) 出现的命令行：

```
mkdir test_dir 2>/dev/null || touch backup/dir/images.txt && find . -iname "*jpg" > backup/dir/images.txt &
```

因此，假设你从具有读写权限的目录运行上述内容，它做了什么以及如何做到这一点？它如何避免不合时宜且可能导致执行中断的错误？下周，除了给你这些答案的结果，我们将讨论圆括号，不要错过了哟！

---

via: <https://www.linux.com/blog/learn/2019/2/logical-ampersand-bash>

作者：[Paul Brown](https://www.linux.com/users/bro66) 选题：[lujun9972](https://github.com/lujun9972) 译者：[zero-MK](https://github.com/zero-mk) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


《贡献者许可协议》是“人魔”般的怪物？

Summary: 非标准的开源贡献者许可协议正在创造类似电影《冲出人魔岛》中“人魔”般的怪物。

非标准的开源贡献者许可协议正在创造类似电影《冲出人魔岛》中“人魔”般的怪物。

# 《贡献者许可协议》是“人魔”般的怪物？

>
> 非标准的开源贡献者许可协议正在创造类似电影《冲出人魔岛》中“人魔”般的怪物。
>
>
>

当我开启作为开源律师的职业生涯时，面临的一个重要问题是需要耗时费力去分析的新形式开源许可协议的激增，正如我的同事 Scott Peterson 在其文章中所述，“[开源许可协议是共享资源](https://opensource.com/law/16/11/licenses-are-shared-resources)”：

>
> 专注于少数许可协议更有好处。通过对少数许可协议达成更广泛共识所积累的经验和讨论更容易减少不确定性，而不是在成千上百的许可协议之间进行有关行动和辩论。
>
>
>

过去多年开源社区对许可协议扩散的反应持积极态度，我很高兴看到大多数开源项目都从一组被工程师和律师熟知的许可协议（例如 GPL、LGPL、AGPL、BSD、MIT、Apache 2）中进行选择。因此，不用将时间浪费在解释许可协议条款上，完全开启了一个低摩擦的生态系统。

一旦项目采用开源许可协议，它通常采用标准的“<ruby> 入站=出站 <rp>  （ </rp> <rt>  inbound=outbound </rt> <rp>  ） </rp></ruby>”模式，创造该短语的 Richard Fontana 将其描述为贡献者不言自明地获得出站项目适用的许可协议的许可，使得贡献者可以轻松参与项目，[无需担心繁文缛节和受到威胁](https://opensource.com/law/11/7/trouble-harmony-part-1)。这是一个非常简单的模式，能够非常聪明地进行上面提到的许可协议选择。

不幸的是，许多项目不选择采用“入站=出站”模式，而是采用某种形式的《<ruby> 贡献者许可协议 <rp>  （ </rp> <rt>  Contributor License Agreement </rt> <rp>  ） </rp></ruby>》（CLA）。CLA 的范围和目的各不相同。读者们可以在 Ben Cotton 的文章《[CLA 与 DCO 有什么不同？](https://opensource.com/article/18/3/cla-vs-dco-whats-difference)》中具体了解《贡献者许可协议》与《<ruby> 开发者原创证书 <rp>  （ </rp> <rt>  Developer Certificates of Origin </rt> <rp>  ） </rp></ruby>》（DCO）的区别。

采用 CLA 的项目在接受贡献之前，要求贡献者提交作为个人或所在公司签署的 CLA 存档。除非是其条款能够被工程师和其代理律师很好理解的标准 CLA（例如下文提到的 Apache 软件基金会非实质性定制的 CLA），否则因为需要非常仔细阅读 CLA 以确保能够完全理解其条款，贡献者通常放弃去深究 CLA。理解非标准 CLA 的过程需要数天或上周才能完成，具体取决于工作负荷以及是否需要与许可协议提交人进行协商。根据我的经验，最终结果是回到标准的 CLA 条款！这个曲折的过程导致大量的时间和精力被浪费。此外，CLA 需要某种形式的签名，增添了许多在大型官僚组织可能更严重的延迟性和复杂性。这并不是一条令人开心的路径，对开源/协作开发模式具有高度侵蚀性。

*请注意，当我提到“标准 CLA”时，我所指的是基于众所周知 CLA（例如 Apache 软件基金会个人或企业 CLA）的 CLA。虽然 Apache 软件基金会的 CLA 由基金会本身以其原始形式使用，但它们通常被以非实质性方式进行修改以供其他组织使用。例如，大多数组织在开始时都小心翼翼地摆脱了许可协议的慈善使命，还定制了许可协议名称。Apache 软件基金会这类非实质性变体需要与本文中描述的类似“人魔”怪物的变体区分开。*

我对最近 CLA 数量的激增表示担忧，我们似乎正在经历十年前在开源许可协议扩散方面遇到的相同现象。事实上，在过去的一年里，在我的办公桌上至少看到 20 种不同的 CLA，它们与常见的 Apache 软件基金会个人或企业 CLA 存在细微但实质性的偏差。这些偏差通常小到无助于澄清条款语言或权利，但其中有些偏差会比较大，这种混合的可憎之物让我想起了 Moreau 博士通过他的活体解剖过程创造的新动物（参见维基百科上的《[冲出人魔岛](https://en.wikipedia.org/wiki/The_Island_of_Doctor_Moreau)》）。无论偏差是小还是大，它们造成的影响可能很大，经常导致混淆、更多的审查时间以及谈判。

例如，律师普遍接受的做法是对许可协议或合同中的术语使用初始定义。无意中使用同一术语的小写形式会导致是否应该使用该术语在标准/字典中定义或协议中更窄或更宽泛定义的模糊性。尽管这对于不经意的观察者来说似乎是一个微不足道的偏差，但这通常会导致许可协议接收/授予的权限显著减少或扩大，或者导致不可接受的歧义。其他偏差起草得如此之差，以至于它们的意义不明确，因此必须彻底拒绝。

在最近的例子中，有一种 CLA 的专利许可语言以令人困惑的方式将术语<ruby> “衍生作品” <rp>  （ </rp> <rt>  derivative works </rt> <rp>  ） </rp></ruby>包括在内，偏离了 Apache 软件基金会 CLA 版本。此 CLA 授予专利许可的范围似乎过于宽泛且可能无限制，它是如此模糊，以至于我被迫拒绝使用它。我不确定这是否是这个特定 CLA 的起草人所预期的结果，但是这次审查花费了大量的时间和成本，最终限制了我们的工程师为该项目做出贡献。这是一个令人伤心的结果，没有人从中受益。

作为一个社区，让我们从之前关于开源许可协议扩散的错误中吸取教训，采用“入站=出站”模式，最好使用 [DCO](http://developercertificate.org/) 而不是 CLA。如果您选择使用 CLA，那么强烈建议使用 Apache 软件基金会个人或企业 CLA 等标准 CLA，而不是创建新的、幻想的或荒谬的类似“人魔”怪物的许可协议。

作者简介：Jeffrey R. Kaufman 是全球领先的开源软件解决方案供应商 Red Hat 公司的开源知识产权律师，还担任<ruby> 托马斯杰斐逊法学院 <rp>  （ </rp> <rt>  Thomas Jefferson School of Law </rt> <rp>  ） </rp></ruby>的兼职教授。在任职 Red Hat 之前，Jeffrey 曾担任<ruby> 高通公司 <rp>  （ </rp> <rt>  Qualcomm Incorporated </rt> <rp>  ） </rp></ruby>的专利顾问，为<ruby> 首席科学家办公室 <rp>  （ </rp> <rt>  Office of the Chief Scientist </rt> <rp>  ） </rp></ruby>提供开源事务咨询。

译者简介：薛亮，集慧智佳知识产权咨询公司总监，擅长专利检索、专利分析、竞争对手跟踪、FTO 分析、开源软件知识产权风险分析，致力于为互联网企业、高科技公司提供知识产权咨询服务。


Ansible 入门秘诀

Summary: 用 Ansible 自动化你的数据中心的关键点。

用 Ansible 自动化你的数据中心的关键点。

# Ansible 入门秘诀

>
> 用 Ansible 自动化你的数据中心的关键点。
>
>
>

Ansible 是一个开源自动化工具，可以从中央控制节点统一配置服务器、安装软件或执行各种 IT 任务。它采用一对多、<ruby> 无客户端 <rt>  agentless </rt></ruby>的机制，从控制节点上通过 SSH 发送指令给远端的客户机来完成任务（当然除了 SSH 外也可以用别的协议）。

Ansible 的主要使用群体是系统管理员，他们经常会周期性地执行一些安装、配置应用的工作。尽管如此，一些非特权用户也可以使用 Ansible，例如数据库管理员就可以通过 Ansible 用 `mysql` 这个用户来创建数据库、添加数据库用户、定义访问权限等。

让我们来看一个简单的使用场景，一位系统管理员每天要配置 100 台服务器，并且必须在每台机器上执行一系列 Bash 命令，然后交付给用户。

这是个简单的例子，但应该能够证明：在 yaml 文件里写好命令然后在远程服务器上运行，是一件非常轻松的事。而且如果运行环境不同，就可以加入判断条件，指明某些命令只能在特定的服务器上运行（如：只在那些不是 Ubuntu 或 Debian 的系统上运行 `yum` 命令）。

Ansible 的一个重要特性是用<ruby> 剧本 <rt>  playbook </rt></ruby>来描述一个计算机系统的最终状态，所以一个剧本可以在服务器上反复执行而不影响其最终状态（LCTT 译注：即是幂等的）。如果某个任务已经被实施过了（如，“用户 `sysman` 已经存在”），那么 Ansible 就会忽略它继续执行后续的任务。

### 定义

* <ruby> 任务 <rt>  task </rt></ruby>：是工作的最小单位，它可以是个动作，比如“安装一个数据库服务”、“安装一个 web 服务器”、“创建一条防火墙规则”或者“把这个配置文件拷贝到那个服务器上去”。
* <ruby> 动作 <rt>  play </rt></ruby>： 由任务组成，例如，一个动作的内容是要“设置一个数据库，给 web 服务用”，这就包含了如下任务：1）安装数据库包；2）设置数据库管理员密码；3）创建数据库实例；4）为该实例分配权限。
* <ruby> 剧本 <rt>  playbook </rt></ruby>：（LCTT 译注：playbook 原指美式橄榄球队的[战术手册](https://usafootball.com/football-playbook/)，也常指“剧本”，此处惯例采用“剧本”译名） 由动作组成，一个剧本可能像这样：“设置我的网站，包含后端数据库”，其中的动作包括：1）设置数据库服务器；2）设置 web 服务器。
* <ruby> 角色 <rt>  role </rt></ruby>：用来保存和组织剧本，以便分享和再次使用它们。还拿上个例子来说，如果你需要一个全新的 web 服务器，就可以用别人已经写好并分享出来的角色来设置。因为角色是高度可配置的（如果编写正确的话），可以根据部署需求轻松地复用它们。
* <ruby>   Ansible 星系  <rt>  Ansible Galaxy </rt></ruby>：是一个在线仓库，里面保存的是由社区成员上传的角色，方便彼此分享。它与 GitHub 紧密集成，因此这些角色可以先在 Git 仓库里组织好，然后通过 Ansible 星系分享出来。

这些定义以及它们之间的关系可以用下图来描述：

请注意上面的例子只是组织任务的方式之一，我们当然也可以把安装数据库和安装 web 服务器的剧本拆开，放到不同的角色里。Ansible 星系上最常见的角色是独立安装、配置每个应用服务，你可以参考这些安装 [mysql](https://galaxy.ansible.com/bennojoy/mysql/) 和 [httpd](https://galaxy.ansible.com/xcezx/httpd/) 的例子。

### 编写剧本的小技巧

学习 Ansible 最好的资源是其[官方文档](http://docs.ansible.com/)。另外，像学习其他东西一样，搜索引擎是你的好朋友。我推荐你从一些简单的任务开始，比如安装应用或创建用户。下面是一些有用的指南：

* 在测试的时候少选几台服务器，这样你的动作可以执行的更快一些。如果它们在一台机器上执行成功，在其他机器上也没问题。
* 总是在真正运行前做一次<ruby> 测试 <rt>  dry run </rt></ruby>，以确保所有的命令都能正确执行（要运行测试，加上 `--check-mode` 参数 ）。
* 尽可能多做测试，别担心搞砸。任务里描述的是所需的状态，如果系统已经达到预期状态，任务会被简单地忽略掉。
* 确保在 `/etc/ansible/hosts` 里定义的主机名都可以被正确解析。
* 因为是用 SSH 与远程主机通信，主控节点必须要能接受密钥，所以你面临如下选择：1）要么在正式使用之前就做好与远程主机的密钥交换工作；2）要么在开始管理某台新的远程主机时做好准备输入 “Yes”，因为你要接受对方的 SSH 密钥交换请求（LCTT 译注：还有另一个不那么安全的选择，修改主控节点的 ssh 配置文件，将 `StrictHostKeyChecking` 设置成 “no”）。
* 尽管你可以在同一个剧本内把不同 Linux 发行版的任务整合到一起，但为每个发行版单独编写剧本会更明晰一些。

### 总结一下

Ansible 是你在数据中心里实施运维自动化的好选择，因为它：

* 无需客户端，所以比其他自动化工具更易安装。
* 将指令保存在 YAML 文件中（虽然也支持 JSON），比写 shell 脚本更简单。
* 开源，因此你也可以做出自己的贡献，让它更加强大！

你是怎样使用 Ansible 让数据中心更加自动化的呢？请在评论中分享您的经验。

---

via: <https://opensource.com/article/18/2/tips-success-when-getting-started-ansible>

作者：[Jose Delarosa](https://opensource.com/users/jdelaros1) 译者：[jdh8383](https://github.com/jdh8383) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


在 Ubuntu 上自动化安装基本应用的方法

Summary: 一些用户，特别是新手，可能希望轻松快速地安装他们喜欢的每个应用。

alfred如何将Google更改为百度搜索

alfred如何将Google更改为百度搜索

alfred如何将Google更改为百度搜索

alfred如何将Google更改为百度搜索

一些用户，特别是新手，可能希望轻松快速地安装他们喜欢的每个应用。

# 在 Ubuntu 上自动化安装基本应用的方法

默认安装的 Ubuntu 并未预先安装所有必需的应用。你可能需要在网上花几个小时或者向其他 Linux 用户寻求帮助才能找到并安装 Ubuntu 所需的应用。如果你是新手，那么你肯定需要花更多的时间来学习如何从命令行（使用 `apt-get` 或 `dpkg`）或从 Ubuntu 软件中心搜索和安装应用。一些用户，特别是新手，可能希望轻松快速地安装他们喜欢的每个应用。如果你是其中之一，不用担心。在本指南中，我们将了解如何使用名为 “Alfred” 的简单命令行程序在 Ubuntu 上安装基本应用。

Alfred 是用 Python 语言编写的自由、开源脚本。它使用 Zenity 创建了一个简单的图形界面，用户只需点击几下鼠标即可轻松选择和安装他们选择的应用。你不必花费数小时来搜索所有必要的应用程序、PPA、deb、AppImage、snap 或 flatpak。Alfred 将所有常见的应用、工具和小程序集中在一起，并自动安装所选的应用。如果你是最近从 Windows 迁移到 Ubuntu Linux 的新手，Alfred 会帮助你在新安装的 Ubuntu 系统上进行无人值守的软件安装，而无需太多用户干预。请注意，还有一个名称相似的 Mac OS 应用，但两者有不同的用途。

### 在 Ubuntu 上安装 Alfred

Alfred 安装很简单！只需下载脚本并启动它。就这么简单。

```
$ wget https://raw.githubusercontent.com/derkomai/alfred/master/alfred.py
$ python3 alfred.py
```

或者，使用 `wget` 下载脚本，如上所示，只需将 `alfred.py` 移动到 `$PATH` 中：

```
$ sudo cp alfred.py /usr/local/bin/alfred
```

使其可执行：

```
$ sudo chmod +x /usr/local/bin/alfred
```

并使用命令启动它：

```
$ alfred
```

### 使用 Alfred 脚本轻松快速地在 Ubuntu 上安装基本应用程序

按照上面所说启动 Alfred 脚本。这就是 Alfred 默认界面的样子。

如你所见，Alfred 列出了许多最常用的应用类型，例如：

* 网络浏览器，
* 邮件客户端，
* 消息，
* 云存储客户端，
* 硬件驱动程序，
* 编解码器，
* 开发者工具，
* Android，
* 文本编辑器，
* Git，
* 内核更新工具，
* 音频/视频播放器，
* 截图工具，
* 录屏工具，
* 视频编码器，
* 流媒体应用，
* 3D 建模和动画工具，
* 图像查看器和编辑器，
* CAD 软件，
* PDF 工具，
* 游戏模拟器，
* 磁盘管理工具，
* 加密工具，
* 密码管理器，
* 存档工具，
* FTP 软件，
* 系统资源监视器，
* 应用启动器等。

你可以选择任何一个或多个应用并立即安装它们。在这里，我将安装 “Developer bundle”，因此我选择它并单击 OK 按钮。

现在，Alfred 脚本将自动你的 Ubuntu 系统上添加必要仓库、PPA 并开始安装所选的应用。

安装完成后，你将看到以下消息。

恭喜你！已安装选定的软件包。

你可以使用以下命令[在 Ubuntu 上查看最近安装的应用](https://www.ostechnix.com/list-installed-packages-sorted-installation-date-linux/)：

```
$ grep " install " /var/log/dpkg.log
```

你可能需要重启系统才能使用某些已安装的应用。类似地，你可以方便地安装列表中的任何程序。

提示一下，还有一个由不同的开发人员编写的类似脚本，名为 `post_install.sh`。它与 Alfred 完全相同，但提供了一些不同的应用。请查看以下链接获取更多详细信息。

* [Ubuntu Post Installation Script](https://www.ostechnix.com/ubuntu-post-installation-script/)

这两个脚本能让懒惰的用户，特别是新手，只需点击几下鼠标就能够轻松快速地安装他们想要在 Ubuntu Linux 中使用的大多数常见应用、工具、更新、小程序，而无需依赖官方或者非官方文档的帮助。

就是这些了。希望这篇文章有用。还有更多好东西。敬请期待！

干杯！

---

via: <https://www.ostechnix.com/an-automated-way-to-install-essential-applications-on-ubuntu/>

作者：[SK](https://www.ostechnix.com/author/sk/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


预约 Emacs 心理医生

# 预约 Emacs 心理医生

>
> Eliza 是一个隐藏于某个 Linux 最流行文本编辑器中的自然语言处理聊天机器人。
>
>
>

欢迎你，今天时期 24 天的 Linux 命令行玩具的又一天。如果你是第一次访问本系列，你可能会问什么是命令行玩具呢。我们将会逐步确定这个概念，但一般来说，它可能是一个游戏，或任何能让你在终端玩的开心的其他东西。

可能你们已经见过了很多我们之前挑选的那些玩具，但我们依然希望对所有人来说都至少有一件新鲜事物。

今天的选择是 Emacs 中的一个彩蛋：Eliza，Rogerian 心理医生，一个准备好倾听你述说一切的终端玩具。

旁白：虽然这个玩具很好玩，但你的健康不是用来开玩笑的。请在假期期间照顾好你自己，无论时身体上还是精神上，若假期中的压力和焦虑对你的健康产生负面影响，请考虑找专业人士进行指导。真的有用。

要启动 [Eliza](https://www.emacswiki.org/emacs/EmacsDoctor)，首先，你需要启动 Emacs。很有可能 Emacs 已经安装在你的系统中了，但若没有，它基本上也肯定在你默认的软件仓库中。

由于我要求本系列的工具一定要时运行在终端内，因此使用 `-nw` 标志来启动 Emacs 让它在你的终端模拟器中运行。

```
$ emacs -nw
```

在 Emacs 中，输入 `M-x doctor` 来启动 Eliza。对于像我这样有 Vim 背景的人可能不知道这是什么意思，只需要按下 `escape`，输入 `x` 然后输入 `doctor`。然后，向它倾述所有假日的烦恼吧。

Eliza 历史悠久，最早可以追溯到 1960 年代中期的 MIT 人工智能实验室。[维基百科](https://en.wikipedia.org/wiki/ELIZA) 上有它历史的详细说明。

Eliza 并不是 Emacs 中唯一的娱乐工具。查看 [手册](https://www.gnu.org/software/emacs/manual/html_node/emacs/Amusements.html) 可以看到一整列好玩的玩具。

你有什么喜欢的命令行玩具值得推荐吗？我们时间不多了，但我还是想听听你的建议。请在下面评论中告诉我，我会查看的。另外也欢迎告诉我你们对本次玩具的想法。

请一定要看看昨天的玩具，[带着这个复刻版吃豆人来到 Linux 终端游乐中心](https://opensource.com/article/18/12/linux-toy-myman)，然后明天再来看另一个玩具！

---

via: <https://opensource.com/article/18/12/linux-toy-eliza>

作者：[Jason Baker](https://opensource.com/users/jason-baker) 选题：[lujun9972](https://github.com/lujun9972) 译者：[lujun9972](https://github.com/lujun9972) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Midori：轻量级开源 Web 浏览器

# Midori：轻量级开源 Web 浏览器

>
> 这是一个对再次回归的轻量级、快速、开源的 Web 浏览器 Midori 的快速回顾。
>
>
>

如果你正在寻找一款轻量级[网络浏览器替代品](https://itsfoss.com/open-source-browsers-linux/)，请试试 Midori。

[Midori](https://www.midori-browser.org/)是一款开源的网络浏览器，它更注重轻量级而不是提供大量功能。

如果你从未听说过 Midori，你可能会认为它是一个新的应用程序，但实际上 Midori 首次发布于 2007 年。

因为它专注于速度，所以 Midori 很快就聚集了一群爱好者，并成为了 Bodhi Linux、SilTaz 等轻量级 Linux 发行版的默认浏览器。

其他发行版如 [elementary OS](https://itsfoss.com/elementary-os-juno-features/) 也使用了 Midori 作为其默认浏览器。但 Midori 的开发在 2016 年左右停滞了，它的粉丝开始怀疑 Midori 已经死了。由于这个原因，elementary OS 从最新版本中删除了它。

好消息是 Midori 还没有死。经过近两年的不活跃，开发工作在 2018 年的最后一个季度恢复了。在后来的版本中添加了一些包括广告拦截器的扩展。

### Midori 网络浏览器的功能

以下是 Midori 浏览器的一些主要功能

* 使用 Vala 编写，使用 GTK+3 和 WebKit 渲染引擎。 \* 标签、窗口和会话管理。 \* 快速拨号。 \* 默认保存下一个会话的选项卡。 \* 使用 DuckDuckGo 作为默认搜索引擎。可以更改为 Google 或 Yahoo。 \* 书签管理。 \* 可定制和可扩展的界面。 \* 扩展模块可以用 C 和 Vala 编写。 \* 支持 HTML5。 \* 少量的扩展程序包括广告拦截器、彩色标签等。没有第三方扩展程序。 \* 表单历史。 \* 隐私浏览。 \* 可用于 Linux 和 Windows。

小知识：Midori 是日语单词，意思是绿色。如果你因此而猜想的话，但 Midori 的开发者实际不是日本人。

### 体验 Midori

这几天我一直在使用 Midori。体验基本很好。它支持 HTML5 并能快速渲染网站。广告拦截器也没问题。正如你对任何标准 Web 浏览器所期望的那样，浏览体验挺顺滑。

缺少扩展一直是 Midori 的弱点，所以我不打算谈论这个。

我注意到的是它不支持国际语言。我找不到添加新语言支持的方法。它根本无法渲染印地语字体，我猜对其他非[罗曼语言](https://en.wikipedia.org/wiki/Romance_languages)也是一样。

我也在 YouTube 中也遇到了麻烦。有些视频会抛出播放错误而其他视频没问题。

Midori 没有像 Chrome 那样吃我的内存，所以这是一个很大的优势。

如果你想尝试 Midori，让我们看下你该如何安装。

### 在 Linux 上安装 Midori

在 Ubuntu 18.04 仓库中不再提供 Midori。但是，可以使用 [Snap 包](https://itsfoss.com/use-snap-packages-ubuntu-16-04/)轻松安装较新版本的 Midori。

如果你使用的是 Ubuntu，你可以在软件中心找到 Midori（Snap 版）并从那里安装。

对于其他 Linux 发行版，请确保你[已启用 Snap 支持](https://itsfoss.com/install-snap-linux/)，然后你可以使用以下命令安装 Midori：

```
sudo snap install midori
```

你可以选择从源代码编译。你可以从 Midori 的网站下载它的代码。

* [下载 Midori](https://www.midori-browser.org/download/)

如果你喜欢 Midori 并希望帮助这个开源项目，请向他们捐赠或[从他们的商店购买 Midori 商品](https://www.midori-browser.org/shop)。

你在使用 Midori 还是曾经用过么？你的体验如何？你更喜欢使用哪种其他网络浏览器？请在下面的评论栏分享你的观点。

---

via: <https://itsfoss.com/midori-browser>

作者：[Abhishek Prakash](https://itsfoss.com/author/abhishek/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Python 七步捉虫法

Summary: 了解一些技巧助你减少代码查错时间。

了解一些技巧助你减少代码查错时间。

# Python 七步捉虫法

>
> 了解一些技巧助你减少代码查错时间。
>
>
>

<img src="content_image/album_201903_10_112119edsn4hvcxwfdg42v.jpg" alt="" title="">

在周五的下午三点钟（为什么是这个时间？因为事情总会在周五下午三点钟发生），你收到一条通知，客户发现你的软件出现一个错误。在有了初步的怀疑后，你联系运维，查看你的软件日志以了解发生了什么，因为你记得收到过日志已经搬家了的通知。

结果这些日志被转移到了你获取不到的地方，但它们正在导入到一个网页应用中——所以到时候你可以用这个漂亮的应用来检索日志，但是，这个应用现在还没完成。这个应用预计会在几天内完成。我知道，你觉得这完全不切实际。然而并不是，日志或者日志消息似乎经常在错误的时间消失不见。在我们开始查错前，一个忠告：经常检查你的日志以确保它们还在你认为它们应该在的地方，并记录你认为它们应该记的东西。当你不注意的时候，这些东西往往会发生令人惊讶的变化。

好的，你找到了日志或者尝试了呼叫运维人员，而客户确实发现了一个错误。甚至你可能认为你已经知道错误在哪儿。

你立即打开你认为可能有问题的文件并开始查错。

### 1、先不要碰你的代码

阅读代码，你甚至可能会想到该阅读哪些部分。但是在开始搞乱你的代码前，请重现导致错误的调用并把它变成一个测试。这将是一个集成测试，因为你可能还有其他疑问，目前你还不能准确地知道问题在哪儿。

确保这个测试结果是失败的。这很重要，因为有时你的测试不能重现失败的调用，尤其是你使用了可以混淆测试的 web 或者其他框架。很多东西可能被存储在变量中，但遗憾的是，只通过观察测试，你在测试里调用的东西并不总是明显可见的。当我尝试着重现这个失败的调用时，我并不是说我要创建一个可以通过的测试，但是，好吧，我确实是创建了一个测试，但我不认为这特别不寻常。

>
> 从自己的错误中吸取教训。
>
>
>

### 2、编写错误的测试

现在，你有了一个失败的测试，或者可能是一个带有错误的测试，那么是时候解决问题了。但是在你开干之前，让我们先检查下调用栈，因为这样可以更轻松地解决问题。

调用栈包括你已经启动但尚未完成地所有任务。因此，比如你正在烤蛋糕并准备往面糊里加面粉，那你的调用栈将是：

* 做蛋糕
* 打面糊
* 加面粉

你已经开始做蛋糕，开始打面糊，而你现在正在加面粉。往锅底抹油不在这个列表中，因为你已经完成了，而做糖霜不在这个列表上因为你还没开始做。

如果你对调用栈不清楚，我强烈建议你使用 [Python Tutor](http://www.pythontutor.com/)，它能帮你在执行代码时观察调用栈。

现在，如果你的 Python 程序出现了错误， Python 解释器会帮你打印出当前调用栈。这意味着无论那一时刻程序在做什么，很明显错误发生在调用栈的底部。

### 3、始终先检查调用栈底部

在栈底你不仅能看到发生了哪个错误，而且通常可以在调用栈的最后一行发现问题。如果栈底对你没有帮助，而你的代码还没有经过代码分析，那么使用代码分析是非常有用的。我推荐 pylint 或者 flake8。通常情况下，它会指出我一直忽略的错误的地方。

如果错误看起来很迷惑，你下一步行动可能是用 Google 搜索它。如果你搜索的内容不包含你的代码的相关信息，如变量名、文件等，那你将获得更好的搜索结果。如果你使用的是 Python 3（你应该使用它），那么搜索内容包含 Python 3 是有帮助的，否则 Python 2 的解决方案往往会占据大多数。

很久以前，开发者需要在没有搜索引擎的帮助下解决问题。那是一段黑暗时光。充分利用你可以使用的所有工具。

不幸的是，有时候问题发生在更早阶段，但只有在调用栈底部执行的地方才显现出来。就像当蛋糕没有膨胀时，忘记加发酵粉的事才被发现。

那就该检查整个调用栈。问题更可能在你的代码而不是 Python 标准库或者第三方包，所以先检查调用栈内你的代码。另外，在你的代码中放置断点通常会更容易检查代码。在调用栈的代码中放置断点，然后看看周围是否如你预期。

“但是，玛丽，”我听到你说，“如果我有一个调用栈，那这些都是有帮助的，但我只有一个失败的测试。我该从哪里开始？”

pdb，一个 Python 调试器。

找到你代码里会被这个调用命中的地方。你应该能够找到至少一个这样的地方。在那里打上一个 pdb 的断点。

#### 一句题外话

为什么不使用 `print` 语句呢？我曾经依赖于 `print` 语句。有时候，它们仍然很方便。但当我开始处理复杂的代码库，尤其是有网络调用的代码库，`print` 语句就变得太慢了。我最终在各种地方都加上了 `print` 语句，但我没法追踪它们的位置和原因，而且变得更复杂了。但是主要使用 pdb 还有一个更重要的原因。假设你添加一条 `print` 语句去发现错误问题，而且 `print` 语句必须早于错误出现的地方。但是，看看你放 `print` 语句的函数，你不知道你的代码是怎么执行到那个位置的。查看代码是寻找调用路径的好方法，但看你以前写的代码是恐怖的。是的，我会用 `grep` 处理我的代码库以寻找调用函数的地方，但这会变得乏味，而且搜索一个通用函数时并不能缩小搜索范围。pdb 就变得非常有用。

你遵循我的建议，打上 pdb 断点并运行你的测试。然而测试再次失败，但是没有任何一个断点被命中。留着你的断点，并运行测试套件中一个同这个失败的测试非常相似的测试。如果你有个不错的测试套件，你应该能够找到一个这样的测试。它会命中了你认为你的失败测试应该命中的代码。运行这个测试，然后当它运行到你的断点，按下 `w` 并检查调用栈。如果你不知道如何查看因为其他调用而变得混乱的调用栈，那么在调用栈的中间找到属于你的代码，并在堆栈中该代码的上一行放置一个断点。再试一次新的测试。如果仍然没命中断点，那么继续，向上追踪调用栈并找出你的调用在哪里脱轨了。如果你一直没有命中断点，最后到了追踪的顶部，那么恭喜你，你发现了问题：你的应用程序名称拼写错了。

>
> 没有经验，小白，一点都没有经验。
>
>
>

### 4、修改代码

如果你仍觉得迷惑，在你稍微改变了一些的地方尝试新的测试。你能让新的测试跑起来么？有什么是不同的呢？有什么是相同的呢？尝试改变一下别的东西。当你有了你的测试，以及可能也还有其它的测试，那就可以开始安全地修改代码了，确定是否可以缩小问题范围。记得从一个新提交开始解决问题，以便于可以轻松地撤销无效地更改。（这就是版本控制，如果你没有使用过版本控制，这将会改变你的生活。好吧，可能它只是让编码更容易。查阅“[版本控制可视指南](https://betterexplained.com/articles/a-visual-guide-to-version-control/)”，以了解更多。）

### 5、休息一下

尽管如此，当它不再感觉起来像一个有趣的挑战或者游戏而开始变得令人沮丧时，你最好的举措是脱离这个问题。休息一下。我强烈建议你去散步并尝试考虑别的事情。

### 6、把一切写下来

当你回来了，如果你没有突然受到启发，那就把你关于这个问题所知的每一个点信息写下来。这应该包括：

* 真正造成问题的调用
* 真正发生了什么，包括任何错误信息或者相关的日志信息
* 你真正期望发生什么
* 到目前为止，为了找出问题，你做了什么工作；以及解决问题中你发现的任何线索。

有时这里有很多信息，但相信我，从零碎中挖掘信息是很烦人。所以尽量简洁，但是要完整。

### 7、寻求帮助

我经常发现写下所有信息能够启迪我想到还没尝试过的东西。当然，有时候我在点击求助邮件（或表单）的提交按钮后立刻意识到问题是是什么。无论如何，当你在写下所有东西仍一无所获时，那就试试向他人发邮件求助。首先是你的同事或者其他参与你的项目的人，然后是该项目的邮件列表。不要害怕向人求助。大多数人都是友善和乐于助人的，我发现在 Python 社区里尤其如此。

Maria McKinley 已在 [PyCascades 2019](https://2019.pycascades.com/) 演讲 [代码查错](https://2019.pycascades.com/talks/hunting-the-bugs)，2 月 23-24，于西雅图。

---

via: <https://opensource.com/article/19/2/steps-hunting-code-python-bugs>

作者：[Maria Mckinley](https://opensource.com/users/parody) 选题：[lujun9972](https://github.com/lujun9972) 译者：[LazyWolfLin](https://github.com/LazyWolfLin) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


在 Emacs 上配置 Anaconda

Warning:

This Python interpreter is in a conda environment, but the environment has

not been activated. Libraries may fail to load.  To activate this environment

please see

C-c C-c  报错提示

# 在 Emacs 上配置 Anaconda

<img src="content_image/album_201903_10_114348i1yyeykya3kyl3kj.jpg" alt="" title="">

也许我所追求的究极 IDE 就是 [Emacs](https://www.gnu.org/software/emacs/) 了。我的目标是使 Emacs 成为一款全能的 Python IDE。本文描述了如何在 Emacs 上配置 Anaconda。（LCTT 译注：Anaconda 自称“世界上最流行的 Python/R 的数据分析平台”）

我的配置信息：

* OS：Trisquel 8.0
* Emacs：GNU Emacs 25.3.2

快捷键说明（[参见完全指南](https://www.math.uh.edu/%7Ebgb/emacs_keys.html)）：

```
C-x = Ctrl + x
M-x = Alt + x
RET = ENTER
```

### 1、下载并安装 Anaconda

#### 1.1 下载

[从这儿](https://www.anaconda.com/download/#linux) 下载 Anaconda。你应该下载 Python 3.x 的版本，因为 Python 2 在 2020 年就不再支持了。你无需预先安装 Python 3.x。这个安装脚本会自动安装它。

#### 1.2 安装

```
cd ~/Downloads
bash Anaconda3-2018.12-Linux-x86.sh
```

### 2、将 Anaconda 添加到 Emacs

#### 2.1 将 MELPA 添加到 Emacs

我们需要用到 `anaconda-mode` 这个 Emacs 包。该包位于 MELPA 仓库中。Emacs25 需要手工添加该仓库。

* [注意：点击本文查看如何将 MELPA 添加到 Emacs](https://melpa.org/#/getting-started)

#### 2.2 为 Emacs 安装 anaconda-mode 包

```
M-x package-install RET
anaconda-mode RET
```

#### 2.3 为 Emacs 配置 anaconda-mode

```
echo "(add-hook 'python-mode-hook 'anaconda-mode)" > ~/.emacs.d/init.el
```

### 3、在 Emacs 上通过 Anaconda 运行你第一个脚本

#### 3.1 创建新 .py 文件

```
C-x C-f
HelloWorld.py RET
```

#### 3.2 输入下面代码

```
print ("Hello World from Emacs")
```

#### 3.3 运行之

```
C-c C-p
C-c C-c
```

输出为：

```
Python 3.7.1 (default, Dec 14 2018, 19:46:24)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> python.el: native completion setup loaded
>>> Hello World from Emacs
>>>
```

我是受到 [Codingquark](https://codingquark.com) 的影响才开始使用 Emacs 的。

有任何错误和遗漏请在评论中写下。干杯！

---

via: <https://idevji.com/configure-anaconda-on-emacs/>

作者：[Devji Chhanga](https://idevji.com/author/admin/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[lujun9972](https://github.com/lujun9972) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


计算机实验室之树莓派：课程 9 屏幕04

Summary: 屏幕04 课程基于屏幕03 课程来构建，它教你如何操作文本。

屏幕04 课程基于屏幕03 课程来构建，它教你如何操作文本。

# 计算机实验室之树莓派：课程 9 屏幕04

屏幕04 课程基于屏幕03 课程来构建，它教你如何操作文本。假设你已经有了[课程 8：屏幕03](/article-10585-1.html) 的操作系统代码，我们将以它为基础。

### 1、操作字符串

能够绘制文本是极好的，但不幸的是，现在你只能绘制预先准备好的字符串。如果能够像命令行那样显示任何东西才是完美的，而理想情况下应该是，我们能够显示任何我们期望的东西。一如既往地，如果我们付出努力而写出一个非常好的函数，它能够操作我们所希望的所有字符串，而作为回报，这将使我们以后写代码更容易。曾经如此复杂的函数，在 C 语言编程中只不过是一个 `sprintf` 而已。这个函数基于给定的另一个字符串和作为描述的额外的一个参数而生成一个字符串。我们对这个函数感兴趣的地方是，这个函数是个变长函数。这意味着它可以带可变数量的参数。参数的数量取决于具体的格式字符串，因此它的参数的数量不能预先确定。

>
> 变长函数在汇编代码中看起来似乎不好理解，然而 ，它却是非常有用和很强大的概念。
>
>
>

这个完整的函数有许多选项，而我们在这里只列出了几个。在本教程中将要实现的选项我做了高亮处理，当然，你可以尝试去实现更多的选项。

函数通过读取格式化字符串来工作，然后使用下表的意思去解释它。一旦一个参数已经使用了，就不会再次考虑它了。函数的返回值是写入的字符数。如果方法失败，将返回一个负数。

表 1.1 sprintf 格式化规则

| 选项 | 含义 |
| --- | --- |
| 除了 `%` 之外的任何支付 | 复制字符到输出。 |
| `%%` | 写一个 % 字符到输出。 |
| `%c` | 将下一个参数写成字符格式。 |
| `%d` 或 `%i` | 将下一个参数写成十进制的有符号整数。 |
| `%e` | 将下一个参数写成科学记数法，使用 eN，意思是 ×10 N。 |
| `%E` | 将下一个参数写成科学记数法，使用 EN，意思是 ×10 N。 |
| `%f` | 将下一个参数写成十进制的 IEEE 754 浮点数。 |
| `%g` | 与 `%e` 和 `%f` 的指数表示形式相同。 |
| `%G` | 与 `%E` 和 `%f` 的指数表示形式相同。 |
| `%o` | 将下一个参数写成八进制的无符号整数。 |
| `%s` | 下一个参数如果是一个指针，将它写成空终止符字符串。 |
| `%u` | 将下一个参数写成十进制无符号整数。 |
| `%x` | 将下一个参数写成十六进制无符号整数（使用小写的 a、b、c、d、e 和 f）。 |
| `%X` | 将下一个参数写成十六进制的无符号整数（使用大写的 A、B、C、D、E 和 F）。 |
| `%p` | 将下一个参数写成指针地址。 |
| `%n` | 什么也不输出。而是复制到目前为止被下一个参数在本地处理的字符个数。 |

除此之外，对序列还有许多额外的处理，比如指定最小长度，符号等等。更多信息可以在 [sprintf - C++ 参考](http://www.cplusplus.com/reference/clibrary/cstdio/sprintf/) 上找到。

下面是调用方法和返回的结果的示例。

表 1.2 sprintf 调用示例

| 格式化字符串 | 参数 | 结果 |
| --- | --- | --- |
| `"%d"` | 13 | 13 |
| `"+%d degrees"` | 12 | +12 degrees |
| `"+%x degrees"` | 24 | +1c degrees |
| `"'%c' = 0%o"` | 65, 65 | ‘A’ = 0101 |
| `"%d * %d%% = %d"` | 200, 40, 80 | 200 \* 40% = 80 |
| `"+%d degrees"` | -5 | +-5 degrees |
| `"+%u degrees"` | -5 | +4294967291 degrees |

希望你已经看到了这个函数是多么有用。实现它需要大量的编程工作，但给我们的回报却是一个非常有用的函数，可以用于各种用途。

### 2、除法

虽然这个函数看起来很强大、也很复杂。但是，处理它的许多情况的最容易的方式可能是，编写一个函数去处理一些非常常见的任务。它是个非常有用的函数，可以为任何底的一个有符号或无符号的数字生成一个字符串。那么，我们如何去实现呢？在继续阅读之前，尝试快速地设计一个算法。

>
> 除法是非常慢的，也是非常复杂的基础数学运算。它在 ARM 汇编代码中不能直接实现，因为如果直接实现的话，它得出答案需要花费很长的时间，因此它不是个“简单的”运算。
>
>
>

最简单的方法或许就是我在 [课程 1：OK01](/article-10458-1.html) 中提到的“除法余数法”。它的思路如下：

1. 用当前值除以你使用的底。
2. 保存余数。
3. 如果得到的新值不为 0，转到第 1 步。
4. 将余数反序连起来就是答案。

例如：

表 2.1 以 2 为底的例子

转换

| 值 | 新值 | 余数 |
| --- | --- | --- |
| 137 | 68 | 1 |
| 68 | 34 | 0 |
| 34 | 17 | 0 |
| 17 | 8 | 1 |
| 8 | 4 | 0 |
| 4 | 2 | 0 |
| 2 | 1 | 0 |
| 1 | 0 | 1 |

因此答案是 10001001 2

这个过程的不幸之外在于使用了除法。所以，我们必须首先要考虑二进制中的除法。

我们复习一下长除法

>
> 假如我们想把 4135 除以 17。
>
>
>
> ```
>    0243 r 4
> 17)4135
>    0        0 × 17 = 0000
>    4135     4135 - 0 = 4135
>    34       200 × 17 = 3400
>    735      4135 - 3400 = 735
>    68       40 × 17 = 680
>    55       735 - 680 = 55
>    51       3 × 17 = 51
>    4        55 - 51 = 4
> ```
>
> 答案：243 余 4
>
>
> 首先我们来看被除数的最高位。我们看到它是小于或等于除数的最小倍数，因此它是 0。我们在结果中写一个 0。
>
>
> 接下来我们看被除数倒数第二位和所有的高位。我们看到小于或等于那个数的除数的最小倍数是 34。我们在结果中写一个 2，和减去 3400。
>
>
> 接下来我们看被除数的第三位和所有高位。我们看到小于或等于那个数的除数的最小倍数是 68。我们在结果中写一个 4，和减去 680。
>
>
> 最后，我们看一下所有的余位。我们看到小于余数的除数的最小倍数是 51。我们在结果中写一个 3，减去 51。减法的结果就是我们的余数。
>
>
>

在汇编代码中做除法，我们将实现二进制的长除法。我们之所以实现它是因为，数字都是以二进制方式保存的，这让我们很容易地访问所有重要位的移位操作，并且因为在二进制中做除法比在其它高进制中做除法都要简单，因为它的数更少。

```
        1011 r 1
1010)1101111
     1010
      11111
      1010
       1011
       1010
          1
```

这个示例展示了如何做二进制的长除法。简单来说就是，在不超出被除数的情况下，尽可能将除数右移，根据位置输出一个 1，和减去这个数。剩下的就是余数。在这个例子中，我们展示了 1101111 2 ÷ 1010 2 = 1011 2 余数为 1 2。用十进制表示就是，111 ÷ 10 = 11 余 1。

你自己尝试去实现这个长除法。你应该去写一个函数 `DivideU32` ，其中 `r0` 是被除数，而 `r1` 是除数，在 `r0` 中返回结果，在 `r1` 中返回余数。下面，我们将完成一个有效的实现。

```
function DivideU32(r0 is dividend, r1 is divisor)
  set shift to 31
  set result to 0
  while shift ≥ 0
     if dividend ≥ (divisor << shift) then
       set dividend to dividend - (divisor <&lt shift)
       set result to result + 1
     end if
     set result to result << 1
     set shift to shift - 1
  loop
  return (result, dividend)
end function
```

这段代码实现了我们的目标，但却不能用于汇编代码。我们出现的问题是，我们的寄存器只能保存 32 位，而 `divisor << shift` 的结果可能在一个寄存器中装不下（我们称之为溢出）。这确实是个问题。你的解决方案是否有溢出的问题呢？

幸运的是，有一个称为 `clz`（<ruby> 计数前导零 <rt>  count leading zeros </rt></ruby>）的指令，它能计算一个二进制表示的数字的前导零的个数。这样我们就可以在溢出发生之前，可以将寄存器中的值进行相应位数的左移。你可以找出的另一个优化就是，每个循环我们计算 `divisor << shift` 了两遍。我们可以通过将除数移到开始位置来改进它，然后在每个循环结束的时候将它移下去，这样可以避免将它移到别处。

我们来看一下进一步优化之后的汇编代码。

```
.globl DivideU32
DivideU32:
result .req r0
remainder .req r1
shift .req r2
current .req r3

clz shift,r1
lsl current,r1,shift
mov remainder,r0
mov result,#0

divideU32Loop$:
  cmp shift,#0
  blt divideU32Return$
  cmp remainder,current

  addge result,result,#1
  subge remainder,current
  sub shift,#1
  lsr current,#1
  lsl result,#1
  b divideU32Loop$
divideU32Return$:
.unreq current
mov pc,lr

.unreq result
.unreq remainder
.unreq shift
```

你可能毫无疑问的认为这是个非常高效的作法。它是很好，但是除法是个代价非常高的操作，并且我们的其中一个愿望就是不要经常做除法，因为如果能以任何方式提升速度就是件非常好的事情。当我们查看有循环的优化代码时，我们总是重点考虑一个问题，这个循环会运行多少次。在本案例中，在输入为 1 的情况下，这个循环最多运行 31 次。在不考虑特殊情况的时候，这很容易改进。例如，当 1 除以 1 时，不需要移位，我们将把除数移到它上面的每个位置。这可以通过简单地在被除数上使用新的 `clz` 命令并从中减去它来改进。在 `1 ÷ 1` 的案例中，这意味着移位将设置为 0，明确地表示它不需要移位。如果它设置移位为负数，表示除数大于被除数，因此我们就可以知道结果是 0，而余数是被除数。我们可以做的另一个快速检查就是，如果当前值为 0，那么它是一个整除的除法，我们就可以停止循环了。

>
> `clz dest,src` 将第一个寄存器 `dest` 中二进制表示的值的前导零的数量，保存到第二个寄存器 `src` 中。
>
>
>

```
.globl DivideU32
DivideU32:
result .req r0
remainder .req r1
shift .req r2
current .req r3

clz shift,r1
clz r3,r0
subs shift,r3
lsl current,r1,shift
mov remainder,r0
mov result,#0
blt divideU32Return$

divideU32Loop$:
  cmp remainder,current
  blt divideU32LoopContinue$

  add result,result,#1
  subs remainder,current
  lsleq result,shift
  beq divideU32Return$
divideU32LoopContinue$:
  subs shift,#1
  lsrge current,#1
  lslge result,#1
  bge divideU32Loop$

divideU32Return$:
.unreq current
mov pc,lr

.unreq result
.unreq remainder
.unreq shift
```

复制上面的代码到一个名为 `maths.s` 的文件中。

### 3、数字字符串

现在，我们已经可以做除法了，我们来看一下另外的一个将数字转换为字符串的实现。下列的伪代码将寄存器中的一个数字转换成以 36 为底的字符串。根据惯例，a % b 表示 a 被 b 相除之后的余数。

```
function SignedString(r0 is value, r1 is dest, r2 is base)
  if value ≥ 0
  then return UnsignedString(value, dest, base)
  otherwise
    if dest > 0 then
      setByte(dest, '-')
      set dest to dest + 1
    end if
    return UnsignedString(-value, dest, base) + 1
  end if
end function

function UnsignedString(r0 is value, r1 is dest, r2 is base)
  set length to 0
  do

    set (value, rem) to DivideU32(value, base)
    if rem &gt 10
    then set rem to rem + '0'
    otherwise set rem to rem - 10 + 'a'
    if dest > 0
    then setByte(dest + length, rem)
    set length to length + 1

  while value > 0
  if dest > 0
  then ReverseString(dest, length)
  return length
end function

function ReverseString(r0 is string, r1 is length)
  set end to string + length - 1
  while end > start
    set temp1 to readByte(start)
    set temp2 to readByte(end)
    setByte(start, temp2)
    setByte(end, temp1)
    set start to start + 1
    set end to end - 1
  end while
end function
```

上述代码实现在一个名为 `text.s` 的汇编文件中。记住，如果你遇到了困难，可以在下载页面找到完整的解决方案。

### 4、格式化字符串

我们继续回到我们的字符串格式化方法。因为我们正在编写我们自己的操作系统，我们根据我们自己的意愿来添加或修改格式化规则。我们可以发现，添加一个 `a % b` 操作去输出一个二进制的数字比较有用，而如果你不使用空终止符字符串，那么你应该去修改 `%s` 的行为，让它从另一个参数中得到字符串的长度，或者如果你愿意，可以从长度前缀中获取。我在下面的示例中使用了一个空终止符。

实现这个函数的一个主要的障碍是它的参数个数是可变的。根据 ABI 规定，额外的参数在调用方法之前以相反的顺序先推送到栈上。比如，我们使用 8 个参数 1、2、3、4、5、6、7 和 8 来调用我们的方法，我们将按下面的顺序来处理：

1. 设置 r0 = 5、r1 = 6、r2 = 7、r3 = 8
2. 推入 {r0,r1,r2,r3}
3. 设置 r0 = 1、r1 = 2、r2 = 3、r3 = 4
4. 调用函数
5. 将 sp 和 #4\*4 加起来

现在，我们必须确定我们的函数确切需要的参数。在我的案例中，我将寄存器 `r0` 用来保存格式化字符串地址，格式化字符串长度则放在寄存器 `r1` 中，目标字符串地址放在寄存器 `r2` 中，紧接着是要求的参数列表，从寄存器 `r3` 开始和像上面描述的那样在栈上继续。如果你想去使用一个空终止符格式化字符串，在寄存器 r1 中的参数将被移除。如果你想有一个最大缓冲区长度，你可以将它保存在寄存器 `r3` 中。由于有额外的修改，我认为这样修改函数是很有用的，如果目标字符串地址为 0，意味着没有字符串被输出，但如果仍然返回一个精确的长度，意味着能够精确的判断格式化字符串的长度。

如果你希望尝试实现你自己的函数，现在就可以去做了。如果不去实现你自己的，下面我将首先构建方法的伪代码，然后给出实现的汇编代码。

```
function StringFormat(r0 is format, r1 is formatLength, r2 is dest, ...)
  set index to 0
  set length to 0
  while index < formatLength
    if readByte(format + index) = '%' then
      set index to index + 1
      if readByte(format + index) = '%' then
        if dest > 0
        then setByte(dest + length, '%')
        set length to length + 1
      otherwise if readByte(format + index) = 'c' then
        if dest > 0
        then setByte(dest + length, nextArg)
        set length to length + 1
      otherwise if readByte(format + index) = 'd' or 'i' then
        set length to length + SignedString(nextArg, dest, 10)
      otherwise if readByte(format + index) = 'o' then
        set length to length + UnsignedString(nextArg, dest, 8)
      otherwise if readByte(format + index) = 'u' then
        set length to length + UnsignedString(nextArg, dest, 10)
      otherwise if readByte(format + index) = 'b' then
        set length to length + UnsignedString(nextArg, dest, 2)
      otherwise if readByte(format + index) = 'x' then
        set length to length + UnsignedString(nextArg, dest, 16)
      otherwise if readByte(format + index) = 's' then
        set str to nextArg
        while getByte(str) != '\0'
          if dest > 0
          then setByte(dest + length, getByte(str))
          set length to length + 1
          set str to str + 1
        loop
      otherwise if readByte(format + index) = 'n' then
        setWord(nextArg, length)
      end if
    otherwise
      if dest > 0
      then setByte(dest + length, readByte(format + index))
      set length to length + 1
    end if
    set index to index + 1
  loop
  return length
end function
```

虽然这个函数很大，但它还是很简单的。大多数的代码都是在检查所有各种条件，每个代码都是很简单的。此外，所有的无符号整数的大小写都是相同的（除了底以外）。因此在汇编中可以将它们汇总。下面是它的汇编代码。

```
.globl FormatString
FormatString:
format .req r4
formatLength .req r5
dest .req r6
nextArg .req r7
argList .req r8
length .req r9

push {r4,r5,r6,r7,r8,r9,lr}
mov format,r0
mov formatLength,r1
mov dest,r2
mov nextArg,r3
add argList,sp,#7*4
mov length,#0

formatLoop$:
  subs formatLength,#1
  movlt r0,length
  poplt {r4,r5,r6,r7,r8,r9,pc}

  ldrb r0,[format]
  add format,#1
  teq r0,#'%'
  beq formatArg$

formatChar$:
  teq dest,#0
  strneb r0,[dest]
  addne dest,#1
  add length,#1
  b formatLoop$

formatArg$:
  subs formatLength,#1
  movlt r0,length
  poplt {r4,r5,r6,r7,r8,r9,pc}

  ldrb r0,[format]
  add format,#1
  teq r0,#'%'
  beq formatChar$

  teq r0,#'c'
  moveq r0,nextArg
  ldreq nextArg,[argList]
  addeq argList,#4
  beq formatChar$

  teq r0,#'s'
  beq formatString$

  teq r0,#'d'
  beq formatSigned$

  teq r0,#'u'
  teqne r0,#'x'
  teqne r0,#'b'
  teqne r0,#'o'
  beq formatUnsigned$

  b formatLoop$

formatString$:
  ldrb r0,[nextArg]
  teq r0,#0x0
  ldreq nextArg,[argList]
  addeq argList,#4
  beq formatLoop$
  add length,#1
  teq dest,#0
  strneb r0,[dest]
  addne dest,#1
  add nextArg,#1
  b formatString$

formatSigned$:
  mov r0,nextArg
  ldr nextArg,[argList]
  add argList,#4
  mov r1,dest
  mov r2,#10
  bl SignedString
  teq dest,#0
  addne dest,r0
  add length,r0
  b formatLoop$

formatUnsigned$:
  teq r0,#'u'
  moveq r2,#10
  teq r0,#'x'
  moveq r2,#16
  teq r0,#'b'
  moveq r2,#2
  teq r0,#'o'
  moveq r2,#8

  mov r0,nextArg
  ldr nextArg,[argList]
  add argList,#4
  mov r1,dest
  bl UnsignedString
  teq dest,#0
  addne dest,r0
  add length,r0
  b formatLoop$
```

### 5、一个转换操作系统

你可以使用这个方法随意转换你希望的任何东西。比如，下面的代码将生成一个换算表，可以做从十进制到二进制到十六进制到八进制以及到 ASCII 的换算操作。

删除 `main.s` 文件中 `bl SetGraphicsAddress` 之后的所有代码，然后粘贴以下的代码进去。

```
mov r4,#0
loop$:
ldr r0,=format
mov r1,#formatEnd-format
ldr r2,=formatEnd
lsr r3,r4,#4
push {r3}
push {r3}
push {r3}
push {r3}
bl FormatString
add sp,#16

mov r1,r0
ldr r0,=formatEnd
mov r2,#0
mov r3,r4

cmp r3,#768-16
subhi r3,#768
addhi r2,#256
cmp r3,#768-16
subhi r3,#768
addhi r2,#256
cmp r3,#768-16
subhi r3,#768
addhi r2,#256

bl DrawString

add r4,#16
b loop$

.section .data
format:
.ascii "%d=0b%b=0x%x=0%o='%c'"
formatEnd:
```

你能在测试之前推算出将发生什么吗？特别是对于 `r3 ≥ 128` 会发生什么？尝试在树莓派上运行它，看看你是否猜对了。如果不能正常运行，请查看我们的排错页面。

如果一切顺利，恭喜你！你已经完成了屏幕04 教程，屏幕系列的课程结束了！我们学习了像素和帧缓冲的知识，以及如何将它们应用到树莓派上。我们学习了如何绘制简单的线条，也学习如何绘制字符，以及将数字格式化为文本的宝贵技能。我们现在已经拥有了在一个操作系统上进行图形输出的全部知识。你可以写出更多的绘制方法吗？三维绘图是什么？你能实现一个 24 位帧缓冲吗？能够从命令行上读取帧缓冲的大小吗？

接下来的课程是[输入](https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/input01.html)系列课程，它将教我们如何使用键盘和鼠标去实现一个传统的计算机控制台。

---

via: <https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen04.html>

作者：[Alex Chadwick](https://www.cl.cam.ac.uk) 选题：[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/) 荣誉推出


让 Linux 启动更快

Summary: 进行 Linux 内核与固件开发的时候，往往需要多次的重启，会浪费大把的时间。

进行 Linux 内核与固件开发的时候，往往需要多次的重启，会浪费大把的时间。

# 让 Linux 启动更快

>
> 进行 Linux 内核与固件开发的时候，往往需要多次的重启，会浪费大把的时间。
>
>
>

<img src="content_image/album_201903_11_150048l95mu41ckzchzrhe.jpg" alt="" title="">

在所有我拥有或使用过的电脑中，启动最快的那台是 20 世纪 80 年代的电脑。在你把手从电源键移到键盘上的时候，BASIC 解释器已经在等待你输入命令了。对于现代的电脑，启动时间从笔记本电脑的 15 秒到小型家庭服务器的数分钟不等。为什么它们的启动时间有差别？

那台直接启动到 BASIC 命令行提示符的 20 世纪 80 年代微电脑，有着一颗非常简单的 CPU，它在通电的时候就立即开始从一个内存地址中获取和执行指令。因为这些系统的 BASIC 在 ROM 里面，基本不需要载入的时间——你很快就进到 BASIC 命令提示符中了。同时代更加复杂的系统，比如 IBM PC 或 Macintosh，需要一段可观的时间来启动（大约 30 秒），尽管这主要是因为需要从软盘上读取操作系统的缘故。在可以加载操作系统之前，只有很小一部分时间是花费在固件上的。

现代服务器往往在从磁盘上读取操作系统之前，在固件上花费了数分钟而不是数秒。这主要是因为现代系统日益增加的复杂性。CPU 不再能够只是运行起来就开始全速执行指令，我们已经习惯于 CPU 频率变化、节省能源的待机状态以及 CPU 多核。实际上，在现代 CPU 内部有数量惊人的更简单的处理器，它们协助主 CPU 核心启动并提供运行时服务，比如在过热的时候压制频率。在绝大多数 CPU 架构中，在你的 CPU 内的这些核心上运行的代码都以不透明的二进制 blob 形式提供。

在 OpenPOWER 系统上，所有运行在 CPU 内部每个核心的指令都是开源的。在有 [OpenBMC](https://en.wikipedia.org/wiki/OpenBMC)（比如 IBM 的 AC922 系统和 Raptor 的 TALOS II 以及 Blackbird 系统）的机器上，这还延伸到了运行在<ruby> 基板管理控制器 <rt>  Baseboard Management Controller </rt></ruby>上的代码。这就意味着我们可以一探究竟，到底为什么从接入电源线到显示出熟悉的登录界面花了这么长时间。

如果你是内核相关团队的一员，你可能启动过许多内核。如果你是固件相关团队的一员，你可能要启动许多不同的固件映像，接着是一个操作系统，来确保你的固件仍能工作。如果我们可以减少硬件的启动时间，这些团队可以更有生产力，并且终端用户在搭建系统或重启安装固件或系统更新的时候会对此表示感激。

过去的几年，Linux 发行版的启动时间已经做了很多改善。现代的初始化系统在处理并行和按需任务上做得很好。在一个现代系统上，一旦内核开始执行，它可以在短短数秒内进入登录提示符界面。这里短短的数秒不是优化启动时间的下手之处，我们要到更早的地方：在我们到达操作系统之前。

在 OpenPOWER 系统上，固件通过启动一个存储在固件闪存芯片上的 Linux 内核来加载操作系统，它运行一个叫做 [Petitboot](https://github.com/open-power/petitboot) 的用户态程序去寻找用户想要启动的系统所在磁盘，并通过 [kexec](https://en.wikipedia.org/wiki/Kexec) 启动它。有了这些优化，启动 Petitboot 环境只占了启动时间的百分之几，所以我们还得从其他地方寻找优化项。

在 Petitboot 环境启动前，有一个先导固件，叫做 [Skiboot](https://github.com/open-power/skiboot)，在它之前有个 [Hostboot](https://github.com/open-power/hostboot)。在 Hostboot 之前是 [Self-Boot Engine](https://github.com/open-power/sbe)，一个晶圆切片（die）上的单独核心，它启动单个 CPU 核心并执行来自 Level 3 缓存的指令。这些组件是我们可以在减少启动时间上取得进展的主要部分，因为它们花费了启动的绝大部分时间。或许这些组件中的一部分没有进行足够的优化或尽可能做到并行？

另一个研究路径是重启时间而不是启动时间。在重启的时候，我们真的需要对所有硬件重新初始化吗？

正如任何现代系统那样，改善启动（或重启）时间的方案已经变成了更多的并行执行、解决遗留问题、（可以认为）作弊的结合体。

---

via: <https://opensource.com/article/19/1/booting-linux-faster>

作者：[Stewart Smith](https://opensource.com/users/stewart-ibm) 选题：[lujun9972](https://github.com/lujun9972) 译者：[alim0x](https://github.com/alim0x) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Akira 是我们一直想要的 Linux 设计工具吗？

Summary: 为了开发一些能够取代那些专有工具的应用，Alessandro Castellani 发起了一个 Kickstarter 活动，并与几位经验丰富的开发人员组队合作。是的，Akira 仍然只是一个想法，只有一个界面原型。

不错不错。

# Akira 是我们一直想要的 Linux 设计工具吗？

先说一下，我不是一个专业的设计师，但我在 Windows 上使用过某些工具（如 Photoshop、Illustrator 等）和 [Figma](https://www.figma.com/)（这是一个基于浏览器的界面设计工具）。我相信 Mac 和 Windows 上还有更多的设计工具。

即使在 Linux 上，也有数量有限的专用[图形设计工具](https://itsfoss.com/best-linux-graphic-design-software/)。其中一些工具如 [GIMP](https://itsfoss.com/gimp-2-10-release/) 和 [Inkscape](https://inkscape.org/) 也被专业人士使用。但不幸的是，它们中的大多数都不被视为专业级。

即使有更多解决方案，我也从未遇到过可以取代 [Sketch](https://www.sketchapp.com/)、Figma 或 Adobe XD 的原生 Linux 应用。任何专业设计师都同意这点，不是吗？

### Akira 是否会在 Linux 上取代 Sketch、Figma 和 Adobe XD？

所以，为了开发一些能够取代那些专有工具的应用，[Alessandro Castellani](https://github.com/Alecaddd) 发起了一个 [Kickstarter 活动](https://www.kickstarter.com/projects/alecaddd/akira-the-linux-design-tool/description)，并与几位经验丰富的开发人员 [Alberto Fanjul](https://github.com/albfan)、[Bilal Elmoussaoui](https://github.com/bilelmoussaoui) 和 [Felipe Escoto](https://github.com/Philip-Scott) 组队合作。

是的，Akira 仍然只是一个想法，只有一个界面原型（正如我最近在 Kickstarter 的[直播流](https://live.kickstarter.com/alessandro-castellani/live-stream/the-current-state-of-akira)中看到的那样）。

### 如果它还不存在，为什么会发起 Kickstarter 活动？

Kickstarter 活动的目的是收集资金，以便雇用开发人员，并花几个月的时间开发，以使 Akira 成为可能。

尽管如此，如果你想支持这个项目，你应该知道一些细节，对吧？

不用担心，我们在他们的直播中问了几个问题 - 让我们看下：

### Akira：更多细节

*图片来源：Kickstarter*

如 Kickstarter 活动描述的那样：

>
> Akira 的主要目的是提供一个快速而直观的工具来**创建 Web 和移动端界面**，更像是 **Sketch**、**Figma** 或 **Adobe XD**，并且是 Linux 原生体验。
>
>
>

他们还详细描述了该工具与 Inkscape、Glade 或 QML Editor 的不同之处。当然，如果你想要了解所有的技术细节，请查看 [Kickstarter](https://www.kickstarter.com/projects/alecaddd/akira-the-linux-design-tool/description)。但是，在此之前，让我们看一看当我询问有关 Akira 的一些问题时他们说了些什么。

**问：**如果你认为你的项目类似于 Figma，人们为什么要考虑安装 Akira 而不是使用基于网络的工具？它是否只是这些工具的克隆 —— 提供原生 Linux 体验，还是有一些非常有趣的东西可以鼓励用户切换（除了是开源解决方案之外）？

**Akira：** 与基于网络的 electron 应用相比，Linux 原生体验总是更好、更快。此外，如果你选择使用 Figma，硬件配置也很重要，但 Akira 将会占用很少的系统资源，并且你可以在不需要上网的情况下完成类似工作。

**问：**假设它成为了 Linux 用户一直在等待的开源方案（拥有专有工具的类似功能）。你有什么维护计划？你是否计划引入定价方案，或依赖捐赠？

**Akira：**该项目主要依靠捐赠（类似于 [Krita 基金会](https://krita.org/en/about/krita-foundation/) 这样的想法）。但是，不会有“专业版”计划，它将免费提供，它将是一个开源项目。

根据我得到的回答，它看起来似乎很有希望，我们应该支持。

* [查看该 Kickstarter 活动](https://www.kickstarter.com/projects/alecaddd/akira-the-linux-design-tool/description)

### 总结

你怎么看 Akira？它只是一个概念吗？或者你希望看到进展？

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

---

via: <https://itsfoss.com/akira-design-tool>

作者：[Ankush Das](https://itsfoss.com/author/ankush/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


使用 shell 构建多进程的 CommandlineFu 爬虫

Summary: CommandlineFu 是一个记录脚本片段的网站，每个片段都有对应的功能说明和对应的标签。我想要做的就是尝试用 shell 写一个多进程的爬虫把这些代码片段记录在一个 org 文件中。

# 使用 shell 构建多进程的 CommandlineFu 爬虫

<img src="content_image/album_201903_11_224237ba1adl8jd18mlady.jpg" alt="" title="">

[CommandlineFu](https://www.commandlinefu.com/) 是一个记录脚本片段的网站，每个片段都有对应的功能说明和对应的标签。我想要做的就是尝试用 shell 写一个多进程的爬虫把这些代码片段记录在一个 org 文件中。

### 参数定义

这个脚本需要能够通过 `-n` 参数指定并发的爬虫数（默认为 CPU 核的数量），还要能通过 `-f` 指定保存的 org 文件路径（默认输出到 stdout）。

```
#!/usr/bin/env bash

proc_num=$(nproc)
store_file=/dev/stdout
while getopts :n:f: OPT; do
    case $OPT in
        n|+n)
            proc_num="$OPTARG"
            ;;
        f|+f)
            store_file="$OPTARG"
            ;;
        *)
            echo "usage: ${0##*/} [+-n proc_num] [+-f org_file} [--]"
            exit 2
    esac
done
shift $(( OPTIND - 1 ))
OPTIND=1
```

### 解析命令浏览页面

我们需要一个进程从 CommandlineFu 的浏览列表中抽取各个脚本片段的 URL，这个进程将抽取出来的 URL 存放到一个队列中，再由各个爬虫进程从进程中读取 URL 并从中抽取出对应的代码片段、描述说明和标签信息写入 org 文件中。

这里就会遇到三个问题:

1. 进程之间通讯的队列如何实现
2. 如何从页面中抽取出 URL、代码片段、描述说明、标签等信息
3. 多进程对同一文件进行读写时的乱序问题

#### 实现进程之间的通讯队列

这个问题比较好解决，我们可以通过一个命名管道来实现：

```
queue=$(mktemp --dry-run)
mkfifo ${queue}
exec 99<>${queue}
trap "rm ${queue} 2>/dev/null" EXIT
```

#### 从页面中抽取想要的信息

从页面中提取元素内容主要有两种方法：

1. 对于简单的 HTML 页面，我们可以通过 `sed`、`grep`、`awk` 等工具通过正则表达式匹配的方式来从 HTML 中抽取信息。
2. 通过 [html-xml-utils](https://www.w3.org/Tools/HTML-XML-utils/) 工具集中的 [hxselect](https://www.w3.org/Tools/HTML-XML-utils/man1/hxselect.html) 来根据 CSS 选择器提取相关元素。

这里我们使用 html-xml-utils 工具来提取：

```
function extract_views_from_browse_page()
{
    if [[ $# -eq 0 ]];then
        local html=$(cat -)
    else
        local html="$*"
    fi
    echo ${html} |hxclean |hxselect -c -s "\n" "li.list-group-item > div:nth-child(1) > div:nth-child(1) > a:nth-child(1)::attr(href)"|sed 's@^@https://www.commandlinefu.com/@'
}

function extract_nextpage_from_browse_page()
{
    if [[ $# -eq 0 ]];then
        local html=$(cat -)
    else
        local html="$*"
    fi
    echo ${html} |hxclean |hxselect -s "\n" "li.list-group-item:nth-child(26) > a"|grep '>'|hxselect -c "::attr(href)"|sed 's@^@https://www.commandlinefu.com/@'
}
```

这里需要注意的是：`hxselect` 对 HTML 解析时要求遵循严格的 XML 规范，因此在用 `hxselect` 解析之前需要先经过 `hxclean` 矫正。另外，为了防止 HTML 过大，超过参数列表长度，这里允许通过管道的形式将 HTML 内容传入。

#### 循环读取下一页的浏览页面，不断抽取代码片段 URL 写入队列

这里要解决的是上面提到的第三个问题: 多进程对管道进行读写时如何保障不出现乱序? 为此，我们需要在写入文件时对文件加锁，然后在写完文件后对文件解锁，在 shell 中我们可以使用 flock 来对文件进行枷锁。 关于 `flock` 的使用方法和注意事项，请参见另一篇博文 [Linux shell flock 文件锁的用法及注意事项](https://github.com/lujun9972/lujun9972.github.com/blob/source/linux%E5%92%8C%E5%AE%83%E7%9A%84%E5%B0%8F%E4%BC%99%E4%BC%B4/linux%20shell%20flock%E6%96%87%E4%BB%B6%E9%94%81%E7%9A%84%E7%94%A8%E6%B3%95%E5%8F%8A%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9.org)。

由于需要在 `flock` 子进程中使用函数 `extract_views_from_browse_page`，因此需要先导出该函数：

```
export -f extract_views_from_browse_page
```

由于网络问题，使用 `curl` 获取内容可能失败，需要重复获取：

```
function fetch()
{
    local url="$1"
    while ! curl -L ${url} 2>/dev/null;do
        :
    done
}
```

`collector` 用来从种子 URL 中抓取待爬的 URL，写入管道文件中，写操作期间管道文件同时作为锁文件：

```
function collector()
{
    url="$*"
    while [[ -n ${url} ]];do
        echo "从$url中抽取"
        html=$(fetch "${url}")
        echo "${html}"|flock ${queue} -c "extract_views_from_browse_page >${queue}"
        url=$(echo "${html}"|extract_nextpage_from_browse_page)
    done
    # 让后面解析代码片段的爬虫进程能够正常退出，而不至于被阻塞.
    for ((i=0;i<${proc_num};i++))
    do
        echo >${queue}
    done
}

```

这里要注意的是， 在找不到下一页 URL 后，我们用一个 for 循环往队列里写入了 `=proc_num=` 个空行，这一步的目的是让后面解析代码片段的爬虫进程能够正常退出，而不至于被阻塞。

### 解析脚本片段页面

我们需要从脚本片段的页面中抽取标题、代码片段、描述说明以及标签信息，同时将这些内容按 org 模式的格式写入存储文件中。

```
  function view_page_handler()
  {
      local url="$1"
      local html="$(fetch "${url}")"
      # headline
      local headline="$(echo ${html} |hxclean |hxselect -c -s "\n" ".col-md-8 > h1:nth-child(1)")"
      # command
      local command="$(echo ${html} |hxclean |hxselect -c -s "\n" ".col-md-8 > div:nth-child(2) > span:nth-child(2)"|pandoc -f html -t org)"
      # description
      local description="$(echo ${html} |hxclean |hxselect -c -s "\n" ".col-md-8 > div.description"|pandoc -f html -t org)"
      # tags
      local tags="$(echo ${html} |hxclean |hxselect -c -s ":" ".functions > a")"
      if [[ -n "${tags}" ]];then
          tags=":${tags}"
      fi
      # build org content
      cat <<EOF |flock -x ${store_file} tee -a ${store_file}
* ${headline}      ${tags}

:PROPERTIES:
:URL:       ${url}
:END:

${description}
#+begin_src shell
${command}
#+end_src

EOF
  }
```

这里抽取信息的方法跟上面的类似，不过代码片段和描述说明中可能有一些 HTML 代码，因此通过 `pandoc` 将之转换为 org 格式的内容。

注意最后输出 org 模式的格式并写入存储文件中的代码不要写成下面这样：

```
    flock -x ${store_file} cat <<EOF >${store_file}
    * ${headline}\t\t ${tags}
    ${description}
    #+begin_src shell
    ${command}
    #+end_src
EOF
```

它的意思是使用 `flock` 对 `cat` 命令进行加锁，再把 `flock` 整个命令的结果通过重定向输出到存储文件中，而重定向输出的这个过程是没有加锁的。

`spider` 从管道文件中读取待抓取的 URL，然后实施真正的抓取动作。

```
function spider()
{
    while :
    do
        if ! url=$(flock ${queue} -c 'read -t 1 -u 99 url && echo $url')
        then
            sleep 1
            continue
        fi

        if [[ -z "$url" ]];then
            break
        fi
        view_page_handler ${url}
    done
}
```

这里要注意的是，为了防止发生死锁，从管道中读取 URL 时设置了超时，当出现超时就意味着生产进程赶不上消费进程的消费速度,因此消费进程休眠一秒后再次检查队列中的 URL。

### 组合起来

```
collector "https://www.commandlinefu.com/commands/browse" &

for ((i=0;i<${proc_num};i++))
do
    spider &
done
wait
```

### 抓取其他网站

通过重新定义 `extract_views_from_browse_page`、 `extract_nextpage_from-browse_page`、 `view_page_handler` 这几个函数， 以及提供一个新的种子 URL，我们可以很容易将其改造成抓取其他网站的多进程爬虫。

例如通过下面这段代码，就可以用来爬取 [xkcd](https://xkcd.com/) 上的漫画：

```
function extract_views_from_browse_page()
{
    if [[ $# -eq 0 ]];then
        local html=$(cat -)
    else
        local html="$*"
    fi
    max=$(echo "${html}"|hxclean |hxselect -c -s "\n" "#middleContainer"|grep "Permanent link to this comic" |awk -F "/" '{print $4}')
    seq 1 ${max}|sed 's@^@https://xkcd.com/@'
}

function extract_nextpage_from_browse_page()
{
    echo ""
}

function view_page_handler()
{
    local url="$1"
    local html="$(fetch "${url}/")"
    local image="https:$(echo ${html} |hxclean |hxselect -c -s "\n" "#comic > img:nth-child(1)::attr(src)")"
    echo ${image}
    wget ${image}
}

collector "https://xkcd.com/" &
```


Windows 下 Emacs 中的 zsh shell

# Windows 下 Emacs 中的 zsh shell

运行跨平台 shell（例如 Bash 或 zsh）的最大优势在于你能在多平台上使用同样的语法和脚本。在 Windows 上设置（替换）shell 挺麻烦的，但所获得的回报远远超出这小小的付出。

MSYS2 子系统允许你在 Windows 上运行 Bash 或 zsh 之类的 shell。使用 MSYS2 很重要的一点在于确保搜索路径都指向 MSYS2 子系统本身：存在太多依赖关系了。

MSYS2 安装后默认的 shell 就是 Bash；zsh 则可以通过包管理器进行安装：

```
pacman -Sy zsh
```

通过修改 `/etc/passwd` 文件可以设置 zsh 作为默认 shell，例如：

```
mkpasswd -c | sed -e 's/bash/zsh/' | tee -a /etc/passwd
```

这会将默认 shell 从 bash 改成 zsh。

要在 Windows 上的 Emacs 中运行 zsh ，需要修改 `shell-file-name` 变量，将它指向 MSYS2 子系统中的 zsh 二进制文件。该二进制 shell 文件在 Emacs `exec-path` 变量中的某个地方。

```
(setq shell-file-name (executable-find "zsh.exe"))
```

不要忘了修改 Emacs 的 `PATH` 环境变量，因为 MSYS2 路径应该先于 Windows 路径。接上一个例子，假设 MSYS2 安装在 `c:\programs\msys2` 中，那么执行：

```
(setenv "PATH" "C:\\programs\\msys2\\mingw64\\bin;C:\\programs\\msys2\\usr\\local\\bin;C:\\programs\\msys2\\usr\\bin;C:\\Windows\\System32;C:\\Windows")
```

在 Emacs 配置文件中设置好这两个变量后，在 Emacs 中运行：

```
M-x shell
```

应该就能看到熟悉的 zsh 提示符了。

Emacs 的终端设置（eterm）与 MSYS2 的标准终端设置（xterm-256color）不一样。这意味着某些插件和主题（提示符）可能不能正常工作 - 尤其在使用 oh-my-zsh 时。

检测 zsh 否则在 Emacs 中运行很简单，使用变量 `$INSIDE_EMACS`。

下面这段代码片段取自 `.zshrc`（当以交互式 shell 模式启动时会被加载），它会在 zsh 在 Emacs 中运行时启动 git 插件并更改主题：

```
# Disable some plugins while running in Emacs
if [[ -n "$INSIDE_EMACS" ]]; then
  plugins=(git)
  ZSH_THEME="simple"
else
  ZSH_THEME="compact-grey"
fi
```

通过在本地 `~/.ssh/config` 文件中将 `INSIDE_EMACS` 变量设置为 `SendEnv` 变量……

```
Host myhost
SendEnv INSIDE_EMACS
```

……同时在 ssh 服务器的 `/etc/ssh/sshd_config` 中设置为 `AcceptEnv` 变量……

```
AcceptEnv LANG LC_* INSIDE_EMACS
```

……这使得在 Emacs shell 会话中通过 ssh 登录另一个运行着 zsh 的 ssh 服务器也能工作的很好。当在 Windows 下的 Emacs 中的 zsh 上通过 ssh 远程登录时，记得使用参数 `-t`，`-t` 参数会强制分配伪终端（之所以需要这样，时因为 Windows 下的 Emacs 并没有真正的 tty）。

跨平台，开源真是个好东西……

---

via: <https://www.onwebsecurity.com/configuration/zsh-shell-inside-emacs-on-windows.html>

作者：[Peter Mosmans](https://www.onwebsecurity.com/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[lujun9972](https://github.com/lujun9972) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


树莓派使用入门：你应该选择哪种树莓派？

# 树莓派使用入门：你应该选择哪种树莓派？

>
> 在我们的《树莓派使用入门》系列的第一篇文章中，我们将学习选择符合你要求的树莓派型号的三个标准。
>
>
>

本文是《14 天学会[树莓派](https://www.raspberrypi.org/)使用》系列文章的第一篇。虽然本系列文章主要面向没有使用过树莓派或 Linux 或没有编程经验的人群，但是肯定有些东西还是需要有经验的读者的，我希望这些读者能够留下他们有益的评论、提示和补充。如果每个人都能贡献，这将会让本系列文章对初学者、其它有经验的读者、甚至是我更受益！

言归正传，如果你想拥有一个树莓派，但不知道应该买哪个型号。或许你希望为你的教学活动或你的孩子买一个，但面对这么多的选择，却不知道应该买哪个才是正确的决定。

关于选择一个新的树莓派，我有三个主要的标准：

* **成本：** 不能只考虑树莓派板的成本，还需要考虑到你使用它时外围附件的成本。在美国，树莓派的成本区间是从 5 美元（树莓派 Zero）到 35 美元（树莓派 3 B 和 3 B+）。但是，如果你选择 Zero，那么你或许还需要一个 USB hub 去连接你的鼠标、键盘、无线网卡、以及某种显示适配器。不论你想使用树莓派做什么，除非你已经有了（假如不是全部）大部分的外设，那么你一定要把这些外设考虑到预算之中。此外，在一些国家，对于许多学生和老师，树莓派（即便没有任何外设）的购置成本也或许是一个不少的成本负担。
* **可获得性：** 根据你所在地去查找你想要的树莓派，因为在一些国家得到某些版本的树莓派可能很容易（或很困难）。在新型号刚发布后，可获得性可能是个很大的问题，在你的市场上获得最新版本的树莓派可能需要几天或几周的时间。
* **用途：** 所在地和成本可能并不会影响每个人，但每个购买者必须要考虑的是买树莓派做什么。因内存、CPU 核心、CPU 速度、物理尺寸、网络连接、外设扩展等不同衍生出八个不同的型号。比如，如果你需要一个拥有更大的“马力”时健壮性更好的解决方案，那么你或许应该选择树莓派 3 B+，它有更大的内存、最快的 CPU、以及更多的核心数。如果你的解决方案并不需要网络连接，并不用于 CPU 密集型的工作，并且需要将它隐藏在一个非常小的空间中，那么一个树莓派 Zero 将是你的最佳选择。

[维基百科的树莓派规格表](https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications) 是比较八种树莓派型号的好办法。

现在，你已经知道了如何找到适合你的树莓派了，下一篇文章中，我将介绍如何购买它。

---

via: <https://opensource.com/article/19/3/which-raspberry-pi-choose>

作者：[Anderson Silva](https://opensource.com/users/ansilva) 选题：[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/) 荣誉推出


“Linux”小程序发布一个月后，我们发现了什么

Summary: 这一个月来，这个小程序得到了八千多人的使用，一百多位贡献者实际参与了翻译贡献，其中贡献最高的“Datura stramonium L”一个人就提交了 646 条翻译！

这个小程序对新手来说太好了，同志们辛苦了！

目前还没有成熟吧，命令解释得很少，而且很多还是没有翻译

首先，tldr 就是只收录最常用的命令用法，而不是求全——求全可以看 man 或者 info。<br />
其次，tldr 项目来源于国外，自然是英文的，我们做这个小程序的意图之一，也是发动大家一起来零零散散的贡献翻译，请不要做伸手党，只有每个人都做一些贡献，世界才能更好。

希望开放个网页版,毕竟在手机上用的时候很少

# “Linux”小程序发布一个月后，我们发现了什么

一个月前，我们[发布](/article-10528-1.html)了一个小程序“**Linux**”，可以用来快速查找 Linux 中的命令常用语法。这个小程序中我们收录了上千条 Linux 命令（严格地说，几乎包含了 Unix/BSD 乃至于 OSX 等的全部命令）。该小程序的数据来源于国外的一个著名开源项目：[tldr.sh](https://tldr.sh/)，其项目托管于 [GitHub](https://github.com/tldr-pages/tldr)。

这个小程序在推出前并没有特别周密的产品设计，我们在推出后，对这个产品进行了频繁的打磨和改进。几乎每天都会发布新的更新版本。甚至连小程序的 Logo 都换了两次。现在是这个：

这一个月来，这个小程序得到了大家的踊跃支持，很多命令都得到了大家的翻译贡献。应该说，这个小程序寄托着我们的一个实验性想法：**我们希望提供一种众包式的机制，可以使大家可以利用碎片式时间来为开源文档提供碎片式的翻译**。大家可能知道，我们的翻译组 LCTT 采用了和一些国际化翻译平台及其它一些开源翻译组织不同的模式，我们通常要求一个译者完成全篇文章的翻译，而非按段落切分，这样可以保证全文的质量和用语稳定。但是，这种模式在我们试图翻译 man 手册时遇到了困难——这可能是文章类和手册类的内容性质不同所造成的。

通过这次的实验，我们发现这种模式在对手册类的内容进行翻译还是有效的。因此，我们接下来会推出针对 man 手册的小程序，会同样采用这种众包方式进行翻译。

当然，在某个条目/手册的翻译成熟后，我们会将其推送会上游，以使更多人受惠。

这一个月来，这个小程序得到了八千多人的使用，一百多位贡献者实际参与了翻译贡献，其中贡献最高的“**Datura stramonium L.**”一个人就提交了 646 条翻译！

下面我来总结一下这一个月来我们的“Linux”小程序的改进要点：

* 除了可以搜索命令名之外，还可以按描述搜索命令
* 贡献排行榜
* 首页随机推荐命令，显示最新更新动态
* 显示风格调整
* 强化贡献者呈现
* 增加了命令的延伸阅读文章
* 增加了中英文切换显示功能

接下来，我们计划进行如下改进：

* 对命令页面中的占位符进行特殊渲染
* 标定某个页面的翻译成熟，可以推送到上游
* 添加评论框，以发表评论和丰富用法示例
* 添加更新提示消息——当你编辑过的消息被再次更新，你可以收到提醒

最后，欢迎大家都来体验一下“Linux”小程序：


Linux 上最好的十六进制编辑器

hexyl的作者是写出fd和bat的那个，那两款软件都十分不错，各位有兴趣可以去他的GitHub主页看看 https://github.com/sharkdp

# Linux 上最好的十六进制编辑器

十六进制编辑器可以让你以十六进制的形式查看/编辑文件的二进制数据，因此其被命名为“十六进制”编辑器。说实话，并不是每个人都需要它。只有必须处理二进制数据的特定用户组才会使用到它。

如果你不知道它是什么，让我来举个例子。假设你拥有一个游戏的配置文件，你可以使用十六进制编辑器打开它们并更改某些值以获得更多的弹药/分数等等。想要了解有关十六进制编辑器的更多信息，你可以参阅 [Wikipedia 页面](https://en.wikipedia.org/wiki/Hex_editor)。

如果你已经知道它用来干什么了 —— 让我们来看看 Linux 上最好的十六进制编辑器。

### 5 个最好的十六进制编辑器

**注意：**这里提到的十六进制编辑器没有特定的排名顺序。

#### 1、Bless Hex Editor

**主要特点：**

* 编辑裸设备（Raw disk）
* 多级撤消/重做操作
* 多个标签页
* 转换表
* 支持插件扩展功能

Bless 是 Linux 上最流行的十六进制编辑器之一。你可以在应用中心或软件中心中找到它。否则，你可以查看它们的 [GitHub 页面](https://github.com/bwrsandman/Bless) 获取构建和相关的说明。

它可以轻松处理编辑大文件而不会降低速度 —— 因此它是一个快速的十六进制编辑器。

* [GitHub 项目](https://github.com/bwrsandman/Bless)

#### 2、GNOME Hex Editor

**主要特点：**

* 以十六进制/ASCII 格式查看/编辑
* 编辑大文件

另一个神奇的十六进制编辑器 —— 专门为 GNOME 量身定做的。我个人用的是 Elementary OS, 所以我可以在应用中心找到它。你也可以在软件中心找到它。否则请参考 [GitHub 页面](https://github.com/GNOME/ghex) 获取源代码。

你可以使用此编辑器以十六进制或 ASCII 格式查看/编辑文件。用户界面非常简单 —— 正如你在上面的图像中看到的那样。

* [官方网站](https://wiki.gnome.org/Apps/Ghex)

#### 3、Okteta

**主要特点：**

* 可自定义的数据视图
* 多个标签页
* 字符编码：支持 Qt、EBCDIC 的所有 8 位编码
* 解码表列出常见的简单数据类型

Okteta 是一个简单的十六进制编辑器，没有那么奇特的功能。虽然它可以处理大部分任务。它有一个单独的模块，你可以使用它嵌入其他程序来查看/编辑文件。

与上述所有编辑器类似，你也可以在应用中心和软件中心上找到列出的编辑器。

* [官方网站](https://www.kde.org/applications/utilities/okteta/)

#### 4、wxHexEditor

**主要特点：**

* 轻松处理大文件
* 支持 x86 反汇编
* 对磁盘设备可以显示扇区指示
* 支持自定义十六进制面板格式和颜色

这很有趣。它主要是一个十六进制编辑器，但你也可以将其用作低级磁盘编辑器。例如，如果你的硬盘有问题，可以使用此编辑器以 RAW 格式编辑原始数据以修复它。

你可以在你的应用中心和软件中心找到它。否则，可以去看看 [Sourceforge](https://sourceforge.net/projects/wxhexeditor/)。

* [官方网站](http://www.wxhexeditor.org/home.php)

#### 5、Hexedit (命令行工具)

**主要特点：**

* 运行在命令行终端上
* 它又快又简单

如果你想在终端上工作，可以继续通过控制台安装 hexedit。它是我最喜欢的 Linux 命令行的十六进制编辑器。

当你启动它时，你必须指定要打开的文件的位置，然后它会为你打开它。

要安装它，只需输入：

```
sudo apt install hexedit
```

### 结束

十六进制编辑器可以方便地进行实验和学习。如果你是一个有经验的人，你应该选择一个有更多功能的——GUI。 尽管这一切都取决于个人喜好。

你认为十六进制编辑器的有用性如何？你用哪一个？我们没有列出你最喜欢的吗？请在评论中告诉我们！

### 额外福利

译者注：要我说，以上这些十六进制编辑器都太丑了。如果你只是想美美的查看一下十六进制输出，那么下面的这个查看器十分值得看看。虽然在功能上还有些不够成熟，但至少在美颜方面可以将上面在座的各位都视作垃圾。

它就是 hexyl，是一个面向终端的简单的十六进制查看器。它使用颜色来区分不同的字节类型（NULL、可打印的 ASCII 字符、ASCII 空白字符、其它 ASCII 字符和非 ASCII 字符）。

上图：

它不仅支持各种 Linux 发行版，还支持 MacOS、FreeBSD、Windows，请自行去其[项目页](https://github.com/sharkdp/hexyl)选用，

---

via: <https://itsfoss.com/hex-editors-linux>

作者：[Ankush Das](https://itsfoss.com/author/ankush/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[zero-mk](https://github.com/zero-mk) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


树莓派使用入门：如何购买一个树莓派

Summary: 在我们的《树莓派使用入门》系列文章的第二篇中，我们将介绍获取树莓派的最佳途径。

在我们的《树莓派使用入门》系列文章的第二篇中，我们将介绍获取树莓派的最佳途径。

# 树莓派使用入门：如何购买一个树莓派

>
> 在我们的《树莓派使用入门》系列文章的第二篇中，我们将介绍获取树莓派的最佳途径。
>
>
>

在本系列指南的第一篇文章中，我们提供了一个关于 [你应该购买哪个版本的树莓派](/article-10611-1.html) 的一些建议。哪个版本才是你想要的，你应该有了主意了，现在，我们来看一下如何获得它。

最显而易见的方式 —— 并且也或许是最安全最简单的方式 —— 非 [树莓派的官方网站](https://www.raspberrypi.org/) 莫属了。如果你从官网主页上点击 “Buy a Raspberry Pi”，它将跳转到官方的 [在线商店](https://www.raspberrypi.org/products/)，在那里，它可以给你提供你的国家所在地的授权销售商。如果你的国家没有在清单中，还有一个“其它”选项，它可以提供国际订购。

第二，查看亚马逊或在你的国家里允许销售新的或二手商品的其它主流技术类零售商。鉴于树莓派比较便宜并且尺寸很小，一些小商家基于转售目的的进出口它，应该是非常容易的。在你下订单时，一定要关注对卖家的评价。

第三，打听你的极客朋友！你可能从没想过一些人的树莓派正在“吃灰”。我已经给家人送了至少三个树莓派，当然它们并不是计划要送的礼物，只是因为他们对这个“迷你计算机”感到很好奇而已。我身边有好多个，因此我让他们拿走一个！

### 不要忘了外设

最后一个建设是：不要忘了外设，你将需要一些外设去配置和操作你的树莓派。至少你会用到键盘、一个 HDMI 线缆去连接显示器、一个 Micro SD 卡去安装操作系统，一个电源线、以及一个好用的鼠标。

如果你没有准备好这些东西，试着从朋友那儿借用，或与树莓派一起购买。你可以从授权的树莓派销售商那儿考虑订购一个起步套装 —— 它可以让你避免查找的麻烦而一次性搞定。

现在，你有了树莓派，在本系列的下一篇文章中，我们将安装树莓派的操作系统并开始使用它。

---

via: <https://opensource.com/article/19/3/how-buy-raspberry-pi>

作者：[Anderson Silva](https://opensource.com/users/ansilva) 选题：[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/) 荣誉推出


如何在 WinSCP 中使用 sudo

Summary: 用截图了解如何在 WinSCP 中使用 sudo。

是不是差点什么，不好使丫 :(

用截图了解如何在 WinSCP 中使用 sudo。

# 如何在 WinSCP 中使用 sudo

>
> 用截图了解如何在 WinSCP 中使用 sudo。
>
>
>

首先你需要检查你尝试使用 WinSCP 连接的 sftp 服务器的二进制文件的位置。

你可以使用以下命令检查 SFTP 服务器二进制文件位置：

```
[root@kerneltalks ~]# cat /etc/ssh/sshd_config |grep -i sftp-server
Subsystem sftp  /usr/libexec/openssh/sftp-server
```

你可以看到 sftp 服务器的二进制文件位于 `/usr/libexec/openssh/sftp-server`。

打开 WinSCP 并单击“高级”按钮打开高级设置。

*WinSCP 高级设置*

它将打开如下高级设置窗口。在左侧面板上选择“Environment”下的 “SFTP”。你会在右侧看到选项。

现在，使用命令 `sudo su -c` 在这里添加 SFTP 服务器值，如下截图所示：

*WinSCP 中的 SFTP 服务器设置*

所以我们在设置中添加了 `sudo su -c /usr/libexec/openssh/sftp-server`。单击“Ok”并像平常一样连接到服务器。

连接之后，你将可以从你以前需要 sudo 权限的目录传输文件了。

完成了！你已经使用 WinSCP 使用 sudo 登录服务器了。

---

via: <https://kerneltalks.com/tools/how-to-use-sudo-access-in-winscp/>

作者：[kerneltalks](https://kerneltalks.com) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


关于圆周率日的趣事与庆祝方式

# 关于圆周率日的趣事与庆祝方式

>
> 技术团队喜欢 3 月 14 日的圆周率日：你是否知道这也是阿尔伯特·爱因斯坦的生日和 Linux 内核1.0.0 发布周年纪念日？来看一些树莓派的趣事和 DIY 项目。
>
>
>

今天，全世界的技术团队都会为一个数字庆祝。3 月 14 日是<ruby> 圆周率日 <rt>  Pi Day </rt></ruby>，人们会在这一天举行吃派比赛、披萨舞会，玩<ruby> 数学梗 <rt>  math puns </rt></ruby>。如果这个数学领域中的重要常数不足以让 3 月 14 日成为一个节日的话，再加上爱因斯坦的生日、Linux 内核 1.0.0 发布的周年纪念日，莱伊·惠特尼在这一天申请了轧花机的专利这些原因，应该足够了吧。（LCTT译注：[轧花机](https://zh.wikipedia.org/wiki/%E8%BB%8B%E6%A3%89%E6%A9%9F)是一种快速而且简单地分开棉花纤维和种子的机器，生产力比人手分离高得多。）

很荣幸，我们能在这一个特殊的日子里一起了解有关它的趣事和与 π 相关的好玩的活动。来吧，和你的团队一起庆祝圆周率日：找一两个点子来进行团队建设，或用新兴技术做一个项目。如果你有为这个大家所喜爱的无限小数庆祝的独特方式，请在评论区与大家分享。

### 圆周率日的庆祝方法：

* 今天是圆周率日的第 31 次周年纪念（LCTT 译注：本文写于 2018 年的圆周率日，故在细节上存在出入。例如今天（2019 年 3 月 14 日）是圆周率日的第 31 次周年纪念)。第一次为它庆祝是在旧金山的<ruby> 探索博物馆 <rt>  Exploratorium </rt></ruby>由物理学家 Larry Shaw 举行。“在[第 1 次周年纪念日](https://www.exploratorium.edu/pi/pi-day-history)当天，工作人员带来了水果派和茶壶来庆祝它。在 1 点 59 分（圆周率中紧接着 3.14 的数字），Shaw 在博物馆外领着队伍环馆一周。队伍中用扩音器播放着‘Pomp and Circumstance’。” 直到 21 年后，在 2009 年 3 月，圆周率正式成为了美国的法定假日。
* 虽然该纪念日起源于旧金山，可规模最大的庆祝活动却是在普林斯顿举行的，这个小镇举办了为期五天的[许多活动](https://princetontourcompany.com/activities/pi-day/)，包括爱因斯坦模仿比赛、掷派比赛，圆周率背诵比赛等等。其中的某些活动甚至会给获胜者提供价值 314.5 美元的奖金。
* <ruby> 麻省理工的斯隆管理学院 <rt>  MIT Sloan School of Management </rt></ruby>正在庆祝圆周率日。他们在 Twitter 上分享着关于 π 和派的圆周率日趣事，详情请关注<ruby> 推特话题 <rt>  Twitter hashtag </rt></ruby> #PiVersusPie 。

### 与圆周率有关的项目与活动：

* 如果你想锻炼你的数学技能，<ruby> 美国国家航空航天局 <rt>  National Aeronautics and Space Administration </rt></ruby>（NASA）的<ruby> 喷气推进实验室 <rt>  Jet Propulsion Lab </rt></ruby>（JPL）发布了[一系列新的数学问题](https://www.jpl.nasa.gov/news/news.php?feature=7074)，希望通过这些问题展现如何把圆周率用于空间探索。这也是美国国家航天局面向学生举办的第五届圆周率日挑战。
* 想要领略圆周率日的精神，最好的方法也许就是开展一个[树莓派](https://opensource.com/resources/raspberry-pi)项目了，无论是和你的孩子还是和你的团队一起完成，都是不错的。树莓派作为一项从 2012 年开启的项目，现在已经售出了数百万块的基本型的电脑主板。事实上，它已经在[通用计算机畅销榜上排名第三](https://www.theverge.com/circuitbreaker/2017/3/17/14962170/raspberry-pi-sales-12-5-million-five-years-beats-commodore-64)了。这里列举一些可能会吸引你的树莓派项目或活动：
	+ 来自谷歌的<ruby> 自己做 AI <rt>  AI-Yourself </rt></ruby>（AIY）项目让你自己创造一个[语音控制的数字助手](http://www.zdnet.com/article/raspberry-pi-this-google-kit-will-turn-your-pi-into-a-voice-controlled-digital-assistant/)或者[一个图像识别设备](http://www.zdnet.com/article/google-offers-raspberry-pi-owners-this-new-ai-vision-kit-to-spot-cats-people-emotions/)。
	+ 在树莓派上[使用 Kubernets](https://opensource.com/article/17/3/kubernetes-raspberry-pi)。
	+ 组装一台[怀旧游戏系统](https://opensource.com/article/18/1/retro-gaming)，目标：拯救桃子公主！
	+ 和你的团队举办一场[树莓派 Jam](https://opensource.com/article/17/5/how-run-raspberry-pi-meetup)。树莓派基金会发布了一个帮助大家顺利举办活动的[指导手册](https://www.raspberrypi.org/blog/support-raspberry-jam-community/)。据该网站说明，树莓派 Jam 旨在“给数字创作中所有年龄段的人提供支持，让世界各地志同道合的人们汇聚起来讨论和分享他们的最新项目，举办讲习班，讨论和派相关的一切。”

### 其他有关圆周率的事情：

* 当前背诵圆周率的[世界纪录保持者](http://www.pi-world-ranking-list.com/index.php?page=lists&category=pi)是 Suresh Kumar Sharma，他在 2015 年 10 月花了 17 小时零 14 分钟背出了 70,030 位数字。然而，[非官方记录](https://www.theguardian.com/science/alexs-adventures-in-numberland/2015/mar/13/pi-day-2015-memory-memorisation-world-record-japanese-akira-haraguchi)的保持者 Akira Haraguchi 声称他可以背出 111,700 位数字。
* 现在，已知的圆周率数字的长度比以往都要多。在 2016 年 11 月，R&D 科学家 Peter Trueb 计算出了 22,459,157,718,361 位圆周率数字，比 2013 年的世界记录多了 [9 万亿数字](https://www.newscientist.com/article/2124418-celebrate-pi-day-with-9-trillion-more-digits-than-ever-before/?utm_medium=Social&utm_campaign=Echobox&utm_source=Facebook&utm_term=Autofeed&cmpid=SOC%7CNSNS%7C2017-Echobox#link_time=1489480071)。据《<ruby> 新科学家 <rt>  New Scientist </rt></ruby>》所述，“最终文件包含了圆周率的 22 万亿位数字，大小接近 9 TB。如果将其打印出来，能用数百万本 1000 页的书装满一整个图书馆。”

祝你圆周率日快乐！

---

via: <https://enterprisersproject.com/article/2018/3/pi-day-12-fun-facts-and-ways-celebrate>

作者：[Carla Rudder](https://enterprisersproject.com/user/crudder) 译者：[wwhio](https://github.com/wwhio) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何 SSH 登录到 Linux 上的特定目录

竟然是deepin终端

为什么我是文件不存在呢  home都是不存在

为什么我是文件不存在呢  home都是不存在

为什么我是文件不存在呢  home都是不存在

为什么我是文件不存在呢？ 家目录都不行

# 如何 SSH 登录到 Linux 上的特定目录

你是否遇到过需要 SSH 登录到远程服务器并立即 `cd` 到一个目录来继续交互式作业？你找对地方了！这个简短的教程描述了如何直接 SSH 登录到远程 Linux 系统的特定目录。而且不仅是 SSH 登录到特定目录，你还可以在连接到 SSH 服务器后立即运行任何命令。这些没有你想的那么难。请继续阅读。

### SSH 登录到远程系统的特定目录

在我知道这个方法之前，我通常首先使用以下命令 SSH 登录到远程系统：

```
$ ssh user@remote-system
```

然后如下 `cd` 进入某个目录：

```
$ cd <some-directory>
```

然而，你不需要使用两个单独的命令。你可以用一条命令组合并简化这个任务。

看看下面的例子。

```
$ ssh -t sk@192.168.225.22 'cd /home/sk/ostechnix ; bash'
```

上面的命令将通过 SSH 连接到远程系统 （192.168.225.22） 并立即进入名为 `/home/sk/ostechnix/` 的目录，并停留在提示符中。

这里，`-t` 标志用于强制分配伪终端，这是一个必要的交互式 shell。

以下是上面命令的输出：

你也可以使用此命令：

```
$ ssh -t sk@192.168.225.22 'cd /home/sk/ostechnix ; exec bash'
```

或者，

```
$ ssh -t sk@192.168.225.22 'cd /home/sk/ostechnix && exec bash -l'
```

这里，`-l` 标志将 bash 设置为登录 shell。

在上面的例子中，我在最后一个参数中使用了 `bash`。它是我的远程系统中的默认 shell。如果你不知道远程系统上的 shell 类型，请使用以下命令：

```
$ ssh -t sk@192.168.225.22 'cd /home/sk/ostechnix && exec $SHELL'
```

就像我已经说过的，它不仅仅是连接到远程系统后 `cd` 进入目录。你也可以使用此技巧运行其他命令。例如，以下命令将进入 `/home/sk/ostechnix/`，然后执行命令 `uname -a` 。

```
$ ssh -t sk@192.168.225.22 'cd /home/sk/ostechnix && uname -a && exec $SHELL'
```

或者，你可以在远程系统上的 `.bash_profile` 文件中添加你想在 SSH 登录后执行的命令。

编辑 `.bash_profile` 文件：

```
$ nano ~/.bash_profile
```

每个命令一行。在我的例子中，我添加了下面这行：

```
cd /home/sk/ostechnix >& /dev/null
```

保存并关闭文件。最后，运行以下命令更新修改。

```
$ source ~/.bash_profile
```

请注意，你应该在远程系统的 `.bash_profile` 或 `.bashrc` 文件中添加此行，而不是在本地系统中。从现在开始，无论何时登录（无论是通过 SSH 还是直接登录），`cd` 命令都将执行，你将自动进入 `/home/sk/ostechnix/` 目录。

就是这些了。希望这篇文章有用。还有更多好东西。敬请关注！

干杯!

---

via: <https://www.ostechnix.com/how-to-ssh-into-a-particular-directory-on-linux/>

作者：[SK](https://www.ostechnix.com/author/sk/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


# 如何使得支持 OpenGL 的 Flatpak 应用和游戏在专有 Nvidia 图形驱动下工作

>
> 一些支持 OpenGL 并打包为 Flatpak 的应用和游戏无法使用专有 Nvidia 驱动启动。本文将介绍如何在不安装开源驱动（Nouveau）的情况下启动这些 Flatpak 应用或游戏。
>
>
>

这有个例子。我在我的 Ubuntu 18.04 桌面上使用专有的 Nvidia 驱动程序 （`nvidia-driver-390`），当我尝试启动以 Flatpak 形式安装的最新版本 [Krita 4.1](https://www.linuxuprising.com/2018/06/free-painting-software-krita-410.html) （构建了 OpenGL 支持）时，显示了如下错误：

```
$ /usr/bin/flatpak run --branch=stable --arch=x86_64 --command=krita --file-forwarding org.kde.krita
Gtk-Message: Failed to load module "canberra-gtk-module"
Gtk-Message: Failed to load module "canberra-gtk-module"
libGL error: No matching fbConfigs or visuals found
libGL error: failed to load driver: swrast
Could not initialize GLX
```

[Winepak](https://www.linuxuprising.com/2018/06/winepak-is-flatpak-repository-for.html) 游戏（以 Flatpak 方式打包的绑定了 Wine 的 Windows 游戏）似乎也受到了这个问题的影响，这个问题从 2016 年出现至今。

要修复使用 OpenGL 和专有 Nvidia 图形驱动时无法启动的 Flatpak 游戏和应用的问题，你需要为已安装的专有驱动安装一个运行时环境。以下是步骤。

1、如果尚未添加 FlatHub 仓库，请添加它。你可以在[此处](https://flatpak.org/setup/)找到针对 Linux 发行版的说明。

2、现在，你需要确定系统上安装的专有 Nvidia 驱动的确切版本。

这一步取决于你使用的 Linux 发行版，我无法涵盖所有情况。下面的说明是面向 Ubuntu（以及 Ubuntu 风格的版本），但希望你可以自己弄清楚系统上安装的 Nvidia 驱动版本。

要在 Ubuntu 中执行此操作，请打开 “软件与更新”，切换到 “附加驱动” 选项卡并记下 Nvidia 驱动包的名称。

比如，你可以看到我的是 “nvidia-driver-390”：

这里还没完成。我们只是找到了 Nvidia 驱动的主要版本，但我们还需要知道次要版本。要获得我们下一步所需的确切 Nvidia 驱动版本，请运行此命令（应该适用于任何基于 Debian 的 Linux 发行版，如 Ubuntu、Linux Mint 等）：

```
apt-cache policy NVIDIA-PACKAGE-NAME
```

这里的 “NVIDIA-PACKAGE-NAME” 是 “软件与更新” 中列出的 Nvidia 驱动包名称。例如，要查看 “nvidia-driver-390” 包的确切安装版本，请运行以下命令：

```
$ apt-cache policy nvidia-driver-390
nvidia-driver-390:
  Installed: 390.48-0ubuntu3
  Candidate: 390.48-0ubuntu3
  Version table:
 *** 390.48-0ubuntu3 500
        500 http://ro.archive.ubuntu.com/ubuntu bionic/restricted amd64 Packages
        100 /var/lib/dpkg/status
```

在这个命令的输出中，查找 “Installed” 部分并记下版本号（不包括 “-0ubuntu3” 之类）。现在我们知道了已安装的 Nvidia 驱动的确切版本（我例子中的是 “390.48”）。记住它，因为下一步我们需要。

3、最后，你可以从 FlatHub 为你已安装的专有 Nvidia 图形驱动安装运行时环境。

要列出 FlatHub 上所有可用的 Nvidia 运行时包，你可以使用以下命令：

```
flatpak remote-ls flathub | grep nvidia
```

幸运地是 FlatHub 上提供这个 Nvidia 驱动的运行时环境。你现在可以使用以下命令继续安装运行时：

针对 64 位系统：

```
flatpak install flathub org.freedesktop.Platform.GL.nvidia-MAJORVERSION-MINORVERSION
```

将 “MAJORVERSION” 替换为 Nvidia 驱动的主要版本（在上面的示例中为 390），将 “MINORVERSION” 替换为次要版本（步骤2，我例子中的为 48）。

例如，要为 Nvidia 图形驱动版本 390.48 安装运行时，你必须使用以下命令：

```
flatpak install flathub org.freedesktop.Platform.GL.nvidia-390-48
```

对于 32 位系统（或能够在 64 位上运行 32 位的应用或游戏），使用以下命令安装 32 位运行时：

```
flatpak install flathub org.freedesktop.Platform.GL32.nvidia-MAJORVERSION-MINORVERSION
```

再说一次，将 “MAJORVERSION” 替换为 Nvidia 驱动的主要版本（在上面的示例中为 390），将 “MINORVERSION” 替换为次要版本（步骤2，我例子中的为 48）。

比如，要为 Nvidia 图形驱动版本 390.48 安装 32 位运行时，你需要使用以下命令：

```
flatpak install flathub org.freedesktop.Platform.GL32.nvidia-390-48
```

以上就是你要运行支持 OpenGL 的 Flatpak 的应用或游戏的方法。

---

via: <https://www.linuxuprising.com/2018/06/how-to-get-flatpak-apps-and-games-built.html>

作者：[Logix](https://plus.google.com/118280394805678839070) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


将 VoIP 电话直接连接到 Asterisk 服务器

Summary: 在我的 Asterisk 服务器上正好有张以太网卡。由于我只用了其中一个，因此我决定将我的 VoIP 电话从本地网络交换机换成连接到 Asterisk 服务器。

# 将 VoIP 电话直接连接到 Asterisk 服务器

<img src="content_image/album_201903_15_103133hay6i1cbda2p41ga.jpg" alt="" title="">

在我的 [Asterisk](https://www.asterisk.org/) 服务器上正好有张以太网卡。由于我只用了其中一个，因此我决定将我的 VoIP 电话从本地网络交换机换成连接到 Asterisk 服务器。

主要的好处是这台运行着未知质量的专有软件的电话，在我的一般家庭网络中不能用了。最重要的是，它不再能访问互联网，因此无需手动配置防火墙。

以下是我配置的方式。

### 私有网络配置

在服务器上，我在 `/etc/network/interfaces` 中给第二块网卡分配了一个静态 IP：

```
auto eth1
iface eth1 inet static
    address 192.168.2.2
    netmask 255.255.255.0
```

在 VoIP 电话上，我将静态 IP 设置成 `192.168.2.3`，DNS 服务器设置成 `192.168.2.2`。我接着将 SIP 注册 IP 地址设置成 `192.168.2.2`。

DNS 服务器实际上是一个在 Asterisk 服务器上运行的 [unbound 守护进程](https://feeding.cloud.geek.nz/posts/setting-up-your-own-dnssec-aware/)。我唯一需要更改的配置是监听第二张网卡，并允许 VoIP 电话进入：

```
server:
    interface: 127.0.0.1
    interface: 192.168.2.2
    access-control: 0.0.0.0/0 refuse
    access-control: 127.0.0.1/32 allow
    access-control: 192.168.2.3/32 allow
```

最后，我在 `/etc/network/iptables.up.rules` 中打开了服务器防火墙上的正确端口：

```
-A INPUT -s 192.168.2.3/32 -p udp --dport 5060 -j ACCEPT
-A INPUT -s 192.168.2.3/32 -p udp --dport 10000:20000 -j ACCEPT
```

### 访问管理页面

现在 VoIP 电话不能在本地网络上用了，因此无法访问其管理页面。从安全的角度来看，这是一件好事，但它有点不方便。

因此，在通过 ssh 连接到 Asterisk 服务器之后，我将以下内容放在我的 `~/.ssh/config` 中以便通过 `http://localhost:8081` 访问管理页面：

```
Host asterisk
    LocalForward 8081 192.168.2.3:80
```

---

via: <https://feeding.cloud.geek.nz/posts/connecting-voip-phone-directly-to-asterisk-server/>

作者：[François Marier](https://fmarier.org/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


HTTP 简史

# HTTP 简史

<img src="content_image/album_201903_15_113125i4xs82s8r5ssa785.jpg" alt="" title="">

>
> 译注：本文来源于 2013 年出版的《[High Performance Browser Networking](https://hpbn.co)》的第九章，因此有些信息略有过时。事实上，现在 HTTP/2 已经有相当的不是，而新的 HTTP/3 也在设计和标准制定当中。
>
>
>

### 介绍

<ruby> 超文本传输协议 <rt>  Hypertext Transfer Protocol </rt></ruby>（HTTP）是互联网上最普遍和广泛采用的应用程序协议之一。它是客户端和服务器之间的通用语言，支持现代 Web。从最初作为单个的关键字和文档路径开始，它已成为不仅仅是浏览器的首选协议，而且几乎是所有连接互联网硬件和软件应用程序的首选协议。

在本文中，我们将简要回顾 HTTP 协议的发展历史。对 HTTP 不同语义的完整讨论超出了本文的范围，但理解 HTTP 的关键设计变更以及每个变更背后的动机将为我们讨论 HTTP 性能提供必要的背景，特别是在 HTTP/2 中即将进行的许多改进。

### HTTP 0.9: 单行协议

<ruby> 蒂姆·伯纳斯·李 <rt>  Tim Berners-Lee </rt></ruby> 最初的 HTTP 提案在设计时考虑到了简单性，以帮助他采用他的另一个新想法：<ruby> 万维网 <rt>  World Wide Web </rt></ruby>。这个策略看起来奏效了：注意，他是一个有抱负的协议设计者。

1991 年，伯纳斯·李概述了这个新协议的动机，并列出了几个高级设计目标：文件传输功能、请求超文档存档索引搜索的能力，格式协商以及将客户端引用到另一个服务器的能力。为了证明该理论的实际应用，构建了一个简单原型，它实现了所提议功能的一小部分。

* 客户端请求是一个 ASCII 字符串。
* 客户端请求以回车符（CRLF）终止。
* 服务器响应是 ASCII 字符流。
* 服务器响应是一种超文本标记语言（HTML）。
* 文档传输完成后连接终止。

然而，即使这听起来也比实际复杂得多。这些规则支持的是一种非常简单的，对 Telnet 友好的协议，一些 Web 服务器至今仍然支持这种协议：

```
$> telnet google.com 80

Connected to 74.125.xxx.xxx

GET /about/

(hypertext response)
(connection closed)
```

请求包含这样一行：`GET` 方法和请求文档的路径。响应是一个超文本文档，没有标题或任何其他元数据，只有 HTML。真的是再简单不过了。此外，由于之前的交互是预期协议的子集，因此它获得了一个非官方的 HTTP 0.9 标签。其余的，就像他们所说的，都是历史。

从 1991 年这些不起眼的开始，HTTP 就有了自己的生命，并在接下来几年里迅速发展。让我们快速回顾一下 HTTP 0.9 的特性：

* 采用客户端-服务器架构，是一种请求-响应协议。
* 采用 ASCII 协议，运行在 TCP/IP 链路上。
* 旨在传输超文本文档（HTML）。
* 每次请求后，服务器和客户端之间的连接都将关闭。

>
> 流行的 Web 服务器，如 Apache 和 Nginx，仍然支持 HTTP 0.9 协议，部分原因是因为它没有太多功能！如果你感兴趣，打开 Telnet 会话并尝试通过 HTTP 0.9 访问 google.com 或你最喜欢的网站，并检查早期协议的行为和限制。
>
>
>

### HTTP/1.0: 快速增长和 Informational RFC

1991 年至 1995 年期间，HTML 规范和一种称为 “web 浏览器”的新型软件快速发展，面向消费者的公共互联网基础设施也开始出现并快速增长。

>
> **完美风暴：1990 年代初的互联网热潮**
>
>
> 基于蒂姆·伯纳斯·李最初的浏览器原型，美国国家超级计算机应用中心（NCSA）的一个团队决定实现他们自己的版本。就这样，第一个流行的浏览器诞生了：NCSA Mosaic。1994 年 10 月，NCSA 团队的一名程序员 Marc Andreessen 与 Jim Clark 合作创建了 Mosaic Communications，该公司后来改名为 Netscape（网景），并于 1994 年 12 月发布了 Netscape Navigator 1.0。从这一点来说，已经很清楚了，万维网已经不仅仅是学术上的好奇心了。
>
>
> 实际上，同年在瑞士日内瓦组织了第一次万维网会议，这导致<ruby> 万维网联盟 <rt>  World Wide Web Consortium </rt></ruby>（W3C）的成立，以帮助指导 HTML 的发展。同样，在 IETF 内部建立了一个并行的<ruby> HTTP 工作组 <rt>  HTTP Working Group </rt></ruby>（HTTP-WG），专注于改进 HTTP 协议。后来这两个团体一直对 Web 的发展起着重要作用。
>
>
> 最后，完美风暴来临，CompuServe，AOL 和 Prodigy 在 1994-1995 年的同一时间开始向公众提供拨号上网服务。凭借这股迅速的浪潮，Netscape 在 1995 年 8 月 9 日凭借其成功的 IPO 创造了历史。这预示着互联网热潮已经到来，人人都想分一杯羹！
>
>
>

不断增长的新 Web 所需功能及其在公共网站上的应用场景很快暴露了 HTTP 0.9 的许多基础限制：我们需要一种能够提供超文本文档、提供关于请求和响应的更丰富的元数据，支持内容协商等等的协议。相应地，新兴的 Web 开发人员社区通过一个特殊的过程生成了大量实验性的 HTTP 服务器和客户端实现来回应：实现，部署，并查看其他人是否采用它。

从这些急速增长的实验开始，一系列最佳实践和常见模式开始出现。1996 年 5 月，<ruby> HTTP 工作组 <rt>  HTTP Working Group </rt></ruby>（HTTP-WG）发布了 RFC 1945，它记录了许多被广泛使用的 HTTP/1.0 实现的“常见用法”。请注意，这只是一个信息性 RFC：HTTP/1.0，如你所知的，它不是一个正式规范或 Internet 标准！

话虽如此，HTTP/1.0 请求看起来应该是：

```
$> telnet website.org 80

Connected to xxx.xxx.xxx.xxx

GET /rfc/rfc1945.txt HTTP/1.0 ❶
User-Agent: CERN-LineMode/2.15 libwww/2.17b3
Accept: */*

HTTP/1.0 200 OK ❷
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 01 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 1 May 1996 12:45:26 GMT
Server: Apache 0.84

(plain-text response)
(connection closed)
```

* ❶ 请求行有 HTTP 版本号，后面跟请求头
* ❷ 响应状态，后跟响应头

前面的交互并不是 HTTP/1.0 功能的详尽列表，但它确实说明了一些关键的协议更改：

* 请求可能多个由换行符分隔的请求头字段组成。
* 响应对象的前缀是响应状态行。
* 响应对象有自己的一组由换行符分隔的响应头字段。
* 响应对象不限于超文本。
* 每次请求后，服务器和客户端之间的连接都将关闭。

请求头和响应头都保留为 ASCII 编码，但响应对象本身可以是任何类型：HTML 文件、纯文本文件、图像或任何其他内容类型。因此，HTTP 的“超文本传输”部分在引入后不久就变成了用词不当。实际上，HTTP 已经迅速发展成为一种超媒体传输，但最初的名称没有改变。

除了媒体类型协商之外，RFC 还记录了许多其他常用功能：内容编码、字符集支持、多部分类型、授权、缓存、代理行为、日期格式等。

>
> 今天，几乎所有 Web 上的服务器都可以并且仍将使用 HTTP/1.0。不过，现在你应该更加清楚了！每个请求都需要一个新的 TCP 连接，这会对 HTTP/1.0 造成严重的性能损失。参见[三次握手](https://hpbn.co/building-blocks-of-tcp/#three-way-handshake)，接着会[慢启动](https://hpbn.co/building-blocks-of-tcp/#slow-start)。
>
>
>

### HTTP/1.1: Internet 标准

将 HTTP 转变为官方 IETF 互联网标准的工作与围绕 HTTP/1.0 的文档工作并行进行，并计划从 1995 年至 1999 年完成。事实上，第一个正式的 HTTP/1.1 标准定义于 RFC 2068，它在 HTTP/1.0 发布大约六个月后，即 1997 年 1 月正式发布。两年半后，即 1999 年 6 月，一些新的改进和更新被纳入标准，并作为 RFC 2616 发布。

HTTP/1.1 标准解决了早期版本中发现的许多协议歧义，并引入了一些关键的性能优化：保持连接，分块编码传输，字节范围请求，附加缓存机制，传输编码和请求管道。

有了这些功能，我们现在可以审视一下由任何现代 HTTP 浏览器和客户端执行的典型 HTTP/1.1 会话：

```
$> telnet website.org 80
Connected to xxx.xxx.xxx.xxx

GET /index.html HTTP/1.1 ❶
Host: website.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4)... (snip)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: __qca=P0-800083390... (snip)

HTTP/1.1 200 OK ❷
Server: nginx/1.0.11
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Via: HTTP/1.1 GWA
Date: Wed, 25 Jul 2012 20:23:35 GMT
Expires: Wed, 25 Jul 2012 20:23:35 GMT
Cache-Control: max-age=0, no-cache
Transfer-Encoding: chunked

100 ❸
<!doctype html>
(snip)

100
(snip)

0 ❹

GET /favicon.ico HTTP/1.1 ❺
Host: www.website.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4)... (snip)
Accept: */*
Referer: http://website.org/
Connection: close ❻
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: __qca=P0-800083390... (snip)

HTTP/1.1 200 OK ❼
Server: nginx/1.0.11
Content-Type: image/x-icon
Content-Length: 3638
Connection: close
Last-Modified: Thu, 19 Jul 2012 17:51:44 GMT
Cache-Control: max-age=315360000
Accept-Ranges: bytes
Via: HTTP/1.1 GWA
Date: Sat, 21 Jul 2012 21:35:22 GMT
Expires: Thu, 31 Dec 2037 23:55:55 GMT
Etag: W/PSA-GAu26oXbDi

(icon data)
(connection closed)
```

* ❶ 请求的 HTML 文件，包括编、字符集和 cookie 元数据
* ❷ 原始 HTML 请求的分块响应
* ❸ 以 ASCII 十六进制数字（256 字节）表示块中的八位元的数量
* ❹ 分块流响应结束
* ❺ 在相同的 TCP 连接上请求一个图标文件
* ❻ 通知服务器不再重用连接
* ❼ 图标响应后，然后关闭连接

哇，这里发生了很多事情！第一个也是最明显的区别是我们有两个对象请求，一个用于 HTML 页面，另一个用于图像，它们都通过一个连接完成。这就是保持连接的实际应用，它允许我们重用现有的 TCP 连接到同一个主机的多个请求，提供一个更快的最终用户体验。参见[TCP 优化](https://hpbn.co/building-blocks-of-tcp/#optimizing-for-tcp)。

要终止持久连接，注意第二个客户端请求通过 `Connection` 请求头向服务器发送显示的 `close`。类似地，一旦传输响应，服务器就可以通知客户端关闭当前 TCP 连接。从技术上讲，任何一方都可以在没有此类信号的情况下终止 TCP 连接，但客户端和服务器应尽可能提供此类信号，以便双方都启用更好的连接重用策略。

>
> HTTP/1.1 改变了 HTTP 协议的语义，默认情况下使用保持连接。这意味着，除非另有说明（通过 `Connection:close` 头），否则服务器应默认保持连接打开。
>
>
> 但是，同样的功能也被反向移植到 HTTP/1.0 上，通过 `Connection:keep-Alive` 头启用。因此，如果你使用 HTTP/1.1，从技术上讲，你不需要 `Connection:keep-Alive` 头，但许多客户端仍然选择提供它。
>
>
>

此外，HTTP/1.1 协议还添加了内容、编码、字符集，甚至语言协商、传输编码、缓存指令、客户端 cookie，以及可以针对每个请求协商的十几个其他功能。

我们不打算详细讨论每个 HTTP/1.1 特性的语义。这个主题可以写一本专门的书了，已经有了很多很棒的书。相反，前面的示例很好地说明了 HTTP 的快速进展和演变，以及每个客户端-服务器交换的错综复杂的过程，里面发生了很多事情！

>
> 要了解 HTTP 协议所有内部工作原理，参考 David Gourley 和 Brian Totty 共同撰写的权威指南： The Definitive Guide。
>
>
>

### HTTP/2: 提高传输性能

RFC 2616 自发布以来，已经成为互联网空前增长的基础：数十亿各种形状和大小的设备，从台式电脑到我们口袋里的小型网络设备，每天都在使用 HTTP 来传送新闻，视频，在我们生活中的数百万的其他网络应用程序都在依靠它。

一开始是一个简单的，用于检索超文本的简单协议，很快演变成了一种通用的超媒体传输，现在十年过去了，它几乎可以为你所能想象到的任何用例提供支持。可以使用协议的服务器无处不在，客户端也可以使用协议，这意味着现在许多应用程序都是专门在 HTTP 之上设计和部署的。

需要一个协议来控制你的咖啡壶？RFC 2324 已经涵盖了超文本咖啡壶控制协议（HTCPCP/1.0）- 它原本是 IETF 在愚人节开的一个玩笑，但在我们这个超链接的新世界中，它不仅仅意味着一个玩笑。

>
> 超文本传输协议（HTTP）是一个应用程序级的协议，用于分布式、协作、超媒体信息系统。它是一种通用的、无状态的协议，可以通过扩展请求方法、错误码和头，用于超出超文本之外的许多任务，比如名称服务器和分布式对象管理系统。HTTP 的一个特性是数据表示的类型和协商，允许独立于传输的数据构建系统。
>
>
> RFC 2616: HTTP/1.1, June 1999
>
>
>

HTTP 协议的简单性是它最初被采用和快速增长的原因。事实上，现在使用 HTTP 作为主要控制和数据协议的嵌入式设备（传感器，执行器和咖啡壶）并不罕见。但在其自身成功的重压下，随着我们越来越多地继续将日常互动转移到网络 —— 社交、电子邮件、新闻和视频，以及越来越多的个人和工作空间，它也开始显示出压力的迹象。用户和 Web 开发人员现在都要求 HTTP/1.1 提供近乎实时的响应能力和协议 性能，如果不进行一些修改，就无法满足这些要求。

为了应对这些新挑战，HTTP 必须继续发展，因此 HTTPbis 工作组在 2012 年初宣布了一项针对 HTTP/2 的新计划：

>
> 已经有一个协议中出现了新的实现经验和兴趣，该协议保留了 HTTP 的语义，但是没有保留 HTTP/1.x 的消息框架和语法，这些问题已经被确定为妨碍性能和鼓励滥用底层传输。
>
>
> 工作组将使用有序的双向流中生成 HTTP 当前语义的新表达式的规范。与 HTTP/1.x 一样，主要传输目标是 TCP，但是应该可以使用其他方式传输。
>
>
> HTTP/2 charter, January 2012
>
>
>

HTTP/2 的主要重点是提高传输性能并支持更低的延迟和更高的吞吐量。主要的版本增量听起来像是一个很大的步骤，但就性能而言，它将是一个重大的步骤，但重要的是要注意，没有任何高级协议语义收到影响：所有的 HTTP 头，值和用例是相同的。

任何现有的网站或应用程序都可以并且将通过 HTTP/2 传送而无需修改。你无需修改应用程序标记来利用 HTTP/2。HTTP 服务器将来一定会使用 HTTP/2，但这对大多数用户来说应该是透明的升级。如果工作组实现目标，唯一的区别应该是我们的应用程序以更低的延迟和更好的网络连接利用率来传送数据。

话虽如此，但我们不要走的太远了。在讨论新的 HTTP/2 协议功能之前，有必要回顾一下我们现有的 HTTP/1.1 部署和性能最佳实践。HTTP/2 工作组正在新规范上取得快速的进展，但即使最终标准已经完成并准备就绪，在可预见的未来，我们仍然必须支持旧的 HTTP/1.1 客户端，实际上，这得十年或更长时间。

---

via: <https://hpbn.co/brief-history-of-http/#http-09-the-one-line-protocol>

作者：[Ilya Grigorik](https://www.igvita.com/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[MjSeven](https://github.com/MjSeven) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在 Linux 中安装、配置和使用 Fish Shell？

# 如何在 Linux 中安装、配置和使用 Fish Shell？

每个 Linux 管理员都可能听到过 shell 这个词。你知道什么是 shell 吗? 你知道 shell 在 Linux 中的作用是什么吗？ Linux 中有多少个 shell 可用？

shell 是一个程序，它是提供用户和内核之间交互的接口。

内核是 Linux 操作系统的核心，它管理用户和操作系统之间的所有内容。Shell 可供所有用户在启动终端时使用。终端启动后，用户可以运行任何可用的命令。当 shell 完成命令的执行时，你将在终端窗口上获取输出。

Bash（全称是 Bourne Again Shell）是运行在今天的大多数 Linux 发行版上的默认的 shell，它非常受欢迎，并具有很多功能。但今天我们将讨论 Fish Shell 。

### 什么是 Fish Shell?

[Fish](https://fishshell.com/) 是友好的交互式 shell ，是一个功能齐全，智能且对用户友好的 Linux 命令行 shell ，它带有一些在大多数 shell 中都不具备的方便功能。

这些功能包括自动补全建议、Sane Scripting、手册页补全、基于 Web 的配置器和 Glorious VGA Color 。你对它感到好奇并想测试它吗？如果是这样，请按照以下安装步骤继续安装。

### 如何在 Linux 中安装 Fish Shell ？

它的安装非常简单，除了少数几个发行版外，它在大多数发行版中都没有。但是，可以使用以下 [fish 仓库](https://download.opensuse.org/repositories/shells:/fish:/release:/) 轻松安装。

对于基于 Arch Linux 的系统, 使用 [Pacman 命令](https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/) 来安装 fish shell。

```
$ sudo pacman -S fish
```

对于 Ubuntu 16.04/18.04 系统来说，请使用 [APT-GET 命令](https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/) 或者 [APT 命令](https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/) 安装 fish shell。

```
$ sudo apt-add-repository ppa:fish-shell/release-3
$ sudo apt-get update
$ sudo apt-get install fish
```

对于 Fedora 系统来说，请使用 [DNF 命令](https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/) 安装 fish shell。

对于 Fedora 29 系统来说：

```
$ sudo dnf config-manager --add-repo https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/shells:fish:release:3.repo
$ sudo dnf install fish
```

对于 Fedora 28 系统来说：

```
$ sudo dnf config-manager --add-repo https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_28/shells:fish:release:3.repo
$ sudo dnf install fish
```

对于 Debian 系统来说，请使用 [APT-GET 命令](https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/) 或者 [APT 命令](https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/) 安装 fish shell。

对于 Debian 9 系统来说：

```
$ sudo wget -nv https://download.opensuse.org/repositories/shells:fish:release:3/Debian_9.0/Release.key -O Release.key
$ sudo apt-key add - < Release.key
$ sudo echo 'deb http://download.opensuse.org/repositories/shells:/fish:/release:/3/Debian_9.0/ /' > /etc/apt/sources.list.d/shells:fish:release:3.list
$ sudo apt-get update
$ sudo apt-get install fish
```

对于 Debian 8 系统来说：

```
$ sudo wget -nv https://download.opensuse.org/repositories/shells:fish:release:3/Debian_8.0/Release.key -O Release.key
$ sudo apt-key add - < Release.key
$ sudo echo 'deb http://download.opensuse.org/repositories/shells:/fish:/release:/3/Debian_8.0/ /' > /etc/apt/sources.list.d/shells:fish:release:3.list
$ sudo apt-get update
$ sudo apt-get install fish
```

对于 RHEL/CentOS 系统来说，请使用 [YUM 命令](https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/) 安装 fish shell。

对于 RHEL 7 系统来说：

```
$ sudo yum-config-manager --add-repo https://download.opensuse.org/repositories/shells:/fish:/release:/3/RHEL_7/shells:fish:release:3.repo
$ sudo yum install fish
```

对于 RHEL 6 系统来说：

```
$ sudo yum-config-manager --add-repo https://download.opensuse.org/repositories/shells:/fish:/release:/3/RedHat_RHEL-6/shells:fish:release:3.repo
$ sudo yum install fish
```

对于 CentOS 7 系统来说：

```
$ sudo yum-config-manager --add-repo https://download.opensuse.org/repositories/shells:fish:release:2/CentOS_7/shells:fish:release:2.repo
$ sudo yum install fish
```

对于 CentOS 6 系统来说：

```
$ sudo yum-config-manager --add-repo https://download.opensuse.org/repositories/shells:fish:release:2/CentOS_6/shells:fish:release:2.repo
$ sudo yum install fish
```

对于 openSUSE Leap 系统来说，请使用 [Zypper 命令](https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/) 安装 fish shell。

```
$ sudo zypper addrepo https://download.opensuse.org/repositories/shells:/fish:/release:/3/openSUSE_Leap_42.3/shells:fish:release:3.repo
$ suod zypper refresh
$ sudo zypper install fish
```

### 如何使用 Fish Shell ？

一旦你成功安装了 fish shell 。只需在你的终端上输入 `fish` ，它将自动从默认的 bash shell 切换到 fish shell 。

```
$ fish
```

### 自动补全建议

当你在 fish shell 中键入任何命令时，它会在输入几个字母后以浅灰色自动建议一个命令。

一旦你得到一个建议然后按下向右光标键（LCTT 译注：原文是左，错的）就能完成它而不是输入完整的命令。

你可以在键入几个字母后立即按下向上光标键检索该命令以前的历史记录。它类似于 bash shell 的 `CTRL+r` 选项。

### Tab 补全

如果你想查看给定命令是否还有其他可能性，那么在键入几个字母后，只需按一下 `Tab` 键即可。

再次按 `Tab` 键可查看完整列表。

### 语法高亮

fish 会进行语法高亮显示，你可以在终端中键入任何命令时看到。无效的命令被着色为 `RED color` 。

同样的，有效的命令以不同的颜色显示。此外，当你键入有效的文件路径时，fish 会在其下面加下划线，如果路径无效，则不会显示下划线。

### 基于 Web 的配置器

fish shell 中有一个很酷的功能，它允许我们通过网络浏览器设置颜色、提示符、功能、变量、历史和键绑定。

在终端上运行以下命令以启动 Web 配置界面。只需按下 `Ctrl+c` 即可退出。

```
$ fish_config
Web config started at 'file:///home/daygeek/.cache/fish/web_config-86ZF5P.html'. Hit enter to stop.
qt5ct: using qt5ct plugin
^C
Shutting down.
```

### 手册页补全

其他 shell 支持可编程的补全，但只有 fish 可以通过解析已安装的手册页自动生成它们。

要使用该功能，请运行以下命令：

```
$ fish_update_completions
Parsing man pages and writing completions to /home/daygeek/.local/share/fish/generated_completions/
 3466 / 3466 : zramctl.8.gz
```

### 如何将 Fish 设置为默认 shell

如果你想测试 fish shell 一段时间，你可以将 fish shell 设置为默认 shell，而不用每次都切换它。

要这样做，首先使用以下命令获取 Fish Shell 的位置。

```
$ whereis fish
fish: /usr/bin/fish /etc/fish /usr/share/fish /usr/share/man/man1/fish.1.gz
```

通过运行以下命令将默认 shell 更改为 fish shell 。

```
$ chsh -s /usr/bin/fish
```

提示：只需验证 Fish Shell 是否已添加到 `/etc/shells` 目录中。如果不是，则运行以下命令以附加它。

```
$ echo /usr/bin/fish | sudo tee -a /etc/shells
```

完成测试后，如果要返回 bash shell ，请使用以下命令。

暂时返回：

```
$ bash
```

永久返回：

```
$ chsh -s /bin/bash
```

---

via: <https://www.2daygeek.com/linux-fish-shell-friendly-interactive-shell/>

作者：[Magesh Maruthamuthu](https://www.2daygeek.com/author/magesh/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[zero-MK](https://github.com/zero-MK) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出