比`tee | 更好的方式切| ... |粘贴`

比`tee | 更好的方式切| ... |粘贴`

尝试在管道中进行“查找”,其中输入如下所示:

alice  5
bob    7
...

我想在数据库的第二列中查找代码并返回相应的名称,并继续使用原始数据和查找到的数据进行运输。

cat source.tab | \
  tee foo.tmp | \
  cut -f 2 | \
  dbstream ... -s "select(select name from my_lookup where code=?)" | \
  paste foo.tmp -

结果应该是:

alice  5  foo
bob    7  bar
...

想象一下,这cat source.tab实际上是一个执行其他预处理的长管道。这dbstream ..可能是其他命令,例如wget | jq

重要提示:我只想启动一次查找过程。

a)这是一个糟糕的主意吗?如果是的话我应该做什么?
b) 有比 更好的模式吗tee tmp | cut | "lookup" | paste tmp -

答案1

这取决于输出的复杂程度以及需要维护多少格式(例如,第一列总是 8 个字符长?等等)。然而while循环可能有效

cat source.tab | while read -r name id
do
  echo "$name $id $(dbstream .... code=$id)"
done

您可以更改循环内发生的情况以设置您喜欢的格式,例如

cat source.tab | while read -r name id
do
  res=$(dbstream ... code=$id)
  printf "%10s %5d %s" $name $id $res
done

根据评论,您只想打电话dbstream一次。这需要dbstream保持输出与输入的顺序相同。

这是一个简单的示例dbstream程序:

#!/bin/sh
for a in "$@"
do
  echo dbstream $$ sees $a
done

我们在输出中包含 PID,这样我们就可以显示它只被调用一次。

现在我们可以使用paste并处理替换:

$ paste source.tab <(./dbstream $(awk '{print $2}' source.tab ))
alice 1 dbstream 20671 sees 1
bob   2 dbstream 20671 sees 2

现在,如果这source.tab是一个缓慢的过程,我建议使用临时文件

例如

#!/bin/bash

tmp=`mktemp`

trap '/bin/rm -f $tmp ; exit' 0 1 2 3 15

cat source.tab > $tmp
paste $tmp <(./dbstream $(awk '{print $2}' $tmp ))

答案2

正确的方法似乎是使用命名管道

例子:

function datastreamWrapper() {

  mypipe=$(mktemp -u)
  mkfifo -m 600 "$mypipe"

  tee >( cut -f2 | datastream ... > "$mypipe") | paste - "$mypipe"

  rm "$mypipe"
}

然后您可以将 datastreamWrapper 放入管道中:

cat source.tab | datastreamWrapper | foo

相关内容