How to suppress console output in Python? - python

I'm using Pygame/SDL's joystick module to get input from a gamepad. Every time I call its get_hat() method it prints to the console. This is problematic since I use the console to help me debug and now it gets flooded with SDL_JoystickGetHat value:0: 60 times every second. Is there a way I can disable this? Either through an option in Pygame/SDL or suppress console output while the function calls? I saw no mention of this in the Pygame documentation.
edit: This turns out to be due to debugging being turned on when the SDL library was compiled.

Just for completeness, here's a nice solution from Dave Smith's blog:
from contextlib import contextmanager
import sys, os
#contextmanager
def suppress_stdout():
with open(os.devnull, "w") as devnull:
old_stdout = sys.stdout
sys.stdout = devnull
try:
yield
finally:
sys.stdout = old_stdout
With this, you can use context management wherever you want to suppress output:
print("Now you see it")
with suppress_stdout():
print("Now you don't")

To complete charles's answer, there are two context managers built in to python, redirect_stdout and redirect_stderr which you can use to redirect and or suppress a commands output to a file or StringIO variable.
import contextlib
with contextlib.redirect_stdout(None):
do_thing()
For a more complete explanation read the docs
A quick update: In some cases passing None might raise some reference errors (e.g. keras.models.Model.fit calls sys.stdout.write which will be problematic), in that case pass an io.StringIO() or os.devnull.

You can get around this by assigning the standard out/error (I don't know which one it's going to) to the null device. In Python, the standard out/error files are sys.stdout/sys.stderr, and the null device is os.devnull, so you do
sys.stdout = open(os.devnull, "w")
sys.stderr = open(os.devnull, "w")
This should disable these error messages completely. Unfortunately, this will also disable all console output. To get around this, disable output right before calling the get_hat() the method, and then restore it by doing
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
which restores standard out and error to their original value.

Here's the relevant block of code from joystick.c (via SVN at http://svn.seul.org/viewcvs/viewvc.cgi/trunk/src/joystick.c?view=markup&revision=2652&root=PyGame)
value = SDL_JoystickGetHat (joy, _index);
#ifdef DEBUG
printf("SDL_JoystickGetHat value:%d:\n", value);
#endif
if (value & SDL_HAT_UP) {
Looks like a problem with having debugging turned on.

Building on #charleslparker's answer:
from contextlib import contextmanager
import sys, os
#contextmanager
def suppress_stdout():
with open(os.devnull, "w") as devnull:
old_stdout = sys.stdout
sys.stdout = devnull
try:
yield
finally:
sys.stdout = old_stdout
print("Now you see it")
with suppress_stdout():
print("Now you don't")
Tests
>>> with suppress_stdout():
os.system('play /mnt/Vancouver/programming/scripts/PHASER.WAV')
/mnt/Vancouver/programming/scripts/PHASER.WAV:
File Size: 1.84k Bit Rate: 90.4k
Encoding: Unsigned PCM
Channels: 1 # 8-bit
Samplerate: 11025Hz
Replaygain: off
Duration: 00:00:00.16
In:100% 00:00:00.16 [00:00:00.00] Out:1.79k [!=====|=====!] Clip:0
Done.
Use this to completely suppress os.system() output:
>>> with suppress_stdout():
os.system('play /mnt/Vancouver/programming/scripts/PHASER.WAV >/dev/null 2>&1')
>>> ## successfully executed
>>> import time
>>> with suppress_stdout():
for i in range(3):
os.system('play /mnt/Vancouver/programming/scripts/PHASER.WAV >/dev/null 2>&1')
time.sleep(0.5)
>>> ## successfully executed
Useful (e.g.) to signal completion of long-running scripts.

I use pythonw.exe (on Windows) instead of python.exe.
In other OSes, you could also redirect output to /dev/nul.
And in order to still see my debug output, I am using the logging module.

As Demolishun mentions in an answer to a closed duplicate question, there is a thread talking about this issue. The thread is from August of 2009 and one of the developers says the debug code was left in on accident. I had installed Pygame 1.9.1 from pip and the debug output is still present.
To get around it for now, I downloaded the source from pygame.org, removed the print statements from src/joystick.c and compiled the code.
I am on OS X 10.7.5 for what it's worth.

If you are on a Debian or Ubuntu machine you can just simply recompile pygame without the messages.
cd /tmp
sudo apt-get build-dep pygame
apt-get source pygame
vim pygame-1.9.1release+dfsg/src/joystick.c
# search for the printf("SDL.. messages and put a // in front
apt-get source --compile pygame
sudo dpkg -i python-pygame_1.9.1release+dfsg-9ubuntu1_amd64.deb
Greetings
Max

The solutions using os.devnull could cause synchronization issues with multi-processing - so multiple processes would wait on the same resource. At least this is what I encountered in windows.
Following solution might be better:
class DummyOutput(object):
def __init__(self, *args, **kwargs):
pass
def write(self, *args, **kwargs):
pass
class suppress_stdout_stderr(object):
def __init__(self):
self.stdout = sys.stdout
self.stderr = sys.stderr
def __enter__(self, *args, **kwargs):
out = DummyOutput()
sys.stdout = out
sys.stderr = out
def __exit__(self, *args, **kwargs):
sys.stdout = self.stdout
sys.stderr = self.stderr
with suppress_stdout_stderr():
print(123, file=sys.stdout)
print(123, file=sys.stderr)

Related

nohup: redirecting stderr to stdout error [duplicate]

I'm using Pygame/SDL's joystick module to get input from a gamepad. Every time I call its get_hat() method it prints to the console. This is problematic since I use the console to help me debug and now it gets flooded with SDL_JoystickGetHat value:0: 60 times every second. Is there a way I can disable this? Either through an option in Pygame/SDL or suppress console output while the function calls? I saw no mention of this in the Pygame documentation.
edit: This turns out to be due to debugging being turned on when the SDL library was compiled.
Just for completeness, here's a nice solution from Dave Smith's blog:
from contextlib import contextmanager
import sys, os
#contextmanager
def suppress_stdout():
with open(os.devnull, "w") as devnull:
old_stdout = sys.stdout
sys.stdout = devnull
try:
yield
finally:
sys.stdout = old_stdout
With this, you can use context management wherever you want to suppress output:
print("Now you see it")
with suppress_stdout():
print("Now you don't")
To complete charles's answer, there are two context managers built in to python, redirect_stdout and redirect_stderr which you can use to redirect and or suppress a commands output to a file or StringIO variable.
import contextlib
with contextlib.redirect_stdout(None):
do_thing()
For a more complete explanation read the docs
A quick update: In some cases passing None might raise some reference errors (e.g. keras.models.Model.fit calls sys.stdout.write which will be problematic), in that case pass an io.StringIO() or os.devnull.
You can get around this by assigning the standard out/error (I don't know which one it's going to) to the null device. In Python, the standard out/error files are sys.stdout/sys.stderr, and the null device is os.devnull, so you do
sys.stdout = open(os.devnull, "w")
sys.stderr = open(os.devnull, "w")
This should disable these error messages completely. Unfortunately, this will also disable all console output. To get around this, disable output right before calling the get_hat() the method, and then restore it by doing
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
which restores standard out and error to their original value.
Here's the relevant block of code from joystick.c (via SVN at http://svn.seul.org/viewcvs/viewvc.cgi/trunk/src/joystick.c?view=markup&revision=2652&root=PyGame)
value = SDL_JoystickGetHat (joy, _index);
#ifdef DEBUG
printf("SDL_JoystickGetHat value:%d:\n", value);
#endif
if (value & SDL_HAT_UP) {
Looks like a problem with having debugging turned on.
Building on #charleslparker's answer:
from contextlib import contextmanager
import sys, os
#contextmanager
def suppress_stdout():
with open(os.devnull, "w") as devnull:
old_stdout = sys.stdout
sys.stdout = devnull
try:
yield
finally:
sys.stdout = old_stdout
print("Now you see it")
with suppress_stdout():
print("Now you don't")
Tests
>>> with suppress_stdout():
os.system('play /mnt/Vancouver/programming/scripts/PHASER.WAV')
/mnt/Vancouver/programming/scripts/PHASER.WAV:
File Size: 1.84k Bit Rate: 90.4k
Encoding: Unsigned PCM
Channels: 1 # 8-bit
Samplerate: 11025Hz
Replaygain: off
Duration: 00:00:00.16
In:100% 00:00:00.16 [00:00:00.00] Out:1.79k [!=====|=====!] Clip:0
Done.
Use this to completely suppress os.system() output:
>>> with suppress_stdout():
os.system('play /mnt/Vancouver/programming/scripts/PHASER.WAV >/dev/null 2>&1')
>>> ## successfully executed
>>> import time
>>> with suppress_stdout():
for i in range(3):
os.system('play /mnt/Vancouver/programming/scripts/PHASER.WAV >/dev/null 2>&1')
time.sleep(0.5)
>>> ## successfully executed
Useful (e.g.) to signal completion of long-running scripts.
I use pythonw.exe (on Windows) instead of python.exe.
In other OSes, you could also redirect output to /dev/nul.
And in order to still see my debug output, I am using the logging module.
As Demolishun mentions in an answer to a closed duplicate question, there is a thread talking about this issue. The thread is from August of 2009 and one of the developers says the debug code was left in on accident. I had installed Pygame 1.9.1 from pip and the debug output is still present.
To get around it for now, I downloaded the source from pygame.org, removed the print statements from src/joystick.c and compiled the code.
I am on OS X 10.7.5 for what it's worth.
If you are on a Debian or Ubuntu machine you can just simply recompile pygame without the messages.
cd /tmp
sudo apt-get build-dep pygame
apt-get source pygame
vim pygame-1.9.1release+dfsg/src/joystick.c
# search for the printf("SDL.. messages and put a // in front
apt-get source --compile pygame
sudo dpkg -i python-pygame_1.9.1release+dfsg-9ubuntu1_amd64.deb
Greetings
Max
The solutions using os.devnull could cause synchronization issues with multi-processing - so multiple processes would wait on the same resource. At least this is what I encountered in windows.
Following solution might be better:
class DummyOutput(object):
def __init__(self, *args, **kwargs):
pass
def write(self, *args, **kwargs):
pass
class suppress_stdout_stderr(object):
def __init__(self):
self.stdout = sys.stdout
self.stderr = sys.stderr
def __enter__(self, *args, **kwargs):
out = DummyOutput()
sys.stdout = out
sys.stderr = out
def __exit__(self, *args, **kwargs):
sys.stdout = self.stdout
sys.stderr = self.stderr
with suppress_stdout_stderr():
print(123, file=sys.stdout)
print(123, file=sys.stderr)

Suppress pygame debug output (console output)

Apparently the version of pygame I'm using has an issue where debug statements have been left in - How to suppress console output in Python? while using joystick.get_axis. That is the issue, but I have been unsuccessful in trying to use the methods presented in those answers. Each of the methods still printed the SDL_JoystickGetAxis value.
I also tried this blog but I was still outputting to the console. Thinking it may be an issue with stdout vs stderr, I tried suppressing stdout then stderr then both, to no avail.
Basically my code is constantly printing SDL_JoystickGetAxis value:0 or whatever the axis value is. How do I suppress these debug statements?
import os
import sys
from contextlib import contextmanager
#contextmanager
def suppress_stdout():
with open(os.devnull, 'w') as devnull:
old_stdout = sys.stdout
sys.stdout = devnull
try:
yield
finally:
sys.stdout = old_stdout
Later on in my code I use that function:
if speedchange == False and headingchange == False:
time.sleep(0.1)
with suppress_stdout():
speed_ax = joys.get_axis(1)
head_ax = joys.get_axis(0)
Which still outputs debug statements
I'm not sure why the code from the blog you linked doesn't work, but you can always just drop this at the top of your code:
sys.stdout = os.devnull
sys.stderr = os.devnull
If you do then need to output error messages, or the like, you can just do this:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
Just in case you are using the context manager linked in the blog wrong, this is how you're supposed to use it:
with suppress_stdout():
# Do blah foo and bar here
This worked for me in python 3.5 and python 2.7
import sys, os
stdout = sys.__stdout__
stderr = sys.__stderr__
print("start")
sys.stdout = open(os.devnull,'w')
sys.stderr = open(os.devnull,'w')
print("don't want")
sys.stdout = stdout
sys.stderr = stderr
print("want")
The output is
start
want

suppress scapy warning message when importing the module

I'm writing a small script, that gathers some information using scapy and then returns some xml code, that I'll pass on to the xmlrpc interface of metasploit. I'd like it that my script only returns xml, and no additional warnings etc.
I can suppress most scapy output, with adding the option verbose=0 to my sr1 command. What I still get before every output, and I assume it returns this warning when I'm loading the module, is:
WARNING: No route found for IPv6 destination :: (no default route?)
I can easily redirect that output, by calling my script like this:
./myscript 2> /dev/null
but I'd like to incorporate this into the script. For that I've found a hint, that one could have a NullDevice class, that doesn't write anything, and then set sys.stderr to an instantiation of that NullDevice class.
This only works unfortunately after I've already loaded the module, so I still have the Warning, and it only redirects any following messages sent to stderr.
How can I suppress that warning message to appear on my screen?
You can get rid of warnings by scapy by adding:
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
before importing Scapy. This will suppress all messages that have a lower level of seriousness than error messages.
for example:
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
...
I think this is the correct way.
>>> import sys
>>> sys.stderr = None # suppress stderr
>>> from scapy.all import *
>>> sys.stderr = sys.__stderr__ # restore stderr
>>> print("other errors can be shown", file=sys.stderr)
other errors can be shown
>>>
I think perhaps the python3 version of scapy prints a message from a different logger or at a high level. Here's some code I've used to suppress output on module import.
from contextlib import contextmanager
# It looks like redirect_stderr will be part of Python 3.5 as follows:
# from contextlib import redirect_stderr
# Perhaps if you're looking at this code and 3.5 is out, this function could be
# removed.
#contextmanager
def redirect_stderr(new_target):
"""
A context manager to temporarily redirect stderr. Example use:
with open(os.devnull, 'w') as f:
with redirect_stderr(f):
# stderr redirected to os.devnull. No annoying import messages
# printed on module import
from scapy.all import *
# stderr restored
"""
import sys
old_target, sys.stderr = sys.stderr, new_target # replace sys.stdout
try:
yield new_target # run some code with the replaced stdout
finally:
sys.stderr = old_target # restore to the previous value
# Don't print the annoying warning message that occurs on import
with open(os.devnull, 'w') as errf:
with redirect_stderr(errf):
from scapy.all import sr, ICMP, IP, traceroute
With Python3, redefining sys.stderr to None threw an exception AttributeError: 'NoneType' object has no attribute 'write'. Instead, defining it to os.devnull does the job:
import os
import sys
sys.stderr = os.devnull # suppress stderr
from scapy.all import *
sys.stderr = sys.__stderr__ # restore stderr

Redirecting stdout to "nothing" in python

I have a large project consisting of sufficiently large number of modules, each printing something to the standard output. Now as the project has grown in size, there are large no. of print statements printing a lot on the std out which has made the program considerably slower.
So, I now want to decide at runtime whether or not to print anything to the stdout. I cannot make changes in the modules as there are plenty of them. (I know I can redirect the stdout to a file but even this is considerably slow.)
So my question is how do I redirect the stdout to nothing ie how do I make the print statement do nothing?
# I want to do something like this.
sys.stdout = None # this obviously will give an error as Nonetype object does not have any write method.
Currently the only idea I have is to make a class which has a write method (which does nothing) and redirect the stdout to an instance of this class.
class DontPrint(object):
def write(*args): pass
dp = DontPrint()
sys.stdout = dp
Is there an inbuilt mechanism in python for this? Or is there something better than this?
Cross-platform:
import os
import sys
f = open(os.devnull, 'w')
sys.stdout = f
On Windows:
f = open('nul', 'w')
sys.stdout = f
On Linux:
f = open('/dev/null', 'w')
sys.stdout = f
A nice way to do this is to create a small context processor that you wrap your prints in. You then just use is in a with-statement to silence all output.
Python 2:
import os
import sys
from contextlib import contextmanager
#contextmanager
def silence_stdout():
old_target = sys.stdout
try:
with open(os.devnull, "w") as new_target:
sys.stdout = new_target
yield new_target
finally:
sys.stdout = old_target
with silence_stdout():
print("will not print")
print("this will print")
Python 3.4+:
Python 3.4 has a context processor like this built-in, so you can simply use contextlib like this:
import contextlib
with contextlib.redirect_stdout(None):
print("will not print")
print("this will print")
If the code you want to surpress writes directly to sys.stdout using None as redirect target won't work. Instead you can use:
import contextlib
import sys
import os
with contextlib.redirect_stdout(open(os.devnull, 'w')):
sys.stdout.write("will not print")
sys.stdout.write("this will print")
If your code writes to stderr instead of stdout, you can use contextlib.redirect_stderr instead of redirect_stdout.
Running this code only prints the second line of output, not the first:
$ python test.py
this will print
This works cross-platform (Windows + Linux + Mac OSX), and is cleaner than the ones other answers imho.
If you're in python 3.4 or higher, there's a simple and safe solution using the standard library:
import contextlib
with contextlib.redirect_stdout(None):
print("This won't print!")
(at least on my system) it appears that writing to os.devnull is about 5x faster than writing to a DontPrint class, i.e.
#!/usr/bin/python
import os
import sys
import datetime
ITER = 10000000
def printlots(out, it, st="abcdefghijklmnopqrstuvwxyz1234567890"):
temp = sys.stdout
sys.stdout = out
i = 0
start_t = datetime.datetime.now()
while i < it:
print st
i = i+1
end_t = datetime.datetime.now()
sys.stdout = temp
print out, "\n took", end_t - start_t, "for", it, "iterations"
class devnull():
def write(*args):
pass
printlots(open(os.devnull, 'wb'), ITER)
printlots(devnull(), ITER)
gave the following output:
<open file '/dev/null', mode 'wb' at 0x7f2b747044b0>
took 0:00:02.074853 for 10000000 iterations
<__main__.devnull instance at 0x7f2b746bae18>
took 0:00:09.933056 for 10000000 iterations
If you're in a Unix environment (Linux included), you can redirect output to /dev/null:
python myprogram.py > /dev/null
And for Windows:
python myprogram.py > nul
You can just mock it.
import mock
sys.stdout = mock.MagicMock()
Your class will work just fine (with the exception of the write() method name -- it needs to be called write(), lowercase). Just make sure you save a copy of sys.stdout in another variable.
If you're on a *NIX, you can do sys.stdout = open('/dev/null'), but this is less portable than rolling your own class.
How about this:
from contextlib import ExitStack, redirect_stdout
import os
with ExitStack() as stack:
if should_hide_output():
null_stream = open(os.devnull, "w")
stack.enter_context(null_stream)
stack.enter_context(redirect_stdout(null_stream))
noisy_function()
This uses the features in the contextlib module to hide the output of whatever command you are trying to run, depending on the result of should_hide_output(), and then restores the output behavior after that function is done running.
If you want to hide standard error output, then import redirect_stderr from contextlib and add a line saying stack.enter_context(redirect_stderr(null_stream)).
The main downside it that this only works in Python 3.4 and later versions.
sys.stdout = None
It is OK for print() case. But it can cause an error if you call any method of sys.stdout, e.g. sys.stdout.write().
There is a note in docs:
Under some conditions stdin, stdout and stderr as well as the original
values stdin, stdout and stderr can be None. It is usually
the case for Windows GUI apps that aren’t connected to a console and
Python apps started with pythonw.
Supplement to iFreilicht's answer - it works for both python 2 & 3.
import sys
class NonWritable:
def write(self, *args, **kwargs):
pass
class StdoutIgnore:
def __enter__(self):
self.stdout_saved = sys.stdout
sys.stdout = NonWritable()
return self
def __exit__(self, *args):
sys.stdout = self.stdout_saved
with StdoutIgnore():
print("This won't print!")
If you don't want to deal with resource-allocation nor rolling your own class, you may want to use TextIO from Python typing. It has all required methods stubbed for you by default.
import sys
from typing import TextIO
sys.stdout = TextIO()
There are a number of good answers in the flow, but here is my Python 3 answer (when sys.stdout.fileno() isn't supported anymore) :
import os
import sys
oldstdout = os.dup(1)
oldstderr = os.dup(2)
oldsysstdout = sys.stdout
oldsysstderr = sys.stderr
# Cancel all stdout outputs (will be lost) - optionally also cancel stderr
def cancel_stdout(stderr=False):
sys.stdout.flush()
devnull = open('/dev/null', 'w')
os.dup2(devnull.fileno(), 1)
sys.stdout = devnull
if stderr:
os.dup2(devnull.fileno(), 2)
sys.stderr = devnull
# Redirect all stdout outputs to a file - optionally also redirect stderr
def reroute_stdout(filepath, stderr=False):
sys.stdout.flush()
file = open(filepath, 'w')
os.dup2(file.fileno(), 1)
sys.stdout = file
if stderr:
os.dup2(file.fileno(), 2)
sys.stderr = file
# Restores stdout to default - and stderr
def restore_stdout():
sys.stdout.flush()
sys.stdout.close()
os.dup2(oldstdout, 1)
os.dup2(oldstderr, 2)
sys.stdout = oldsysstdout
sys.stderr = oldsysstderr
To use it:
Cancel all stdout and stderr outputs with:
cancel_stdout(stderr=True)
Route all stdout (but not stderr) to a file:
reroute_stdout('output.txt')
To restore stdout and stderr:
restore_stdout()
Why don't you try this?
sys.stdout.close()
sys.stderr.close()
Will add some example to the numerous answers here:
import argparse
import contextlib
class NonWritable:
def write(self, *args, **kwargs):
pass
parser = argparse.ArgumentParser(description='my program')
parser.add_argument("-p", "--param", help="my parameter", type=str, required=True)
#with contextlib.redirect_stdout(None): # No effect as `argparse` will output to `stderr`
#with contextlib.redirect_stderr(None): # AttributeError: 'NoneType' object has no attribute 'write'
with contextlib.redirect_stderr(NonWritable): # this works!
args = parser.parse_args()
The normal output would be:
>python TEST.py
usage: TEST.py [-h] -p PARAM
TEST.py: error: the following arguments are required: -p/--param
I use this. Redirect stdout to a string, which you subsequently ignore. I use a context manager to save and restore the original setting for stdout.
from io import StringIO
...
with StringIO() as out:
with stdout_redirected(out):
# Do your thing
where stdout_redirected is defined as:
from contextlib import contextmanager
#contextmanager
def stdout_redirected(new_stdout):
save_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield None
finally:
sys.stdout = save_stdout

Redirect stdout to a file in Python?

How do I redirect stdout to an arbitrary file in Python?
When a long-running Python script (e.g, web application) is started from within the ssh session and backgounded, and the ssh session is closed, the application will raise IOError and fail the moment it tries to write to stdout. I needed to find a way to make the application and modules output to a file rather than stdout to prevent failure due to IOError. Currently, I employ nohup to redirect output to a file, and that gets the job done, but I was wondering if there was a way to do it without using nohup, out of curiosity.
I have already tried sys.stdout = open('somefile', 'w'), but this does not seem to prevent some external modules from still outputting to terminal (or maybe the sys.stdout = ... line did not fire at all). I know it should work from simpler scripts I've tested on, but I also didn't have time yet to test on a web application yet.
If you want to do the redirection within the Python script, setting sys.stdout to a file object does the trick:
# for python3
import sys
with open('file', 'w') as sys.stdout:
print('test')
A far more common method is to use shell redirection when executing (same on Windows and Linux):
$ python3 foo.py > file
There is contextlib.redirect_stdout() function in Python 3.4+:
from contextlib import redirect_stdout
with open('help.txt', 'w') as f:
with redirect_stdout(f):
print('it now prints to `help.text`')
It is similar to:
import sys
from contextlib import contextmanager
#contextmanager
def redirect_stdout(new_target):
old_target, sys.stdout = sys.stdout, new_target # replace sys.stdout
try:
yield new_target # run some code with the replaced stdout
finally:
sys.stdout = old_target # restore to the previous value
that can be used on earlier Python versions. The latter version is not reusable. It can be made one if desired.
It doesn't redirect the stdout at the file descriptors level e.g.:
import os
from contextlib import redirect_stdout
stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, redirect_stdout(f):
print('redirected to a file')
os.write(stdout_fd, b'not redirected')
os.system('echo this also is not redirected')
b'not redirected' and 'echo this also is not redirected' are not redirected to the output.txt file.
To redirect at the file descriptor level, os.dup2() could be used:
import os
import sys
from contextlib import contextmanager
def fileno(file_or_fd):
fd = getattr(file_or_fd, 'fileno', lambda: file_or_fd)()
if not isinstance(fd, int):
raise ValueError("Expected a file (`.fileno()`) or a file descriptor")
return fd
#contextmanager
def stdout_redirected(to=os.devnull, stdout=None):
if stdout is None:
stdout = sys.stdout
stdout_fd = fileno(stdout)
# copy stdout_fd before it is overwritten
#NOTE: `copied` is inheritable on Windows when duplicating a standard stream
with os.fdopen(os.dup(stdout_fd), 'wb') as copied:
stdout.flush() # flush library buffers that dup2 knows nothing about
try:
os.dup2(fileno(to), stdout_fd) # $ exec >&to
except ValueError: # filename
with open(to, 'wb') as to_file:
os.dup2(to_file.fileno(), stdout_fd) # $ exec > to
try:
yield stdout # allow code to be run with the redirected stdout
finally:
# restore stdout to its previous value
#NOTE: dup2 makes stdout_fd inheritable unconditionally
stdout.flush()
os.dup2(copied.fileno(), stdout_fd) # $ exec >&copied
The same example works now if stdout_redirected() is used instead of redirect_stdout():
import os
import sys
stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, stdout_redirected(f):
print('redirected to a file')
os.write(stdout_fd, b'it is redirected now\n')
os.system('echo this is also redirected')
print('this is goes back to stdout')
The output that previously was printed on stdout now goes to output.txt as long as stdout_redirected() context manager is active.
Note: stdout.flush() does not flush
C stdio buffers on Python 3 where I/O is implemented directly on read()/write() system calls. To flush all open C stdio output streams, you could call libc.fflush(None) explicitly if some C extension uses stdio-based I/O:
try:
import ctypes
from ctypes.util import find_library
except ImportError:
libc = None
else:
try:
libc = ctypes.cdll.msvcrt # Windows
except OSError:
libc = ctypes.cdll.LoadLibrary(find_library('c'))
def flush(stream):
try:
libc.fflush(None)
stream.flush()
except (AttributeError, ValueError, IOError):
pass # unsupported
You could use stdout parameter to redirect other streams, not only sys.stdout e.g., to merge sys.stderr and sys.stdout:
def merged_stderr_stdout(): # $ exec 2>&1
return stdout_redirected(to=sys.stdout, stdout=sys.stderr)
Example:
from __future__ import print_function
import sys
with merged_stderr_stdout():
print('this is printed on stdout')
print('this is also printed on stdout', file=sys.stderr)
Note: stdout_redirected() mixes buffered I/O (sys.stdout usually) and unbuffered I/O (operations on file descriptors directly). Beware, there could be buffering issues.
To answer, your edit: you could use python-daemon to daemonize your script and use logging module (as #erikb85 suggested) instead of print statements and merely redirecting stdout for your long-running Python script that you run using nohup now.
you can try this too much better
import sys
class Logger(object):
def __init__(self, filename="Default.log"):
self.terminal = sys.stdout
self.log = open(filename, "a")
def write(self, message):
self.terminal.write(message)
self.log.write(message)
sys.stdout = Logger("yourlogfilename.txt")
print "Hello world !" # this is should be saved in yourlogfilename.txt
The other answers didn't cover the case where you want forked processes to share your new stdout.
To do that:
from os import open, close, dup, O_WRONLY
old = dup(1)
close(1)
open("file", O_WRONLY) # should open on 1
..... do stuff and then restore
close(1)
dup(old) # should dup to 1
close(old) # get rid of left overs
Quoted from PEP 343 -- The "with" Statement (added import statement):
Redirect stdout temporarily:
import sys
from contextlib import contextmanager
#contextmanager
def stdout_redirected(new_stdout):
save_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield None
finally:
sys.stdout = save_stdout
Used as follows:
with open(filename, "w") as f:
with stdout_redirected(f):
print "Hello world"
This isn't thread-safe, of course, but neither is doing this same dance manually. In single-threaded programs (for example in scripts) it is a popular way of doing things.
import sys
sys.stdout = open('stdout.txt', 'w')
Here is a variation of Yuda Prawira answer:
implement flush() and all the file attributes
write it as a contextmanager
capture stderr also
.
import contextlib, sys
#contextlib.contextmanager
def log_print(file):
# capture all outputs to a log file while still printing it
class Logger:
def __init__(self, file):
self.terminal = sys.stdout
self.log = file
def write(self, message):
self.terminal.write(message)
self.log.write(message)
def __getattr__(self, attr):
return getattr(self.terminal, attr)
logger = Logger(file)
_stdout = sys.stdout
_stderr = sys.stderr
sys.stdout = logger
sys.stderr = logger
try:
yield logger.log
finally:
sys.stdout = _stdout
sys.stderr = _stderr
with log_print(open('mylogfile.log', 'w')):
print('hello world')
print('hello world on stderr', file=sys.stderr)
# you can capture the output to a string with:
# with log_print(io.StringIO()) as log:
# ....
# print('[captured output]', log.getvalue())
You need a terminal multiplexer like either tmux or GNU screen
I'm surprised that a small comment by Ryan Amos' to the original question is the only mention of a solution far preferable to all the others on offer, no matter how clever the python trickery may be and how many upvotes they've received. Further to Ryan's comment, tmux is a nice alternative to GNU screen.
But the principle is the same: if you ever find yourself wanting to leave a terminal job running while you log-out, head to the cafe for a sandwich, pop to the bathroom, go home (etc) and then later, reconnect to your terminal session from anywhere or any computer as though you'd never been away, terminal multiplexers are the answer. Think of them as VNC or remote desktop for terminal sessions. Anything else is a workaround. As a bonus, when the boss and/or partner comes in and you inadvertently ctrl-w / cmd-w your terminal window instead of your browser window with its dodgy content, you won't have lost the last 18 hours-worth of processing!
Based on this answer: https://stackoverflow.com/a/5916874/1060344, here is another way I figured out which I use in one of my projects. For whatever you replace sys.stderr or sys.stdout with, you have to make sure that the replacement complies with file interface, especially if this is something you are doing because stderr/stdout are used in some other library that is not under your control. That library may be using other methods of file object.
Check out this way where I still let everything go do stderr/stdout (or any file for that matter) and also send the message to a log file using Python's logging facility (but you can really do anything with this):
class FileToLogInterface(file):
'''
Interface to make sure that everytime anything is written to stderr, it is
also forwarded to a file.
'''
def __init__(self, *args, **kwargs):
if 'cfg' not in kwargs:
raise TypeError('argument cfg is required.')
else:
if not isinstance(kwargs['cfg'], config.Config):
raise TypeError(
'argument cfg should be a valid '
'PostSegmentation configuration object i.e. '
'postsegmentation.config.Config')
self._cfg = kwargs['cfg']
kwargs.pop('cfg')
self._logger = logging.getlogger('access_log')
super(FileToLogInterface, self).__init__(*args, **kwargs)
def write(self, msg):
super(FileToLogInterface, self).write(msg)
self._logger.info(msg)
Programs written in other languages (e.g. C) have to do special magic (called double-forking) expressly to detach from the terminal (and to prevent zombie processes). So, I think the best solution is to emulate them.
A plus of re-executing your program is, you can choose redirections on the command-line, e.g. /usr/bin/python mycoolscript.py 2>&1 1>/dev/null
See this post for more info: What is the reason for performing a double fork when creating a daemon?
I know this question is answered (using python abc.py > output.log 2>&1 ), but I still have to say:
When writing your program, don't write to stdout. Always use logging to output whatever you want. That would give you a lot of freedom in the future when you want to redirect, filter, rotate the output files.
As mentioned by #jfs, most solutions will not properly handle some types of stdout output such as that from C extensions. There is a module that takes care of all this on PyPI called wurlitzer. You just need its sys_pipes context manager. It's as easy as using:
from contextlib import redirect_stdout
import os
from wurlitzer import sys_pipes
log = open("test.log", "a")
with redirect_stdout(log), sys_pipes():
print("print statement")
os.system("echo echo call")
Based on previous answers on this post I wrote this class for myself as a more compact and flexible way of redirecting the output of pieces of code - here just to a list - and ensure that the output is normalized afterwards.
class out_to_lt():
def __init__(self, lt):
if type(lt) == list:
self.lt = lt
else:
raise Exception("Need to pass a list")
def __enter__(self):
import sys
self._sys = sys
self._stdout = sys.stdout
sys.stdout = self
return self
def write(self,txt):
self.lt.append(txt)
def __exit__(self, type, value, traceback):
self._sys.stdout = self._stdout
Used as:
lt = []
with out_to_lt(lt) as o:
print("Test 123\n\n")
print(help(str))
Updating. Just found a scenario where I had to add two extra methods, but was easy to adapt:
class out_to_lt():
...
def isatty(self):
return True #True: You're running in a real terminal, False:You're being piped, redirected, cron
def flush(self):
pass
There are other versions using context but nothing this simple. I actually just googled to double check it would work and was surprised not to see it, so for other people looking for a quick solution that is safe and directed at only the code within the context block, here it is:
import sys
with open('test_file', 'w') as sys.stdout:
print('Testing 1 2 3')
Tested like so:
$ cat redirect_stdout.py
import sys
with open('test_file', 'w') as sys.stdout:
print('Testing 1 2 3')
$ python redirect_stdout.py
$ cat test_file
Testing 1 2 3

Categories