分类 技术 下的文章

许多 Linux 命令现在都有使其输出更易于理解的选项。让我们了解一些可以让我们心爱的操作系统更友好的东西。

不是每个人都以二进制方式思考,他们不想在大脑中给大数字插入逗号来了解文件的大小。因此,Linux 命令在过去的几十年里不断发展,以更人性化的方式向用户显示信息,这一点也不奇怪。在今天的文章中,我们将看一看各种命令所提供的一些选项,它们使得数据变得更容易理解。

为什么默认显示不更友好一些?

如果你想知道为什么默认不显示得更人性化,毕竟,我们人类才是计算机的默认用户啊。你可能会问自己:“为什么我们不竭尽全力输出对每个人都有意义的命令的响应?”主要的答案是:改变命令的默认输出可能会干扰许多其它进程,这些进程是在期望默认响应之上构建的。其它的工具,以及过去几十年开发的脚本,如果突然以一种完全不同的格式输出,而不是它们过去所期望的那样,可能会被一种非常丑陋的方式破坏。

说真的,也许我们中的一些人可能更愿意看文件大小中的所有数字,即 1338277310 而不是 1.3G。在任何情况下,切换默认习惯都可能造成破坏,但是为更加人性化的响应提供一些简单的选项只需要让我们学习一些命令选项而已。

可以显示人性化数据的命令

有哪些简单的选项可以使 Unix 命令的输出更容易解析呢?让我们来看一些命令。

top

你可能没有注意到这个命令,但是在 top 命令中,你可以通过输入 E(大写字母 E)来更改显示全部内存使用的方式。连续按下将数字显示从 KiB 到 MiB,再到 GiB,接着是 TiB、PiB、EiB,最后回到 KiB。

认识这些单位吧?这里有一组定义:

2`10 = 1,024 = 1 KiB (kibibyte)
2`20 = 1,048,576 = 1 MiB (mebibyte)
2`30 = 1,073,741,824 = 1 GiB (gibibyte)
2`40 = 1,099,511,627,776 = 1 TiB (tebibyte)
2`50 = 1,125,899,906,842,624 = PiB (pebibyte)
2`60 = 1,152,921,504,606,846,976 = EiB (exbibyte)
2`70 = 1,180,591,620,717,411,303,424 = 1 ZiB (zebibyte)
2`80 = 1,208,925,819,614,629,174,706,176 = 1 YiB (yobibyte)

这些单位与千字节(KB)、兆字节(MB)和千兆字节(GB)密切相关。虽然它们很接近,但是它们之间仍有很大的区别:一组是基于 10 的幂,另一组是基于 2 的幂。例如,比较千字节和千兆字节,我们可以看看它们不同点:

KB = 1000 = 10`3
KiB = 1024 = 2`10

以下是 top 命令输出示例,使用 KiB 为单位默认显示:

top - 10:49:06 up 5 days, 35 min,  1 user,  load average: 0.05, 0.04, 0.01
Tasks: 158 total,   1 running, 118 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.2 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  6102680 total,  4634980 free,   392244 used,  1075456 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  5407432 avail Mem

在按下 E 之后,单位变成了 MiB:

top - 10:49:31 up 5 days, 36 min,  1 user,  load average: 0.03, 0.04, 0.01
Tasks: 158 total,   2 running, 118 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.6 sy,  0.0 ni, 99.4 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem : 5959.648 total, 4526.348 free,  383.055 used, 1050.246 buff/cache
MiB Swap: 2047.996 total, 2047.996 free,    0.000 used. 5280.684 avail Mem

再次按下 E,单位变为 GiB:

top - 10:49:49 up 5 days, 36 min,  1 user,  load average: 0.02, 0.03, 0.01
Tasks: 158 total,   1 running, 118 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
GiB Mem :    5.820 total,    4.420 free,    0.374 used,    1.026 buff/cache
GiB Swap:    2.000 total,    2.000 free,    0.000 used.    5.157 avail Mem

你还可以通过按字母 e 来更改为显示每个进程使用内存的数字单位。它将从默认的 KiB 到 MiB,再到 GiB、TiB,接着到 PiB(估计你能看到小数点后的很多 0),然后返回 KiB。下面是按了一下 e 之后的 top 输出:

top - 08:45:28 up 4 days, 22:32,  1 user,  load average: 0.02, 0.03, 0.00
Tasks: 167 total,   1 running, 118 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.2 us,  0.0 sy,  0.0 ni, 99.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  6102680 total,  4641836 free,   393348 used,  1067496 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  5406396 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  784 root      20   0  543.2m  26.8m  16.1m S   0.9  0.5   0:22.20 snapd
  733 root      20   0  107.8m   2.0m   1.8m S   0.4  0.0   0:18.49 irqbalance
22574 shs       20   0  107.5m   5.5m   4.6m S   0.4  0.1   0:00.09 sshd
    1 root      20   0  156.4m   9.3m   6.7m S   0.0  0.2   0:05.59 systemd

du

du 命令显示磁盘空间文件或目录使用了多少,如果使用 -h 选项,则将输出大小调整为最合适的单位。默认情况下,它以千字节(KB)为单位。

$ du camper*
360     camper_10.jpg
5684    camper.jpg
240     camper_small.jpg
$ du -h camper*
360K    camper_10.jpg
5.6M    camper.jpg
240K    camper_small.jpg

df

df 命令也提供了一个 -h 选项。请注意在下面的示例中是如何以千兆字节(GB)和兆字节(MB)输出的:

$ df -h | grep -v loop
Filesystem      Size  Used Avail Use% Mounted on
udev            2.9G     0  2.9G   0% /dev
tmpfs           596M  1.7M  595M   1% /run
/dev/sda1       110G  9.0G   95G   9% /
tmpfs           3.0G     0  3.0G   0% /dev/shm
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
tmpfs           3.0G     0  3.0G   0% /sys/fs/cgroup
tmpfs           596M   16K  596M   1% /run/user/121
/dev/sdb2       457G   73M  434G   1% /apps
tmpfs           596M     0  596M   0% /run/user/1000

下面的命令使用了 -h 选项,同时使用 -T 选项来显示我们正在查看的文件系统的类型。

$ df -hT /mnt2
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sdb2      ext4  457G   73M  434G   1% /apps

ls

即使是 ls,它也为我们提供了调整大小显示的选项,保证是最合理的单位。

$ ls -l camper*
-rw-rw-r-- 1 shs shs  365091 Jul 14 19:42 camper_10.jpg
-rw-rw-r-- 1 shs shs 5818597 Jul 14 19:41 camper.jpg
-rw-rw-r-- 1 shs shs  241844 Jul 14 19:45 camper_small.jpg
$ ls -lh camper*
-rw-rw-r-- 1 shs shs 357K Jul 14 19:42 camper_10.jpg
-rw-rw-r-- 1 shs shs 5.6M Jul 14 19:41 camper.jpg
-rw-rw-r-- 1 shs shs 237K Jul 14 19:45 camper_small.jpg

free

free 命令允许你以字节(B),千字节(KB),兆字节(MB)和千兆字节(GB)为单位查看内存使用情况。

$ free -b
              total        used        free      shared  buff/cache   available
Mem:     6249144320   393076736  4851625984     1654784  1004441600  5561253888
Swap:    2147479552           0  2147479552
$ free -k
              total        used        free      shared  buff/cache   available
Mem:        6102680      383836     4737924        1616      980920     5430932
Swap:       2097148           0     2097148
$ free -m
              total        used        free      shared  buff/cache   available
Mem:           5959         374        4627           1         957        5303
Swap:          2047           0        2047
$ free -g
              total        used        free      shared  buff/cache   available
Mem:              5           0           4           0           0           5
Swap:             1           0           1

tree

虽然 tree 命令与文件或内存计算无关,但它也提供了非常人性化的文件视图,它分层显示文件以说明文件是如何组织的。当你试图了解如何安排目录内容时,这种显示方式非常有用。(LCTT 译注:也可以看看 pstree,它以树状结构显示进程树。)

$ tree
.g to 
├── 123
├── appended.png 
├── appts
├── arrow.jpg
├── arrow.png
├── bin
│   ├── append
│   ├── cpuhog1
│   ├── cpuhog2
│   ├── loop
│   ├── mkhome
│   ├── runme

stat

stat 命令是另一个以非常人性化的格式显示信息的命令。它提供了更多关于文件的元数据,包括文件大小(以字节和块为单位)、文件类型、设备和 inode(索引节点)、文件所有者和组(名称和数字 ID)、以数字和 rwx 格式显示的文件权限以及文件的最后访问和修改日期。在某些情况下,它也可能显示最初创建文件的时间。

$ stat camper*
  File: camper_10.jpg
  Size: 365091          Blocks: 720        IO Block: 4096   regular file
Device: 801h/2049d      Inode: 796059      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/     shs)   Gid: ( 1000/     shs)
Access: 2018-07-19 18:56:31.841013385 -0400
Modify: 2018-07-14 19:42:25.230519509 -0400
Change: 2018-07-14 19:42:25.230519509 -0400
 Birth: -
  File: camper.jpg
  Size: 5818597         Blocks: 11368      IO Block: 4096   regular file
Device: 801h/2049d      Inode: 796058      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/     shs)   Gid: ( 1000/     shs)
Access: 2018-07-19 18:56:31.845013872 -0400
Modify: 2018-07-14 19:41:46.882024039 -0400
Change: 2018-07-14 19:41:46.882024039 -0400
 Birth: -

总结

Linux 命令提供了许多选项,可以让用户更容易理解或比较它们的输出。对于许多命令,-h 选项会显示更友好的输出格式。对于其它的,你可能必须通过使用某些特定选项或者按下某个键来查看你希望的输出。我希望这其中一些选项会让你的 Linux 系统看起来更友好一点。


via: https://www.networkworld.com/article/3296631/linux/displaying-data-in-a-human-friendly-way-on-linux.html

作者:Sandra Henry-Stocker 选题:lujun9972 译者:MjSeven 校对:wxy

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

今天,我偶然发现了一组适用于命令行重度用户的实用 BASH 脚本,这些脚本被称为 Bash-Snippets,它们对于那些整天都与终端打交道的人来说可能会很有帮助。想要查看你居住地的天气情况?它为你做了。想知道股票价格?你可以运行显示股票当前详细信息的脚本。觉得无聊?你可以看一些 YouTube 视频。这些全部在命令行中完成,你无需安装任何严重消耗内存的 GUI 应用程序。

在撰写本文时,Bash-Snippets 提供以下 19 个实用工具:

  1. Cheat – Linux 命令备忘单。
  2. Cloudup – 一个将 GitHub 仓库备份到 bitbucket 的工具。
  3. Crypt – 加解密文件。
  4. Cryptocurrency – 前 10 大加密货币的实时汇率转换。
  5. Currency – 货币转换器。
  6. Geo – 提供 wan、lan、router、dns、mac 和 ip 的详细信息。
  7. Lyrics – 从命令行快速获取给定歌曲的歌词。
  8. Meme – 创造命令行表情包。
  9. Movies – 搜索并显示电影详情。
  10. Newton – 执行数值计算一直到符号数学解析。(to 校正:这里不理解)
  11. Qrify – 将给定的字符串转换为二维码。
  12. Short – 缩短 URL
  13. Siteciphers – 检查给定 https 站点启用或禁用的密码。
  14. Stocks – 提供某些股票的详细信息。
  15. Taste – 推荐引擎提供三个类似的项目,如提供物品(如书籍、音乐、艺术家、电影和游戏等。)
  16. Todo – 命令行待办事项管理。
  17. Transfer – 从命令行快速传输文件。
  18. Weather – 显示你所在地的天气详情。
  19. Youtube-Viewer – 从终端观看 YouTube 视频。

作者可能会在将来添加更多实用程序和/或功能,因此我建议你密切关注该项目的网站或 GitHub 页面以供将来更新。

安装

你可以在任何支持 BASH 的操作系统上安装这些脚本。

首先,克隆 git 仓库,使用以下命令:

$ git clone https://github.com/alexanderepstein/Bash-Snippets

进入目录:

$ cd Bash-Snippets/

切换到最新的稳定版本:

$ git checkout v1.22.0

最后,使用以下命令安装 Bash-Snippets:

$ sudo ./install.sh

这将询问你要安装哪些脚本。只需输入 Y 并按回车键即可安装相应的脚本。如果你不想安装某些特定脚本,输入 N 并按回车键。

Do you wish to install currency [Y/n]: y

要安装所有脚本,运行:

$ sudo ./install.sh all

要安装特定的脚本,比如 currency,运行:

$ sudo ./install.sh currency

你也可以使用 Linuxbrew 包管理器来安装它。

安装所有的工具,运行:

$ brew install bash-snippets

安装特定的工具:

$ brew install bash-snippets --without-all-tools --with-newton --with-weather

另外,对于那些基于 Debian 系统的,例如 Ubuntu、Linux Mint,可以添加 PPA 源:

$ sudo add-apt-repository ppa:navanchauhan/bash-snippets
$ sudo apt update
$ sudo apt install bash-snippets

用法

需要网络连接才能使用这些工具。用法很简单。让我们来看看如何使用其中的一些脚本,我假设你已经安装了所有脚本。

1、 Currency – 货币转换器

这个脚本根据实时汇率转换货币。输入当前货币代码和要交换的货币,以及交换的金额,如下所示:

$ currency
What is the base currency: INR
What currency to exchange to: USD
What is the amount being exchanged: 10

=========================
| INR to USD
| Rate: 0.015495
| INR: 10
| USD: .154950
=========================

你也可以在单条命令中传递所有参数,如下所示:

$ currency INR USD 10

参考以下屏幕截图:

Bash-Snippets

2、 Stocks – 显示股票价格详细信息

如果你想查看一只股票价格的详细信息,输入股票即可,如下所示:

$ stocks Intel

INTC stock info
=============================================
| Exchange Name: NASDAQ
| Latest Price: 34.2500
| Close (Previous Trading Day): 34.2500
| Price Change: 0.0000
| Price Change Percentage: 0.00%
| Last Updated: Jul 12, 4:00PM EDT
=============================================

上面输出了 Intel 股票 的详情。

3、 Weather – 显示天气详细信息

让我们查看以下天气详细信息,运行以下命令:

$ weather

示例输出:

正如你在上面屏幕截图中看到的那样,它提供了 3 天的天气预报。不使用任何参数的话,它将根据你的 IP 地址显示天气详细信息。你还可以显示特定城市或国家/地区的天气详情,如下所示:

$ weather Chennai

同样,你可以查看输入以下命令来查看月相(月亮的形态):

$ weather moon

示例输出:

4、 Crypt – 加解密文件

此脚本对 openssl 做了一层包装,允许你快速轻松地加密和解密文件。

要加密文件,使用以下命令:

$ crypt -e [original file] [encrypted file]

例如,以下命令将加密 ostechnix.txt,并将其保存在当前工作目录下,名为 encrypt_ostechnix.txt

$ crypt -e ostechnix.txt encrypt_ostechnix.txt

输入两次文件密码:

Encrypting ostechnix.txt...
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:
Successfully encrypted

上面命令将使用 AES 256 位密钥加密给定文件。密码不要保存在纯文本文件中。你可以加密 .pdf、.txt、 .docx、 .doc、 .png、 .jpeg 类型的文件。

要解密文件,使用以下命令:

$ crypt -d [encrypted file] [output file]

例如:

$ crypt -d encrypt_ostechnix.txt ostechnix.txt

输入密码解密:

Decrypting encrypt_ostechnix.txt...
enter aes-256-cbc decryption password:
Successfully decrypted

5、 Movies – 查看电影详情

使用这个脚本,你可以查看电影详情。

以下命令显示了一部名为 “mother” 的电影的详情:

$ movies mother

==================================================
| Title: Mother
| Year: 2009
| Tomato: 95%
| Rated: R
| Genre: Crime, Drama, Mystery
| Director: Bong Joon Ho
| Actors: Hye-ja Kim, Bin Won, Goo Jin, Je-mun Yun
| Plot: A mother desperately searches for the killer who framed her son for a girl's horrific murder.
==================================================

6、 显示类似条目

要使用这个脚本,你需要从这里 获取 API 密钥。不过不用担心,它完全是免费的。一旦你获得 API 密钥后,将以下行添加到 ~/.bash_profileexport TASTE_API_KEY=”你的 API 密钥放在这里”。(LCTT 译注: TasteDive 是一个推荐引擎,它会根据你的品味推荐相关项目。)

现在你可以根据你提供的项目查看类似项目,如下所示:

$ taste -i Red Hot Chilli Peppers

7、 Short – 缩短 URL

这个脚本会缩短给定的 URL。

$ short <URL>

8、 Geo – 显示网络的详情

这个脚本会帮助你查找网络的详细信息,例如广域网、局域网、路由器、 dns、mac 地址和 ip 地址。

例如,要查找你的局域网 ip,运行:

$ geo -l

我系统上的输出:

192.168.43.192

查看广域网 ip:

$ geo -w

在终端中输入 geo 来查看更多详细信息。

$ geo
Geo
Description: Provides quick access for wan, lan, router, dns, mac, and ip geolocation data
Usage: geo [flag]
 -w Returns WAN IP
 -l Returns LAN IP(s)
 -r Returns Router IP
 -d Returns DNS Nameserver
 -m Returns MAC address for interface. Ex. eth0
 -g Returns Current IP Geodata
Examples:
 geo -g
 geo -wlrdgm eth0
Custom Geo Output =>
[all] [query] [city] [region] [country] [zip] [isp]
Example: geo -a 8.8.8.8 -o city,zip,isp
 -o [options] Returns Specific Geodata
 -a [address] For specific ip in -s
 -v Returns Version
 -h Returns Help Screen
 -u Updates Bash-Snippets

9、 Cheat – 显示 Linux 命令的备忘单

想参考 Linux 命令的备忘单吗?这是可能的。以下命令将显示 curl 命令的备忘单:

$ cheat curl

只需用你选择的命令替换 curl 即可显示其备忘单。这对于快速参考你要使用的任何命令非常有用。

10、 Youtube-Viewer – 观看 YouTube 视频

使用此脚本,你可以直接在终端上搜索或打开 YouTube 视频。(LCTT 译注:在媒体播放器中,而不是文本的终端中打开)

让我们来看一些有关 Ed Sheeran 的视频。

$ ytview Ed Sheeran

从列表中选择要播放的视频。所选内容将在你的默认媒体播放器中播放。

要查看艺术家的近期视频,你可以使用:

$ ytview -c [channel name]

要寻找视频,只需输入:

$ ytview -s [videoToSearch]

或者:

$ ytview [videoToSearch]

11、 cloudup – 备份 GitHub 仓库到 bitbucket

你在 GitHub 上托管过任何项目吗?如果托管过,那么你可以随时间 GitHub 仓库备份到 bitbucket,它是一种用于源代码和开发项目的基于 Web 的托管服务。

你可以使用 -a 选项一次性备份指定用户的所有 GitHub 仓库,或者备份单个仓库。

要备份 GitHub 仓库,运行:

$ cloudup

系统将要求你输入 GitHub 用户名, 要备份的仓库名称以及 bitbucket 用户名和密码等。

12、 Qrify – 将字符串转换为二维码

这个脚本将任何给定的文本字符串转换为二维码。这对于发送链接或者保存一串命令到手机非常有用。

$ qrify convert this text into qr code

示例输出:

很酷,不是吗?

13、 Cryptocurrency

它将显示十大加密货币实时汇率。

输入以下命令,然后单击回车来运行:

$ cryptocurrency

14、 Lyrics

这个脚本从命令行快速获取一首歌曲的歌词。

例如,我将获取 “who is it” 歌曲的歌词,这是一首由 迈克尔·杰克逊 Michael Jackson 演唱的流行歌曲。

$ lyrics -a michael jackson -s who is it

15、 Meme

这个脚本允许你从命令行创建简单的表情贴图。它比基于 GUI 的表情包生成器快得多。

要创建一个表情贴图,只需输入:

$ meme -f mymeme
Enter the name for the meme's background (Ex. buzz, doge, blb ): buzz
Enter the text for the first line: THIS IS A
Enter the text for the second line: MEME

这将在你当前的工作目录创建 jpg 文件。

16、 Newton

厌倦了解决复杂的数学问题?你来对了。Newton 脚本将执行数值计算,乃至于符号数学解析。

17、 Siteciphers

这个脚本可以帮助你检查在给定的 https 站点上启用/禁用哪些加密算法。(LCTT 译注:指 HTTPS 通讯中采用的加密算法)

$ siteciphers google.com

18、 Todo

它允许你直接从终端创建日常任务。

让我们来创建一些任务。

$ todo -a The first task
01). The first task Tue Jun 26 14:51:30 IST 2018

要添加其它任务,只需添加任务名称重新运行上述命令即可。

$ todo -a The second task
01). The first task Tue Jun 26 14:51:30 IST 2018
02). The second task Tue Jun 26 14:52:29 IST 2018

要查看任务列表,运行:

$ todo -g
01). The first task Tue Jun 26 14:51:30 IST 2018
02). A The second task Tue Jun 26 14:51:46 IST 2018

一旦你完成了任务,就可以将其从列表中删除,如下所示:

$ todo -r 2
Sucessfully removed task number 2
01). The first task Tue Jun 26 14:51:30 IST 2018

要清除所有任务,运行:

$ todo -c
Tasks cleared.

19、 Transfer

Transfer 脚本允许你通过互联网快速轻松地传输文件和目录。

让我们上传一个文件:

$ transfer test.txt
Uploading test.txt
################################################################################################################################################ 100.0%
Success!
Transfer Download Command: transfer -d desiredOutputDirectory ivmfj test.txt
Transfer File URL: https://transfer.sh/ivmfj/test.txt

该文件将上传到 transfer.sh 站点。Transfer.sh 允许你一次上传最大 10 GB 的文件。所有共享文件在 14 天后自动过期。如你所见,任何人都可以通过 Web 浏览器访问 URL 或使用 transfer 目录来下载文件,当然,transfer 必须安装在他/她的系统中。

现在从你的系统中移除文件。

$ rm -fr test.txt

现在,你可以随时(14 天内)从 transfer.sh 站点下载该文件,如下所示:

$ transfer -d Downloads ivmfj test.txt

获取关于此实用脚本的更多详情,参考以下指南。

获得帮助

如果你不知道如何使用特定脚本,只需输入该脚本的名称,然后按下 ENTER 键,你将会看到使用细节。以下示例显示 Qrify 脚本的帮助信息。

$ qrify
Qrify
Usage: qrify [stringtoturnintoqrcode]
Description: Converts strings or urls into a qr code.
 -u Update Bash-Snippet Tools
 -m Enable multiline support (feature not working yet)
 -h Show the help
 -v Get the tool version
Examples:
 qrify this is a test string
 qrify -m two\\nlines
 qrify github.com # notice no http:// or https:// this will fail

更新脚本

你可以随时使用 -u 选项更新已安装的工具。以下命令更新 “weather” 工具。

$ weather -u

卸载

你可以使用以下命令来卸载这些工具。

克隆仓库:

$ git clone https://github.com/alexanderepstein/Bash-Snippets

进入 Bash-Snippets 目录:

$ cd Bash-Snippets

运行以下命令来卸载脚本:

$ sudo ./uninstall.sh

输入 y,并按下回车键来移除每个脚本。

Do you wish to uninstall currency [Y/n]: y

另请阅读:

好了,这就是全部了。我必须承认,在测试这些脚本时我印象很深刻。我真的很喜欢将所有有用的脚本组合到一个包中的想法。感谢开发者。试一试,你不会失望的。

干杯!


via: https://www.ostechnix.com/collection-useful-bash-scripts-heavy-commandline-users/

作者:SK 选题:lujun9972 译者:MjSeven 校对:wxy

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

如果你在 Linux 终端工作,那么与非 Linux 用户一同工作时可能遇到困难。这些工具有助于文档兼容性和企业即时消息。

我大部分时间都在使用 Shell(命令行、终端或其它随便什么你使用的平台)上。但是,当我需要与大量其他人合作时,这可能会有点挑战,特别是在大型企业公司中 —— 除了 shell 外其他都使用。

当公司内的其他人使用与你不同的平台时,问题就会变得更加严重。我倾向于使用 Linux。如果我在 Linux 终端上做了很多日常工作,而我的大多数同事都使用 Windows 10(完全使用 GUI 端),那么事情就会变得……有问题。

幸运的是,在过去的几年里,我已经想出如何处理这些问题。我已经找到了在非 Unix 的企业环境中使用 Linux(或其他类 Unix 系统)Shell 的方法。这些工具/技巧同样适用于在公司服务器上工作的系统管理员们,就像对开发人员或营销人员一样。

让我们首先关注对于大公司中的许多人来说似乎最难解决的两个方面:文档兼容性和企业即时消息。

Linux 和非 Linux 系统之间的文档兼容性

出现的最大问题之一是简单的文字处理文档的兼容性。

假设你的公司已在 Microsoft Office 上进行了标准化。这让你难过。但不要失去希望!有很多方法可以使它(基本)可用 —— 甚至在 shell 中。

两个工具在我的武器库中至关重要:PandocWordgrinder

Wordgrinder 是一个简单、直观的文字处理器。它可能不像 LibreOffice 那样功能齐全(或者,实际上,任何主要的 GUI 文字处理应用程序),但速度很快。它很稳定。它有足够的功能(和文件格式)来完成工作。事实上,我完全在 Wordgrinder 中写了我的大部分文章和书籍。

但是有一个问题(你知道肯定会有)。

Wordgrinder 不支持 .doc(或 .docx)文件。这意味着它无法读取使用 Windows 和 MS Office 的同事发送给你的大多数文件。

这就是 Pandoc 的用武之地。它是一个简单的文档转换器,可以将各种文件作为输入(MS Word、LibreOffice、HTML、markdown 等)并将它们转换为其他内容。它支持的格式数量绝对是惊人的 —— PDF、ePub、各种幻灯片格式。它确实使格式之间的文档转换变得轻而易举。

这并不是说我不会偶尔遇到格式或功能问题。要转换有大量自定义格式、某些脚本和嵌入式图表的 Word 文档?是的,在这个过程中会丢失很多。

但实际上,Pandoc(用于转换文件)和 Wordgrinder(用于文档编辑)的组合已经证明非常有用和强大。

Linux 和非 Linux 系统之间的企业即时消息传递

每家公司都喜欢在即时通讯系统上实现标准化 —— 所有员工都可以使用它来保持实时联系。

在命令行中,这可能会变得棘手。如果贵公司使用 Google 环聊怎么办?或者 Novell GroupWise Messenger 怎么样?既没有官方支持,也没有基于终端的客户端。

谢天谢地,还有 Finch 和 Hangups

Finch 是 Pidgin(开源,多协议消息客户端)的终端版本。它支持各种协议,包括 Novell GroupWise、(很快会死的)AOL Instant Messenger 以及其他一些协议。

而 Hangups 是 Google Hangouts 客户端的开源实现 —— 包含消息历史记录和精美的标签界面。

这些方案都不会为你提供语音或视频聊天功能,但对于基于文本的消息,它们的工作得非常好。它们并不完美(Finch 的用户界面需要时间习惯),但它们肯定足以与你的同事保持联系。

这些方案能否让你在纯文本 shell 中舒适地过完一个工作日?可能不会。就个人而言,我发现(使用这些工具和其他工具)我可以轻松地将 80% 的时间花在纯文本界面上。

这感觉很棒。


via: https://www.networkworld.com/article/3235688/linux/linux-command-line-tools-for-working-with-non-linux-users.html

作者:Bryan Lunduke 译者:geekpi 校对:wxy

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

通过这篇教程提升你的 top 命令的知识。

尝试找出你的机器正在运行什么程序,以及哪个进程耗尽了内存导致系统非常非常慢 —— 这是 top 命令所能胜任的工作。

top 是一个非常有用的程序,其作用类似于 Windows 任务管理器或 MacOS 的活动监视器。在 *nix 机器上运行 top 将实时显示系统上运行的进程的情况。

$ top

取决于你运行的 top 版本,你会看到类似如下内容:

top - 08:31:32 up 1 day,  4:09,  0 users,  load average: 0.20, 0.12, 0.10
Tasks:   3 total,   1 running,   2 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.5 us,  0.3 sy,  0.0 ni, 99.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem:   4042284 total,  2523744 used,  1518540 free,   263776 buffers
KiB Swap:  1048572 total,        0 used,  1048572 free.  1804264 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    1 root      20   0   21964   3632   3124 S   0.0  0.1   0:00.23 bash
  193 root      20   0  123520  29636   8640 S   0.0  0.7   0:00.58 flask
  195 root      20   0   23608   2724   2400 R   0.0  0.1   0:00.21 top

你所用的 top 版本可能跟这个看起来不一样,特别是在显示的列上。

如何阅读输出的内容

你可以根据输出判断你正在运行的内容,但尝试去解释结果你可能会有些困惑。

前几行包含一堆统计信息(详细信息),后跟一个包含结果列的表(列)。让我们从后者开始吧。

这些是系统正在运行的进程。默认按 CPU 使用率降序排序。这意味着在列表顶部的程序正使用更多的 CPU 资源并对你的系统造成更重的负担。对于资源使用而言,这些程序是字面上的消耗资源最多的(top)进程。不得不说,top 这个名字起得很妙。

最右边的 COMMAND 一列报告进程名(启动它们的命令)。在这个例子里,进程名是 bash(一个我们正在运行 top 的命令解释器)、flask(一个 Python 写的 web 框架)和 top 自身。

其它列提供了关于进程的有用信息:

  • PID:进程 ID,一个用来定位进程的唯一标识符
  • USER:运行进程的用户
  • PR:任务的优先级
  • NI:Nice 值,优先级的一个更好的表现形式
  • VIRT:虚拟内存的大小,单位是 KiB(kibibytes)
  • RES:常驻内存大小,单位是 KiB(物理内存和虚拟内存的一部分)
  • SHR:共享内存大小,单位是 KiB(共享内存和虚拟内存的一部分)
  • S:进程状态,一般 I 代表空闲,R 代表运行,S 代表休眠,Z 代表僵尸进程,Tt 代表停止(还有其它更少见的选项)
  • %CPU:自从上次屏幕更新后的 CPU 使用率
  • %MEM:自从上次屏幕更新后的 RES 常驻内存使用率
  • TIME+:自从程序启动后总的 CPU 使用时间
  • COMMAND:启动命令,如之前描述那样

确切知道 VIRTRESSHR 值代表什么在日常操作中并不重要。重要的是要知道 VIRT 值最高的进程就是内存使用最多的进程。当你在用 top 排查为什么你的电脑运行无比卡的时候,那个 VIRT 数值最大的进程就是元凶。如果你想要知道共享内存和物理内存的确切意思,请查阅 top 手册的 Linux Memory Types 段落。

是的,我说的是 kibibytes 而不是 kilobytes。通常称为 kilobyte 的 1024 值实际上是 kibibyte。希腊语的 kilo(χίλιοι)意思是一千(例如一千米是 1000 米,一千克是 1000 克)。Kibi 是 kilo 和 binary 的合成词,意思是 1024 字节(或者 2 10 )。但是,因为这个词很难说,所以很多人在说 1024 字节的时候会说 kilobyte。top 试图在这里使用恰当的术语,所以按它说的理解就好。

屏幕更新说明

实时屏幕更新是 Linux 程序可以做的 非常酷 的事之一。这意味着程序能实时更新它们显示的内容,所以看起来是动态的,即使它们用的是文本。非常酷!在我们的例子中,更新时间间隔很重要,因为一些统计数据(%CPU%MEM)是基于上次屏幕更新的数值的。

因为我们运行在一个持久性的程序中,我们就可以输入一些命令来实时修改配置(而不是停止应用,然后用一个不同的命令行选项再次运行)。

按下 h 调用帮助界面,该界面也显示了默认延迟(屏幕更新的时间间隔)。这个值默认(大约)是 3 秒,但你可以输入 d(大概是 delay 的意思)或者 s(可能是 screen 或 seconds 的意思)来修改它。

细节

在进程列表上面有一大堆有用的信息。有些细节看起来有点儿奇怪,让人困惑。但是一旦你花点儿时间来逐个过一遍,你会发现,在紧要关头,这些是非常有用的。

第一行包含系统的大致信息:

  • top:我们正在运行 top!你好!top
  • XX:YY:XX:当前时间,每次屏幕更新的时候更新
  • up(接下去是 X day, YY:ZZ):系统的 uptime,或者自从系统启动后已经过去了多长时间
  • load average(后跟三个数字):分别是过去一分钟、五分钟、15 分钟的系统负载)

第二行(Task)显示了正在运行的任务的信息,不用解释。它显示了进程总数和正在运行的、休眠中的、停止的进程数和僵尸进程数。这实际上是上述 S(状态)列的总和。

第三行(%Cpu(s))显示了按类型划分的 CPU 使用情况。数据是屏幕刷新之间的值。这些值是:

  • us:用户进程
  • sy:系统进程
  • ninice#Etymology) 用户进程
  • id:CPU 的空闲时间,这个值比较高时说明系统比较空闲
  • wa:等待时间,或者消耗在等待 I/O 完成的时间
  • hi:消耗在硬件中断的时间
  • si:消耗在软件中断的时间
  • st:“虚拟机管理程序从该虚拟机窃取的时间”

你可以通过点击 t(toggle)来展开或折叠 Task%Cpu(s) 行。

第四行(Kib Mem)和第五行(KiB Swap)提供了内存和交换空间的信息。这些数值是:

  • 总内存容量
  • 已用内存
  • 空闲内存
  • 内存的缓冲值
  • 交换空间的缓存值

默认它们是用 KiB 为单位展示的,但是按下 E(扩展内存缩放 extend memory scaling)可以轮换不同的单位:KiB、MiB、GiB、TiB、PiB、EiB(kilobytes、megabytes、gigabytes、terabytes、petabytes 和 exabytes)

top 用户手册有更多选项和配置项信息。你可以运行 man top 来查看你系统上的文档。还有很多 HTML 版的 man 手册,但是请留意,这些手册可能是针对不同 top 版本的。

两个 top 的替代品

你不必总是用 top 查看系统状态。你可以根据你的情况用其它工具来协助排查问题,尤其是当你想要更图形化或更专业的界面的时候。

htop

htop 很像 top,但是它带来了一些非常有用的东西:它可以以图形界面展示 CPU 和内存使用情况。

这是我们在刚才运行 top 的同一环境中 htop 的样子。显示更简洁,但功能却很丰富。

任务统计、负载、uptime 和进程列表仍然在,但是它有了漂亮、彩色、动态的每核 CPU 使用情况,还有图形化的内存使用情况。

以下是不同颜色的含义(你也可以通过按下 h 来获得这些信息的帮助)。

CPU 任务优先级或类型:

  • 蓝色:低优先级
  • 绿色:正常优先级
  • 红色:内核任务
  • 蓝色:虚拟任务
  • 条状图末尾的值是已用 CPU 的百分比

内存:

  • 绿色:已经使用的内存
  • 蓝色:缓冲的内存
  • 黄色:缓存内存
  • 条状图末尾的值显示已用内存和总内存

如果颜色对你没用,你可以运行 htop -C 来禁用它们;那样 htop 将使用不同的符号来展示 CPU 和内存类型。

它的底部有一组激活的快捷键提示,可以用来操作过滤结果或改变排序顺序。试着按一些快捷键看看它们能做什么。不过尝试 F9 时要小心,它会调出一个信号列表,这些信号会杀死(即停止)一个过程。我建议在生产环境之外探索这些选项。

htop 的作者 Hisham Muhammad(是的,htop 的名字就是源自 Hisham 的)在二月份的 FOSDEM 2018 做了一个简短的演讲。他阐述了 htop 不仅有简洁的图形界面,还有更现代的进程信息统计展示方式,这都是之前的工具(如 top)所不具备的。

你可以在手册页面htop 网站阅读更多关于 htop 的信息。(提示:网站背景是一个动态的 htop。)

docker stats

如果你在用 Docker,你可以运行 docker stats 来为容器状态生成一个有丰富上下文的界面。

这可能比 top 更有帮助,因为它不是按进程分类,而是按容器分类的。这点特别有用,当某个容器运行缓慢时,查看哪个容器耗资源最多比运行 top 再找到容器的进程要快。

借助于上面对 tophtop 术语的解释,你应该会更容易理解 docker stats 中的那些。然而,docker stats 文档对每一列都提供了详尽的描述。


via: https://opensource.com/article/18/8/top-tips-speed-up-computer

作者:Katie McLaughlin 选题:lujun9972 译者:ypingcn 校对:pityonline

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

嗨!今天是 netdev 会议的第 2 天,我只参加了早上的会议,但它非常有趣。今早会议的主角是 Van Jacobson 给出的一场名为 “从尽可能快中变化:教网卡以时间”的演讲,它的主题是关于互联网中拥塞控制的未来!!!

下面我将尝试着对我从这次演讲中学到的东西做总结,我几乎肯定下面的内容有些错误,但不管怎样,让我们开始吧!

这次演讲是关于互联网是如何从 1988 开始改变的,为什么现在我们需要新的算法,以及我们可以怎样改变 Linux 的网络栈来更容易地实现这些算法。

什么是拥塞控制?

在网络上的任何成员总是无时无刻地发送信息包,而在互联网上的连接之间有着极其不同的速度(某些相比其他极其缓慢),而有时候它们将被塞满!当互联网的一个设备以超过它能处理的速率接收信息包时,它将丢弃某些信息包。

你所能想象的最天真的发送信息包方式是:

  1. 将你必须发送的信息包一次性发送完。
  2. 假如你发现其中有的信息包被丢弃了,就马上重新发送这些包。

结果表明假如你按照上面的思路来实现 TCP,互联网将会崩溃并停止运转。我们知道它会崩溃是因为在 1986 年确实发生了崩溃的现象。为了解决这个问题,专家发明了拥塞控制算法 —— 描述如何避免互联网的崩溃的原始论文是 Van Jacobson 于 1988 年发表的 拥塞避免与控制(30 年前!)。

从 1988 年后互联网发生了什么改变?

在演讲中,Van Jacobson 说互联网的这些已经发生了改变:在以前的互联网上,交换机可能总是拥有比服务器更快的网卡,所以这些位于互联网中间层的服务器也可能比客户端更快,并且并不能对客户端发送信息包的速率有多大影响。

很显然今天已经不是这样的了!众所周知,今天的计算机相比于 5 年前的计算机在速度上并没有多大的提升(我们遇到了某些有关光速的问题)。所以我想路由器上的大型交换机并不会在速度上大幅领先于数据中心里服务器上的网卡。

这听起来有些糟糕,因为这意味着客户端更容易在中间层的连接中达到饱和,而这将导致互联网变慢(而且 缓冲膨胀 将带来更高的延迟)。

所以为了提高互联网的性能且不让每个路由上的任务队列都达到饱和,客户端需要表现得更好并且在发送信息包的时候慢一点。

以更慢的速率发送更多的信息包以达到更好的性能

下面的结论真的让我非常意外 —— 以更慢的速率发送信息包实际上可能会带来更好的性能(即便你是在整个传输过程中,这样做的唯一的人),下面是原因:

假设你打算发送 10MB 的数据,在你和你需要连接的客户端之间有一个中间层,并且它的传输速率非常低,例如 1MB/s。假设你可以辨别这个慢连接(或者更多的后续中间层)的速度,那么你有 2 个选择:

  1. 一次性将这 10MB 的数据发送完,然后看看会发生什么。
  2. 减慢速率使得你能够以 1MB/s 的速率传给它。

现在,无论你选择何种方式,你可能都会发生丢包的现象。所以这样看起来,你可能需要选择一次性发送所有的信息包这种方式,对吧?不!!实际上在你的数据流的中间环节丢包要比在你的数据流的最后丢包要好得多。假如在中间环节有些包被丢弃了,你需要送往的那个客户端可以察觉到这个事情,然后再告诉你,这样你就可以再次发送那些被丢弃的包,这样便没有多大的损失。但假如信息包在最末端被丢弃,那么客户端将完全没有办法知道你一次性发送了所有的信息包!所以基本上在某个时刻被丢弃的包没有让你收到 ACK 信号时,你需要启用超时机制,并且还得重新发送它们。而超时往往意味着需要花费很长时间!

所以为什么以更慢的速率发送数据会更好呢?假如你发送数据的速率快于连接中的瓶颈,这时所有的信息包将会在某个地方堆积成一个队列,这个队列将会被塞满,然后在你的数据流的最末端的信息包将会被丢弃。并且像我们刚才解释的那样,处于数据流最后面的信息包很有可能丢弃!所以相比于最初以合适的速率发送信息包,一次性发送它们将会触发超时机制,发送 10MB 的数据将会花费更长的时间。

我认为这非常酷,因为这个过程并不需要与互联网中的其他人合作 —— 即便其他的所有人都已非常快的速率传送他们的信息包,对你来说以合适的速率(中间层的瓶颈速率)传送你自己的信息包仍然更有优势。

如何辨别发送数据的合适速率:BBR!

在上面我说过:“假设你可以辨别出位于你的终端和服务器之间慢连接的速率……”,那么如何做到呢?来自 Google(Jacobson 工作的地方)的某些专家已经提出了一个算法来估计瓶颈的速率!它叫做 BBR,由于本次的分享已经很长了,所以这里不做具体介绍,但你可以参考 BBR:基于拥塞的拥塞控制来自晨读论文的总结 这两处链接。

(另外,https://blog.acolyer.org 的每日“晨读论文”总结基本上是我学习和理解计算机科学论文的唯一方式,它有可能是整个互联网上最好的博客之一!)

网络代码被设计为运行得“尽可能快“

所以,假设我们相信我们想以一个更慢的速率(例如以我们连接中的瓶颈速率)来传输数据。这很好,但网络软件并不是被设计为以一个可控速率来传输数据的!下面是我所理解的大多数网络软件怎么做的:

  1. 现在有一个队列的信息包来临;
  2. 然后软件读取队列并尽可能快地发送信息包;
  3. 就这样,没有了。

这个过程非常呆板 —— 假设我以一个非常快的速率发送信息包,而另一端的连接却非常慢。假如我所拥有的就是一个放置所有信息包的队列,当我实际要发送数据时,我并没有办法来控制这个发送过程,所以我便不能减慢这个队列传输的速率。

一个更好的方式:给每个信息包一个“最早的出发时间”

BBR 协议将会修改 Linux 内核中 skb 的数据结构(这个数据结构被用来表达网络信息包),使得它有一个时间戳,这个时间戳代表着这个信息包应该被发送出去的最早时间。

对于 Linux 网络栈我不知道更多的详情了,但对于我来说,这个协议最有趣的地方是这个改动并不是一个非常大的改动!它只是添加了一个额外的时间戳而已。

用时间轮盘替换队列!!!

一旦我们将时间戳打到这些信息包上,我们怎样在合适的时间将它们发送出去呢?使用时间轮盘

在前不久的“我们喜爱的论文”活动中(这是关于这次聚会描述的某些好的链接),有一个演讲谈论了关于时间轮盘的话题。时间轮盘是一类用来指导 Linux 的进程调度器决定何时运行进程的算法。

Van Jacobson 说道:时间轮盘实际上比队列调度工作得更好 —— 它们都提供常数时间的操作,但因为某些缓存机制,时间轮盘的常数要更小一些。我真的没有太明白这里他说的关于性能的解释。

他说道:关于时间轮盘的一个关键点是你可以很轻松地用时间轮盘实现一个队列(但反之不能!)—— 假如每次你增加一个新的信息包,在最开始你说我想让它现在就被发送走,很显然这样你就可以得到一个队列了。而这个时间轮盘方法是向后兼容的,它使得你可以更容易地实现某些更加复杂的对流量非常敏感的算法,例如让你针对不同的信息包以不同的速率去发送它们。

或许我们可以通过改善 Linux 来修复互联网!

对于任何影响到整个互联网规模的问题,最为棘手的问题是当你要做出改善时,例如改变互联网协议的实现,你需要面对各种不同的设备。你要面对 Linux 的机器、BSD 的机器、Windows 的机器、各种各样的手机、瞻博或者思科的路由器以及数量繁多的其他设备!

但是在网络环境中 Linux 处于某种有趣的位置上!

  • Android 手机运行着 Linux
  • 大多数的消费级 WiFi 路由器运行着 Linux
  • 无数的服务器运行着 Linux

所以在任何给定的网络连接中,实际上很有可能在两端都有一台 Linux 机器(例如一个 Linux 服务器、或者一个 Linux 路由器、一台 Android 设备)。

所以重点是假如你想大幅改善互联网上的拥塞状况,只需要改变 Linux 网络栈就会大所不同(或许 iOS 网络栈也是类似的)。这也就是为什么在本次的 Linux 网络会议上有这样的一个演讲!

互联网仍在改变!酷!

通常我以为 TCP/IP 仍然是上世纪 80 年代的东西,所以当从这些专家口中听说这些我们正在设计的网路协议仍然有许多严重的问题时,真的是非常有趣,并且听说现在有不同的方式来设计它们。

当然也确实是这样 —— 网络硬件以及和速度相关的任何设备,以及人们使用网络来干的各种事情(例如观看 Netflix 的节目)等等,一直都在随着时间发生着改变,所以正因为这样,我们需要为 2018 年的互联网而不是为 1988 年的互联网设计我们不同的算法。


via: https://jvns.ca/blog/2018/07/12/netdev-day-2--moving-away-from--as-fast-as-possible/

作者:Julia Evans 译者:FSSlc 校对:wxy

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

这篇文章受到了我与同事讨论使用 切片 slice 作为 stack 的一次聊天的启发。后来话题聊到了 Go 语言中的切片是如何工作的。我认为这些信息对别人也有用,所以就把它记录了下来。

数组

任何关于 Go 语言切片的讨论都要从另一个数据结构也就是 数组 array 开始。Go 的数组有两个特性:

  1. 数组的长度是固定的;[5]int 是由 5 个 int 构成的数组,和 [3]int 不同。
  2. 数组是值类型。看下面这个示例:
package main

import "fmt"

func main() {
        var a [5]int
        b := a
        b[2] = 7
        fmt.Println(a, b) // prints [0 0 0 0 0] [0 0 7 0 0]
}

语句 b := a 定义了一个类型是 [5]int 的新变量 b,然后把 a 中的内容 复制b 中。改变 ba 中的内容没有影响,因为 ab 是相互独立的值。 1

切片

Go 语言的切片和数组的主要有如下两个区别:

  1. 切片没有一个固定的长度。切片的长度不是它类型定义的一部分,而是由切片内部自己维护的。我们可以使用内置的 len 函数知道它的长度。 2
  2. 将一个切片赋值给另一个切片时 不会 对切片内容进行复制操作。这是因为切片没有直接持有其内部数据,而是保留了一个指向 底层数组 3 的指针。数据都保留在底层数组里。

基于第二个特性,两个切片可以享有共同的底层数组。看下面的示例:

  1. 对切片取切片
package main

import "fmt"

func main() {
        var a = []int{1,2,3,4,5}
        b := a[2:]
        b[0] = 0
        fmt.Println(a, b) // prints [1 2 0 4 5] [0 4 5]
}

在这个例子里,ab 享有共同的底层数组 —— 尽管 b 在数组里的起始偏移量不同,两者的长度也不同。通过 b 修改底层数组的值也会导致 a 里的值的改变。

  1. 将切片传进函数
package main

import "fmt"

func negate(s []int) {
        for i := range s {
                s[i] = -s[i]
        }
}

func main() {
        var a = []int{1, 2, 3, 4, 5}
        negate(a)
        fmt.Println(a) // prints [-1 -2 -3 -4 -5]
}

在这个例子里,a 作为形参 s 的实参传进了 negate 函数,这个函数遍历 s 内的元素并改变其符号。尽管 nagate 没有返回值,且没有访问到 main 函数里的 a。但是当将之传进 negate 函数内时,a 里面的值却被改变了。

大多数程序员都能直观地了解 Go 语言切片的底层数组是如何工作的,因为它与其它语言中类似数组的工作方式类似。比如下面就是使用 Python 重写的这一小节的第一个示例:

Python 2.7.10 (default, Feb  7 2017, 00:08:15)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = [1,2,3,4,5]
>>> b = a
>>> b[2] = 0
>>> a
[1, 2, 0, 4, 5]

以及使用 Ruby 重写的版本:

irb(main):001:0> a = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):002:0> b = a
=> [1, 2, 3, 4, 5]
irb(main):003:0> b[2] = 0
=> 0
irb(main):004:0> a
=> [1, 2, 0, 4, 5]

在大多数将数组视为对象或者是引用类型的语言也是如此。 4

切片头

切片同时拥有值和指针特性的神奇之处在于理解切片实际上是一个 结构体 struct 类型。通常在 反射 reflect 包内相应部分之后的这个结构体被称作 切片头 slice header 。切片头的定义大致如下:

package runtime

type slice struct {
        ptr   unsafe.Pointer
        len   int
        cap   int
}

这很重要,因为和 map 以及 chan 这两个类型不同,切片是值类型,当被赋值或者被作为参数传入函数时候会被复制过去。

程序员们都能理解 square 的形参 vmain 中声明的 v 的是相互独立的。请看下面的例子:

package main

import "fmt"

func square(v int) {
        v = v * v
}

func main() {
        v := 3
        square(v)
        fmt.Println(v) // prints 3, not 9
}

因此 square 对自己的形参 v 的操作没有影响到 main 中的 v。下面这个示例中的 s 也是 main 中声明的切片 s 的独立副本, 而不是 指向 mains 的指针。

package main

import "fmt"

func double(s []int) {
        s = append(s, s...)
}

func main() {
        s := []int{1, 2, 3}
        double(s)
        fmt.Println(s, len(s)) // prints [1 2 3] 3
}

Go 的切片是作为值传递而不是指针这一点不太寻常。当你在 Go 内定义一个结构体时,90% 的时间里传递的都是这个结构体的指针 5 。切片的传递方式真的很不寻常,我能想到的唯一与之相同的例子只有 time.Time

切片作为值传递而不是作为指针传递这一特殊行为会让很多想要理解切片的工作原理的 Go 程序员感到困惑。你只需要记住,当你对切片进行赋值、取切片、传参或者作为返回值等操作时,你是在复制切片头结构的三个字段:指向底层数组的指针、长度,以及容量。

总结

我们来用引出这一话题的切片作为栈的例子来总结下本文的内容:

package main

import "fmt"

func f(s []string, level int) {
        if level > 5 {
               return
        }
        s = append(s, fmt.Sprint(level))
        f(s, level+1)
        fmt.Println("level:", level, "slice:", s)
}

func main() {
        f(nil, 0)
}

main 函数的最开始我们把一个 nil 切片传给了函数 f 作为 level 0 。在函数 f 里我们把当前的 level 添加到切片的后面,之后增加 level 的值并进行递归。一旦 level 大于 5,函数返回,打印出当前的 level 以及它们复制到的 s 的内容。

level: 5 slice: [0 1 2 3 4 5]
level: 4 slice: [0 1 2 3 4]
level: 3 slice: [0 1 2 3]
level: 2 slice: [0 1 2]
level: 1 slice: [0 1]
level: 0 slice: [0]

你可以注意到在每一个 levels 的值没有被别的 f 的调用影响,尽管当计算更高的 level 时作为 append 的副产品,调用栈内的四个 f 函数创建了四个底层数组 6 ,但是没有影响到当前各自的切片。

扩展阅读

如果你想要了解更多 Go 语言内切片运行的原理,我建议看看 Go 博客里的这些文章:

相关文章:

  1. If a map isn't a reference variable, what is it?
  2. What is the zero value, and why is it useful?
  3. The empty struct
  4. Should methods be declared on T or *T

  1. 这不是数组才有的特性,在 Go 语言里中 一切 赋值都是复制过去的。
  2. 你也可以在对数组使用 len 函数,但是其结果本来就人尽皆知。
  3. 有时也叫做 后台数组 backing array ,以及更不严谨的说法是后台切片。
  4. Go 语言里我们倾向于说值类型以及指针类型,因为 C++ 的 引用 reference 类型这个词产生误会。但在这里我认为调用数组作为引用类型是没有问题的。
  5. 如果你的结构体有定义在其上的方法或者用于满足某个接口,那么你传入结构体指针的比率可以飙升到接近 100%。
  6. 证明留做习题。

via: https://dave.cheney.net/2018/07/12/slices-from-the-ground-up

作者:Dave Cheney 译者:name1e5s 校对:pityonline

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