在Bash脚本中拆分并重新组合stdin

时间:2018-03-22 10:42:39

标签: bash redirect stream pipe tee

假设我有流式文本数据进入我的Bash脚本,每行有一条记录,我想在每行附加该行的一些功能并将其吐出到stdout

record1      record1  fn(record1)
record2  ->  record2  fn(record2)
...          ...

这可以比较容易,例如,Awk。但是,假设我应用于我的输入数据的函数如果应用于流数据则效率提高了几个数量级(并且我有很多它,所以线性Awk处理绝对不是一个选项)。这是我提出的解决方案:

input="$(mktemp)"
trap "rm -rf ${input}" EXIT
cat > "${input}"
paste "${input}" <(some_function "${input}")

这可以通过将stdin重定向到临时文件(cat行),然后使用进程替换paste将文件放在一起来实现。然而,这对我来说似乎有些混乱(例如,UUOC),我认为用exec(用于重定向)和tee可能有一种“更好”的方法,但我是不太确定如何。

这可以做得更好吗?

2 个答案:

答案 0 :(得分:0)

简单(?)解决方法

最简单的解决方案可能是修改您的功能,使其输入始终在实际输出之前打印。数学上:将f(x)=y替换为g(x)="x f(x)"

如果无法轻松修改功能,请使用以下方法之一。

无文件方法

如果你有足够的内存,你可以通过将stdin保存到变量来修改当前的无文件工作方法:

input="$(cat)"
paste - <(yourFunction <<< "$input") <<< "$input"

实际答案

编辑: 这可能对大文件无效。 tee可以交错两个输出。我正在研究一个更好的解决方案。我不知道我是否会找到更好的一个。如果您发现修改此方法的安全性,请随意编辑此答案。

(tee >(yourFunction) | pr -Ts --columns 2)

第一部分打印stdin,然后是stdin通过你的函数。如果输入是作为文件cat file <(yourFunction < file)提供的话,基本上与file相同。

第二部分用作paste 1st_half_of_output 2nd_half_of_output

实施例

假设以下三行作为stdin

Lorem Ipsum
What a nice 2nd line
\t\r\n[]().*?+<>\

你的职能是

f() { awk '{print "#" NR}'; }

然后会打印tee >(f)编辑: 订单无法保证。请参阅上面的说明。

Lorem Ipsum
What a nice 2nd line
\t\r\n[]().*?+<>\
#1
#2
#3

pr会将该输出转换为

Lorem Ipsum #1
What a nice 2nd line    #2
\t\r\n[]().*?+<>\   #3

在每个#之前,有一个制表符作为列分隔符。

答案 1 :(得分:0)

这并不能消除对临时文件的需求,但至少可以摆脱对cat无用的使用:

input="$(mktemp)"
trap "rm -rf ${input}" EXIT
tee "${input}" | paste - <(some_function "${input}")