首页 文章

如何管stderr,而不是stdout?

提问于
浏览
809

我有一个程序将信息写入 stdoutstderr ,我需要 grep 通过什么来stderr,而忽略stdout .

我当然可以分两步完成:

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

但我宁愿能够在没有临时文件的情况下做到这一点 . 有没有任何智能管道技巧?

10 回答

  • 1

    你在用bash吗?如果是这样:

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

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

  • 320

    对于那些想要将stdout和stderr永久重定向到文件的人来说,grep onstderr,但保持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
    
  • 963

    如果你想一想“重定向”和“管道”的真实情况,那么可视化的东西要容易得多 . bash中的重定向和管道做一件事:修改进程文件描述符0,1和2指向的位置(参见/ proc / [pid] / fd / *) .

    当命令行中存在 pipe 或"|"运算符时,首先要做的是bash创建一个fifo并将左侧命令's FD 1 to this fifo, and points the right side command' s FD 0指向同一个fifo .

    接下来,从左到右评估每一侧的重定向运算符,并且每当发生描述符的重复时使用当前设置 . 这一点非常重要,因为自首次设置管道以来,FD1(左侧)和FD0(右侧)已经从通常的状态改变,这些的重复将反映出这一事实 .

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

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

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

    • 创建了一个管道(fifo) . "command FD1"指向此管道 . "grep FD0"也指向这个管道

    • "command FD2"指向"command FD1"当前指向的位置(管道)

    • "command FD1"指向/ dev / null

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

    相反,如果您运行以下内容:

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

    这是发生的事情:

    • 创建了一个管道,并指向了"command FD 1"和"grep FD 0"

    • "command FD 1"指向/ dev / null

    • "command FD 2"指向FD 1当前指向的位置(/ dev / null)

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

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

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

  • 182

    我想出了一个解决方案,使用命名管道将 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

    我试着跟着,发现它也有用,

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

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

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

    摘自LDP

  • 5

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

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

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

  • 89

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

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

    对于手头的情况:

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

    首先将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最初指向的打开文件描述的事实 - 即,管 .

  • -3

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

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

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

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

相关问题