如何管stderr,而不是stdout?

时间:2010-02-26 15:53:28

标签: bash grep stdout pipe stderr

我有一个程序将信息写入stdoutstderr,我需要grep通过 stderr 的内容,而忽略标准输出

我当然可以分两步完成:

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

但我希望能够在没有临时文件的情况下执行此操作。有没有任何智能管道技巧?

13 个答案:

答案 0 :(得分:1045)

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

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

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

请注意,I / O重定向序列是从左向右解释的,但在解释I / O重定向之前会设置管道。文件描述符(如1和2)是对打开文件描述的引用。操作2>&1使文件描述符2也称为stderr引用与文件描述符1相同的打开文件描述,也就是stdout当前所指的(参见dup2()open())。操作>/dev/null然后更改文件描述符1,使其引用/dev/null的打开文件描述,但这不会改变文件描述符2引用打开文件描述哪个文件描述符1这一事实最初指的是 - 即管道。

答案 1 :(得分:329)

或者将stderr和stdout的输出交换使用: -

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

这会创建一个新的文件描述符(3)并将其分配到与1(stdout)相同的位置,然后将fd 1(stdout)分配给与fd 2(stderr)相同的位置,最后分配fd 2(stderr)和fd 3(stdout)在同一个地方。 Stderr现在可以作为stdout使用,旧stdout保存在stderr中。这可能有点过分,但希望提供有关bash文件描述符的更多详细信息(每个进程有9个可用)。

答案 2 :(得分:194)

在Bash中,您还可以使用process substitution重定向到子shell:

command > >(stdlog pipe)  2> >(stderr pipe)

对于手头的案例:

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

答案 3 :(得分:167)

如果您这样做,请结合这些答案中的最佳答案:

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

...然后所有stdout都保存为stdout 所有stderr都保存为stderr,但是你不会在stderr中看到包含字符串“something”的任何行。

这具有独特的优势,即不会反转或丢弃标准输出和标准输出,也不会将它们放在一起,也不会使用任何临时文件。

答案 4 :(得分:92)

如果您想一想“重定向”和“管道”的真实情况,那么可视化的内容要容易得多。 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
  4. 因此,“命令”写入其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)
    4. 因此,“command”中的所有stdout和stderr都转到/ dev / null。没有任何东西进入管道,因此“grep”将关闭而不在屏幕上显示任何内容。

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

      最后一点。程序是否将某些内容写入FD1或FD2,完全取决于程序员。良好的编程习惯要求错误消息应该转到FD 2并且正常输出转换为FD 1,但是您经常会发现混合两者的草率编程或者忽略惯例。

答案 5 :(得分:32)

你正在使用bash吗?如果是这样的话:

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

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

答案 6 :(得分:9)

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

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

答案 7 :(得分:5)

这会将command1 stderr重定向到command2 stdin,同时保留command1 stdout。

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

取自LDP

答案 8 :(得分:1)

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

在这里。

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

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

答案 9 :(得分:1)

这也有效(我发现它更容易记住)

command 2> /dev/fd/1 | grep 'something'

有关 /dev/fd 目录 here

的更多信息

答案 10 :(得分:0)

您可以使用rc shell

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

以下示例说明了如何丢弃stdout并将管道stderr传送到rc中的grep:

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

您可以在不离开Bash的情况下完成此操作:

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

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

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

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

答案 11 :(得分:-2)

我倾向于做类似的事情

  

作曲家测试&>> / tmp / bob && vim / tmp / bob && rm / tmp / bob

答案 12 :(得分:-3)

我尝试按照,发现它也有效,

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