我有一个将信息写入stdout
and的程序stderr
,我需要处理stderr
with grep
,stdout
搁置一边。
使用临时文件,可以分两步完成:
command > /dev/null 2> temp.file
grep 'something' temp.file
但是,如果没有临时文件,如何使用一个命令和管道来实现呢?
首先将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 最初指向的打开文件描述(即管道)这一事实。
或者交换标准错误和标准输出的输出,使用:
command 3>&1 1>&2 2>&3
这将创建一个新的文件描述符 (3) 并将其分配到与 1(标准输出)相同的位置,然后将 fd 1(标准输出)分配到与 fd 2(标准错误)相同的位置,最后分配 fd 2(标准错误) ) 到与 fd 3 (标准输出)相同的位置。
标准错误现在可作为标准输出使用,旧的标准输出保留在标准错误中。这可能有点矫枉过正,但它希望能提供有关 Bash 文件描述符的更多详细信息(每个进程有九个可用)。
在 Bash 中,您还可以使用进程替换重定向到子 shell :
command > >(stdlog pipe) 2> >(stderr pipe)
对于手头的情况:
command 2> >(grep 'something') >/dev/null
结合最好的这些答案,如果你这样做:
command 2> >(grep -v something 1>&2)
...然后所有标准输出都保留为标准输出,所有标准错误都保留为标准错误,但您不会在标准错误中看到任何包含字符串“某物”的行。
这具有不反转或丢弃 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'
这是发生的事情,按顺序:
因此,“命令”写入其 FD 2(stderr)的所有输出都会进入管道,并由另一侧的“grep”读取。“命令”写入其 FD 1 (stdout) 的所有输出都进入 /dev/null。
相反,您运行以下命令:
command >/dev/null 2>&1 | grep 'something'
这是发生的事情:
因此,“命令”中的所有标准输出和标准错误都转到 /dev/null。没有任何东西进入管道,因此“grep”将关闭而不在屏幕上显示任何内容。
另请注意,重定向(文件描述符)可以是只读 (<)、只写 (>) 或读写 (<>)。
最后一点。程序是否向 FD1 或 FD2 写入内容完全取决于程序员。良好的编程习惯规定错误消息应发送到 FD 2,正常输出到 FD 1,但您经常会发现草率的编程混合了两者或忽略了约定。
如果您使用的是 Bash,请使用:
command >/dev/null |& grep "something"
http://www.gnu.org/software/bash/manual/bashref.html#Pipelines
对于那些想要将 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
这会将 command1 stderr 重定向到 command2 stdin,同时保持 command1 stdout 不变。
exec 3>&1
command1 2>&1 >&3 3>&- | command2 3>&-
exec 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
之后删除命名管道可能是个好主意。
我尝试跟随,发现它也有效,
command > /dev/null 2>&1 | grep 'something'