尝试在管道中进行“查找”,其中输入如下所示:
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