Vamos supor que você tenha um pipeline como o seguinte:
$ a | b
Se b
parar de processar stdin, depois de um tempo, o tubo será preenchido e gravado, a
até o stdout, será bloqueado (até que o b
processo comece novamente ou a matriz morra).
Se eu quisesse evitar isso, ficaria tentado a usar um tubo maior (ou, mais simplesmente buffer(1)
) assim:
$ a | buffer | b
Isso simplesmente me daria mais tempo, mas no final a
acabaria.
O que eu gostaria de ter (para um cenário muito específico que estou abordando) é ter um canal "vazado" que, quando cheio, soltaria alguns dados (idealmente, linha por linha) do buffer para permitir a
continuar processamento (como você provavelmente pode imaginar, os dados que fluem no canal são dispensáveis, ou seja, ter os dados processados por b
é menos importante do que a
poder executar sem bloquear).
Para resumir, eu adoraria ter algo como um buffer limitado e com vazamento:
$ a | leakybuffer | b
Provavelmente eu poderia implementá-lo facilmente em qualquer idioma, estava me perguntando se há algo "pronto para usar" (ou algo como uma única linha de base) que estou perdendo.
Nota: nos exemplos, estou usando pipes regulares, mas a pergunta se aplica igualmente aos pipes nomeados
Embora tenha concedido a resposta abaixo, também decidi implementar o comando leakybuffer porque a solução simples abaixo tinha algumas limitações: https://github.com/CAFxX/leakybuffer
Respostas:
A maneira mais fácil seria canalizar através de algum programa que define a saída sem bloqueio. Aqui está um simples perl oneliner (que você pode salvar como leakybuffer ) que faz isso:
então você
a | b
se torna:o que é que faz é ler a entrada e gravar na saída (igual a
cat(1)
), mas a saída é sem bloqueio - o que significa que, se a gravação falhar, retornará erro e perderá dados, mas o processo continuará com a próxima linha de entrada, pois ignoramos convenientemente erro. O processo é do tipo buffer de linha, como você deseja, mas veja a observação abaixo.você pode testar com, por exemplo:
você obterá o
output
arquivo com linhas perdidas (a saída exata depende da velocidade do seu shell, etc.) assim:você vê onde o shell perdeu as linhas depois
12773
, mas também uma anomalia - o perl não tinha buffer suficiente para,12774\n
mas fez por1277
isso escreveu exatamente isso - e o próximo número75610
não inicia no início da linha, tornando-o pequeno feio.Isso poderia ser melhorado com a detecção do perl quando a gravação não foi bem-sucedida e, posteriormente, tente liberar o restante da linha enquanto ignora as novas linhas que chegam, mas isso complicaria muito mais o script do perl, portanto é deixado como um exercício para o leitor interessado :)
Atualização (para arquivos binários): Se você não estiver processando linhas terminadas de nova linha (como arquivos de log ou similares), precisará alterar um pouco o comando, ou o perl consumirá grandes quantidades de memória (dependendo da frequência com que os caracteres de nova linha aparecem em sua entrada):
também funcionará corretamente para arquivos binários (sem consumir memória extra).
Update2 - saída melhor do arquivo de texto: Evitando buffers de saída (em
syswrite
vez deprint
):parece corrigir problemas com "linhas mescladas" para mim:
(Nota: pode-se verificar em quais linhas as linhas foram cortadas com:
perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' output
oneliner)fonte
perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_WRONLY|O_NONBLOCK; while (<STDIN>) { print }' | aplay -t raw -f dat --buffer-size=16000
, o perl parece alocar continuamente mais memória até ser eliminada pelo gerente do OOM.dd
'sdd oflag=nonblock status=none
.$| = 1
suasyswrite()
abordagem evita gravações curtas, na verdade, desde que as linhas sejam razoavelmente curtas.