问题
I have the following code in a loop:
while true:
# Define shell_command
p1 = Popen(shell_command, shell=shell_type, stdout=PIPE, stderr=PIPE, preexec_fn=os.setsid)
result = p1.stdout.read();
# Define condition
if condition:
break;
where shell_command
is something like ls
(it just prints stuff).
I have read in different places that I can close/terminate/exit a Popen
object in a variety of ways, e.g. :
p1.stdout.close()
p1.stdin.close()
p1.terminate
p1.kill
My question is:
- What is the proper way of closing a
subprocess
object once we are done using it? - Considering the nature of my script, is there a way to open a
subprocess
object only once and reuse it with different shell commands? Would that be more efficient in any way than opening newsubprocess
objects each time?
Update
I am still a bit confused about the sequence of steps to follow depending on whether I use p1.communicate()
or p1.stdout.read()
to interact with my process.
From what I understood in the answers and the comments:
If I use p1.communicate()
I don't have to worry about releasing resources, since communicate()
would wait until the process is finished, grab the output and properly close the subprocess
object
If I follow the p1.stdout.read()
route (which I think fits my situation, since the shell command is just supposed to print stuff) I should call things in this order:
p1.wait()
p1.stdout.read()
p1.terminate()
Is that right?
回答1:
What is the proper way of closing a subprocess object once we are done using it?
stdout.close()
and stdin.close()
will not terminate a process unless it exits itself on end of input or on write errors.
.terminate() and .kill() both do the job, with kill
being a bit more "drastic" on POSIX systems, as SIGKILL
is sent, which cannot be ignored by the application. Specific differences are explained in this blog post, for example. On Windows, there's no difference.
Also, remember to .wait() and to close the pipes after killing a process to avoid zombies and force the freeing of resources.
A special case that is often encountered are processes which read from STDIN and write their result to STDOUT, closing themselves when EOF is encountered. With these kinds of programs, it's often sensible to use subprocess.communicate:
>>> p = Popen(["sort"], stdin=PIPE, stdout=PIPE)
>>> p.communicate("4\n3\n1")
('1\n3\n4\n', None)
>>> p.returncode
0
This can also be used for programs which print something and exit right after:
>>> p = Popen(["ls", "/home/niklas/test"], stdin=PIPE, stdout=PIPE)
>>> p.communicate()
('file1\nfile2\n', None)
>>> p.returncode
0
Considering the nature of my script, is there a way to open a subprocess object only once and reuse it with different shell commands? Would that be more efficient in any way than opening new subprocess objects each time?
I don't think the subprocess
module supports this and I don't see what resources could be shared here, so I don't think it would give you a significant advantage.
回答2:
Considering the nature of my script, is there a way to open a subprocess object only once and reuse it with different shell commands?
Yes.
#!/usr/bin/env python
from __future__ import print_function
import uuid
import random
from subprocess import Popen, PIPE, STDOUT
MARKER = str(uuid.uuid4())
shell_command = 'echo a'
p = Popen('sh', stdin=PIPE, stdout=PIPE, stderr=STDOUT,
universal_newlines=True) # decode output as utf-8, newline is '\n'
while True:
# write next command
print(shell_command, file=p.stdin)
# insert MARKER into stdout to separate output from different shell_command
print("echo '%s'" % MARKER, file=p.stdin)
# read command output
for line in iter(p.stdout.readline, MARKER+'\n'):
if line.endswith(MARKER+'\n'):
print(line[:-len(MARKER)-1])
break # command output ended without a newline
print(line, end='')
# exit on condition
if random.random() < 0.1:
break
# cleanup
p.stdout.close()
if p.stderr:
p.stderr.close()
p.stdin.close()
p.wait()
Put while True
inside try: ... finally:
to perform the cleanup in case of exceptions. On Python 3.2+ you could use with Popen(...):
instead.
Would that be more efficient in any way than opening new subprocess objects each time?
Does it matter in your case? Don't guess. Measure it.
回答3:
The "correct" order is:
Create a thread to read stdout (and a second one to read stderr, unless you merged them into one).
Write commands to be executed by the child to stdin. If you're not reading stdout at the same time, writing to stdin can block.
Close stdin (this is the signal for the child that it can now terminate by itself whenever it is done)
When stdout returns EOF, the child has terminated. Note that you need to synchronize the stdout reader thread and your main thread.
call
wait()
to see if there was a problem and to clean up the child process
If you need to stop the child process for any reason (maybe the user wants to quit), then you can:
Close stdin if the child terminates when it reads EOF.
Kill the with
terminate()
. This is the correct solution for child processes which ignore stdin.If the child doesn't respond, try
kill()
In all three cases, you must call wait()
to clean up the dead child process.
回答4:
- Depends on what you expect the process to do; you should always call
p1.wait()
in order to avoid zombies. Other steps depend on the behaviour of the subprocess; if it produces any output, you should consume the output (e.g.p1.read()
...but this would eat lots of memory) and only then call thep1.wait()
; or you may wait for some timeout and callp1.terminate()
to kill the process if you think it doesn't work as expected, and possible callp1.wait()
to clean the zombie.
Alternatively, p1.communicate(...)
would do the handling if io and waiting for you (not the killing).
- Subprocess objects aren't supposed to be reused.
来源:https://stackoverflow.com/questions/9585874/proper-way-of-re-using-and-closing-a-subprocess-object