Q: how to "spawn" an external cmd and catch its stdin,stdout,stderr (on Win32)
Pat Knight
p.knight at ktgroup.co.uk
Fri Oct 22 06:00:58 EDT 1999
Sorry if this article is a little long - it contains about 90 lines of source.
In article <380F22AA.5EF2D410 at RUS.Uni-Stuttgart.De>, Andreas Rozek
<Andreas.Rozek at RUS.Uni-Stuttgart.De> wrote:
>is there any possibility to launch an external program (without repla-
>cing the current process - i.e., no os.exec* call alone) and catch the
>stdin, stderr and stdout IO streams (the latter also renders spawn and
>fork together with exec* unusable)?
On UNIX, you do need to go with fork/exec, also using pipes to set up the
output streams of the child process. The commands module in the Python
standard library does this for you.
>
>A platform-independent solution would be preferrable - however, it's
>more important for me to get it for Windows 98!
Sadly, fork/exec doesn't work on any of the Windows platforms. Happily, Mark
Hammond's fantastic Pythonwin package provides access to the Win32
CreateProcess API together with the pipe handling APIs you'll need. You can
get Pythonwin from Mark's Python Starship page at
http://starship.python.net/crew/mhammond/
If the child process you want to run is a console application or a .bat file,
you'll need a further step to stop a DOS console popping up and to correctly
detect termination of the child process. Microsoft have documented this and
provided sample ('C') code in various knowledge base articles:
Redirection Issues on Windows 95 MS-DOS Applications
Last reviewed: April 10, 1997
Article ID: Q150956
Spawn Console Processes with Redirected Standard Handles
Last reviewed: September 9, 1998
Article ID: Q190351
Article Q150956 gives sample source code for a program called conspawn which
will run the target program and pass output back to you. You'll need to check
that it does what you want with command line arguments and all the I/O streams
you care about.
Here's the code we use after we found all this out.We use it on NT 4.x, Win95
and Win98. YMMV. You'll probably want a very recent version of Pythonwin.
Cheers,
Pat
Pat Knight, http://www.wideawake.co.uk
import win32pipe
import pywintypes
import win32process
import win32con
import win32api
import win32file
import string
import types
ver = win32api.GetVersionEx()[3] # distinguish NT and Win95/98
if ver == 2: # we're on NT
PROCESS_RUNNER = 'cmd.exe /c'
else:
PROCESS_RUNNER = '<name of program based on MS conspawn.exe>'
def mkarg(arg):
result = string.replace(arg,'"','"""')
if string.count(arg, ' ') > 0:
return ' "' + result + '"'
else:
return ' ' + result
def mkcommand(command, args):
# On 95 waspawn.exe is used to run child processes, it prevents a bug
# when running dos apps/batch files, a console would otherwise be
# created to run the batch file in, and this would hang around
# afterwards leaving WA waiting for it. Also stops a console from
# popping up.
# On NT cmd.exe is used since otherwise we couldn't invoke DOS
# commands. This also prevents console windows from popping up.
our_command = PROCESS_RUNNER + mkarg(command)
if type(args) == types.StringType:
our_command = our_command + mkarg(args)
else:
for arg in args:
our_command = our_command + mkarg(arg)
return our_command
def alt_popen(command_line):
start_up_info = win32process.STARTUPINFO()
security_attributes = pywintypes.SECURITY_ATTRIBUTES()
security_attributes.bInheritHandle = 1
# create pipe for stdout of the child
Pyh_read_end, Pyh_write_end = win32pipe.CreatePipe(security_attributes, 0)
# Make our end of the pipe uninheritable by duplicating it
# with inheritance set to FALSE
Pyh_read_end2 = win32api.DuplicateHandle(
win32api.GetCurrentProcess(), Pyh_read_end,
win32api.GetCurrentProcess(), 0, 0, win32con.DUPLICATE_SAME_ACCESS)
win32file.CloseHandle(Pyh_read_end)
# duplicate the file handle to act as the standard error of the child.
Pyh_write_end2 = win32api.DuplicateHandle(
win32api.GetCurrentProcess(), Pyh_write_end,
win32api.GetCurrentProcess(), 0, 0, win32con.DUPLICATE_SAME_ACCESS)
# Create a pipe to be used for the childs stdin
Pyh_read_end_for_stdin, Pyh_write_end_for_stdin = win32pipe.CreatePipe(
security_attributes, 0)
# Make our end of the pipe uninheritable by duplicating it
# with inheritance set to FALSE
Pyh_write_end_for_stdin2 = win32api.DuplicateHandle(
win32api.GetCurrentProcess(),
Pyh_write_end_for_stdin,
win32api.GetCurrentProcess(),
0, 0, win32con.DUPLICATE_SAME_ACCESS)
win32file.CloseHandle(Pyh_write_end_for_stdin)
# set the STARTUPINFO fields
start_up_info.dwFlags = 1 |256 #win32process.STARTF_USESTDHANDLES
start_up_info.wShowWindow = 0; # SW_HIDE
start_up_info.hStdOutput = Pyh_write_end
start_up_info.hStdError = Pyh_write_end2
start_up_info.hStdInput = Pyh_read_end_for_stdin
# create the process
process_info = win32process.CreateProcess(None, command_line,
None, None, 1, 0, None,
None, start_up_info)
win32file.CloseHandle(Pyh_write_end2)
win32file.CloseHandle(Pyh_read_end_for_stdin)
return Pyh_read_end2, process_info[0]
More information about the Python-list
mailing list