标准流

在 Linux 系统中,"标准流"(Standard Streams)是程序与外部环境进行数据交互的核心机制,标准流主要包括以下三种类型:

  1. 标准输入(stdin):这是程序或shell接收数据的通道。通常情况下,当你在命令行中输入内容时,就是通过标准输入将这些内容传递给程序的。例如,当你输入ls命令来列出目录内容时,这个命令就是通过标准输入传递给系统的。
  2. 标准输出(stdout):这是用于输出程序运行的正常结果的通道。继续以ls命令为例,该命令执行后,列出的目录内容就是通过标准输出显示在你终端上的。
  3. 标准错误(stderr):这也是一种输出通道,不过它专门用于输出错误信息或者诊断信息。比如你用 ls 去访问一个不存在的路径,终端会提示 “No such file or directory(没有那个文件或目录)”,这类错误信息就是通过标准错误通道输出的。

img

我们知道,在Linux(Unix)中,一切皆文件,标准输入、标准输出和标准错误本质上也是被当作文件来处理的,而文件都会有一个称之为[文件描述符](https://zhida.zhihu.com/search?content_id=257402604&content_type=Article&match_order=1&q=文件描述符&zhida_source=entity) fd 的数字编号,上述三种标准流对应的文件描述符分别为:

  • 标准输入(stdin):0
  • 标准输出(stdout):1
  • 标准错误(stderr):2

重定向

在 Linux 中,虽然标准输出(stdout)和标准错误(stderr)是两个不同的通道,但它们在终端中看起来是一样的:都直接显示在屏幕上,且字体、颜色、格式也通常相同。也就是说,从用户的角度来看,两者的输出效果是没有区别的。

那么如果我们想将命令(或程序)的正常输出和错误信息显示区分开了?比如说我想将错误信息输出到一个文件中,方便以后定位问题。

Shell 提供了一个非常强大的功能,就是可以重定向这些流通道,将它们的输出引导到其它地方,比如文件、另一个流通道,甚至是另一个命令的输入。这就是所谓的“重定向”。

我们前面提到,标准流是有编号的,结合这些编号,我们就可以用 < 或 > 操作符进行流重定向了:

  • >:用于重定向输出到文件或另一个流。如果目标是文件,会覆盖原有内容。这是最常用的操作。
  • >>:跟 > 类似,不过它是追加到文件,而不是覆盖。
  • <:用于将文件内容作为输入,重定向给程序,代替标准输入。

标准输入stdout重定向到文件

# 会覆盖原文件的内容
{command} > {file}
# 追加到文件末尾
{command} >> {file}

标准错误stderr重定向到文件

command 2> /file

stdout和stderr输出到同一个文件

$ command > file 2>&1

这条命令看起来有点奇怪,这里解释一下这条命令:

  1. > file:首先将 stdout重定向到 file。此时 stdout 指向 file,而 stderr 仍默认指向终端屏幕。
  2. 2>&1:然后将 stderr重定向到 stdout 的当前目标(即 file)。此时两者都指向 file。img

标准输入stdin重定向

平时我们在终端敲键盘输入内容,这其实就是通过 stdin 传递给程序。但我们也可以反过来,把文件的内容重定向为输入,传给程序。这和把程序的输出重定向到文件不同,是一个“反向操作”。

在重定向输入时,有三种符号。这三种符号分别对应三种不同的用法:

  • <:把一个文件的内容作为输入传给命令
  • <<:从终端读取多行输入,直到遇到我们指定的“结束符”(如EOF)为止
  • <<<:把一段字符串作为输入传给命令

示例一:使用 < 从文件读取输入

比如我们想知道 hello.txt 文件里有多少行内容。可以用 wc -l 命令来统计文件的行数。

$ wc -l < hello.txt
5492

这句命令的意思是:把 hello.txt 文件的内容作为输入传给 wc -l,然后返回该文件的行数。

示例二:使用 << 从终端多行输入

接下来我们试试 << 的用法。这个符号允许我们输入多行文本,直到我们输入一个特殊标记(比如 EOF),然后把这段输入传给命令:

$ wc -l << EOF
> one line
> two lines
> three lines
> EOF
3

你可能会疑惑为什么明明输入了 4 行,结果只统计了 3 行?其实 EOF 那一行是结束标记,不属于真正的输入内容,所以不会被统计在内。

也可以指定其它字符串作为结束符:

$ wc -l << 123
> z
> z
> z
> 123
3

示例三:使用 <<< 从字符串读取

如果只想传一段小的字符串作为输入,可以使用 <<<:

$ wc -l <<< "one two three"
1  # 一行

# 用 -w 参数统计词数:
$ wc -w <<< "one two three"
3

示例四:输入来自文件,输出重定向到另一个文件

我们还可以把输入来自文件的结果重定向到另一个文件,比如:

$ wc -l < hello.txt > num_lines.txt
# 屏幕上没有任何输出

$ cat num_lines.txt 
5492

这条命令的意思是:从 hello.txt 读取内容传给 wc -l 进行行数统计,然后把结果写入 num_lines.txt 文件中。

参考

[Linux那些事 06 | stdin/stdout/stderr的爱恨纠葛](https://zhuanlan.zhihu.com/p/1903353588475986078)