假设我有一个流,它包含三列以t
为分隔符的数据。
为了处理流中的数据,后面的两列需要由prog1
处理,而第一列和prog1
的输出(1列(将由prog2
处理。
prog1
:从stdin读取数据(两列(,并将其输出写入stdoutprog2
:从stdin读取数据(两列,一列来自原始流,另一列来自prog1
的输出(,并将其输出写入stdout
考虑到prog1
和prog2
在启动时都会花费大量时间,重复启动程序是不可接受的。因此,以下片段并不能真正解决问题:
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避免了大量的麻烦。