Eu tenho um script que chama dois comandos:
long_running_command | print_progress
As long_running_command
impressões de um progresso, mas eu estou infeliz com ele. Estou usando print_progress
para torná-lo mais agradável (ou seja, imprimo o progresso em uma única linha).
O problema: conectar um pipe ao stdout também ativa um buffer de 4K, para o bom programa de impressão não recebe nada ... nada ... nada ... muito ... :)
Como posso desativar o buffer 4K do long_running_command
(não, não tenho a fonte)?
Respostas:
Você pode usar o
unbuffer
comando (que faz parte doexpect
pacote), por exemplounbuffer
conecta-selong_running_command
através de um pseudoterminal (pty), que faz com que o sistema o trate como um processo interativo, portanto, não usa o buffer de 4 kiB no pipeline, que é a causa provável do atraso.Para pipelines mais longos, pode ser necessário descomprimir cada comando (exceto o final), por exemplo
fonte
expect_unbuffer
e está noexpect-dev
pacote, não noexpect
pacoteexpect-dev
fornece ambosunbuffer
eexpect_unbuffer
(o primeiro é um link simbólico para o último). Os links estão disponíveis desdeexpect 5.44.1.14-1
(2009).Outra maneira de esfolar esse gato é usar o
stdbuf
programa, que faz parte do GNU Coreutils (o FreeBSD também possui um).Isso desativa o buffer completamente para entrada, saída e erro. Para algumas aplicações, o buffer de linha pode ser mais adequado por razões de desempenho:
Observe que ele funciona apenas para
stdio
buffer (printf()
,fputs()
...) para aplicativos vinculados dinamicamente e somente se esse aplicativo não ajustar o buffer de seus fluxos padrão sozinho, embora isso deva cobrir a maioria dos aplicativos.fonte
sudo stdbuff … command
obras, emborastdbuff … sudo command
não.stdbuf
não funcionatee
porquetee
substitui os padrões definidos porstdbuf
. Veja a página de manual destdbuf
.stdbuf
usa oLD_PRELOAD
mecanismo para inserir sua própria biblioteca carregada dinamicamentelibstdbuf.so
. Isso significa que ele não funcionará com esses tipos de executáveis: com recursos de setuid ou de arquivo definidos, vinculados estaticamente, sem usar a libc padrão. Nesses casos, é melhor usar as soluções comunbuffer
/script
/socat
. Veja também stdbuf com setuid / capacidades .Outra maneira de ativar o modo de saída com buffer de linha
long_running_command
é usar oscript
comando que executa o seulong_running_command
em um pseudo terminal (pty).fonte
script
é um comando tão antigo, deve estar disponível em todas as plataformas do tipo Unix.-q
no Linux:script -q -c 'long_running_command' /dev/null | print_progress
stdin
, o que impossibilita a execuçãolong_running_command
em segundo plano, pelo menos quando iniciado no terminal interativo. Para contornar o problema, consegui redirecionar o stdin de/dev/null
, pois o meulong_running_command
não usastdin
.Para
grep
,sed
eawk
você pode forçar a saída a ser buffer de linha. Você pode usar:Força a saída a ser buffer de linha. Por padrão, a saída é buffer de linha quando a saída padrão é um terminal e o buffer do bloco é contrário.
Tornar a linha de saída em buffer.
Consulte esta página para obter mais informações: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffering.html
fonte
Se houver um problema com a libc modificando seu buffer / liberação quando a saída não for para um terminal, tente socat . Você pode criar um fluxo bidirecional entre quase qualquer tipo de mecanismo de E / S. Um deles é um programa bifurcado falando com uma pseudo-tty.
O que faz é
Se isso lhe der a mesma saída que
long_running_command
, então você pode continuar com um tubo.Edit: Uau Não vi a resposta do buffer! Bem, o socat é uma ótima ferramenta de qualquer maneira, então eu posso deixar essa resposta
fonte
socat -u exec:long_running_command,pty,end-close -
aquiVocê pode usar
O problema é que a libc faz buffer de linha quando o stdout é exibido na tela e buffer total quando o stdout é exibido em um arquivo. Mas sem buffer para stderr.
Eu não acho que seja o problema com o buffer de pipe, é tudo sobre a política de buffer da libc.
fonte
zsh
(de onde|&
vem adaptado do csh) ebash
, quando você fazcmd1 >&2 |& cmd2
, os fd 1 e 2 são conectados ao stdout externo. Por isso, ele evita o armazenamento em buffer quando esse stdout externo é um terminal, mas apenas porque a saída não passa pelo tubo (print_progress
não imprime nada). Portanto, é o mesmo quelong_running_command & print_progress
(exceto que print_progress stdin é um canal que não possui gravador). Você pode verificar com emls -l /proc/self/fd >&2 |& cat
comparação comls -l /proc/self/fd |& cat
.|&
é uma abreviação2>&1 |
, literalmente. Assimcmd1 |& cmd2
écmd1 1>&2 2>&1 | cmd2
. Portanto, os fd 1 e 2 acabam conectados ao stderr original e nada fica gravado no canal. (s/outer stdout/outer stderr/g
no meu comentário anterior).Costumava ser o caso, e provavelmente ainda é o caso, de que quando a saída padrão é gravada em um terminal, a linha é armazenada em buffer por padrão - quando uma nova linha é gravada, a linha é gravada no terminal. Quando a saída padrão é enviada para um tubo, ela é totalmente armazenada em buffer - portanto, os dados são enviados apenas para o próximo processo no pipeline quando o buffer de E / S padrão é preenchido.
Essa é a fonte do problema. Não tenho certeza se há muito que você pode fazer para corrigi-lo sem modificar a gravação do programa no canal. Você pode usar a
setvbuf()
função com o_IOLBF
sinalizador para colocar incondicionalmentestdout
no modo de buffer de linha. Mas não vejo uma maneira fácil de aplicar isso em um programa. Ou o programa pode fazerfflush()
nos pontos apropriados (após cada linha de saída), mas o mesmo comentário se aplica.Suponho que, se você substituísse o pipe por um pseudo-terminal, a biblioteca de E / S padrão pensaria que a saída era um terminal (porque é um tipo de terminal) e alinharia o buffer automaticamente. Essa é uma maneira complexa de lidar com as coisas, no entanto.
fonte
Sei que essa é uma pergunta antiga e já tinha muitas respostas, mas se você deseja evitar o problema do buffer, tente algo como:
Isso produzirá os logs em tempo real e também os salvará no
logs.txt
arquivo, e o buffer não afetará mais otail -f
comando.fonte
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for example) then that will override corresponding changes by 'stdbuf'.
Não acho que o problema esteja com o cano. Parece que seu processo de execução demorada não está liberando seu próprio buffer com frequência suficiente. Alterar o tamanho do buffer do canal seria um truque para contorná-lo, mas não acho possível sem reconstruir o kernel - algo que você não gostaria de fazer como um hack, pois provavelmente a aversley afeta muitos outros processos.
fonte
De acordo com este post aqui , você pode tentar reduzir o ulimit do pipe para um único bloco de 512 bytes. Certamente não desativará o buffer, mas bem, 512 bytes é muito menor que 4K: 3
fonte
Da mesma forma que a resposta de Chad , você pode escrever um pequeno script como este:
Em seguida, use este
scriptee
comando como um substituto paratee
.Infelizmente, não consigo obter uma versão como essa para funcionar perfeitamente no Linux, então parece limitada aos unixes no estilo BSD.
No Linux, isso está próximo, mas você não recebe seu prompt de volta quando ele termina (até você pressionar enter, etc) ...
fonte
script
emula um terminal, então sim, acredito que ele desativa o buffer. Ele também faz eco de cada caractere enviado a ele - e é por isso que elecat
é enviado/dev/null
no exemplo. No que diz respeito ao programa em execuçãoscript
, ele está conversando com uma sessão interativa. Acredito que seja semelhante aexpect
esse respeito, masscript
provavelmente faz parte do seu sistema básico.tee
é enviar uma cópia do fluxo para um arquivo. Para onde o arquivo é especificadoscriptee
?cat
comandotee myfile.txt
e obter o efeito desejado.Encontrei esta solução inteligente:
(echo -e "cmd 1\ncmd 2" && cat) | ./shell_executable
Isso faz o truque.
cat
lerá a entrada adicional (até EOF) e a passará para o canal depois deecho
colocar seus argumentos no fluxo de entrada deshell_executable
.fonte
cat
não vê a saída doecho
; basta executar dois comandos em um subshell e a saída de ambos é enviada para o canal. O segundo comando no subshell ('cat') lê do stdin pai / externo, é por isso que funciona.De acordo com isso, o tamanho do buffer do pipe parece estar definido no kernel e exigiria que você recompile seu kernel para alterar.
fonte