我怎么能管stderr,而不是stdout?

我有一个程序将信息写入stdoutstderr,我需要用grep处理stderr,将stdout放在一边。

使用临时文件,可以分两步完成:

command > /dev/null 2> temp.filegrep 'something' temp.file

但是,如果没有临时文件,使用一个命令和管道,如何实现这一点?

533032 次浏览

首先将stderr重定向到stdout-管道;然后将stdout重定向到/dev/null(不更改stderr的去向):

command 2>&1 >/dev/null | grep 'something'

有关各种I/O重定向的详细信息,请参阅Bash参考手册中关于重定向的章节。

请注意,I/O重定向的顺序是从左到右解释的,但是在解释I/O重定向之前设置了管道。文件描述符如1和2是对打开文件描述的引用。操作2>&1使文件描述符2又名stderr引用与文件描述符1又名stdout当前引用的相同的打开文件描述(参见#1#2)。操作>/dev/null然后更改文件描述符1,使其引用/dev/null的打开文件描述,但这并没有改变文件描述符2引用文件描述符1最初指向的打开文件描述的事实,即管道。

或者要交换标准错误和标准输出的输出,请使用:

command 3>&1 1>&2 2>&3

这将创建一个新的文件描述符(3)并将其分配到与1(标准输出)相同的位置,然后将fd 1(标准输出)分配到与fd 2(标准错误)相同的位置,最后将fd 2(标准错误)分配到与fd 3(标准输出)相同的位置。

标准错误现在可以作为标准输出使用,旧的标准输出保留在标准错误中。这可能有点矫枉过正,但希望它能提供有关Bash文件描述符的更多详细信息(每个进程有九个可用)。

在Bash中,您还可以使用过程替换重定向到子shell:

command > >(stdout pipe)  2> >(stderr pipe)

对于手头的案件:

command 2> >(grep 'something') >/dev/null

对于那些想要将stdout和stderr永久重定向到文件的人,在stderr上使用grep,但保留stdout以将消息写入tty:

# save tty-stdout to fd 3exec 3>&1# switch stdout and stderr, grep (-v) stderr for nasty messages and append to filesexec 2> >(grep -v "nasty_msg" >> std.err) >> std.out# goes to the std.outecho "my first message" >&1# goes to the std.errecho "a error message" >&2# goes nowhereecho "this nasty_msg won't appear anywhere" >&2# goes to the ttyecho "a message on the terminal" >&3

结合这些最好的答案,如果你这样做:

command 2> >(grep -v something 1>&2)

…然后所有stdout都保留为stdout所有stderr都保留为stderr,但您不会在stderr中看到任何包含字符串“某物”的行。

这具有不反转或丢弃stdout和stderr的独特优势,也不会将它们合并在一起,也不会使用任何临时文件。

如果你想一想“重定向”和“管道”到底是怎么回事,就更容易可视化了。bash中的重定向和管道只做一件事:修改进程文件描述符0、1和2指向的位置(参见 /proc/[pid]/fd/*)。

当命令行上出现管道或“|”运算符时,首先发生的事情是bash创建一个FIFO并将左侧命令的FD 1指向该FIFO,并将右侧命令的FD 0指向相同的FIFO。

接下来,每一侧的重定向运算符都被评估从左到右,并且每当描述符发生重复时都使用当前设置。这很重要,因为自从首先设置管道以来,FD1(左侧)和FD0(右侧)已经从它们通常的状态发生了变化,这些的任何重复都将反映这一事实。

因此,当您键入以下内容时:

command 2>&1 >/dev/null | grep 'something'

以下是发生的事情,按顺序:

  1. 创建了一个管道(FIFO)。“命令FD1”指向此管道。“grep FD0”也指向此管道
  2. “命令FD2”指向“命令FD1”当前指向的位置(管道)
  3. "命令FD1"指向 /dev/null

因此,“命令”写入其FD 2(stderr)的所有输出都进入管道并由另一侧的“grep”读取。“命令”写入其FD 1(stdout)的所有输出都进入 /dev/null.

如果不是,则运行以下命令:

command >/dev/null 2>&1 | grep 'something'

以下是发生的事情:

  1. 创建一个管道,并将“命令FD 1”和“grep FD 0”指向它
  2. “命令FD 1”指向 /dev/null
  3. “命令FD 2”指向FD 1当前指向的位置(/dev/null)

因此,所有stdout和stderr从“命令”转到 /dev/null.没有东西进入管道,因此“grep”将关闭而不在屏幕上显示任何内容。

另请注意,重定向(文件描述符)可以是只读(<)、只写(>)或读写(<>)。

最后一点。程序是向FD1还是FD2写入某些内容,完全取决于程序员。良好的编程实践要求错误消息应发送到FD2,正常输出应发送到FD1,但您经常会发现草率的编程将两者混合在一起或忽略了约定。

我试着跟随,发现它工作,以及,

command > /dev/null 2>&1 | grep 'something'

如果您使用Bash,请使用:

command >/dev/null |& grep "something"

http://www.gnu.org/software/bash/manual/bashref.html#Pipelines

这将重定向命令1 stderr到命令2 stdin,同时保持命令1 stdout不变。

exec 3>&1command1 2>&1 >&3 3>&- | command2 3>&-exec 3>&-

取自LDP

我刚刚想出了一个解决方案,使用命名管道将stdout发送到一个命令,将stderr发送到另一个命令。

开始吧。

mkfifo stdout-targetmkfifo stderr-targetcat < stdout-target | command-for-stdout &cat < stderr-target | command-for-stderr &main-command 1>stdout-target 2>stderr-target

之后删除命名管道可能是个好主意。

您可以使用rc shell

首先安装软件包(小于1 MB)。

这是一个如何丢弃标准输出并将标准错误管道到rc中的grep的示例:

find /proc/ >[1] /dev/null |[2] grep task

你可以在不离开Bash的情况下做到这一点:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

您可能已经注意到,您可以通过在管道之后使用括号来指定要管道的文件描述符。

标准文件描述符的编号如下:

  • 0:标准输入
  • 1:标准输出
  • 2:标准误差