Para iniciar programas dos meus scripts Python, estou usando o seguinte método:
def execute(command):
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = process.communicate()[0]
exitCode = process.returncode
if (exitCode == 0):
return output
else:
raise ProcessException(command, exitCode, output)
Então, quando inicio um processo como Process.execute("mvn clean install")
, meu programa aguarda até que o processo termine e só então recebo a saída completa do meu programa. Isso é chato se eu estiver executando um processo que demora um pouco para terminar.
Posso deixar meu programa escrever a saída do processo linha por linha, pesquisando a saída do processo antes que ela termine em um loop ou algo assim?
** [EDIT] Desculpe, não pesquisei muito bem antes de postar esta pergunta. Rosquear é realmente a chave. Encontrei um exemplo aqui que mostra como fazê-lo: ** Python Subprocess.Popen from a thread
python
subprocess
Ingo Fischer
fonte
fonte
Respostas:
Você pode usar o iter para processar linhas assim que as saídas de comandá-los:
lines = iter(fd.readline, "")
. Aqui está um exemplo completo mostrando um caso de uso típico (obrigado a @jfs por ajudar):fonte
for line in popen.stdout: print(line.decode(), end='')
. Para dar suporte ao Python 2 e 3, use os bytes literais:b''
caso contrário,lines_iterator
nunca termina no Python 3.bufsize=1
(pode melhorar o desempenho em Python 2), perto dopopen.stdout
tubo explicitamente (sem esperar que a coleta de lixo para cuidar dele) e aumentosubprocess.CalledProcessError
(comocheck_call()
,check_output()
fazer). Aprint
declaração é diferente no Python 2 e 3: você pode usar o softspace hackprint line,
(nota: vírgula) para evitar duplicar todas as novas linhas como seu código e transmitiruniversal_newlines=True
no Python 3, para obter texto em vez de resposta relacionada a bytes .execute(["python", "-u", "child_thread.py"])
. Mais informações: stackoverflow.com/questions/14258500/…Ok, eu consegui resolvê-lo sem threads (qualquer sugestão de por que usar threads seria melhor é apreciada) usando um trecho desta pergunta Interceptando o stdout de um subprocesso enquanto ele está sendo executado
fonte
print line,
parasys.stdout.write(nextline); sys.stdout.flush()
. Caso contrário, ele imprimiria a cada duas linhas. Então, novamente, isso está usando a interface do Notebook do IPython, então talvez algo mais estivesse acontecendo - independentemente de chamar explicitamente asflush()
obrasstderr=subprocess.STDOUT
parastderr=subprocess.PIPE
e depois ligarprocess.stderr.readline()
de dentro do loop, parece que estou em conflito com o próprio conflito que é alertado na documentação dosubprocess
módulo.stdout=subprocess.PIPE,stderr=subprocess.STDOUT
capturar o stderr e acredito (mas ainda não testei) que ele também captura o stdin.Para imprimir a saída do subprocesso linha por linha assim que seu buffer stdout for liberado no Python 3:
Aviso: você não precisa
p.poll()
- o loop termina quando eof é atingido. E você não precisaiter(p.stdout.readline, '')
- o bug de leitura antecipada foi corrigido no Python 3.Veja também, Python: leia a entrada de streaming de subprocess.communicate () .
fonte
sys.stdout.flush()
no pai - o stdout é buffer de linha se não for redirecionado para um arquivo / canal e, portanto, a impressãoline
libera o buffer automaticamente. Você também não precisasys.stdout.flush()
do filho - passe-u
a opção de linha de comando.>
, executepython -u your-script.py > some-file
. Aviso:-u
opção que eu mencionei acima (não precisa usarsys.stdout.flush()
).p.wait()
- é chamado na saída dowith
bloco. Usep.returncode
.Na verdade, existe uma maneira muito simples de fazer isso quando você deseja imprimir a saída:
Aqui, estamos simplesmente apontando o subprocesso para nosso próprio stdout e usando a API de êxito ou exceção existente.
fonte
shell=True
@tokland
tentei seu código e o corrigi para 3,4 e o Windows dir.cmd é um comando dir simples, salvo como arquivo cmd
fonte
iter()
eend='\r\n'
são desnecessários. O Python usa o modo de novas linhas universais por padrão, ou seja, qualquer um'\n'
é convertido'\r\n'
durante a impressão.'latin'
provavelmente é uma codificação errada, você pode usaruniversal_newlines=True
para obter saída de texto no Python 3 (decodificado usando a codificação preferida da localidade). Não pare.poll()
, pode haver dados não lidos em buffer. Se o script Python estiver sendo executado em um console, sua saída será com buffer de linha; você pode forçar o buffer de linha usando a-u
opção - você não precisaflush=True
aqui.Caso alguém queira ler ambos
stdout
estderr
ao mesmo tempo usando threads, é isso que eu criei:Eu só queria compartilhar isso, pois acabei tentando fazer algo semelhante, mas nenhuma das respostas resolveu meu problema. Espero que ajude alguém!
Observe que, no meu caso de uso, um processo externo mata o processo que nós
Popen()
.fonte
Para quem tenta as respostas a esta pergunta para obter o stdout de um script Python, observe que o Python armazena em buffer seu stdout e, portanto, pode demorar um pouco para vê-lo.
Isso pode ser corrigido adicionando o seguinte após cada gravação stdout no script de destino:
fonte
import
o outro script; examinemultiprocessing
outhreading
se precisar de execução paralela.subprocess.run("/path/to/python/executable", "pythonProgramToRun.py")
Em Python> = 3.5, o
subprocess.run
Works funciona para mim:(obter a saída durante a execução também funciona sem
shell=True
) https://docs.python.org/3/library/subprocess.html#subprocess.runfonte
subprocess.run()
chamada retornará apenas quando o subprocesso terminar a execução.>>> import subprocess; subprocess.run('top')
também parece imprimir "durante a execução" (e o topo nunca termina). Talvez eu não esteja entendendo alguma diferença sutil?stdout=subprocess.PIPE
você poderá lê-la somente após atop
conclusão. Seu programa Python é bloqueado durante a execução do subprocesso.run
método ainda funciona se você estiver interessado apenas em ver a saída conforme ela é gerada. Se você deseja fazer algo com a saída em python de forma assíncrona, está certo de que não funciona.Para responder à pergunta original, a melhor maneira de o IMO é apenas redirecionar o subprocesso
stdout
diretamente para o programastdout
(opcionalmente, o mesmo pode ser feitostderr
, como no exemplo abaixo)fonte
stdout
estderr
faz a mesma coisa com menos código. Embora eu suponha que explícito é melhor que implícito.Esse PoC lê constantemente a saída de um processo e pode ser acessado quando necessário. Somente o último resultado é mantido, todas as outras saídas são descartadas, impedindo o PIPE de ficar sem memória:
print_date.py
saída: Você pode ver claramente que só há saída a partir do intervalo ~ 2,5s, nada entre elas.
fonte
Isso funciona pelo menos em Python3.4
fonte
Nenhuma das respostas aqui abordou todas as minhas necessidades.
Um pouco de experiência: estou usando um ThreadPoolExecutor para gerenciar um pool de threads, cada um iniciando um subprocesso e executando-os simultaneamente. (No Python2.7, mas isso também deve funcionar na versão 3.x mais recente). Eu não quero usar threads apenas para reunir a saída, pois quero o maior número possível de outras coisas (um pool de 20 processos usaria 40 threads apenas para executar; 1 para o processo e 1 para stdout ... e mais se você quiser stderr eu acho)
Estou retirando muitas exceções e outras aqui, então isso se baseia no código que funciona na produção. Espero que não estraguei tudo na cópia e colar. Além disso, feedback muito bem-vindo!
Tenho certeza de que há custos adicionais sendo adicionados aqui, mas isso não é uma preocupação no meu caso. Funcionalmente, ele faz o que eu preciso. A única coisa que não resolvi é por que isso funciona perfeitamente para mensagens de log, mas vejo algumas
print
mensagens aparecerem mais tarde e de uma só vez.fonte
No Python 3.6, usei isso:
fonte
subprocess.call()
possui algumas verrugas que são corrigidas pelas funções mais recentes; no Python 3.6 você geralmente usariasubprocess.run()
para isso; por conveniência, a função de invólucro antigasubprocess.check_output()
também está disponível - ela retorna a saída real do processo (esse código retornaria apenas o código de saída, mas mesmo assim imprimia algo indefinido).