Completamente a saída do comando buffer antes de enviar o comando para outro comando?

8

Existe uma maneira de executar somente um comando após o outro ser feito sem um arquivo temporário? Eu tenho um comando mais longo e outro comando que formata a saída e envia para um servidor HTTP usando curl. Se eu apenas executar commandA | commandB , commandB iniciará curl , conecte-se ao servidor e comece a enviar dados. Como commandA demora muito, o servidor HTTP expira. Eu posso fazer o que quiser com commandA > /tmp/file && commandB </tmp/file && rm -f /tmp/file

Por curiosidade eu quero saber se existe uma maneira de fazer isso sem o arquivo temporário. Eu tentei mbuffer -m 20M -q -P 100 , mas o processo de ondulação ainda é iniciado logo no início. O Mbuffer espera apenas até que commandA termine o envio dos dados. (Os dados em si são apenas algumas centenas de kb no máximo)

    
por Josef 04.03.2015 / 18:15

4 respostas

8

Isso é semelhante a algumas outras respostas. Se você tiver o pacote “moreutils”, deverá ter o comando sponge . Experimente

commandA | sponge | { IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; }

O comando sponge é basicamente um filtro de passagem (como cat ) exceto que ele não começa a gravar a saída até ler a entrada inteira. Ou seja, "absorve" os dados e depois os libera quando você os aperta (como uma esponja). Então, até certo ponto, isso é "trapacear" - Se houver uma quantidade não trivial de dados, sponge certamente usará um arquivo temporário. Mas é invisível para você; você não precisa se preocupar com coisas domésticas como escolher um nome de arquivo exclusivo e limpar depois.

O { IFS= read -r x; { printf "%s\n" "$x"; cat; } | commandB; } lê a primeira linha de saída de sponge . Lembre-se de que isso não aparece até que commandA tenha terminado. Então ele dispara commandB , escreve a primeira linha no pipe, e invoca cat para ler o restante da saída e gravar no pipe.

    
por 04.03.2015 / 21:17
4

Comandos em pipe line são iniciados simultaneamente, você precisa armazenar commandA output em algum lugar para usar mais tarde. Você pode evitar o arquivo temporário usando a variável:

output=$(command A; echo A)
printf '%s' "${output%%A}" | commandB
    
por 04.03.2015 / 18:35
1

Eu não conheço nenhum utilitário UNIX padrão que possa resolver esse problema. Uma opção seria usar awk para acumular commandA output e liberá-lo para commandB de uma só vez, assim

commandA  | awk '{x = x ORS $0}; END{printf "%s", x | "commandB"}'

Tenha em atenção que isto poderia ser intensivo na memória, uma vez que awk está a construir uma cadeia a partir da sua entrada.

    
por 04.03.2015 / 18:32
0

Você pode resolver o requisito com um pequeno script. Essa variante em particular evita o arquivo temporário e o potencial de memória em detrimento de processos adicionais.

#!/bin/bash
#
IFS= read LINE

if test -n "$LINE"
then
    test -t 2 && echo "Starting $*" >&2
    (
        echo "$LINE"
        cat

    ) | "$@"
else
    exit 0
fi

Se você fosse chamar o script waituntil (e torná-lo executável, colocá-lo no PATH , etc), você o usaria assim

commandA... | waituntil commandB...

Exemplo

( sleep 3 ; date ; id ) | waituntil nl
    
por 04.03.2015 / 18:38