1155

我有一个将信息写入stdoutand的程序stderr,我需要处理stderrwith grepstdout搁置一边。

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

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

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

4

11 回答 11

1404

首先将stderr重定向到stdout——管道;然后将标准输出重定向到/dev/null(不改变标准错误的去向):

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

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

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

于 2010-02-26T15:55:18.973 回答
383

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

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

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

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

于 2010-03-04T18:18:10.600 回答
245

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

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

对于手头的情况:

command 2> >(grep 'something') >/dev/null
于 2012-02-09T19:14:08.850 回答
216

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

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

...然后所有标准输出都保留为标准输出所有标准错误都保留为标准错误,但您不会在标准错误中看到任何包含字符串“某物”的行。

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

于 2013-04-10T21:05:15.163 回答
115

如果您考虑一下“重定向”和“管道”到底发生了什么,那么将事物可视化会容易得多。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)。“command FD1”指向这个管道。“grep FD0”也指向这个管道
  2. “command FD2”指向“command FD1”当前指向的位置(管道)
  3. “命令 FD1” 指向 /dev/null

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

相反,您运行以下命令:

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

这是发生的事情:

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

因此,“命令”中的所有标准输出和标准错误都转到 /dev/null。没有任何东西进入管道,因此“grep”将关闭而不在屏幕上显示任何内容。

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

最后一点。程序是否向 FD1 或 FD2 写入内容完全取决于程序员。良好的编程习惯规定错误消息应发送到 FD 2,正常输出到 FD 1,但您经常会发现草率的编程混合了两者或忽略了约定。

于 2013-08-20T18:09:19.243 回答
45

如果您使用的是 Bash,请使用:

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

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

于 2014-04-18T21:56:16.273 回答
11

对于那些想要将 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
于 2012-11-14T08:59:08.373 回答
8

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

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

取自自民党

于 2014-10-07T07:39:05.660 回答
3

我刚刚想出了一个使用命名管道发送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

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

于 2018-08-09T18:35:48.673 回答
0

您可以使用rc shell

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

这是一个如何丢弃标准输出并将标准错误传递给grep in的示例rc

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

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

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

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

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

  • 0:标准输入
  • 1:标准输出
  • 2:标准误差
于 2019-03-17T19:17:17.790 回答
-3

我尝试跟随,发现它也有效,

command > /dev/null 2>&1 | grep 'something'
于 2014-04-11T03:27:32.560 回答