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

Nei*_*gan 5 bash pipe shell-script text-processing

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

alice  5
bob    7
...
Run Code Online (Sandbox Code Playgroud)

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

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

结果应该是:

alice  5  foo
bob    7  bar
...
Run Code Online (Sandbox Code Playgroud)

想象一下,这cat source.tab真的是一个很长的管道,可以进行其他预处理。这dbstream ..可能是其他一些命令,比如wget | jq

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

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

Ste*_*ris 8

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

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

您可以将循环内发生的事情更改为您喜欢的格式,例如

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

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

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

#!/bin/sh
for a in "$@"
do
  echo dbstream $$ sees $a
done
Run Code Online (Sandbox Code Playgroud)

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

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

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

现在,如果这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 ))
Run Code Online (Sandbox Code Playgroud)