在Bash脚本中多次读取标准输入

5
我的脚本从stdin接受一个流。我想要无论如何将第一行传递到stdout,并使用-v过滤其余的行并将其传递到stdout。
我使用tee解决了这个问题,但我想知道这是否保证始终在grep之前打印head的输出?如果head被替换为阻塞20分钟才打印任何内容的东西,那么该输出是否会出现在grep输出后的stdout末尾?
tee >(head -n 1) >(tail -n +2 | grep -v -E "$PATTERN")

如果订单没有得到保证,那么正确的做法是什么?

为什么不试一下呢? - MrPaulch
两个进程替换的另一个明显问题是,尽管 head输出一行,但它很可能读取大于一个字节的块以查找第一个换行符,因此可能会消耗不止第一行。 - chepner
3个回答

5

您正在过度思考,无需使用teeheadtail

您可以使用read读取第一行并将其打印出来,然后在其余部分上使用grep

$ printf "foo\nbar\nquux\n" | { read v; echo "$v"; grep -v bar; }
foo
quux

或者,将逻辑合并到单个 awk 语句中,从而完全避免此问题:

$ printf "foo\nbar\nquux\n" | awk 'NR==1{print;next} !/bar/'
foo
quux

1

你对于多线程的担忧是正确的。这两个子shell将会并行运行,因此不能保证哪一个会先运行。为了强制执行操作顺序,在搜索其余输入之前,请先读取和打印第一行。

read line && printf '%s\n' "$line"
tee >(grep -v -E "$PATTERN")

很酷,是的,在这种情况下使用read是有道理的。我想我对一般情况下的解决方案很感兴趣(在这种情况下我不能用read替换其中之一)。 - Rob Crowell

1
我认为我会选择sed:
printf "Line1\nfoo\nbar\n" | sed '1n;/bar/d'

输出:

Line1
foo

那就是说,如果它是第一行,就打印并跳到下一行,否则如果该行包含bar,则删除它。

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