有没有一种方法可以在bash中将输出重定向到不同的位置,并使用不同的过滤器?

5
如果我有一个进程a.out,我可以使用./a.out | grep foo来查看经过foo过滤的a.out的标准输出。我还可以说./a.out 2>&1 | grep foo来查看被foo过滤的错误和输出。使用tee命令,我可以将标准输出发送到终端和可能的文件输出。但是有没有一种方法可以分别过滤它们?就像这样:
./a.out | tee grep foo file.txt

但是我希望将写入file.txt的内容过滤成匹配foo,而不是屏幕上显示的内容...或者更好的是,将屏幕上显示的内容通过baz进行过滤,如果在bash中没有现成的方法,我会自己编写“tee”,但我想应该有某种方法...

3个回答

3

想要在终端显示所有内容并过滤输出到文件,可以尝试以下命令:

./a.out| tee /dev/tty | grep foo > file

如果您使用的是带有/proc文件系统(如Linux)的系统,则可以使用以下命令将输出筛选到终端:

{ ./a.out | tee /proc/self/fd/3 | grep foo > file; } 3>&1 | grep bar

但是,甚至这样可能也有些过于劳累了。只需要这样做:

./a.out | awk '/foo/{ print > "file" } 1' | grep bar

最后一个是如何工作的?awk匹配a.out的stdout中的foo并将这些行打印到“file”中...但它们如何到达grep bar?我猜这与awk脚本末尾的1有关? - Palace Chan
脚本末尾的1会导致每一行都被打印。在实际使用中,您可以直接写/bar/而不是将内容传送到grep bar - William Pursell

3
非常简单,只需使用进程替换来处理文件句柄:
./a.out | tee >(grep foo > out.txt) | grep baz

同时请注意,tee 命令可以接受任意多个参数,因此您可以进行如下操作:

./a.out | tee >(grep foo > foo.txt) >(grep bar > bar.txt) [etc]

这段代码的意思是,stdout会被重定向到$outfilter > out.txt过滤器,而stderr则会被重定向到$errfilter > err.txt过滤器...但我能否使用进程替换来对stdout进行两个不同的操作?比如让它既输出到out.txt,又输出到终端? - Palace Chan
你说得对,我已经调整了答案以正确回答问题。只需将与stdout相关的任务添加到tee中的参数即可。 - Grisha Levit
哦,太酷了!我有点困惑为什么是 tee <(grep food > out.txt) | grep baz...难道不是 <(bla) 告诉 tee,从 <() 构造中的进程获取输入吗?如果我把 <(grep foo > out.txt) 看作一个参数文件,那就有意义了,但我原以为 <() 和 >() 分别表示“从 <() 中的命令输出获取 stdin”和“将 stdout 传递给 >() 中的命令”? - Palace Chan
宫殿,你说得对,那是个笔误。正确的结构应该是 tee >(filter1 > file1) >(command2 > file2) 等等。 - Grisha Levit

2
{ { ( myCommandThatOutputsOnStdOutandStdErr; ) \
| ( awk ' ... filters stdout ... ' - ; ) >&3; } 2>&1 \
| ( awk ' ... filters stderr ... ' - ; ) >&4; } 3>&1 4>&2

这是伪代码。 awk模糊语句不是真实的,但可以由您自己的过滤器替换。思路是两个独立的过滤器可以分别在stderr和stdout上操作,但会将未被过滤的stdout保留在stdout上,未被过滤的stderr保留在stderr上。

最终的3>&14>&2可以很容易地像3>my.stdout4>my.stderr一样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接