Linux中的管道和重定向
1. 简介
大多数 shell 提供改变应用程序输入和输出流动方式的能力。这可以将输出从终端引导到文件或其他应用程序中,或者从文件而不是终端读取输入。
2.标准输入输出
在我们了解重定向如何在 shell 中工作之前,我们首先需要了解标准输入和输出过程。
所有应用程序都有三个独特的流将它们连接到外部世界。这些被称为标准输入或标准输入;标准输出或标准输出;和标准错误或标准错误。
标准输入是获取交互式程序输入的默认机制。当直接在终端中运行时,这通常是到键盘的直接链接,否则不连接任何东西。
标准输出是从程序写入输出的默认机制。这通常是到输出终端的链接,但出于性能原因经常被缓冲。当程序在慢速网络链接上运行时,这一点尤其重要。
标准错误是编写程序输出的另一种机制。这通常是到同一输出终端的不同链接,但没有缓冲,因此我们可以立即写入错误,而正常输出可以一次写入一行。
3.重定向到文件
我们运行应用程序时的一个常见需求是将输出定向到文件而不是终端。具体来说,我们在这里所做的是将标准输出流替换为我们想要的输出流:在这种情况下,是一个文件。
**这通常通过在要运行的应用程序和要写入输出的文件之间的 > 运算符来完成。**例如,我们可以将ls 命令的输出发送到一个名为files的文件中,如下所示:
$ ls > files
我们可以在任何命令之后执行此操作,包括任何需要的参数:
$ ls -1 *.txt > text-files
3.1.附加到文件
当我们使用*>时,我们将输出写入文件,替换所有内容。如果需要,我们也可以**使用»附加到文件中*:
$ ls -1 *.txt > files
$ ls -1 *.text >> files
$ ls -1 *.log >> files
如果需要,我们可以使用它来使用各种输出方式构建文件,包括仅使用echo来精确输出行:
$ echo Text Files > files
$ ls -1 *.txt >> files
$ echo Log Files >> files
$ ls -1 *.log >> files
3.2. 重定向标准错误
有时,我们需要重定向标准错误而不是标准输出。这以相同的方式工作,但我们需要指定确切的流。
所有三个标准流都有 ID 值,如 POSIX 规范中定义并在 C 语言中使用。标准输入为 0,标准输出为 1,标准错误为 2。
当我们使用重定向运算符时,默认情况下,这适用于标准输出。不过,我们可以通过在其前面加上流 ID 来明确指定要重定向的流。
例如,要重定向标准错误,我们将使用cat 命令2>:
$ cat does-not-exist 2> log
如果我们愿意,我们可以保持一致并使用*1>*来重定向标准输出,尽管这与我们之前看到的相同。
我们还可以使用&>同时重定向标准输出和标准错误:
$ ls -1 &> log
这会将命令的所有输出(无论它在哪个流上)发送到同一个文件中。不过,这只适用于某些 shell——例如,我们可以在bash或zsh中使用它,但不能在sh中使用。
3.3. 重定向到流
有时我们想重定向到流而不是文件。我们可以通过使用以&为前缀的流 ID代替文件名来实现这一点。例如,我们可以使用>&2重定向到标准错误:
$ echo Standard Output >&1
$ echo Standard Error >&2
我们可以通过从一个流重定向到另一个流来将其与上述结合起来以组合流。一个常见的结构是**将标准错误组合到标准输出中,**以便两者可以一起使用。我们使用2>&1来实现这一点——字面上将流 2 重定向到流 1:
# ls -1 2>&1
我们有时可以使用它来创建新的流,只需使用新的 ID。不过,这个流必须已经先在其他地方使用过,否则就是一个错误。大多数情况下,这首先用作流源。
例如,我们可以通过第三个流使用3>&2 2>&1 1>&3交换标准输出和标准错误:
$ ls -1 3>&2 2>&1 1>&3
此构造不能在所有 shell 中正常工作。在bash中,最终结果是直接交换标准输出和标准错误。在zsh中,结果是标准输出和标准错误都以标准输出结束。
3.4. 重定向多个流
我们可以很容易地结合以上来同时重定向标准输出和标准错误。这几乎完全符合我们的预期——我们只需将两个不同的重定向组合在同一个命令上:
$ ls -1 > stdout.log 2> stderr.log
如果我们尝试将两个流重定向到同一个文件,这将无法正常工作。这里发生的是两个流都被单独重定向 ,并且以第二个获胜者为准,而不是将两者合并到同一个文件中。
如果我们想将两者重定向到同一个文件中,那么我们可以使用*&>*,就像我们上面看到的那样,或者我们可以使用流组合运算符。如果我们希望使用流组合运算符,那么我们必须在重定向到文件后执行此操作,否则只会重定向标准输出:
$ ls -1 > log 2>&1
4. 从文件中读取
有时我们也想实现相反的效果——将文件重定向到应用程序中。我们使用<*运算符*执行此操作,文件的内容将替换应用程序的标准输入:
$ wc < /usr/share/dict/words
235886 235886 2493109
当我们这样做时,应用程序可以接收的唯一输入来自这个来源,并且这一切都会立即发生。这实际上与用户在应用程序一开始就输入文件的全部内容时相同。
但是,文件的结尾也会通知应用程序,许多应用程序可以使用它来停止处理。
5. 应用程序之间的管道
我们可以执行的最后一个操作是将一个应用程序的输出定向到另一个应用程序。这通常称为管道,并使用| 运算符:**
$ ls | wc
11 11 138
这直接将我们第一个应用程序的标准输出连接到第二个应用程序的标准输入,然后让数据直接在它们之间流动。
5.1.处理标准错误
默认情况下不连接标准错误,因此我们仍然会在控制台中看到写入该流的任何内容。这是设计使然,因为标准错误是为错误报告而设计的,而不是为正常的应用程序输出而设计的。
如果我们也想重定向标准错误,那么我们可以使用上面的技术首先将其重定向到标准输出,然后通过管道传输到下一个应用程序:
$ ls i-dont-exist | wc
ls: i-dont-exist: No such file or directory
0 0 0
$ ls i-dont-exist 2>&1 | wc
1 7 44
如果我们只想传递标准错误,那么我们需要交换标准输出和标准错误流,正如我们之前看到的。
$ ls 3>&2 2>&1 1>&3 | wc -l
some-file
0
$ ls i-dont-exist 3>&2 2>&1 1>&3 | wc -l
1
5.2. 组合管道
在应用程序之间进行管道传输时,我们还可以构建任意链,在多个应用程序之间进行管道传输以实现结果:
$ docker images | cut -d' ' -f1 | tail -n +2 | sort -u | wc -l
17
这个命令看起来很吓人,但我们可以将其分解为各个部分:
- docker images – 获取所有 Docker 镜像的列表
- cut -d’ ’ -f1 - 剪切此输出以仅返回第一列,其中列以空格分隔
- tail -n +2 – 限制为从第 2 行开始
- sort -u – 排序这个列表,只返回唯一的条目
- wc -l - 计算行数
所以我们这里有一个命令来获取唯一 docker 镜像的数量,忽略镜像的版本。
许多控制台应用程序正是为这种用途而设计的,这就是为什么它们通常可以使用标准输入的输入并写入标准输出的原因。
某些应用程序也有允许这样做的特殊模式——例如, git有所谓的porcelain和plumbing命令,其中管道命令专门设计为以这种方式组合,而porcelain命令是为人类而设计的。