Tubo semi-assíncrono

11

Suponha que eu tenha o seguinte canal:

a | b | c | d

Como posso aguardar a conclusão de c (ou b ) em sh ou bash ? Isso significa que o script d pode iniciar a qualquer momento (e não precisa ser aguardado), mas requer saída completa de c para funcionar corretamente.

O caso de uso é um difftool para git que compara imagens. É chamado por git e precisa processar sua entrada (a a | b | c part) e exibir os resultados da comparação (a d part). O chamador excluirá a entrada necessária para a e b . Isso significa que, antes de retornar do script, o processo c (ou b ) deve ser finalizado. Por outro lado, não posso esperar por d porque isso significa que estou aguardando a entrada do usuário.

Eu sei que posso gravar os resultados de c em um arquivo temporário ou talvez usar um FIFO em bash . (Não tenho certeza se o FIFO vai ajudar, no entanto.) É possível conseguir isso sem arquivos temporários em sh ?

EDITAR

Talvez seja suficiente descobrir o ID do processo c (ou b ) de maneira confiável. Então, o pipe inteiro poderia ser iniciado de forma assíncrona, e eu poderia esperar por um ID de processo. Algo ao longo das linhas de

wait $(a | b | { c & [print PID of c] ; } | d)

EDIT ^ 2

Encontrei uma solução, comentários (ou soluções ainda melhores) são bem-vindos.

    
por krlmlr 24.02.2014 / 14:01

6 respostas

6

a | b | { c; [notify];} | d

A notificação pode ser feita, por ex. por um sinal para um PID que foi passado em uma variável de ambiente ( kill -USR1 $EXTPID ) ou criando um arquivo ( touch /path/to/file ).

Outra ideia:

Você executa o próximo processo (aquele para poder começar a aguardar) no pipeline:

a | b | { c; exec >&-; nextprocess;} | d

ou

a | b | { c; exec >&-; nextprocess &} | d
    
por 24.02.2014 / 14:09
5

Se eu entendi sua pergunta corretamente, isso deve funcionar:

a | b | c | { (exec <&3 3<&-; d) &} 3<&0

(o truque do fd3 é porque algumas (mais) shells redirecionam o stdin para / dev / null com & ).

    
por 24.02.2014 / 16:52
4

No bash, você pode usar uma substituição de processo . O comando na substituição do processo é executado de forma assíncrona, não é aguardado.

a | b | c > >(d)
    
por 25.02.2014 / 01:52
3

Isso é o que eu encontrei por tentativa e erro, com a ajuda da entrada de Hauke:

a | b | { c; kill -PIPE $$; } | d

Equivalente:

a | b | ( c; kill -PIPE $$; ) | d

(O último é mais explícito, porque {} será executado em uma subshell mesmo assim dentro de um pipe.)

Alguns outros sinais (incluindo QUIT , TERM e USR1 ) funcionam também, no entanto, neste caso, a descrição do sinal é mostrada no terminal.

Gostaria de saber se essa é a intenção original do sinal PIPE . De acordo com o manual :

SIGPIPE: The PIPE signal is sent to a process when it attempts to write to a pipe without a process connected to the other end.

Isso significa que, quando eu enviar artificialmente um sinal de canalização para o subshell, ele termina silenciosamente, deixando o consumidor final ( d ) sozinho.

Isso funciona em sh e bash .

    
por 24.02.2014 / 15:29
0

Você pode usar o programa sponge do pacote "moreutils":

a | b | c | sponge | d

A esponja irá para o final da saída de c antes de enviar a d . Espero que seja o que você queria.

    
por 11.11.2015 / 11:48
0

Você pode apenas fazer:

a | b | c | (d ; cat > /dev/null)

Assim, quando d terminar, o cat absorverá o restante da saída de c até que termine.

Ok. Após comentários, acho que a resposta é iniciar diretamente d em segundo plano.

Faça:

a | b | c | (d &)

ou use a solução de Stephane Chazelas se houver problemas com d de leitura de stdin .

    
por 24.02.2014 / 20:51