Divida uma entrada para um comando diferente e combine o resultado

8

Eu sei como combinar o resultado de comando diferente

paste -t',' <(commanda) <(commandb)

Eu sei canalizar a mesma entrada para comando diferente

cat myfile | tee >(commanda) >(commandb)

Agora, como combinar esses comandos? Para que eu possa fazer

cat myfile | tee >(commanda) >(commandb) | paste -t',' resulta resultb

Digamos que eu tenho um arquivo

meu arquivo:

1
2
3
4

Eu quero fazer um novo arquivo

1 4 2
2 3 4
3 2 6
4 1 8

eu usei

cat myfile | tee >(tac) >(awk '{print $1*2}') | paste

me dá resultado verticalmente, onde eu realmente quero colá-los na ordem horizontal.

user40129
fonte
pode ser necessário escrever dois fluxos para separar pipes de nomes e combiná-los com um programa de monitor.
6151515

Respostas:

8

Quando você faz o tee para várias substituições de processo, não é garantido que você obtenha a saída em qualquer ordem específica, então é melhor ficar com

paste -t',' <(commanda < file) <(commandb < file)

Supondo que cat myfilerepresenta algum pipeline caro, acho que você precisará armazenar a saída, em um arquivo ou em uma variável:

output=$( some expensive pipeline )
paste -t',' <(commanda <<< "$output") <(commandb <<< "$output")

Usando seu exemplo:

output=$( seq 4 )
paste -d' ' <(cat <<<"$output") <(tac <<<"$output") <(awk '$1*=2' <<<"$output")
1 4 2
2 3 4
3 2 6
4 1 8

Outro pensamento: FIFOs e um único pipeline

mkfifo resulta resultb
seq 4 | tee  >(tac > resulta) >(awk '$1*=2' > resultb) | paste -d ' ' - resulta resultb
rm resulta resultb
1 4 2
2 3 4
3 2 6
4 1 8
Glenn Jackman
fonte
posso usar sth como / dev / fd / 3-9?
user40129
4

O yashshell possui recursos exclusivos ( redirecionamento de pipeline e redirecionamento de processo ) que facilitam a tarefa:

cat myfile | (
  exec 3>>|4
  tee /dev/fd/5 5>(commanda >&3 3>&-) 3>&- |
    commandb 3>&- |
    paste -d , /dev/fd/4 - 3>&-
)

3>>|4( redirecionamento de pipeline ) cria um canal em que o final de gravação está no fd 3 e o final da leitura no fd 4.

3>(commanda>&3)é o redirecionamento de processo , um pouco como a substituição do processo ksh / zsh / bash, mas apenas faz o redirecionamento e não o substitui pelo /dev/fd/n. ksh's >(cmd)é mais ou menos o mesmo que yash' s n>(cmd) /dev/fd/n(não né um descritor de arquivo escolhido por kshsobre o qual você não tem controle).

Stéphane Chazelas
fonte
3

Com zsh:

pee() (
  n=0 close_in= close_out= inputs=() outputs=()
  merge_command=$1; shift
  for cmd do
    eval "coproc $cmd $close_in $close_out"

    exec {i}<&p {o}>&p
    inputs+=($i) outputs+=($o)
    eval i$n=$i o$n=$o
    close_in+=" {i$n}<&-" close_out+=" {o$n}>&-"
    ((n++))
  done
  coproc :
  read -p
  eval tee /dev/fd/$^outputs $close_in "> /dev/null &
    " exec $merge_command /dev/fd/$^inputs $close_out
)

Então use como:

$ echo abcd | pee 'paste -d,' 'tr a A' 'tr b B' 'tr c C'
Abcd,aBcd,abCd

Isso foi adaptado dessa outra questão, na qual você encontrará explicações detalhadas e dicas sobre as limitações (cuidado com os impasses!).

Stéphane Chazelas
fonte
0

Para o seu exemplo particular, não deve haver necessidade pastee o resto. Muitas vezes, é verdade que, quando encontramos um limite com o conjunto de ferramentas padrão, é porque o que queremos fazer de uma maneira pode ser feito de outra. Tal como:

set 1 2 3 4
while [ "$#" -gt 0 ]
do    echo "$1" "$#" "$(($1*2))"
shift;done

... que imprime ...

1 4 2
2 3 4
3 2 6
4 1 8

Você pode obter um arquivo com conteúdo como você menciona em seu "$@"array de shell como ...

set -f; IFS='
'; set -- $(cat)

E para validar os valores de arg em um loop como o acima, você pode alterar um pouco o teste inicial ...

while { [ "$1" -eq "${1-1}" ] ;} 2>&"$((2+!$#))"
do    echo "$1" "$#" "$(($1*2))"
shift;done  3>/dev/null >outfile

... que imprime um erro no stderr somente se uma linha lida com set -- $(cat)contiver uma linha que não consiste inteiramente de um único número inteiro.

mikeserv
fonte