在外壳中,“2>&1” 是什么意思?

在 Unix shell 中,如果要将stderrstdout组合到stdout流中以进行进一步的操作,则可以在命令末尾附加以下内容:

2>&1

因此,如果我想在g++的输出中使用head ,则可以执行以下操作:

g++ lots_of_errors 2>&1 | head

因此我只能看到前几个错误。

我总是很难记住这一点,而且我经常不得不去查找它,这主要是因为我不完全理解这个特殊技巧的语法。

有人可以分手并逐个字符地解释2>&1含义吗?

答案

文件描述符 1 是标准输出( stdout )。
文件描述符 2 是标准错误( stderr )。

这是记住此构造的一种方法(尽管并不完全准确):首先, 2>1可能是将stderr重定向到stdout的好方法。但是,它实际上将被解释为 “将stderr重定向到名为1的文件”。 &表示后面是文件描述符而不是文件名。因此,构造变为: 2>&1

echo test > afile.txt

将 stdout 重定向到afile.txt 。这和做的一样

echo test 1> afile.txt

要重定向 stderr,请执行以下操作:

echo test 2> afile.txt

>&是将流重定向到另一个文件描述符的语法 - 0 为 stdin,1 为 stdout 和 2 为 stderr。

您可以通过执行以下操作将 stdout 重定向到 stderr:

echo test 1>&2 # or echo test >&2

或相反亦然:

echo test 2>&1

因此,简而言之... 2>将 stderr 重定向到(未指定)文件,附加&1会将 stderr 重定向到 stdout。

有关重定向的一些技巧

关于此的某些语法特殊性可能具有重要的行为。关于重定向, STDERRSTDOUT和参数排序有一些小示例。

1 - 覆盖还是附加?

符号>均值重定向

  • >表示发送到一个完整的完整文件 ,如果存在noclobber覆盖目标(请参阅稍后的#3 noclobber bash 功能)。
  • >>表示除发送外还将附加到目标(如果存在)。

无论如何,如果文件不存在,则会创建该文件。

2- Shell 命令行取决于顺序!!

为了测试这一点,我们需要一个简单的命令,该命令将在两个输出上发送一些信息

$ ls -ld /tmp /tnt
ls: cannot access /tnt: No such file or directory
drwxrwxrwt 118 root root 196608 Jan  7 11:49 /tmp

$ ls -ld /tmp /tnt >/dev/null
ls: cannot access /tnt: No such file or directory

$ ls -ld /tmp /tnt 2>/dev/null
drwxrwxrwt 118 root root 196608 Jan  7 11:49 /tmp

(当然,希望您没有一个名为/tnt的目录)。好吧,我们有!!

因此,让我们看看:

$ ls -ld /tmp /tnt >/dev/null
ls: cannot access /tnt: No such file or directory

$ ls -ld /tmp /tnt >/dev/null 2>&1

$ ls -ld /tmp /tnt 2>&1 >/dev/null
ls: cannot access /tnt: No such file or directory

最后一个命令行将STDERR转储到控制台,这似乎不是预期的行为... 但是...

如果要对一个输出,另一个输出或两个输出进行后期过滤

$ ls -ld /tmp /tnt | sed 's/^.*$/<-- & --->/'
ls: cannot access /tnt: No such file or directory
<-- drwxrwxrwt 118 root root 196608 Jan  7 12:02 /tmp --->

$ ls -ld /tmp /tnt 2>&1 | sed 's/^.*$/<-- & --->/'
<-- ls: cannot access /tnt: No such file or directory --->
<-- drwxrwxrwt 118 root root 196608 Jan  7 12:02 /tmp --->

$ ls -ld /tmp /tnt >/dev/null | sed 's/^.*$/<-- & --->/'
ls: cannot access /tnt: No such file or directory

$ ls -ld /tmp /tnt >/dev/null 2>&1 | sed 's/^.*$/<-- & --->/'

$ ls -ld /tmp /tnt 2>&1 >/dev/null | sed 's/^.*$/<-- & --->/'
<-- ls: cannot access /tnt: No such file or directory --->

请注意,本段的最后一个命令行与上一段完全相同,在那段我写的似乎不是预期的行为 (因此,这甚至可能是预期的行为)。

嗯,关于重定向, 在两个输出上执行不同的操作有一些技巧:

$ ( ls -ld /tmp /tnt | sed 's/^/O: /' >&9 ) 9>&2  2>&1  | sed 's/^/E: /'
O: drwxrwxrwt 118 root root 196608 Jan  7 12:13 /tmp
E: ls: cannot access /tnt: No such file or directory

注意: &9描述符会由于( ) 9>&2自发出现。

附录:nota!在新版本的>4.0 )中,有一种新功能和更性感的语法可以执行以下操作:

$ ls -ld /tmp /tnt 2> >(sed 's/^/E: /') > >(sed 's/^/O: /')
O: drwxrwxrwt 17 root root 28672 Nov  5 23:00 /tmp
E: ls: cannot access /tnt: No such file or directory

最后是这样的级联输出格式:

$ ((ls -ld /tmp /tnt |sed 's/^/O: /' >&9 ) 2>&1 |sed 's/^/E: /') 9>&1| cat -n
     1  O: drwxrwxrwt 118 root root 196608 Jan  7 12:29 /tmp
     2  E: ls: cannot access /tnt: No such file or directory

附录:nota!两种方式都具有相同的新语法:

$ cat -n <(ls -ld /tmp /tnt 2> >(sed 's/^/E: /') > >(sed 's/^/O: /'))
     1  O: drwxrwxrwt 17 root root 28672 Nov  5 23:00 /tmp
     2  E: ls: cannot access /tnt: No such file or directory

STDOUT经过一个特定的过滤器时, STDERR到达另一个过滤器,最后合并的两个输出都经过第三个命令过滤器。

3 - 关于noclobber选项和>|词汇句法

那是关于覆盖

set -o noclobber指示 bash 覆盖任何现有文件时, >|语法使您可以克服此限制:

$ testfile=$(mktemp /tmp/testNoClobberDate-XXXXXX)

$ date > $testfile ; cat $testfile
Mon Jan  7 13:18:15 CET 2013

$ date > $testfile ; cat $testfile
Mon Jan  7 13:18:19 CET 2013

$ date > $testfile ; cat $testfile
Mon Jan  7 13:18:21 CET 2013

现在,文件每次都会被覆盖:

$ set -o noclobber

$ date > $testfile ; cat $testfile
bash: /tmp/testNoClobberDate-WW1xi9: cannot overwrite existing file
Mon Jan  7 13:18:21 CET 2013

$ date > $testfile ; cat $testfile
bash: /tmp/testNoClobberDate-WW1xi9: cannot overwrite existing file
Mon Jan  7 13:18:21 CET 2013

通过>|

$ date >| $testfile ; cat $testfile
Mon Jan  7 13:18:58 CET 2013

$ date >| $testfile ; cat $testfile
Mon Jan  7 13:19:01 CET 2013

取消设置此选项和 / 或查询是否已设置。

$ set -o | grep noclobber
noclobber           on

$ set +o noclobber

$ set -o | grep noclobber
noclobber           off

$ date > $testfile ; cat $testfile
Mon Jan  7 13:24:27 CET 2013

$ rm $testfile

4 - 最后一招等等

为了重定向给定命令的两个输出,我们看到正确的语法可能是:

$ ls -ld /tmp /tnt >/dev/null 2>&1

对于这种特殊情况,有一个快捷语法: &> ... 或>&

$ ls -ld /tmp /tnt &>/dev/null

$ ls -ld /tmp /tnt >&/dev/null

注意:如果2>&1存在,则1>&2也是正确的语法:

$ ls -ld /tmp /tnt 2>/dev/null 1>&2

4b - 现在,我将让您考虑:

$ ls -ld /tmp /tnt 2>&1 1>&2  | sed -e s/^/++/
++/bin/ls: cannot access /tnt: No such file or directory
++drwxrwxrwt 193 root root 196608 Feb  9 11:08 /tmp/

$ ls -ld /tmp /tnt 1>&2 2>&1  | sed -e s/^/++/
/bin/ls: cannot access /tnt: No such file or directory
drwxrwxrwt 193 root root 196608 Feb  9 11:08 /tmp/

4c - 如果您对更多信息感兴趣

您可以点击以下内容阅读精美的手册:

man -Len -Pless\ +/^REDIRECTION bash

控制台中;-)

我发现了这篇关于重定向的精彩文章: 关于重定向的全部信息

将标准输出和标准错误都重定向到文件

$ 命令&> 文件

这种单行代码使用&>运算符将两个输出流 - stdout 和 stderr - 从命令重定向到文件。这是 Bash 的快捷方式,用于将两个流快速重定向到相同的目的地。

在 Bash 重定向两个流之后,文件描述符表如下所示:

在此处输入图片说明

如您所见,stdout 和 stderr 现在都指向file 。因此,任何写入 stdout 和 stderr 的内容都将写入file

有两种方法可以将两个流重定向到相同的目的地。您可以一个接一个地重定向每个流:

$ 命令 > 文件 2>&1

这是将两个流都重定向到文件的更常见的方法。首先将 stdout 重定向到文件,然后将 stderr 复制为与 stdout 相同。因此,两个流最终都指向file

当 Bash 看到多个重定向时,它将从左到右对其进行处理。让我们看一下步骤,看看如何发生。在运行任何命令之前,Bash 的文件描述符表如下所示:

在此处输入图片说明

现在,Bash 处理第一个重定向 > 文件。我们之前已经看过了,它使 stdout 指向文件:

在此处输入图片说明

Next Bash 看到第二个重定向 2>&1。我们之前从未见过这种重定向。这将文件描述符 2 复制为文件描述符 1 的副本,我们得到:

在此处输入图片说明

这两个流都已重定向到文件。

但是在这里要小心!写作

命令 > 文件 2>&1

与写作不同:

$ 命令 2>&1 > 文件

重定向的顺序在 Bash 中很重要!此命令仅将标准输出重定向到文件。 stderr 仍将打印到终端。要了解为什么会发生这种情况,让我们再次执行这些步骤。因此,在运行命令之前,文件描述符表如下所示:

在此处输入图片说明

现在,Bash 从左到右处理重定向。它首先看到 2>&1,因此将 stderr 复制到 stdout。文件描述符表变为:

在此处输入图片说明

现在,Bash 看到了第二个重定向>file ,并将 stdout 重定向到 file:

在此处输入图片说明

你看到这里发生了什么吗? Stdout 现在指向文件,但是 stderr 仍然指向终端!写入 stderr 的所有内容仍会打印到屏幕上!因此,请务必非常小心重定向的顺序!

另请注意,在 Bash 中,

$ 命令&> 文件

与以下内容完全相同:

$ 命令 >&file

数字指的是文件描述符(fd)。

  • 零是stdin
  • 一个是stdout
  • 二是stderr

2>&1将 fd 2 重定向到 1。

如果程序使用它们,那么它适用于任意数量的文件描述符。

如果忘记了它们,可以查看/usr/include/unistd.h

/* Standard file descriptors.  */
#define STDIN_FILENO    0   /* Standard input.  */
#define STDOUT_FILENO   1   /* Standard output.  */
#define STDERR_FILENO   2   /* Standard error output.  */

就是说,我已经编写了使用非标准文件描述符进行自定义日志记录的 C 工具,因此除非将其重定向到文件或其他内容,否则您将看不到它。

该构造将标准错误流( stderr )发送到标准输出( stdout )的当前位置 - 该货币问题似乎已被其他答案所忽略。

您可以使用此方法将任何输出句柄重定向到另一个,但是最常用于将stdoutstderr流引导到单个流中进行处理。

一些例子是:

# Look for ERROR string in both stdout and stderr.
foo 2>&1 | grep ERROR

# Run the less pager without stderr screwing up the output.
foo 2>&1 | less

# Send stdout/err to file (with append) and terminal.
foo 2>&1 |tee /dev/tty >>outfile

# Send stderr to normal location and stdout to file.
foo >outfile1 2>&1 >outfile2

请注意,最后一个不会stderr定向到outfile2 -它会在遇到参数时将其重定向到stdoutoutfile1 ), 然后stdout重定向到outfile2

这允许一些相当复杂的欺骗。

2>&1是 POSIX shell 构造。以下是逐项标记的细分:


2 :“ 标准错误 ” 输出文件描述符。

>&复制输出文件描述符运算符( 输出重定向运算符>的变体)。给定[x]>&[y] ,使x表示的文件描述符成为输出文件描述符y的副本。

1标准输出 ” 输出文件描述符。

表达式2>&1拷贝文件描述符1到位置2 ,于是写入到任何输出2在执行环境(“标准误差”)进入到最初由所描述的相同的文件1 (“标准输出”)。


进一步说明:

文件描述符 :“每个进程的唯一非负整数,用于标识打开的文件以用于文件访问。”

标准输出 / 错误 :请参考外壳文档的 “ 重定向”部分中的以下说明:

打开的文件用从零开始的十进制数字表示。可能的最大值是实现定义的;但是,所有实现应至少支持 0 到 9(含),以供应用程序使用。这些数字称为 “文件描述符”。值 0、1 和 2 具有特殊含义和常规用法,并且由某些重定向操作隐含;这些值可能会与其他值无关。它们分别称为标准输入,标准输出和标准误差。程序通常从标准输入获取输入,然后将输出写入标准输出。错误消息通常写在标准错误上。重定向运算符可以以一个或多个数字(不允许插入字符)开头,以指定文件描述符号。

2 是控制台标准错误。

1 是控制台标准输出。

这是标准的 Unix,Windows 也遵循 POSIX。

例如,当您跑步时

perl test.pl 2>&1

标准错误将重定向到标准输出,因此您可以同时看到两个输出:

perl test.pl > debug.log 2>&1

执行后,您可以在 debug.log 中看到所有输出,包括错误。

perl test.pl 1>out.log 2>err.log

然后,标准输出转到 out.log,标准错误转到 err.log。

我建议您尝试了解这些。

回答您的问题:它接受任何错误输出(通常发送到 stderr)并将其写入标准输出(stdout)。

例如,当您需要分页显示所有输出时,这会很有用。有些程序喜欢将使用情况信息打印到 stderr 中。

为了帮助您记住

  • 1 = 标准输出(程序打印正常输出)
  • 2 = 标准错误(程序打印错误)

“2>&1” 只是将发送到 stderr 的所有内容都指向 stdout。

我还建议您阅读有关错误重定向的文章,其中详细介绍该主题。

从程序员的角度来看,这恰恰意味着:

dup2(1, 2);

请参见手册页

了解2>&1副本也可以解释为什么...

command >file 2>&1

... 与... 不同

command 2>&1 >file

第一个将两个流都发送到file ,而第二个将错误发送到stdout ,并将普通输出发送到file