Sending output to stdout and also to a file
David Bolen
db3l at fitlinxx.com
Mon Jul 17 22:06:47 EDT 2000
"donotspam-jen at personic.com" <dnotspam-jen at personic.com> writes:
> Is there a way to have output from system commands go to stdout and also to
> a file? For example, is there a way to get the results of the MS DOS "DIR"
> command and have it appear both in a file and also on the screen?
>
> The particular issue I'm trying to solve is that I have a script that calls
> PVCS commands. PVCS sends things to both stderr and stdout, which by
> default go to the screen. I'd like to capture this output and put it both
> on the screen and also in a log file that can be reviewed later.
To trap output of a system command you want to use popen() to run the
command giving you the output via a File object. (For
Win95/98/NT-without-console, you should use win32pipe.popen() from the
Win32 extensions for reliable pipes to child processes, but there's a
side effect in that you won't get the result code of the process on
the close()).
If you just want to capture all output of your command (stdout and
stderr), you can add a "2>&1" to the end of the command to reroute
stderr to the same place stdout is going - which is your process).
Once you have the output, you can do anything you want with it.
For example, here's a small function that I use to execute commands
and dump their output prefaced with a few spaces (to indent it with
respects to the other output that my script is creating). I also
include a dump of the command and some execution time information
since this normally gets used with background scripts that run lots of
commands across a bunch of sites.
- - - - - - - - - - - - - - - - - - - - - - - - -
import os
import time
def Execute (Command):
"""
Executes a command string, accepting all output and logging it.
The function automatically appends a stderr redirect to include
stderr in the output.
"""
print "Executing", Command
start_time = time.time()
pipe = os.popen(Command + " 2>&1")
while 1:
line = pipe.readline()
if not line:
break
print " ", line,
rc = pipe.close()
stop_time = time.time()
if rc:
print "Command completed with exit code", rc,
else:
print "Command completed successfully",
print "(elapsed time = %d s)" % (stop_time - start_time)
return rc
- - - - - - - - - - - - - - - - - - - - - - - - -
Now the above function just prints the command output with the
indentation, but what I normally do is redirect sys.stdout to a log
class that I have which logs to a log file with an optional copy to
stdout. The log class also prepends a timestamp (and a "site" id
which is just a string used when I walk a bunch of sites for
operations). This ensures I get a log of everything that is happening
in my script, including all child processes.
- - - - - - - - - - - - - - - - - - - - - - - - -
import sys
import time
import os
class LogClass:
"Class for generating log messages to the log file and optionally stdout"
def __init__ (self, Filename=None, DupStdout=0):
if (Filename != None):
self._file = open(Filename,"a")
else:
self._file = sys.stdout
self._needprefix = 1
self._sitename = None
if DupStdout:
self._dupstdout = sys.stdout
else:
self._dupstdout = None
def write(self, Message):
message = ""
if (self._needprefix):
message = message + "[%s-%d" % \
(time.strftime("%H:%M:%S",
time.localtime(time.time())),
os.getpid())
if (self._sitename):
message = message + "-" + self._sitename
message = message + "] "
self._needprefix = 0
message = message + Message
self._file.write(message)
self._file.flush()
if self._dupstdout:
self._dupstdout.write(message)
self._dupstdout.flush()
if (Message[-1] == "\n"):
self._needprefix = 1
def log(self, Message):
self.write(Message)
self.write("\n")
if self._dupstdout:
self._dupstdout.write(Message)
self._dupstdout.write("\n")
def setsite(self, SiteName):
self._sitename = SiteName
- - - - - - - - - - - - - - - - - - - - - - - - -
So from a script you would do something like:
# Create log class to log file
log = LogClass('filename')
or
# Create log class to log file and include duplicate to stdout
log = LogClass('filename',DupStdout=1)
and then reassign sys.stdout (and sys.stderr if you want) to the log:
sys.stdout = log
and then whenever you want to execute a command, just use:
Execute('command string')
It's normally nice to save the old sys.stdout/stderr and then restore
them at the end of your script (say perhaps in a top level try/finally
clause) in case your script is included as a module from some other
script.
Hope this gives you some ideas.
--
-- David
--
/-----------------------------------------------------------------------\
\ David Bolen \ E-mail: db3l at fitlinxx.com /
| FitLinxx, Inc. \ Phone: (203) 708-5192 |
/ 860 Canal Street, Stamford, CT 06902 \ Fax: (203) 316-5150 \
\-----------------------------------------------------------------------/
More information about the Python-list
mailing list