如何通过Shell中的不同程序处理数据流中的不同列



假设我有一个流,它包含三列以t为分隔符的数据。

为了处理流中的数据,后面的两列需要由prog1处理,而第一列和prog1的输出(1列(将由prog2处理。

  • prog1:从stdin读取数据(两列(,并将其输出写入stdout
  • prog2:从stdin读取数据(两列,一列来自原始流,另一列来自prog1的输出(,并将其输出写入stdout

考虑到prog1prog2在启动时都会花费大量时间,重复启动程序是不可接受的。因此,以下片段并不能真正解决问题:

IFS='t'  # C-v tab
while read c0 c1 c2; do
paste <(echo ${c0}) <(echo -e"${c1}t${c2}" | prog1) | prog2
done </dev/stdin

任何线索或提示都将不胜感激。

以下内容?

# create a temporary fifo
fifo=$(mktemp -u)
mkfifo "$fifo"
# split and get first column
tee >(cut -f1 >"$fifo") |
# extract 2,3 column and pass to prog1
cut -f2,3 | prog1 |
# join with first column
paste "$fifo" - |
prog2

通过创建命名FIFO,我们可以使用tee制作stdout流的多个副本,每个副本都可以作为stdin提供给一个单独的独立循环,从而使paste处于顶级。

#!/usr/bin/env bash
# create a temporary directory for our FIFOs, delete it when the script exits
tempdir=$(mktemp -t -d tempdir.XXXXXX) || exit
die() { rc=$?; rm -rf "$tempdir"; exit "$rc"; }
trap die EXIT
# create FIFOs in that temporary directory, start a background copy of tee
# ...to copy stdin into them.
mkfifo "$tempdir"/stdin{1,2}
tee "$tempdir/stdin1" >"$tempdir/stdin2" & tee_pid=$!
exec </dev/null # stdin is for tee, not us
# Run just one copy of paste, starting the while read loops as child processes
paste 
<(while read c0 c1 c2; do echo "$c0"; done <"$tempdir/stdin1") 
<(while read c0 c1 c2; do
printf '%st%sn' "$c1" "$c2"
done <"$tempdir/stdin2" | prog1) 
| prog2

请注意,while read循环并不是特别有效——它们最好用awk调用来代替——但期望的是,您将插入已知的好代码来代替它们。

使用echo -e会产生不幸的副作用——例如,它会破坏数据中的反斜杠,而且在POSIX标准化版本的echo中也无法正常工作(甚至在bash自己的echo中,当使用使其严格符合POSIX的可选运行时标志运行时(;而使用CCD_ 17避免了大量的麻烦。

最新更新