Como redireciono o stdout para um arquivo arbitrário no Python?
Quando um script Python de execução demorada (por exemplo, aplicativo Web) é iniciado a partir da sessão ssh e backgounded, e a sessão ssh é fechada, o aplicativo gera IOError e falha no momento em que tenta gravar no stdout. Eu precisava encontrar uma maneira de fazer com que o aplicativo e os módulos saíssem para um arquivo, em vez de stdout para evitar falhas devido ao IOError. Atualmente, emprego o nohup para redirecionar a saída para um arquivo, e isso faz o trabalho, mas fiquei imaginando se havia uma maneira de fazer isso sem usar o nohup, por curiosidade.
Eu já tentei sys.stdout = open('somefile', 'w')
, mas isso não parece impedir a saída de alguns módulos externos para o terminal (ou talvez a sys.stdout = ...
linha não tenha disparado). Sei que deve funcionar com scripts mais simples nos quais testei, mas também não tive tempo ainda para testar em um aplicativo da web.
script.p > file
someprocess | python script.py
? Por que envolvernohup
?print
instruções para aplicar ologging
módulo a partir do stdlib. Em seguida, você pode redirecionar a saída para qualquer lugar, ter controle sobre a quantidade que deseja, etc. Na maioria dos casos, o código de produção não deve,print
maslog
.Respostas:
Se você deseja fazer o redirecionamento no script Python, definir
sys.stdout
um objeto de arquivo faz o seguinte:Um método muito mais comum é usar o redirecionamento de shell ao executar (o mesmo no Windows e Linux):
fonte
from sys import stdout
, talvez porque crie uma cópia local. Também você pode usá-lo comwith
, por exemplowith open('file', 'w') as sys.stdout: functionThatPrints()
. Agora você pode implementarfunctionThatPrints()
usandoprint
instruções normais .stdout = sys.stdout
para que você possa devolvê-la quando terminarsys.stdout = stdout
,. Dessa forma, se você está sendo chamado de uma função que usa,print
você não estraga tudo.buffering=0
desativa o buffer (pode afetar negativamente o desempenho (10 a 100 vezes)).buffering=1
ativa o buffer de linha para que você possa usartail -f
para uma saída orientada a linha.sys.stdout = sys.__stdout__
-lo para recuperá-lo.Há
contextlib.redirect_stdout()
função no Python 3.4:É similar à:
que pode ser usado em versões anteriores do Python. A última versão não é reutilizável . Pode ser feito um, se desejado.
Ele não redireciona o stdout no nível dos descritores de arquivo, por exemplo:
b'not redirected'
e'echo this also is not redirected'
não são redirecionados para ooutput.txt
arquivo.Para redirecionar no nível do descritor de arquivo,
os.dup2()
pode ser usado:O mesmo exemplo funciona agora se
stdout_redirected()
for usado em vez deredirect_stdout()
:A saída que foi impressa anteriormente no stdout agora passa para
output.txt
enquanto ostdout_redirected()
gerenciador de contexto estiver ativo.Nota:
stdout.flush()
não libera buffers de C stdio no Python 3, onde a E / S é implementada diretamente nas chamadasread()
/write()
system. Para liberar todos os fluxos de saída C stdio abertos, você poderia chamarlibc.fflush(None)
explicitamente se algum ramal C usar E / S baseada em stdio:Você pode usar o
stdout
parâmetro para redirecionar outros fluxos, não apenassys.stdout
para mesclarsys.stderr
esys.stdout
:Exemplo:
Nota:
stdout_redirected()
combina E / S com buffer (sys.stdout
geralmente) e E / S sem buffer (operações diretamente nos descritores de arquivo). Cuidado, pode haver problemas de buffer .Para responder, sua edição: você pode usar
python-daemon
para daemonizar seu script e usar ologging
módulo (como sugerido pelo @ erikb85 ) em vez deprint
instruções e apenas redirecionar o stdout para o seu script Python de longa execução que você executa usandonohup
agora.fonte
stdout_redirected
é útil. Esteja ciente de que isso não funciona em documentos de teste, pois oSpoofOut
manipulador especial que o doctest usa para substituirsys.stdout
não possui umfileno
atributo.ValueError("Expected a file (`.fileno()`) or a file descriptor")
, é um bug. Tem certeza de que não aumenta?doctest.sys.__stdout__
onde normalmente usaríamossys.stdout
. Isso não é um problema com sua função, apenas uma acomodação necessária para o doctest, pois substitui o stdout por um objeto que não possui todos os atributos que um arquivo verdadeiro teria.stdout_redirected()
possui umstdout
parâmetro, você pode defini-lo comosys.__stdout__
se desejar redirecionar o stdout python original (que deve ter um válido.fileno()
na maioria dos casos). Não faz nada para a corrente,sys.stdout
se forem diferentes. Não usedoctest.sys
; está disponível por acidente.with stdout_redirected(to=fd):
with merged_stderr_stdout():
print('...'); print('...', file=sys.stderr)
você pode tentar isso muito melhor
fonte
logger
ousyslog
?def __getattr__(self, attr): return getattr(self.terminal, attr)
def flush(self):
método também à classeLogger
.As outras respostas não cobriram o caso em que você deseja que os processos bifurcados compartilhem seu novo stdout.
Fazer isso:
fonte
sys.stdout.flush()
antes daclose(1)
instrução para garantir que o'file'
arquivo de redirecionamento obtenha a saída. Além disso, você pode usar umtempfile.mkstemp()
arquivo no lugar de'file'
. E tenha cuidado para não ter outros threads em execução que possam roubar o primeiro identificador de arquivo do sistema operacional após oos.close(1)
mas antes que ele'file'
seja aberto para usá-lo.os.dup2()
e envolvê-la em um gerenciador de contexto, como mostrado na minha respostaCitado em PEP 343 - A declaração "with" (declaração de importação adicionada):
Redirecionar stdout temporariamente:
Utilizado da seguinte forma:
Isso não é seguro para threads, é claro, mas também não está fazendo a mesma dança manualmente. Nos programas de thread único (por exemplo, em scripts), é uma maneira popular de fazer as coisas.
fonte
os.system('echo not redirected')
,. Minha resposta mostra como redirecionar essa saídaredirect_stdout
emcontextlib
fonte
Aqui está uma variação da resposta de Yuda Prawira :
flush()
e todos os atributos de arquivostderr
também.
fonte
Com base nesta resposta: https://stackoverflow.com/a/5916874/1060344 , eis uma outra maneira de descobrir qual uso em um dos meus projetos. Para o que você substituir
sys.stderr
ou substituirsys.stdout
, você deve garantir que a substituição esteja em conformidade com afile
interface, especialmente se isso for algo que você está fazendo, porque stderr / stdout é usado em alguma outra biblioteca que não está sob seu controle. Essa biblioteca pode estar usando outros métodos de objeto de arquivo.Confira desta maneira, onde eu ainda deixo tudo funcionar stderr / stdout (ou qualquer outro arquivo) e também envie a mensagem para um arquivo de log usando o recurso de log do Python (mas você pode realmente fazer qualquer coisa com isso):
fonte
Você precisa de um multiplexador de terminal como o tmux ou a tela GNU
Estou surpreso que um pequeno comentário de Ryan Amos à pergunta original seja a única menção de uma solução preferível a todas as outras em oferta, não importa o quão esperto o truque do python possa ser e quantas votações recebidas. Além do comentário de Ryan, o tmux é uma boa alternativa à tela do GNU.
Mas o princípio é o mesmo: se você quiser deixar um emprego terminal enquanto estiver desconectado, vá ao café para um sanduíche, vá ao banheiro, vá para casa (etc) e depois reconecte-se ao seu sessão de terminal de qualquer lugar ou computador como se você nunca estivesse fora, os multiplexadores de terminal são a resposta. Pense neles como VNC ou desktop remoto para sessões de terminal. Qualquer outra coisa é uma solução alternativa. Como bônus, quando o chefe e / ou o parceiro entram e você inadvertidamente pressiona sua janela do terminal em vez da janela do navegador com seu conteúdo desonesto, você não perde as últimas 18 horas de processamento !
fonte
Programas escritos em outros idiomas (por exemplo, C) precisam fazer mágica especial (chamada de bifurcação dupla) expressamente para se desconectar do terminal (e para evitar processos zumbis). Então, acho que a melhor solução é imitá-los.
Uma vantagem de reexecutar seu programa é que você pode escolher redirecionamentos na linha de comando, por exemplo
/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
Consulte esta publicação para obter mais informações: Qual é o motivo da execução de um fork duplo ao criar um daemon?
fonte
systemd
,upstart
) ou outro utilitário (daemon(1)
) para lidar com o padrão da bifurcação.