理解Linux中的标准流及重定向

数据流,就是数据传输的通道。在Linux中,一个程序启动时,将会自动开启三个数据流通道:标准输入流、标准输出流、标准错误流。本文详细介绍标准数据流的概念及其重定向的使用。

数据流的简单理解

想象一下,凌晨一点,你似乎完全没有困意,坐在电脑前疯狂地输入一条条的命令,屏幕上滚动的字符告诉你,程序正按你预想的方式执行。突然,一段红色的文字打断了你的思路:程序报错了。

在上面这个场景中,你从键盘输入的信息就是输入流,在屏幕上打印出来的提示信息就是输出流,而红色的报错就是错误流。

数据流的概念

经常有人把数据流比作水管,水管中的“水”就是要传输的数据。跟水管一样,数据流有一个特点:具有方向性。
以前面的场景为例子,输入流的方向是从键盘到程序,输出流、错误流的方向是从程序到屏幕。

根据百度百科的定义:数据流(data stream)是一组有序,有起点和终点的字节的数据序列。

Linux中的标准流

Linux中有三个标准流,在一个程序启动的时候它们会自动开启。它们分别是:

  • 标准输入流(stdin: standard input,)
  • 标准输出流(stdout: standard output)
  • 标准错误流(stderr: standard error)

对应的文件描述符分别是:

  • stdin:0
  • stdout:1
  • stderr:2

程序运行的时候从输入流读取数据,作为程序的输入,程序运行过程中输出的信息被传送到输出流,类似的,错误信息被传送到错误流。

标准输入流

标准输入流默认是从键盘输入的信息。

例如,在Linux中,使用passwd命令来修改密码:

1
2
3
4
5
6
pi@raspberrypi:~ $ passwd 
为 pi 更改 STRESS 密码。
Current password:
新的 密码:
重新输入新的 密码:
passwd:已成功更新密码

在修改密码的过程中,你需要从键盘输入当前的密码和新的密码。标准输入流就是键盘,你输入的密码从标准输入流传送到passwd程序。

除了键盘之外,标准输入流还可以是文件,或者其它程序的输出,后面会讲到。

标准输出流

输出流分为标准输出流和标准错误流,默认情况下,它们都会输出到屏幕。

以编译程序为例子,编译过程中的调试信息(标准输出流)和编译发生的错误(标准错误流)都会被显示在屏幕上。

标准输出流也可以被重定向到另外一个程序,或者一个文件。例如将当前目录中的文件列表写入到一个文件中:

1
ls > a.txt

默认情况下,ls命令会输出当前目录中的文件和文件夹。不过这条命令运行后并没有任何输出,因为ls命令的输出被重定向到文件a.txt。如果查看a.txt的文件内容,就会看到当前目录中的文件和文件夹列表。

标准错误流

与标准输出流类似,标准错误流默认也会输出到屏幕上。例如:

1
2
pi@raspberrypi:~ $ rm /
rm: 无法删除'/': 是一个目录

重定向

在Linux中,标准输入流默认来自键盘输入,标准输出流和标准错误流默认发送到屏幕。在必要的时候,可以对修改输入流的来源、修改输出流的目的,这就是重定向。

常用的重定向的符号:

  1. >: 将标准输出流重定向到文件(清空文件后写入)。
  2. >>:将标准输出流重定向到文件(追加写入)。
  3. <:将文件作为命令的标准输入流。

标准输出流重定向

1
2
3
4
5
# 以下命令执行后没有任何输出,因为输出流被重定向到文件,执行后可以在a.txt中查看ls的输出
ls > a.txt

# 将ls命令返回的结果追加写入到文件
ls >> a.txt

输入流重定向

1
2
3
4
5
# 将文件内容发送到远程主机(文件内容作为命令的输入流)
nc 192.168.1.1 80 < a.txt

# 更复杂的例子:将a.txt发送到远程主机,并将输出(远程主机返回的内容)写入到b.txt文件
nc 192.168.1.1 80 < a.txt > b.txt

错误流重定向

默认情况下,重定向符号>>>只能对标准输出流进行重定向,而没有对标准错误流重定向。
例如,使用rm命令删除根目录时,会报错无法删除:

1
2
pi@raspberrypi:~ $ rm /
rm: 无法删除'/': 是一个目录

尝试将输出写入到一个文件中:

1
2
pi@raspberrypi:~ $ rm / > b.txt
rm: 无法删除'/': 是一个目录

可以看到,错误信息依旧被显示到屏幕上,查看b.txt时,发现什么内容都没有写入。
这是因为,重定向符号“>”默认只会重定向标准输出流,而不会重定向标准错误流。所以标准错误流依旧按照默认方式输出到屏幕上。

如果想对错误流进行重定向,可以使用以下语法:

1
rm / 2> b.txt

即,在重定向符号前加上了一个2,这个2就是标准错误流的文件描述符。
命令执行后没有任何输出,在文件b.txt的内容中,可以看到:rm: 无法删除'/': 是一个目录

将标准输出流写入到output.txt,同时标准错误流写入到error.txt文件:

1
cat b.txt xxxxx.txt >output.txt 2>error.txt

将标准错误流重定向标准输出流,将标准输出流写入文件:

1
cat b.txt xxxxx.txt >output.txt  2>&1

上述命令中,2表示标准错误流,1表示标准输出流(加符&的原因是,区分文件1和标准输出流)。

管道符

管道符“|”可以把一个程序的标准输出流作为另外一个程序的标准输入流,即前一个程序的输出作为后一个程序的输入。

在没有使用管道符的时候,获取可以登录shell的用户数量可以分为以下3步:

  1. 匹配/etc/passwd文件中包含“/bin/bash”字符串(即可以登录shell的用户)的行,并写入到文件a.txt中:
  2. wc -l a.txt命令统计a.txt文件中的行数。
  3. 删除a.txt文件。

命令如下:

1
2
3
4
pi@raspberrypi:~ $ grep "/bin/bash" /etc/passwd > a.txt
pi@raspberrypi:~ $ wc -l a.txt
3 a.txt
pi@raspberrypi:~ $ rm a.txt

如果使用管道符,只需要一行命令:

1
2
pi@raspberrypi:~ $ grep "/bin/bash"  /etc/passwd | wc -l
3

首先,grep读取/etc/passwd文件,匹配并输出包含/bin/bash的行,输出内容作为wc命令的输入;
最后,wc -l命令统计输入的行数,并最终输出到屏幕上。