2

I have seen this question answered in reference to Bash, but can't find one for Python. Apologies if this is repeating something.

Is it possible to print to the terminal and an output file with one command? I'm familiar with using print >> and sys.stdout = WritableObject, but I'd like to avoid having to double print commands for each line I want logged.

I'm using Python 2.6, just in case such knowledge is necessary.

More importantly, I want this to run on a Windows-based system using IDLE's command line. So, in essence, I want the python script to report to IDLE's terminal and a given log file.

EDIT: For anyone who finds this and decides to go with the answer I chose, if you need help understanding context managers (like I did), I recommend Doug Hellman's Python Modules of the Week for clarification. This one details the context library. For help with decorators see this Stack Overflow question's answers.

Community
  • 1
  • 1
Nathanus
  • 267
  • 2
  • 8
  • Does http://stackoverflow.com/questions/616645/how-do-i-duplicate-sys-stdout-to-a-log-file-in-python not solve your problem? I'm not sure what you mean by 'having to double print' -- please clarify. – phooji Mar 14 '11 at 18:36

3 Answers3

6

Replace sys.stdout.

class PrintAndLog(object):
    def __init__(self, fileOrPath): # choose which makes more sense
        self._file = ...

    def write(s):
        sys.stdout.write(s)
        self._file.write(s)

    def close(self):
        self._file.close()
    # insert wrappers for .flush, .writelines

_old_stdout = sys.stdout
sys.stdout = PrintAndLog(f)
... # print and stuff
sys.stdout = _old_stdout

Can be put into a context manager (this is at least the third time I see something like this on SO alone...):

from contextlib import contextmanager

@contextmanager
def replace_stdout(f):
    old_stdout = sys.stdout
    try:
        sys.stdout = PrintAndLog(f)
        yield
    finally:
        sys.stdout = old_stdout
  • This is probably the right way of doing it if you want to retain calls to `print`, though, `self._file` might be a better name, to avoid shadowing the built-in. – Chinmay Kanchi Mar 14 '11 at 18:42
  • @Chinmay: How could an object's attribute shadow a builtin? I'll still add the underscore, as the file should indeed be private. –  Mar 14 '11 at 18:43
  • @delnan I've never heard of context manager.... will look into it, and using this method. Thanks. Also, since you've seen this request before, if you could find a link to a previous incarnation? If my search was not thorough enough, I'd like some insight into what I missed. – Nathanus Mar 14 '11 at 18:44
  • 1
    This can be further generalized to be a TeeStream that takes in two writable objects instead of restricting it to `stdout`. – unholysampler Mar 14 '11 at 18:45
  • It's fine to use `self.file`, since there's no ambiguity between `self.file` and `file`--`self` is never implicit in Python. – Glenn Maynard Mar 14 '11 at 18:45
  • @Nathanus: See [PEP 343](http://www.python.org/dev/peps/pep-0343/) for a rather formal, but correct and useful introduction. @unholysampler: Indeed, and that would make a nice addition to the standard library, but for now it's left as an exercise for the reader ;) –  Mar 14 '11 at 18:46
  • 1
    It's a minor point, but you're better off putting `sys.stdout = ...` inside the `try` instead of outside it. That way, if an async exception like `KeyboardInterrupt` happens after the assignment but before the exception handler is entered, it'll still clean up properly. – Glenn Maynard Mar 14 '11 at 18:48
  • If you don't define the rest of the file interface you'll probably run into trouble with third-party code, eg. `flush`, `writelines`, `closed`, `isatty`, `name`. (The simpler way to do this is to just wrap `print`, but you need Python 3 to do that.) – Glenn Maynard Mar 14 '11 at 18:54
  • @Glenn: Moved the sys.stdout replacement. Concerning comment #2: Is anyone actually using anything except `.write` and `.flush` on `sys.stdout`? Well, if anyone runs into trouble, it's easy to add a wrapper method. –  Mar 14 '11 at 19:10
2

Why not just write a function?

def myPrint(anOpenFileObject, message):
    print message
    anOpenFileObject.write(message)
Chinmay Kanchi
  • 62,729
  • 22
  • 87
  • 114
0

If you're in Unix: At the start of your program, you could mkfifo a named pipe, and launch in the bg a cat from it to a tee of the terminal and the desired output file. Then throughout your program, output to the fd of the named pipe. Finally, rm the named pipe just before exiting.

But honestly I would just make a wrapper around print that prints to both dests.

AlcubierreDrive
  • 3,654
  • 2
  • 29
  • 45