zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Linux中的输入、输出和错误重定向

2023-02-25 18:02:49 时间

如果你熟悉基本的 Linux 命令,还应该学习输入输出重定向的概念。

我们都知道 Linux 命令的功能,它接受一个输入,然后给你一个输出。在这里起作用的包含一些重要的角色,我们今天就来介绍一下这些角色。

stdin, stdout 和 stderr

当你运行 Linux 命令时,有三个数据流在其中起作用:

  • stdin:(Standard input,标准输入)是输入数据的源。默认情况下,stdin 是从键盘输入的任何文本,它的流 ID(stream ID) 为 0;
  • stdout:(Standard output,标准输出)是命令的输出结果。默认情况下,它会显示在屏幕上,它的流 ID(stream ID) 为 1;
  • stderr:(Standard error,标准错误)是命令产生的错误消息(如果有)。默认情况下,屏幕上也会显示 stderr。它的流 ID(stream ID)是2。

这些流包含存储在内存缓冲区(buffer memory)中的纯文本数据。

把它想象成一个水流,你需要水源,比如水龙头,用管道连接到它,可以将其存储在水桶(文件)中,也可以给植物浇水(打印)。如果需要,还可以将其连接到另外一个水龙头上,也就是改变水的流向(重定向)。

Linux 中也有这种重定向的概念。你可以将 stdin, stdout 和 stderr 从其原本的目标,重定向到另一个文件或命令(甚至是打印机等外围设备)。

接下来我们来介绍一下重定向是如何工作的,以及如何使用它。

输出重定向

第一种也是最简单的重定向形式是输出重定向,也称为标准输出重定向。

默认情况下,命令的输出是显示在屏幕上。比如,我使用 ls 命令列出当前目录下的所有文件:

[gliu@fedora work]$ ls
appstxt new.txt static-ip.txt

通过输出重定向,可以将输出重定向到文件。如果此输出文件不存在,那么 shell 将创建它。

command > file

比如,我们将上述 ls 命令的输出保存到名为 output.txt 的文本文件中:

[gliu@fedora work]$ ls > output.txt

输出文件是预先创建的

那么这个 output.txt 文件的内容是什么呢?我们用 cat 命令来看一下:

[gliu@fedora work]$ cat output.txt
appstxt
new.txt
output.txt
static-ip.txt

有没有注意到里面也包含 output.txt?将输出重定向输出到的文件(output.txt)是在运行预期命令之前创建的。为什么呢?因为它需要准备好输出的目的地,输出将被发送到该目的地。

追加而不是删除

一个经常被忽略的问题是,如果重定向到一个已经存在的文件,shell 将首先删除该文件。这意味着输出文件的现有内容将被删除,并替换为命令的输出。

如果不想删除原有的内容,而只是追加,那么可以使用 >> 重定向语法:

command >> file

你可以在当前的 shell 会话中禁止删除,使用 set -C

将输出重定向到文件,可以将输出的内容保存起来以供将来参考;另外如果输出的内容太多,占用了大篇幅的屏幕时,将内容保存到文件,就更方便查看和分析了,这就像收集日志文件一样。

管道重定向

在介绍 stdin 重定向之前,我们先来了解一下管道重定向,这是更加常见的,我们会经常使用管道重定向。

关于管道重定向,可以参阅我们先前的文章:Linux 中的管道是什么?管道重定向是如何工作的?

通过管道重定向,可以将命令的标准输出发送到另一个命令的标准输入。

command 1 | command 2

我们来举个例子,如果我们要查看当前目录中文件的数量,可以使用 ls -1(注意是数字1,不是字母L)来显示当前目录中的文件:

[gliu@fedora work]$ ls -1
appstxt
new.txt
output.txt
static-ip.txt

我们知道 wc 命令用于计算文件中的行数,所以我们可以结合这个命令,如下:

[gliu@fedora work]$ ls -1 | wc -l
4

使用管道,两个命令共享相同的内存缓冲区,第一个命令的输出存储在缓冲区中,然后该缓冲区将用作下一个命令的输入。

你将看到管道中最后一个命令的结果。这一点很明显,因为先前命令的 stdout 被重定向到下一个命令,而不是打印在屏幕上。

管道重定向或管道不限于仅连接两个命令,你也可以连接多个命令,只要一个命令的输出可用作下一个命令输入。

command_1 | command_2 | command_3 | command_4

请注意,stdout/stdin是一块数据,而不是文件名

一些新的Linux用户在使用重定向时会感到困惑,如果命令返回一组文件名作为输出,则不能将这些文件名用作参数。

比如,使用 find 命令查找扩展名为 .txt 的文件,无法通过管道将查找到的这些文件移动到新的目录,不能这样操作:

find . -type f -name "*.txt" | mv destination_directory

这就是为什么我们经常会看到 find 命令与 exec 或 xargs 命令组合使用的原因。这些命令可以将大量的文本“文件名”转换为文件名,且可作为参数传递。

find . -type f -name "*.txt" | xargs -t -I{} mv {} ../new_dir

关于find 与 exec 或 xargs 命令组合使用的相关内容,可参考我们先前的文章:

find 与 exec 命令的结合,是一个功能强大的搜索工具

如何在Linux中使用xargs命令

输入重定向

使用 stdin 重定向可以将文本文件的内容传递给终端命令:

command < file

但是这种并不常用,因为大多数 Linux 命令都可以接受文件名作为参数,因此通常不需要 stdin 重定向。比如:

head < filename.txt

上面的命令可以直接写为:head filename.ext(也就是不用重定向符号 <)。

也不是说 stdin 重定向完全没有用,有些命令是依赖于它的。以 tr 命令为例,这个命令可以做很多事情,但在下面的例子中,它将输入文本从小写转为大写:

tr a-z A-Z < filename.txt

事实上,建议在管道上使用标准输入重定向,以避免不必要地使用 cat 命令。

比如上面的例子,很多人就习惯使用 cat,然而这里并没有必要使用cat:

cat filename.txt | tr a-z A-Z

合并重定向

你可以根据需要组合使用 stdin,stdout 和管道重定向。

比如,下面的命令列出当前目录下所有的 txt 文件,然后统计一下文件的总数,并将这个结果保存到一个新文件中:

ls *.txt | wc -l > count.txt

错误重定向

有时候,当你运行一些命令或脚本时,会在屏幕上看到一条错误信息:

[gliu@fedora work]$ ls -l ffffff > output.txt
ls: cannot access 'ffffff': No such file or directory

在本文的开头,我们提到过有三种数据流,stderr 是其中之一,它默认将输出显示在屏幕上。

你也可以重定向 stderr。由于它是一个输出数据流,因此可以使用与标准输出重定向相同的 > 或 >> 重定向符号。

但是,当 stdout 和 stderr 都作为输出数据流时,如何区分它们呢?通过它们的流 ID(stream ID,也称为文件描述符)。

Data stream

stream ID

stdin

0

stdout

1

stderror

2

-t

-list

-u

-update

-x

–extract, –get

-j

–bzip2

-z

–gzip, –gunzip, –ungzip

默认情况下,当你使用输出重定向符号 > 时,它实际上是 1 >。换句话说,ID 为 1 的数据流是在这里输出。

所以当你要重定向 stderr 时,可以使用它的ID,比如 2> 或者 2>>。这表示输出重定向用于数据流 stderr(ID为2)。

stderr 重定向示例

我们来举几个例子。假如我们只想要保存错误信息,那么可以这样:

[gliu@fedora work]$ ls fffff 2> error.txt
[gliu@fedora work]$ cat error.txt
ls: cannot access 'fffff': No such file or directory

这个很简单。我们再来个稍微复杂一点的(并且很有用的):

[gliu@fedora work]$ ls -l new.txt ffff > output.txt 2> error.txt
[gliu@fedora work]$ cat output.txt
-rw-rw-r-- 1 gliu gliu 0 May 5 10:25 new.txt
[gliu@fedora work]$ cat error.txt
ls: cannot access 'ffff': No such file or directory

在上面的例子中,ls 命令尝试显示两个文件的信息。其中一个文件能成功,另一个会出错。所以我在这里做的是将 stdout 输出重定向到 output.txt 文件中,将 stderr 重定向到 error.txt 中(使用 2>)。

此外,我们还可以将 stdout 和 stderr 重定向到同一个文件,是有办法做到这一点的。

在下面的例子中,我使用追加模式(append mode)将 stderr 重定向到文件 combined.txt 中(使用 2>>);然后,同样使用追加模式将标准输出 stdout 重定向到同一个文件 combined.txt 中(使用 >>):

[gliu@fedora work]$ ls -l new.txt fff 2>> combined.txt >> combined.txt
[gliu@fedora work]$ cat combined.txt
ls: cannot access 'fff': No such file or directory
-rw-rw-r-- 1 gliu gliu 0 May 5 10:25 new.txt

另一种方法,也是首选的方法,是使用类似于 2>&1 的东西,可以大致的翻译为“将 stderr 重定向到与 stdout 相同的地址中”。

我们以前面的示例为例,这次使用 2>&1 将 stdout 和 stderr 重定向到同一个文件:

[gliu@fedora work]$ ls -l new.txt fff > output.txt 2>&1
[gliu@fedora work]$ cat output.txt
ls: cannot access 'fff': No such file or directory
-rw-rw-r-- 1 gliu gliu 0 May 5 10:25 new.txt

在这里需要注意的一点是,不能想当然的以为 2>>&1 为追加模式,因为 2>&1 本身就是追加模式。

此外,你也可以先使用 2>,然后使用 1>&2 将 stdout 重定向到与 stderr 相同的文件上。基本上,>& 是将一个输出数据流重定向到另一个上。

我们来总结一下

1)有三种数据流,其中一个是输入数据流 stdin(0),两个输出数据流为 stdout(1) 和 stderr(2);

2)键盘输入是默认的标准输入设备,屏幕是默认的输出设备;

3)输出重定向使用 > 或者 >>(用于追加模式);

4)输入重定向使用 <;

5)stderr 可以使用 2> 或者 2>>;

6)stderr 和 stdout 可以组合为:2>&1。

至此我们了解了关于重定向的相关知识,那么为了更加深入的学习,还可以了解一下 tee 命令,该命令可以将数据显示到标准输出中,同时保存到文件中。我们稍后会分享这个命令的用法。

以上就是本次分享全部内容,欢迎讨论。