blob: 73ff77c9516c46b913b4c854a49d943a9896b25c [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 Lundhbb7eeff2000-07-09 17:59:32 +000092try:
93 from os import popen2
94except NameError:
95 def popen2(cmd, mode='t', bufsize=-1):
96 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
97 specified, it sets the buffer size for the I/O pipes. The file objects
98 (child_stdout, child_stdin) are returned."""
99 if type(mode) is type(0) and bufsize == -1:
100 bufsize = mode
101 mode = 't'
102 assert mode in ('t', 'b')
103 _cleanup()
104 inst = Popen3(cmd, 0, bufsize)
105 return inst.fromchild, inst.tochild
Guido van Rossumcaa9f231997-04-21 14:15:55 +0000106
Fredrik Lundhbb7eeff2000-07-09 17:59:32 +0000107try:
108 from os import popen3
109except NameError:
110 def popen3(cmd, mode='t', bufsize=-1):
111 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
112 specified, it sets the buffer size for the I/O pipes. The file objects
113 (child_stdout, child_stdin, child_stderr) are returned."""
114 if type(mode) is type(0) and bufsize == -1:
115 bufsize = mode
116 mode = 't'
117 assert mode in ('t', 'b')
118 _cleanup()
119 inst = Popen3(cmd, 1, bufsize)
120 return inst.fromchild, inst.tochild, inst.childerr
121
122try:
123 from os import popen4
124except NameError:
125 pass # not on unix
Guido van Rossum0357d021997-08-11 03:27:24 +0000126
127def _test():
128 teststr = "abc\n"
129 print "testing popen2..."
130 r, w = popen2('cat')
Fredrik Lundhbb7eeff2000-07-09 17:59:32 +0000131 print r, w
Guido van Rossum0357d021997-08-11 03:27:24 +0000132 w.write(teststr)
133 w.close()
134 assert r.read() == teststr
135 print "testing popen3..."
Fredrik Lundhbb7eeff2000-07-09 17:59:32 +0000136 try:
137 r, w, e = popen3(['cat'])
138 except:
139 r, w, e = popen3('cat')
140 print r, w, e
Guido van Rossum0357d021997-08-11 03:27:24 +0000141 w.write(teststr)
142 w.close()
143 assert r.read() == teststr
144 assert e.read() == ""
Guido van Rossum068d5721999-04-20 12:27:31 +0000145 for inst in _active[:]:
146 inst.wait()
Guido van Rossum0357d021997-08-11 03:27:24 +0000147 assert not _active
148 print "All OK"
149
150if __name__ == '__main__':
151 _test()