blob: 2fd9a19786c86bc10196c2b5c87210a5c9f9dd12 [file] [log] [blame]
Guido van Rossum54f22ed2000-02-04 15:10:34 +00001"""Spawn a command with pipes to its stdin, stdout, and optionally stderr.
2
3The normal os.popen(cmd, mode) call spawns a shell command and provides a
4file interface to just the input or output of the process depending on
5whether mode is 'r' or 'w'. This module provides the functions popen2(cmd)
6and popen3(cmd) which return two or three pipes to the spawned command.
7"""
8
Guido van Rossum9a22de11995-01-12 12:29:47 +00009import os
10import sys
11import string
12
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000013MAXFD = 256 # Max number of file descriptors (os.getdtablesize()???)
Guido van Rossum9a22de11995-01-12 12:29:47 +000014
Guido van Rossum0357d021997-08-11 03:27:24 +000015_active = []
16
17def _cleanup():
18 for inst in _active[:]:
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000019 inst.poll()
Guido van Rossum0357d021997-08-11 03:27:24 +000020
21class Popen3:
Guido van Rossum54f22ed2000-02-04 15:10:34 +000022 """Class representing a child process. Normally instances are created
23 by the factory functions popen2() and popen3()."""
24
Guido van Rossumda286661997-09-29 04:04:39 +000025 def __init__(self, cmd, capturestderr=0, bufsize=-1):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000026 """The parameter 'cmd' is the shell command to execute in a
27 sub-process. The 'capturestderr' flag, if true, specifies that
28 the object should capture standard error output of the child process.
29 The default is false. If the 'bufsize' parameter is specified, it
30 specifies the size of the I/O buffers to/from the child process."""
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000031 if type(cmd) == type(''):
32 cmd = ['/bin/sh', '-c', cmd]
33 p2cread, p2cwrite = os.pipe()
34 c2pread, c2pwrite = os.pipe()
35 if capturestderr:
36 errout, errin = os.pipe()
37 self.pid = os.fork()
38 if self.pid == 0:
39 # Child
40 os.close(0)
41 os.close(1)
42 if os.dup(p2cread) <> 0:
43 sys.stderr.write('popen2: bad read dup\n')
44 if os.dup(c2pwrite) <> 1:
45 sys.stderr.write('popen2: bad write dup\n')
46 if capturestderr:
47 os.close(2)
48 if os.dup(errin) <> 2: pass
49 for i in range(3, MAXFD):
50 try:
51 os.close(i)
52 except: pass
53 try:
54 os.execvp(cmd[0], cmd)
55 finally:
56 os._exit(1)
57 # Shouldn't come here, I guess
58 os._exit(1)
59 os.close(p2cread)
60 self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
61 os.close(c2pwrite)
62 self.fromchild = os.fdopen(c2pread, 'r', bufsize)
63 if capturestderr:
64 os.close(errin)
65 self.childerr = os.fdopen(errout, 'r', bufsize)
66 else:
67 self.childerr = None
68 self.sts = -1 # Child not completed yet
69 _active.append(self)
Guido van Rossum54f22ed2000-02-04 15:10:34 +000070
Guido van Rossum0357d021997-08-11 03:27:24 +000071 def poll(self):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000072 """Return the exit status of the child process if it has finished,
73 or -1 if it hasn't finished yet."""
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000074 if self.sts < 0:
75 try:
76 pid, sts = os.waitpid(self.pid, os.WNOHANG)
77 if pid == self.pid:
78 self.sts = sts
79 _active.remove(self)
80 except os.error:
81 pass
82 return self.sts
Guido van Rossum54f22ed2000-02-04 15:10:34 +000083
Guido van Rossum0357d021997-08-11 03:27:24 +000084 def wait(self):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000085 """Wait for and return the exit status of the child process."""
Guido van Rossum45e2fbc1998-03-26 21:13:24 +000086 pid, sts = os.waitpid(self.pid, 0)
87 if pid == self.pid:
88 self.sts = sts
89 _active.remove(self)
90 return self.sts
Guido van Rossumcaa9f231997-04-21 14:15:55 +000091
Fredrik Lundh9ac81f62000-07-09 23:35:24 +000092if hasattr(os, "popen2"):
93 def popen2(cmd, mode='t', bufsize=-1):
94 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
95 specified, it sets the buffer size for the I/O pipes. The file objects
96 (child_stdout, child_stdin) are returned."""
97 w, r = os.popen2(cmd, mode, bufsize)
98 return r, w
99else:
Fredrik Lundhbb7eeff2000-07-09 17:59:32 +0000100 def popen2(cmd, mode='t', bufsize=-1):
101 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
102 specified, it sets the buffer size for the I/O pipes. The file objects
103 (child_stdout, child_stdin) are returned."""
104 if type(mode) is type(0) and bufsize == -1:
105 bufsize = mode
106 mode = 't'
107 assert mode in ('t', 'b')
108 _cleanup()
109 inst = Popen3(cmd, 0, bufsize)
110 return inst.fromchild, inst.tochild
Guido van Rossumcaa9f231997-04-21 14:15:55 +0000111
Fredrik Lundh9ac81f62000-07-09 23:35:24 +0000112if hasattr(os, "popen3"):
113 def popen3(cmd, mode='t', bufsize=-1):
114 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
115 specified, it sets the buffer size for the I/O pipes. The file objects
116 (child_stdout, child_stdin, child_stderr) are returned."""
117 w, r, e = os.popen3(cmd, mode, bufsize)
118 return r, w, e
119else:
Fredrik Lundhbb7eeff2000-07-09 17:59:32 +0000120 def popen3(cmd, mode='t', bufsize=-1):
121 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
122 specified, it sets the buffer size for the I/O pipes. The file objects
123 (child_stdout, child_stdin, child_stderr) are returned."""
124 if type(mode) is type(0) and bufsize == -1:
125 bufsize = mode
126 mode = 't'
127 assert mode in ('t', 'b')
128 _cleanup()
129 inst = Popen3(cmd, 1, bufsize)
130 return inst.fromchild, inst.tochild, inst.childerr
131
Fredrik Lundh9ac81f62000-07-09 23:35:24 +0000132if hasattr(os, "popen4"):
133 def popen4(cmd, mode='t', bufsize=-1):
134 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
135 specified, it sets the buffer size for the I/O pipes. The file objects
136 (child_stdout_stderr, child_stdin) are returned."""
137 w, r = os.popen4(cmd, mode, bufsize)
138 return r, w
139else:
140 pass # not yet on unix
Guido van Rossum0357d021997-08-11 03:27:24 +0000141
142def _test():
Tim Peters84f28db2000-08-20 05:57:36 +0000143 cmd = "cat"
Guido van Rossum0357d021997-08-11 03:27:24 +0000144 teststr = "abc\n"
Tim Peters84f28db2000-08-20 05:57:36 +0000145 resultstr = teststr
146 if os.name == "nt":
147 cmd = "more"
148 resultstr = "\n" + resultstr
Guido van Rossum0357d021997-08-11 03:27:24 +0000149 print "testing popen2..."
Tim Peters84f28db2000-08-20 05:57:36 +0000150 r, w = popen2(cmd)
Guido van Rossum0357d021997-08-11 03:27:24 +0000151 w.write(teststr)
152 w.close()
Tim Peters84f28db2000-08-20 05:57:36 +0000153 assert r.read() == resultstr
Guido van Rossum0357d021997-08-11 03:27:24 +0000154 print "testing popen3..."
Fredrik Lundhbb7eeff2000-07-09 17:59:32 +0000155 try:
Tim Peters84f28db2000-08-20 05:57:36 +0000156 r, w, e = popen3([cmd])
Fredrik Lundhbb7eeff2000-07-09 17:59:32 +0000157 except:
Tim Peters84f28db2000-08-20 05:57:36 +0000158 r, w, e = popen3(cmd)
Guido van Rossum0357d021997-08-11 03:27:24 +0000159 w.write(teststr)
160 w.close()
Tim Peters84f28db2000-08-20 05:57:36 +0000161 assert r.read() == resultstr
Guido van Rossum0357d021997-08-11 03:27:24 +0000162 assert e.read() == ""
Guido van Rossum068d5721999-04-20 12:27:31 +0000163 for inst in _active[:]:
164 inst.wait()
Guido van Rossum0357d021997-08-11 03:27:24 +0000165 assert not _active
166 print "All OK"
167
168if __name__ == '__main__':
169 _test()