Estou usando um script python como um driver para um código hidrodinâmico. Quando chega a hora de executar a simulação, eu uso subprocess.Popen
para executar o código, coletar a saída de stdout e stderr em um subprocess.PIPE
--- então posso imprimir (e salvar em um arquivo de log) as informações de saída e verificar se há erros . O problema é que não tenho ideia de como o código está progredindo. Se eu executá-lo diretamente a partir da linha de comando, ele fornecerá informações sobre em que iteração está, a que horas, qual será o próximo passo etc.
Existe uma maneira de armazenar a saída (para registro e verificação de erros) e também produzir uma saída de transmissão ao vivo?
A seção relevante do meu código:
ret_val = subprocess.Popen( run_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True )
output, errors = ret_val.communicate()
log_file.write(output)
print output
if( ret_val.returncode ):
print "RUN failed\n\n%s\n\n" % (errors)
success = False
if( errors ): log_file.write("\n\n%s\n\n" % errors)
Originalmente, eu estava canalizando a run_command
passagem tee
para que uma cópia fosse diretamente para o arquivo de log e o fluxo ainda saísse diretamente para o terminal - mas, dessa forma, não posso armazenar nenhum erro (para o meu conhecimento).
Editar:
Solução temporária:
ret_val = subprocess.Popen( run_command, stdout=log_file, stderr=subprocess.PIPE, shell=True )
while not ret_val.poll():
log_file.flush()
depois, em outro terminal, execute tail -f log.txt
(st log_file = 'log.txt'
).
fonte
Popen.poll
como em uma pergunta anterior do Stack Overflow .git
) fazem isso apenas se sua saída for um "dispositivo tty" (testado via libcisatty()
). Nesse caso, talvez você precise abrir um pseudo-tty.flush
, e não é necessário ler a partir do tubo stderr se o subprocesso produz muito saída stderr. Não há espaço suficiente em um campo de comentário para explicar isso ...Respostas:
Você tem duas maneiras de fazer isso, criando um iterador a partir das funções
read
oureadline
e faça:ou
Ou você pode criar um
reader
e umwriter
arquivo. Passe owriter
paraPopen
e leia a partir doreader
Dessa forma, você terá os dados gravados na
test.log
saída padrão e na saída.A única vantagem da abordagem de arquivo é que seu código não é bloqueado. Enquanto isso, você pode fazer o que quiser e ler sempre que quiser, de
reader
uma maneira sem bloqueio. Quando você usaPIPE
, as funçõesread
ereadline
serão bloqueadas até que um caractere seja gravado no canal ou uma linha seja gravada no canal respectivamente.fonte
iter(process.stdout.readline, b'')
(ou seja, a sentinela passado para iter precisa ser uma string binária, uma vezb'' != ''
.for line in iter(process.stdout.readline, b''): sys.stdout.buffer.write(line)
process = subprocess.Popen(command, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) for line in iter(process.stdout.readline, b'') sys.stdout.write(line.decode(sys.stdout.encoding))
Resumo executivo (ou versão "tl; dr"): é fácil quando há no máximo um
subprocess.PIPE
, caso contrário, é difícil.Talvez seja hora de explicar um pouco sobre como
subprocess.Popen
funciona.(Advertência: isso é para Python 2.x, embora 3.x seja semelhante; e eu sou bastante confuso com a variante do Windows. Entendo muito melhor o material POSIX.)
A
Popen
função precisa lidar com zero a três fluxos de E / S, um pouco simultaneamente. Estes são indicadosstdin
estdout
,stderr
como de costume.Você pode fornecer:
None
, indicando que você não deseja redirecionar o fluxo. Ele os herdará como de costume. Observe que, nos sistemas POSIX, pelo menos, isso não significa que ele usará o Pythonsys.stdout
, apenas o stdout real do Python ; veja a demonstração no final.int
valor Este é um descritor de arquivo "bruto" (pelo menos no POSIX). (Nota lateral:PIPE
eSTDOUT
na verdade sãoint
s internamente, mas são descritores "impossíveis", -1 e -2.)fileno
método.Popen
encontrará o descritor para esse fluxo, usandostream.fileno()
e, em seguida, prossiga como para umint
valor.subprocess.PIPE
, indicando que o Python deve criar um canal.subprocess.STDOUT
(stderr
apenas): diga ao Python para usar o mesmo descritor que parastdout
. Isso só faz sentido se você forneceu umNone
valor (não ) parastdout
, e mesmo assim, é necessário apenas se você definirstdout=subprocess.PIPE
. (Caso contrário, você pode apenas fornecer o mesmo argumento fornecidostdout
, por exemploPopen(..., stdout=stream, stderr=stream)
,.)Os casos mais fáceis (sem tubos)
Se você não redirecionar nada (deixe todos os três como o
None
valor padrão ou o fornecimento explícitosNone
),Pipe
é fácil. Ele só precisa desativar o subprocesso e deixá-lo funcionar. Ou, se você redirecionar para um nãoPIPE
-int
ou um fluxofileno()
- ainda é fácil, pois o sistema operacional faz todo o trabalho. O Python só precisa desativar o subprocesso, conectando seu stdin, stdout e / ou stderr aos descritores de arquivo fornecidos.O caso ainda fácil: um tubo
Se você redirecionar apenas um fluxo, as
Pipe
coisas ainda serão fáceis. Vamos escolher um fluxo de cada vez e assistir.Suponha que você queira fornecer alguns
stdin
, mas deixestdout
estderr
vá sem redirecionar ou vá para um descritor de arquivo. Como processo pai, seu programa Python simplesmente precisa usarwrite()
para enviar dados pelo canal . Você pode fazer isso sozinho, por exemplo:ou você pode passar os dados stdin para
proc.communicate()
, o que faz ostdin.write
mostrado acima. Não há saída retornando, portanto,communicate()
há apenas outro trabalho real: ele também fecha o cano para você. (Se você não ligar,proc.communicate()
deverá ligarproc.stdin.close()
para fechar o canal, para que o subprocesso saiba que não há mais dados chegando.)Suponha que você deseja capturar
stdout
, mas licençastdin
estderr
só. Novamente, é fácil: basta ligarproc.stdout.read()
(ou equivalente) até que não haja mais saída. Comoproc.stdout()
é um fluxo de E / S normal do Python, você pode usar todas as construções normais, como:ou, novamente, você pode usar
proc.communicate()
, o que simplesmente faz issoread()
por você.Se você deseja capturar apenas
stderr
, funciona da mesma forma que comstdout
.Há mais um truque antes que as coisas fiquem difíceis. Suponha que você queira capturar
stdout
e também capture,stderr
mas no mesmo canal que stdout:Neste caso,
subprocess
"fraudes"! Bem, ele tem que fazer isso, para que não seja realmente trapaça: ele inicia o subprocesso com seu stdout e seu stderr direcionados para o (único) descritor de pipe que retorna ao processo pai (Python). No lado pai, há novamente apenas um descritor de pipe único para ler a saída. Toda a saída "stderr" é exibidaproc.stdout
e, se você chamarproc.communicate()
, o resultado do stderr (segundo valor na tupla) seráNone
, não uma sequência.Os casos difíceis: dois ou mais tubos
Todos os problemas surgem quando você deseja usar pelo menos dois tubos. De fato, o
subprocess
próprio código tem este bit:Mas, infelizmente, aqui fizemos pelo menos dois e talvez três tubos diferentes, então os
count(None)
retornos são 1 ou 0. Devemos fazer as coisas da maneira mais difícil.No Windows, isso costuma
threading.Thread
acumular resultados paraself.stdout
eself.stderr
, e o thread pai forneceself.stdin
dados de entrada (e fecha o canal).No POSIX, ele usa,
poll
se disponível, caso contrárioselect
, para acumular saída e fornecer entrada stdin. Tudo isso é executado no processo / thread pai (único).Threads ou poll / select são necessários aqui para evitar conflitos. Suponha, por exemplo, que redirecionemos todos os três fluxos para três canais separados. Suponha ainda que haja um pequeno limite para a quantidade de dados que podem ser inseridos em um tubo antes que o processo de gravação seja suspenso, aguardando que o processo de leitura "limpe" o tubo da outra extremidade. Vamos definir esse pequeno limite para um único byte, apenas para ilustração. (Na verdade, é assim que as coisas funcionam, exceto que o limite é muito maior que um byte.)
Se o processo pai (Python) tentar gravar vários bytes - digamos,
'go\n'
paraproc.stdin
, o primeiro byte entra e o segundo faz com que o processo Python seja suspenso, aguardando o subprocesso ler o primeiro byte, esvaziando o canal.Enquanto isso, suponha que o subprocesso decida imprimir um amigável "Olá! Não entre em pânico!" cumprimento. Ele
H
entra no tubo stdout, mase
faz com que seja suspenso, esperando que seu pai leia issoH
, esvaziando o tubo stdout.Agora estamos paralisados: o processo Python está adormecido, esperando para terminar de dizer "vá" e o subprocesso também está adormecido, esperando para terminar de dizer "Olá! Não entre em pânico!".
O
subprocess.Popen
código evita esse problema com threading-or-select / poll. Quando os bytes podem passar por cima dos tubos, eles vão. Quando não podem, apenas um segmento (não todo o processo) precisa dormir - ou, no caso de seleção / pesquisa, o processo Python espera simultaneamente por "pode escrever" ou "dados disponíveis", grava no stdin do processo somente quando houver espaço e lê seu stdout e / ou stderr somente quando os dados estiverem prontos. Oproc.communicate()
código (na verdade,_communicate
onde os casos cabeludos são tratados) retorna uma vez que todos os dados stdin (se houver) foram enviados e todos os dados stdout e / ou stderr foram acumulados.Se você quiser ler os dois
stdout
estderr
em dois canais diferentes (independentemente de qualquerstdin
redirecionamento), também precisará evitar o conflito. O cenário de impasse aqui é diferente - ocorre quando o subprocesso gravastderr
algum tempo enquanto você extrai dadosstdout
ou vice-versa - mas ainda está lá.The Demo
Prometi demonstrar que, não redirecionado, o Python
subprocess
es grava no stdout subjacente, nãosys.stdout
. Então, aqui está um código:Quando executado:
Observe que a primeira rotina falhará se você adicionar
stdout=sys.stdout
, pois umStringIO
objeto não possuifileno
. O segundo omitirá ohello
se você adicionarstdout=sys.stdout
uma vezsys.stdout
que foi redirecionado paraos.devnull
.(Se você redirecionar de Python arquivo descritor de-1, o sub-processo vai seguir esse redirecionamento. A
open(os.devnull, 'w')
chamada produz um fluxo cujofileno()
é maior do que 2.)fonte
sys.stdout
) vai para o stdout do Python , não para o stdout do programa python (sys.
). O que eu admito é uma ... distinção estranha. Existe uma maneira melhor de expressar isso?asyncio
código que implementa a "parte difícil" (ele lida com vários pipes simultaneamente) de maneira portátil . Você pode compará-lo com o código que usa vários threads (teed_call()
) para fazer o mesmo .Também podemos usar o iterador de arquivo padrão para ler stdout em vez de usar a construção de iter com readline ().
fonte
Se você puder usar bibliotecas de terceiros, poderá usar algo como
sarge
(divulgação: eu sou o mantenedor). Essa biblioteca permite acesso sem bloqueio aos fluxos de saída dos subprocessos - está em camadas sobre osubprocess
módulo.fonte
Solução 1: log
stdout
ANDstderr
simultaneamente em tempo realUma solução simples que registra simultaneamente stdout AND stderr, linha por linha em tempo real, em um arquivo de log.
Solução 2: Uma função
read_popen_pipes()
que permite iterar nos dois pipes (stdout / stderr), simultaneamente em tempo realfonte
Uma solução boa, porém "pesada", é usar o Twisted - veja a parte inferior.
Se você estiver disposto a viver apenas com algo stdout nesse sentido, deve funcionar:
(Se você usa read (), ele tenta ler o "arquivo" inteiro que não é útil, o que realmente poderíamos usar aqui é algo que lê todos os dados que estão no canal no momento)
Pode-se também tentar abordar isso com rosqueamento, por exemplo:
Agora, poderíamos adicionar stderr potencialmente, tendo dois threads.
Observe, no entanto, a documentação do subprocesso desencoraja o uso desses arquivos diretamente e recomenda o uso
communicate()
(principalmente relacionados a deadlocks que acho que não são um problema acima) e as soluções são um pouco desajeitadas, então realmente parece que o módulo do subprocesso não é suficiente o trabalho (veja também: http://www.python.org/dev/peps/pep-3145/ ) e precisamos procurar outra coisa.Uma solução mais envolvida é usar o Twisted, como mostrado aqui: https://twistedmatrix.com/documents/11.1.0/core/howto/process.html
A maneira como você faz isso com o Twisted é criar seu processo usando
reactor.spawnprocess()
e fornecendo umProcessProtocol
que processa a saída de forma assíncrona. O código Python de amostra Twisted está aqui: https://twistedmatrix.com/documents/11.1.0/core/howto/listings/process/process.pyfonte
read()
chamada até o subprocesso sair e o processo pai receberEOF
no canal.Além de todas essas respostas, uma abordagem simples também pode ser a seguinte:
Faça um loop pelo fluxo legível, desde que seja legível e, se obtiver um resultado vazio, pare-o.
A chave aqui é que
readline()
retorna uma linha (com\n
no final), desde que haja uma saída e vazia, se realmente estiver no final.Espero que isso ajude alguém.
fonte
Com base em todas as opções acima, sugiro uma versão ligeiramente modificada (python3):
None
Código:
fonte
returncode
parte foi crucial no meu caso.Parece que a saída com buffer de linha funcionará para você; nesse caso, algo como o seguinte pode ser adequado. (Advertência: não foi testado.) Isso fornecerá o stdout do subprocesso em tempo real. Se você deseja ter stderr e stdout em tempo real, precisará fazer algo mais complexo
select
.fonte
Por que não definir
stdout
diretamente parasys.stdout
? E se você também precisar enviar para um log, poderá simplesmente substituir o método de gravação de f.fonte
stdout
descritor de arquivo para o descritor de arquivo do objeto de arquivo passado. O método write nunca seria chamado (pelo menos é o que o subprocesso faz para stderr, eu acho que é o mesmo para stdout).Todas as soluções acima que eu tentei falharam ao separar as saídas stderr e stdout (vários pipes) ou bloqueadas para sempre quando o buffer do pipe do SO estava cheio, o que acontece quando o comando que você está executando produz muito rapidamente (há um aviso para isso em python manual do subprocesso poll (). A única maneira confiável que encontrei foi através do select, mas esta é uma solução apenas para posix:
fonte
Semelhante às respostas anteriores, mas a seguinte solução funcionou para mim no Windows usando o Python3 para fornecer um método comum para imprimir e efetuar logon em tempo real ( obtendo-saída em tempo real usando python ):
fonte
Eu acho que o
subprocess.communicate
método é um pouco enganador: ele realmente preenche o stdout e o stderr que você especificar nosubprocess.Popen
.No entanto, lendo o
subprocess.PIPE
que você pode fornecer aossubprocess.Popen
's stdout e stderr parâmetros acabará por preencher buffers tubos OS e impasse seu aplicativo (especialmente se você tem vários processos / threads que usam mustsubprocess
).Minha solução proposta é fornecer arquivos stdout e stderr - e ler o conteúdo dos arquivos em vez de ler no deadlock
PIPE
. Esses arquivos podem sertempfile.NamedTemporaryFile()
- que também podem ser acessados para leitura enquanto estão sendo gravados porsubprocess.communicate
.Abaixo está um exemplo de uso:
E este é o código fonte que está pronto para ser usado com tantos comentários quanto eu poderia fornecer para explicar o que ele faz:
Se você estiver usando python 2, primeiro instale a versão mais recente do pacote subprocess32 da pypi.
fonte
Aqui está uma aula que estou usando em um dos meus projetos. Ele redireciona a saída de um subprocesso para o log. No começo, tentei simplesmente substituir o método write, mas isso não funciona como o subprocesso nunca o chamará (o redirecionamento ocorre no nível do editor de arquivos). Então, eu estou usando meu próprio pipe, semelhante a como é feito no subprocesso-módulo. Isso tem a vantagem de encapsular toda a lógica de log / impressão no adaptador e você pode simplesmente transmitir instâncias do logger para
Popen
:subprocess.Popen("/path/to/binary", stderr = LogAdapter("foo"))
Se você não precisa de log, mas simplesmente deseja usá-
print()
lo, obviamente pode remover grandes partes do código e manter a classe mais curta. Você também pode expandi-lo por um__enter__
e__exit__
método e chamarfinished
em__exit__
para que você possa facilmente utilizá-la como contexto.fonte
Nenhuma das soluções Pythonic funcionou para mim. Descobriu-se que
proc.stdout.read()
ou similar pode bloquear para sempre.Portanto, eu uso
tee
assim:Esta solução é conveniente se você já estiver usando
shell=True
.${PIPESTATUS}
captura o status de sucesso de toda a cadeia de comando (disponível apenas no Bash). Se eu omitisse o&& exit ${PIPESTATUS}
, isso sempre retornaria zero, poistee
nunca falha.unbuffer
pode ser necessário imprimir cada linha imediatamente no terminal, em vez de esperar muito tempo até que o "buffer do tubo" seja preenchido. No entanto, o buffer não engole o status de saída de assert (SIG Abort) ...2>&1
também registra stderror no arquivo.fonte