命令行上的数据科学第二版 二、开始

2023-03-31 14:40:29 浏览数 (2)

原文:https://datascienceatthecommandline.com/2e/chapter-2-getting-started.html 贡献者:Ting-xin

在这一章中,我需要确定你能够利用命令行做数据科学,为此你需要能满足一些条件。条件主要分为三个部分:(1)拥有与我在本书中使用的相同的数据集,(2)拥有一个适当的环境,拥有我在本书中使用的所有命令行工具,(3)了解使用命令行时的基本概念。

首先,我描述了如何下载数据集。其次,我解释了如何安装 Docker 镜像,它是一个基于 Ubuntu Linux 的虚拟环境,包含所有必要的命令行工具。随后,我通过例子介绍了基本的 Unix 概念。

在本章结束时,你将掌握进行数据科学的第一步,也就是获取数据。

2.1 获取数据

数据集下载步骤:

  1. 数据集压缩包下载地址:https://www.datascienceatthecommandline.com/2e/data.zip
  2. 创建一个新目录:你可以给这个目录起任何你想要的名字,但是我建议你使用小写字母、数字,可能还有连字符或下划线,以便更容易在命令行中使用。比如:dsatcl2e-data,然后记住这个目录在哪里
  3. 将 ZIP 文件移动到新的目录中,并将其解压
  4. 这个目录下每章都有一个对应的子目录

接下来我将介绍如何安装包含处理这些数据的环境,它包含的所有必要的命令行工具。

2.2 安装 Docker 镜像

在本书中,我们使用了许多不同的命令行工具。Unix 通常预装了许多命令行工具,并提供了许多包含相关工具的包。自己独立安装这些包通常不会太难。然而,我们也会使用那些不能以包的形式提供的工具,这也需要更多涉及安装的手动操作。为了获得必要的命令行工具而不必经历每个工具的安装过程,我建议安装专门为本书创建的 Docker 镜像,无论你的操作系统是 Windows、macOS 还是 Linux 。

Docker 镜像是一个或多个应用及其所有依赖项的包。Docker 容器是一个运行镜像的隔离环境,你可以使用docker命令行工具(这也是你下面要做的)或 Docker GUI 来管理 Docker 镜像和容器。在某种程度上,Docker 容器就像一个虚拟机,只是 Docker 容器使用的资源要少得多。在本章的最后,我会推荐了一些资源来学习更多关于 Docker 的知识。

如果你仍然喜欢在本地而不是在 Docker 容器中运行命令行工具,那么你当然可以自己单独安装这些命令行工具。请注意,这是一个非常耗时的过程。附录中列出了本书中使用的所有命令行工具。安装说明仅适用于 Ubuntu。本书中使用的脚本和数据集可以通过克隆本书的 GitHub 仓库得。

为了安装 Docker 镜像,首先需要从 Docker 网站中下载并安装 Docker 本身。安装 Docker 后,你就可以在终端或命令提示符下调用以下命令来下载 Docker 镜像(不要输入入美元符号):

代码语言:javascript复制
$ docker pull datasciencetoolbox/dsatcl2e

然后你可以运行 Docker 镜像,如下所示:

代码语言:javascript复制
$ docker run --rm -it datasciencetoolbox/dsatcl2e

现在你处于一个称为 Docker 容器的隔离环境中,它安装了所有必要的命令行工具。如果下面的命令绘制了一头热情的牛,那么这就表示一切工作正常:

代码语言:javascript复制
$ cowsay "Let's moove!"
 ______________
< Let's moove! >
 --------------
           ^__^
           (oo)_______
            (__)       )/
                ||----w |
                ||     ||

如果你想在容器和机器中交换数据,你可以为容器可以添加一个卷,这意味着机器中的本地目录将被映射到容器内的一个目录。所以我建议你首先创建一个新目录,然后进入这个新目录,然后在 macOS 或 Linux 上运行以下命令:

代码语言:javascript复制
$ docker run --rm -it -v "$(pwd)":/data datasciencetoolbox/dsatcl2e

或者在 Windows 上使用命令提示符(也称为cmd)上运行以下命令:

代码语言:javascript复制
C:> docker run --rm -it -v "�%":/data datasciencetoolbox/dsatcl2e

或者当你使用 Windows PowerShell 上运行以下命令:

代码语言:javascript复制
PS C:> docker run --rm -it -v ${PWD}:/data datasciencetoolbox/dsatcl2e

在上面的命令中,选项-v指示docker将当前目录映射到容器内的/data目录,因此这也是 Docker 容器和机器交换数据的地方。

如果你想要知道更多关于 Docker 镜像的知识,请访问该 网址

当这些都完成后,你可以通过输入exit命令来关闭 Docker 容器。

2.3 基本的 Unix 概念

在第一章中,我简单的给大家展示了命令行是什么。如果现在你正在运行 Docker 镜像,那么我们就可以真正开始了。在这一节中,我将讨论几个概念和工具,为了能在命令行中轻松地进行数据科学研究,你需要了解这些概念和工具。如果到目前为止,你主要用的都是图形用户界面,那么这次可能是一个相当大的改变。但是不要担心,我会从头开始,然后逐渐进入更高级的主题。

本节不是一个完整的 Unix 课程。我将只解释与做数据科学有关的概念和工具。Docker 镜像的优势之一是很多东西都已经设置好了。如果你想了解更多,请参考本章末尾的进一步阅读部分。

2.3.1 环境

现在我们刚刚进入了一个全新的环境,因此在做任何事情之前,我们都有必要对这个环境有一个大体的了解。该环境大致定义为四层,我将简单的的从上到下的介绍它们。

命令行工具

首先,也是最重要的是我们使用的命令行工具。我们通过输入相应的命令来使用它们。命令行工具有许多种类型(这个将在下一节讨论),常见例子有:lscatjq

终端

终端是第二个概念,它是我们输入命令的应用。如果你看到书中提到的以下文字:

代码语言:javascript复制
$ seq 3
1
2
3

然后你也可以跟着在你的终端上输入seq 3,按下Enter,结果就会生成一个数字序列。不要输入美元符号$,它只是告诉你这是一个你可以在终端输入的命,这个美元符号被称为提示符。

Shell

第三层是 Shell。一旦我们输入命令并按下Enter,终端就将命令发送给 Shell, Shell 是一个解释命令的程序。我使用的是 ZShell,还有许多其他可用的 Shell,比如 Bash 和 Fish。

操作系统

第四层是操作系统,在我们的例子中是 GNU/Linux。Linux 是内核的名字,它是操作系统的核心。内核直接与 CPU、磁盘和其他硬件接触,内核还执行我们的命令行工具。GNU,代表 GNU’s Not UNIX,指的是一套基本工具。Docker 镜像是基于一个特定的 GNU/Linux 发行版,该发行版称为 Ubuntu。

2.3.2 执行命令行工具

现在你已经对环境有了基本的了解,是时候尝试一些命令了。在你的终端中键入以下内容(不带美元符号),然后按Enter

代码语言:javascript复制
$ pwd
/home/dst

你刚刚执行了一个包含单个命令行工具的命令。工具pwd输出你当前所在目录的名称。默认情况下,你登录的是你的主目录。

ZShell 种内置的命令行工具cd允许你导航到不同的目录:

代码语言:javascript复制
$ cd /data/ch02 # ➊

$ pwd # ➋
/data/ch02

$ cd .. # ➌

$ pwd # ➍
/data

$ cd ch02 # ➎

➊ 导航到目录/data/ch02

➋ 打印当前目录。

➌ 导航到父目录。

➍ 再次打印当前目录。

➎ 导航到子目录ch02

cd之后的部分指定你想要去的那个目录。命令后面的值被称为命令行参数选项。两个点表示父目录。顺便说一下,一个点指的是当前目录。虽然cd .不会有任何影响,但你仍然会看到一个点被用在其他地方。接下来让我们尝试一个不同的命令:

代码语言:javascript复制
$ head -n 3 movies.txt
Matrix
Star Wars
Home Alone

这里我们将三个命令行参数传递给head。第一个是选项。这里我使用了短选项-n。有时一个短的选项有一个长的变量的意思,现在这种情况下就是--lines,第二个是属于选项的值,第三个是文件名。这个特定的命令的意思是输出文件/data/ch02/movies.txt的前三行内容。

2.3.3 命令行工具的 5 种类型

我们一直在说术语命令行工具,但是到目前为止也没有解释它的真正含义。我把它作为一个总称,指的是任何可以从命令行执行的东西(图 2.1)。实际上,每个命令行工具都是以下五种类型之一:

  • 二进制的可执行文件
  • Shell 内置程序
  • 解释脚本
  • Shell 函数
  • 别名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uRi0900m-1680148019177)(null)]

图 2.1:命令行工具作为一个总称

我们需要知道命令行之间的区别。Docker 镜像预安装的命令行工具主要包括前两种类型(二进制可执行文件和 Shell 内置程序)。其他三种类型(解释脚本、Shell 函数和别名)允许我们进一步构建我们的数据科学工具箱,从而成为更高效、更高产的数据科学家。

二进制可执行文件

二进制可执行文件是传统意义上的程序,它是通过将源代码编译为机器代码而产生的。这意味着当你在文本编辑器中打开文件时是一个乱码。

Shell 内置工具

Shell 内置工具是 Shell 提供的命令行工具,在我们的例子中是 ZShell(或zsh),它的内置工具包括cdpwd。不同 Shell 的内置工具可能不同。Shell 内置工具像二进制可执行文件一样不容易检查或更改。

解释脚本

解释脚本是一个可以由二进制可执行文件执行的文本文件。常用的脚本包括:Python、R 和 Bash 脚本。解释脚本的一个很大的优点就是你可以阅读和修改它。下面的脚本可以用 Python 执行,之所以可以被执行,不是因为它的文件扩展名是.py,而是因为脚本的第一行定义了应该执行它的二进制。

代码语言:javascript复制
$ bat fac.py
───────┬──────────────────────────────────────────────────────────────
       │ File: fac.py
───────┼──────────────────────────────────────────────────────────────
   1   │ #!/usr/bin/env python
   2   │
   3   │ def factorial(x):
   4   │     result = 1
   5   │     for i in range(2, x   1):
   6   │         result *= i
   7   │     return result
   8   │
   9   │ if __name__ == "__main__":
  10   │     import sys
  11   │     x = int(sys.argv[1])
  12   │     sys.stdout.write(f"{factorial(x)}n")
───────┴──────────────────────────────────────────────────────────────

这个脚本的作用是计算整数的阶乘,我们可以从命令行调用它,如下所示:

代码语言:javascript复制
$ ./fac.py 5
120

在第四章中,我们将详细讨论如何使用解释脚本创建可重用的命令行工具。

Shell 函数

在我们的例子中,Shell 函数是由zsh执行的函数。它们提供了与脚本相似的功能,但是它们通常(但不一定)比脚本小,也更倾向于个人化。下面的命令定义了一个名为fac的函数,就像上面解释的 Python 脚本一样,它计算我们作为参数传递的整数的阶乘。它通过使用seq生成一个数字列表,使用paste将这些数字放在一行中作为分隔符,并将该等式传递给bc,后者对其求值并输出结果。

代码语言:javascript复制
$ fac() { (echo 1; seq $1_ | paste -s -d* - | bc; }

$ fac 5
120

文件~/.zshrc是 ZShell 的配置文件,也是定义 Shell 函数的好地方,在这里定义之后就一直可用了。

别名

别名就像宏一样。如果你发现自己经常用相同的参数(或部分参数)执行某个命令,你就可以为它定义一个别名来节省时间。当你不断拼错某个命令时,别名也非常有用(Chris Wiggins 维护了一个有用的别名列表)。下面的命令就定义了这样一个别名:

代码语言:javascript复制
$ alias l='ls --color -lhF --group-directories-first'

$ alias les=less

现在,如果你在命令行上输入以下内容,Shell 将用它的值替换它发现的每个别名:

代码语言:javascript复制
$ cd /data

$ l
total 40K
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch02/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch03/
drwxr-xr-x 3 dst dst 4.0K Mar  3 10:38 ch04/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch05/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch06/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch07/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch08/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch09/
drwxr-xr-x 4 dst dst 4.0K Mar  3 10:38 ch10/
drwxr-xr-x 3 dst dst 4.0K Mar  3 10:38 csvconf/

$ cd ch02

别名比 Shell 函数简单,因为它们不允许参数。由于参数的原因,无法使用别名定义函数fac。尽管如此,别名可以让你节省大量的击键次数。像 Shell 函数一样,别名通常在文件.zshrc中定义。该文件位于你的主目录下,要查看当前定义的所有别名,可以不带参数地运行alias。试试看,你看到了什么?

在本书中,我们将主要关注最后三种类型的命令行工具:解释脚本、Shell 函数和别名,因为这些类型很容易改变。命令行工具的目的是使你的生活更加轻松,并使你成为更有生产力和效率的数据科学家。你可以通过type找到命令行工具的类型(它本身是一个 Shell 内置的工具):

代码语言:javascript复制
$ type -a pwd
pwd is a shell builtin
pwd is /usr/bin/pwd
pwd is /bin/pwd

$ type -a cd
cd is a shell builtin

$ type -a fac
fac is a shell function

$ type -a l
l is an alias for ls --color -lhF --group-directories-first

typepwd返回了三个命令行工具。在这种情况下,当你输入pwd时,将使用第一个命令行工具。在下一节中,我们将学习如何组合命令行工具。

2.3.4 组合命令行工具

因为大多数命令行工具都遵循 Unix 哲学,它们被设计成只做一件事,但是做得非常好。例如,命令行工具grep用来过滤行数据,wc用来计数行数据,sort可以排序行数据。命令行的强大之处在于它能够组合这些小而强大的命令行工具。

命令行的能力是通过管理这些工具的通信流实现的。每个工具都有三个标准通信流:标准输入、标准输出和标准错误。这些通常被简写为stdin``stdout``stderr

默认情况下,标准输出和标准错误都被重定向到终端,因此正常输出和任何错误信息都被打印在屏幕上。图 2.2 对pwdrev都进行了说明,如果你运行rev,你会看到什么都没有发生。这是因为rev期望有输入,默认情况下,就是在键盘上按下任何键。试着输入一个句子并按下回车键,rev就会立即对你的输入进行反向响应。你可以按Ctrl D来停止发送输入,然后rev就会停止。

图 2.2:工具的通信流:标准输入(stdin)、标准输出(stdout)、标准误差(stderr

但是实际上,我们不会使用键盘作为输入源,而是使用其他工具产生的输出和文件的内容。例如,通过curl,我们可以下载 Lewis Carrol 写的《Alice’s Adventures in Wonderland》这本书,然后用管道把它送到下一个工具(curl 将再第三章讨论)。管道通过用管道操作符|完成的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FTbZZlk1-1680148019354)(null)]

图 2.3:一个工具的输出通过管道传输到另一个工具

我们可以用管道将curl的输出连接到grep,以过滤每行的数据。想象一下,如果我们想看看目录中列出的章节,我们就可以将curlgrep结合起来使用,如下所示:

代码语言:javascript复制
$ curl -s "https://www.gutenberg.org/files/11/11-0.txt" | grep " CHAPTER"
 CHAPTER I.     Down the Rabbit-Hole
 CHAPTER II.    The Pool of Tears
 CHAPTER III.   A Caucus-Race and a Long Tale
 CHAPTER IV.    The Rabbit Sends in a Little Bill
 CHAPTER V.     Advice from a Caterpillar
 CHAPTER VI.    Pig and Pepper
 CHAPTER VII.   A Mad Tea-Party
 CHAPTER VIII.  The Queen’s Croquet-Ground
 CHAPTER IX.    The Mock Turtle’s Story
 CHAPTER X.     The Lobster Quadrille
 CHAPTER XI.    Who Stole the Tarts?
 CHAPTER XII.   Alice’s Evidence

如果我们想知道这本书有多少章节,我们可以使用wc,它非常擅长计数:

代码语言:javascript复制
$ curl -s "https://www.gutenberg.org/files/11/11-0.txt" |
> grep " CHAPTER" |
> wc -l # ➊
12

➊ 选项-l指定wc应该只输出传递给它的行数。默认情况下,它还返回字符数和字数。

你可以把管道操作看成是一种自动的复制和粘贴。一旦你掌握了使用管道操作符组合工具的技巧,你会发现它几乎没有任何限制。

2.3.5 重定向输入和输出

除了将一个工具的输出输送到另一个工具外,你还可以将其保存到一个文件中。该文件将被保存在当前目录下,除非给出完整的路径。这被称为输出重定向,其工作原理如下:

代码语言:javascript复制
$ curl "https://www.gutenberg.org/files/11/11-0.txt" | grep " CHAPTER" > chapter
s.txt
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  170k  100  170k    0     0   183k      0 --:--:-- --:--:-- --:--:--  184k

$ cat chapters.txt
 CHAPTER I.     Down the Rabbit-Hole
 CHAPTER II.    The Pool of Tears
 CHAPTER III.   A Caucus-Race and a Long Tale
 CHAPTER IV.    The Rabbit Sends in a Little Bill
 CHAPTER V.     Advice from a Caterpillar
 CHAPTER VI.    Pig and Pepper
 CHAPTER VII.   A Mad Tea-Party
 CHAPTER VIII.  The Queen’s Croquet-Ground
 CHAPTER IX.    The Mock Turtle’s Story
 CHAPTER X.     The Lobster Quadrille
 CHAPTER XI.    Who Stole the Tarts?
 CHAPTER XII.   Alice’s Evidence

在这里,我们将grep的输出保存在/data/ch02目录下一个名为chapters.txt的文件中。如果这个文件还不存在,它将被创建。如果这个文件已经存在,其内容将被覆盖。图 2.4 说明了输出重定向在概念上是如何工作的。注意,标准错误仍然被重定向到终端:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-35jdchrL-1680148019207)(null)]

图 2.4:工具的输出可以重定向到一个文件

你还可以使用>>将输出附加到文件中,这意味着输出被添加到原始内容之后:

代码语言:javascript复制
$ echo -n "Hello" > greeting.txt

$ echo " World" >> greeting.txt

工具echo输出你指定的值。代表换行符-n选项指定echo不输出尾随换行符n

如果你需要存储中间结果,将输出保存到文件中是非常有用的,例如在以后的阶段继续分析。要再次使用文件greeting.txt的内容,我们可以使用cat,它读取一个文件并打印它。

代码语言:javascript复制
$ cat greeting.txt
Hello World

$ cat greeting.txt | wc -w # ➊
2

-w选项表示wc只统计字数。

使用小于号(<)可以获得相同的结果:

代码语言:javascript复制
$ < greeting.txt wc -w
2

通过小于号(<)这种方式,你直接将文件传递给wc的标准输入,而不需要运行一个额外的进程。图 2.5 说明了这两种方式的工作原理。同样,最终的输出也是一样的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6X1nBfvn-1680148019382)(null)]

图 2.5:使用文件内容作为输入的两种方式

像许多命令行工具一样,wc允许将一个或多个文件名指定为参数。例如:

代码语言:javascript复制
$ wc -w greeting.txt movies.txt
 2 greeting.txt
11 movies.txt
13 total

注意,在这种情况下,wc也输出文件的名称。

你可以通过将任何工具的输出重定向到一个名为/dev/null的特殊文件来保留它。我经常这样做来保留错误消息(见图 2.6 的说明)。下面的内容将导致cat产生一个错误信息,因为它找不到404.txt这个文件:

代码语言:javascript复制
$ cat movies.txt 404.txt
Matrix
Star Wars
Home Alone
Indiana Jones
Back to the Future
/usr/bin/cat: 404.txt: No such file or directory

你可以将标准错误重定向到/dev/null,如下所示:

代码语言:javascript复制
$ cat movies.txt 404.txt 2> /dev/null # ➊
Matrix
Star Wars
Home Alone
Indiana Jones
Back to the Future

2指标准错误

图 2.6:将stderr重定向到/dev/null

注意不要从同一个文件中读出和写入。如果你这样做,你会得到一个空文件。这是因为输出被重定向的工具会立即打开该文件进行写入,从而将其清空。这有两个解决办法:(1)写到一个不同的文件,然后用mv重命名;(2)使用sponge,它在写到一个文件之前吸收了所有的输入。图 2.7 说明了这是如何工作的:

图 2.7:除非你使用sponge,否则你不能在一个管道中读取和写入同一个文件

例如,假设你已经使用dseq生成了一个文件dates.txt,现在你想使用nl添加行号。如果运行下面的代码,文件dates.txt将会是空的。

代码语言:javascript复制
$ dseq 5 > dates.txt

$ < dates.txt nl > dates.txt

$ bat dates.txt
───────┬────────────────────────────────────────────────────────────────────────
       │ File: dates.txt   <EMPTY>
───────┴────────────────────────────────────────────────────────────────────────

所以说你可以使用我刚刚描述的解决方法之一:

代码语言:javascript复制
$ dseq 5 > dates.txt 
$ < dates.txt nl > dates-nl.txt

$ bat dates-nl.txt
───────┬────────────────────────────────────────────────────────────────────────
       │ File: dates-nl.txt
───────┼────────────────────────────────────────────────────────────────────────
   1   │      1  2022-03-04
   2   │      2  2022-03-05
   3   │      3  2022-03-06
   4   │      4  2022-03-07
   5   │      5  2022-03-08
───────┴────────────────────────────────────────────────────────────────────────

$ dseq 5 > dates.txt 
$ < dates.txt nl | sponge dates.txt

$ bat dates.txt
───────┬────────────────────────────────────────────────────────────────────────
       │ File: dates.txt
───────┼────────────────────────────────────────────────────────────────────────
   1   │      1  2022-03-04
   2   │      2  2022-03-05
   3   │      3  2022-03-06
   4   │      4  2022-03-07
   5   │      5  2022-03-08
───────┴────────────────────────────────────────────────────────────────────────

2.3.6 使用文件和目录

作为数据科学家,我们处理大量数据。这些数据通常存储在文件中。了解如何在命令行上处理文件(以及它们所在的目录)是很重要的。使用 GUI 可以做的每一个动作,都可以用命令行工具来完成(等等)。在这一节中,我将介绍列举、创建、移动、复制、重命名和删除文件和目录的最重要的方法。

ls可以列出一个目录的内容。如果不指定目录,它会列出当前目录的内容。我更喜欢ls有一个长列表格式,并且目录和文件分组,目录在前。我使用别名l,而不是每次都输入相应的选项。

代码语言:javascript复制
$ ls /data/ch10
alice.txt  count.py  count.R  __pycache__  Untitled1337.ipynb

$ alias l
l='ls --color -lhF --group-directories-first'

$ l /data/ch10
total 180K
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 __pycache__/
-rw-r--r-- 1 dst dst 164K Mar  3 10:38 alice.txt
-rwxr--r-- 1 dst dst  408 Mar  3 10:38 count.py*
-rw-r--r-- 1 dst dst  460 Mar  3 10:38 count.R
-rw-r--r-- 1 dst dst 1.7K Mar  3 10:38 Untitled1337.ipynb

你已经看到了我们如何通过使用>>>重定向输出来创建新文件。如果你需要将文件移动到不同的目录,你可以使用mv

代码语言:javascript复制
$ mv hello.txt /data/ch02

你也可以使用mv重命名文件:

代码语言:javascript复制
$ cd data
$ mv hello.txt bye.txt

你也可以重命名或移动整个目录。如果你不再需要一个文件,你用rm删除它:

代码语言:javascript复制
$ rm bye.txt

如果你想要删除整个目录及其所有内容,请指定-r选项,它代表递归:

代码语言:javascript复制
$ rm -r /data/ch02/old

如果要复制一个文件,使用cp。这对于创建备份非常有用:

代码语言:javascript复制
$ cp server.log server.log.bak

你可以使用mkdir创建目录:

代码语言:javascript复制
$ cd /data

$ mkdir logs

$ l
total 44K
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:39 ch02/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch03/
drwxr-xr-x 3 dst dst 4.0K Mar  3 10:38 ch04/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch05/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch06/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch07/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch08/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:38 ch09/
drwxr-xr-x 4 dst dst 4.0K Mar  3 10:38 ch10/
drwxr-xr-x 3 dst dst 4.0K Mar  3 10:38 csvconf/
drwxr-xr-x 2 dst dst 4.0K Mar  3 10:39 logs/

使用命令行工具来管理你的文件,一开始可能很可怕,因为你没有文件系统的图形概览来提供即时反馈。有一些可视化的文件管理器可以帮助解决这个问题,比如 GNU Midnight Commander、Ranger 和 Vifm。这些都没有安装在 Docker 镜像中,但你可以通过运行 sudo apt install,然后选择 mc、ranger 或 vifm,自己安装一个。

上面所有的命令行工具都接受代表 verbose 的-v选项,这样它们就可以输出正在发生的事情。例如:

代码语言:javascript复制
$ mkdir -v backup
/usr/bin/mkdir: created directory 'backup'

$ cp -v * backup
/usr/bin/cp: -r not specified; omitting directory 'backup'
/usr/bin/cp: -r not specified; omitting directory 'ch02'
/usr/bin/cp: -r not specified; omitting directory 'ch03'
/usr/bin/cp: -r not specified; omitting directory 'ch04'
/usr/bin/cp: -r not specified; omitting directory 'ch05'
/usr/bin/cp: -r not specified; omitting directory 'ch06'
/usr/bin/cp: -r not specified; omitting directory 'ch07'
/usr/bin/cp: -r not specified; omitting directory 'ch08'
/usr/bin/cp: -r not specified; omitting directory 'ch09'
/usr/bin/cp: -r not specified; omitting directory 'ch10'
/usr/bin/cp: -r not specified; omitting directory 'csvconf'
/usr/bin/cp: -r not specified; omitting directory 'logs'

除了mkdir之外的所有工具也接受-i选项,它代表“交互式”,这样工具就会要求你确认。例如:

代码语言:javascript复制
$ rm -i *
zsh: sure you want to delete all 12 files in /data [yn]? n

2.3.7 管理输出

有时,一个工具或工具序列产生了太多的输出,无法包含在书中。与其手动改变这样的输出,我更喜欢通过一个辅助工具的管道将其透明化。你不一定要这样做,尤其是如果你对完整的输出感兴趣。

以下是我用来管理输出的工具:

我们可以使用trim来限制输出给定的高度和宽度,默认情况下,输出被修剪为 10 行和终端的宽度,但也可以传递一个负数以禁止修剪高度和/或宽度。例如:

代码语言:javascript复制
$ cat /data/ch07/tips.csv | trim 5 25
bill,tip,sex,smoker,day,…
16.99,1.01,Female,No,Sun…
10.34,1.66,Male,No,Sun,D…
21.01,3.5,Male,No,Sun,Di…
23.68,3.31,Male,No,Sun,D…
… with 240 more lines

我用来管理输出的其他工具有:headtailfoldpastecolumn,附录中包含了每种方法的示例。

如果输出是逗号分隔的值,我通常通过csvlook将它转换成一个好看的表格。如果你运行csvlook,你将看到完整的表格。我通过trim重新定义了csvlook,这样表格就缩短了:

代码语言:javascript复制
$ which csvlook
csvlook() {
        /usr/bin/csvlook "$@" | trim | sed 's/- | -/──┼──/g;s/| -/├──/g;s/- |/──
┤/;s/|/│/g;2s/-/─/g'
}

$ csvlook /data/ch07/tips.csv
│  bill │   tip │ sex    │ smoker │ day  │ time   │ size │
├───────┼───────┼────────┼────────┼──────┼────────┼──────┤
│ 16.99 │  1.01 │ Female │  False │ Sun  │ Dinner │    2 │
│ 10.34 │  1.66 │ Male   │  False │ Sun  │ Dinner │    3 │
│ 21.01 │  3.50 │ Male   │  False │ Sun  │ Dinner │    3 │
│ 23.68 │  3.31 │ Male   │  False │ Sun  │ Dinner │    2 │
│ 24.59 │  3.61 │ Female │  False │ Sun  │ Dinner │    4 │
│ 25.29 │  4.71 │ Male   │  False │ Sun  │ Dinner │    4 │
│  8.77 │  2.00 │ Male   │  False │ Sun  │ Dinner │    2 │
│ 26.88 │  3.12 │ Male   │  False │ Sun  │ Dinner │    4 │
… with 236 more lines

我使用bat来显示文件的内容,其中行号和语法会突出显示。例如源代码:

代码语言:javascript复制
$ bat /data/ch04/stream.py
───────┬────────────────────────────────────────────────────────────────────────
       │ File: /data/ch04/stream.py
───────┼────────────────────────────────────────────────────────────────────────
   1   │ #!/usr/bin/env python
   2   │ from sys import stdin, stdout
   3   │ while True:
   4   │     line = stdin.readline()
   5   │     if not line:
   6   │         break
   7   │     stdout.write("%dn" % int(line)**2)
   8   │     stdout.flush()
───────┴────────────────────────────────────────────────────────────────────────

有时,当我想明确指出文件中的空格、制表符和换行符时,我会添加-A选项。

有时将中间输出写到文件中很有用。这允许你在管道中的任何步骤完成后对其进行检查。你可以在你的管道中插入工具tee。我经常用它来检查最终输出的一部分,同时将完整的输出写入文件(见图 2.8)。在这个例子中,完整的输出被写入even.txt,前 5 行被使用trim打印:

代码语言:javascript复制
$ seq 0 2 100 | tee even.txt | trim 5
0
2
4
6
8
… with 46 more lines

图 2.8:使用tee将中间输出写入文件

最后,为了插入由命令行工具生成的图片(除了屏幕截图和图表之外的每张图片),我使用了display。但是如果你运行display,你会发现它不起作用。在第七章中,我介绍了四个选项,让你从命令行中显示生成的图像。

2.3.8 帮助

当你在命令行中摸索时,可能会需要帮助,即使是最有经验的用户在某些时候也需要帮助。我们不可能记住所有不同的命令行工具及其可能的参数。幸运的是,命令行提供了几种获得帮助的方法。

获得帮助最重要的命令或许是man,是手动的简称。它包含大多数命令行工具的信息。如果我忘记了工具tar的选项,这种情况经常发生,我只需使用以下命令访问它的手册页:

代码语言:javascript复制
$ man tar | trim 20
TAR(1)                          GNU TAR Manual                          TAR(1)

NAME
       tar - an archiving utility

SYNOPSIS
   Traditional usage
       tar {A|c|d|r|t|u|x}[GnSkUWOmpsMBiajJzZhPlRvwo] [ARG...]

   UNIX-style usage
       tar -A [OPTIONS] ARCHIVE ARCHIVE

       tar -c [-f ARCHIVE] [OPTIONS] [FILE...]

       tar -d [-f ARCHIVE] [OPTIONS] [FILE...]

       tar -t [-f ARCHIVE] [OPTIONS] [MEMBER...]

       tar -r [-f ARCHIVE] [OPTIONS] [FILE...]

… with 1147 more lines

并非每个命令行工具都有手册页。以cd为例:

代码语言:javascript复制
$ man cd
No manual entry for cd

对于像cd这样的 Shell 内置,你可以参考zshbuildins手册页:

代码语言:javascript复制
$ man zshbuiltins | trim
ZSHBUILTINS(1)              General Commands Manual             ZSHBUILTINS(1)

NAME
       zshbuiltins - zsh built-in commands

SHELL BUILTIN COMMANDS
       Some shell builtin commands take options as described in individual en‐
       tries; these are often referred to in the  list  below  as  `flags'  to
       avoid  confusion  with  shell options, which may also have an effect on
       the behaviour of builtin commands.  In this introductory section,  `op‐
… with 2735 more lines

/可以搜索,按q可以退出。尝试为cd找到合适的部分。

较新的命令行工具通常也没有手册页。在这种情况下,最好的办法是使用--help(或-h)选项调用工具。例如:

代码语言:javascript复制
$ jq --help | trim
jq - commandline JSON processor [version 1.6]

Usage:  /usr/bin/jq [options] <jq filter> [file...]
        /usr/bin/jq [options] --args <jq filter> [strings...]
        /usr/bin/jq [options] --jsonargs <jq filter> [JSON_TEXTS...]

jq is a tool for processing JSON inputs, applying the given filter to
its JSON text inputs and producing the filter's results as JSON on
standard output.

… with 37 more lines

指定--help选项也适用于命令行工具,比如cat。但是,相应的手册页通常会提供更多信息。如果在尝试了这三种方法后,你仍然有不明白的地方,那为啥不 Google 一下呢。在附录中,列出了本书中使用的所有命令行工具。除了如何安装每个命令行工具,它还显示了如何获得帮助。

手册页可能非常冗长,难以阅读。工具tldr是一个由社区维护的命令行工具帮助页面的集合,旨在成为传统手册页面的一个更简单、更易用的补充。下面是一个显示tartldr页面的示例:

代码语言:javascript复制
$ tldr tar | trim 20

  tar

  Archiving utility.
  Often combined with a compression method, such as gzip or bzip2.
  More information: https://www.gnu.org/software/tar.

  - [c]reate an archive and write it to a [f]ile:
    tar cf target.tar file1 file2 file3

  - [c]reate a g[z]ipped archive and write it to a [f]ile:
    tar czf target.tar.gz file1 file2 file3

  - [c]reate a g[z]ipped archive from a directory using relative paths:
    tar czf target.tar.gz --directory=path/to/directory .

  - E[x]tract a (compressed) archive [f]ile into the current directory [v]erbos…
    tar xvf source.tar[.gz|.bz2|.xz]

  - E[x]tract a (compressed) archive [f]ile into the target directory:
… with 12 more lines

如你所见,tldr没有像man经常做的那样按字母顺序列出许多选项,而是通过给你一个实际例子。

2.4 总结

在本章中,你学习了如何通过安装 Docker 镜像来获得所有需要的命令行工具。我还介绍了一些基本的命令行概念以及如何获得帮助。现在你已经具备了所有必要的要素,也已经为 OSEMN 数据科学模型的第一步做好了准备:获取数据。

2.5 进一步探索

  • 本书的副标题是向 Jerry Peek、Shelley Powers、Tim O’Reilly 和 Mike Loukides 的史诗般的书《Unix Power Tools》致敬。在该书 51 个章节和一千多页中,它几乎涵盖了关于 Unix 的所有知识,它的重量超过 4 磅,所以你可以考虑买本电子书。
  • 网站 explainshell 解析了一条命令或一连串的命令,并对每个部分提供了简短的解释。这对快速理解一个新的命令或选项很有用,而不必粗略地阅读相关的手册页面。
  • Docker 确实是一个出色的软件。在本章中,我简要介绍了如何下载 Docker 镜像和运行 Docker 容器,但学习如何创建自己的 Docker 镜像可能是值得的。Sean Kane 和 Karl Matthias 的《Docker: Up & Running》一书是一个很好的资源。

0 人点赞