Uma maneira melhor do que 'tee | corte | … | colar'

5

Tentando fazer uma "pesquisa" em um pipeline, onde a entrada é assim:

alice  5
bob    7
...

Eu quero procurar códigos na segunda coluna em um banco de dados e retornar o nome correspondente, e continuar com os dados originais e pesquisados.

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

O resultado deve ser:

alice  5  foo
bob    7  bar
...

Imagine por um minuto que cat source.tab é realmente um longo pipeline que faz outro pré-processamento. E que dbstream .. poderia ser algum outro comando, digamos wget | jq .

Importante: quero apenas iniciar o processo de pesquisa uma vez.

a) é uma idéia terrível, e se sim, o que devo fazer em vez disso?
b) existe um padrão melhor que tee tmp | cut | "lookup" | paste tmp - ?

    
por Neil McGuigan 29.07.2016 / 01:03

2 respostas

8

Depende de quão complicada é a saída e de quanto a formatação precisa ser mantida (por exemplo, a primeira coluna tem sempre oito caracteres? etc.). No entanto, um loop while pode funcionar

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

Você pode alterar o que acontece dentro do loop para formatar o que quiser eg

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

Como comentário, você só quer chamar dbstream uma vez. Isso requer dbstream para manter a saída na mesma ordem de entrada.

Aqui está um exemplo simples de dbstream program:

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

Incluímos o PID na saída para que possamos mostrar que ele é chamado apenas uma vez.

Agora podemos usar paste e substituir processo:

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

Agora, se o source.tab for um processo lento, recomendo usar um arquivo temporário

por exemplo

#!/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 ))
    
por 29.07.2016 / 01:11
0

A maneira correta parece ser usar um pipe nomeado

Exemplo:

function datastreamWrapper() {

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

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

  rm "$mypipe"
}

Você pode colocar o datastreamWrapper em seu pipeline:

cat source.tab | datastreamWrapper | foo
    
por 30.03.2017 / 22:14