我在循环中有以下代码:
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;
其中shell_command
类似于ls
(它只是打印内容)。
我已经在不同的地方阅读过我可以通过各种方式关闭/终止/退出Popen
对象,例如:
p1.stdout.close()
p1.stdin.close()
p1.terminate
p1.kill
我的问题是:
subprocess
对象的正确方法是什么? subprocess
对象并使用不同的shell命令重用它?除了每次打开新的subprocess
对象之外,这会更有效吗?我仍然对要遵循的步骤顺序感到困惑,具体取决于我是使用p1.communicate()
还是p1.stdout.read()
来与我的流程进行互动。
根据我在答案和评论中的理解:
如果我使用p1.communicate()
,我不必担心释放资源,因为communicate()
会等到进程完成,抓取输出并正确关闭subprocess
对象< / p>
如果我遵循p1.stdout.read()
路线(我认为适合我的情况,因为shell命令只是打印东西)我应该按顺序调用东西:
p1.wait()
p1.stdout.read()
p1.terminate()
是吗?
答案 0 :(得分:9)
完成使用后,关闭子进程对象的正确方法是什么?
stdout.close()
和stdin.close()
不会终止进程,除非它在输入结束时或写入错误时退出。
.terminate()
和.kill()
都做了这项工作,kill
在POSIX系统上更加“激烈”,因为SIGKILL
被发送,这是不可忽视的应用程序。例如,解释具体差异in this blog post。在Windows上,没有区别。
此外,请记住.wait()
并在杀死进程后关闭管道以避免僵尸并强制释放资源。
经常遇到的特殊情况是从STDIN读取并将结果写入STDOUT的进程,在遇到EOF时自行关闭。使用这些类型的程序,使用subprocess.communicate
:
>>> p = Popen(["sort"], stdin=PIPE, stdout=PIPE)
>>> p.communicate("4\n3\n1")
('1\n3\n4\n', None)
>>> p.returncode
0
这也可以用于打印某些内容的程序,然后立即退出:
>>> p = Popen(["ls", "/home/niklas/test"], stdin=PIPE, stdout=PIPE)
>>> p.communicate()
('file1\nfile2\n', None)
>>> p.returncode
0
考虑到我的脚本的性质,有没有办法只打开一个子进程对象并使用不同的shell命令重用它?除了每次打开新的子进程对象之外,它会更有效吗?
我不认为subprocess
模块支持这个,我不知道这里可以分享哪些资源,所以我认为它不会给你带来显着的优势。
答案 1 :(得分:2)
“正确”的顺序是:
创建一个线程来读取stdout(第二个读取stderr,除非你将它们合并为一个)。
将要执行的命令写入stdin。如果您没有同时读取stdout,写入stdin可能会阻止。
关闭stdin(这是孩子的信号,它现在可以自动终止)
当stdout返回EOF时,孩子已经终止。请注意,您需要同步stdout阅读器线程和主线程。
致电wait()
以查看是否存在问题并清理子流程
如果您因任何原因需要停止子进程(可能是用户想要退出),那么您可以:
如果孩子在读取EOF时终止,则关闭stdin。
用terminate()
杀死。这是忽略stdin的子进程的正确解决方案。
如果孩子没有回应,请尝试kill()
在所有这三种情况下,您都必须致电wait()
来清理死亡的子进程。
答案 2 :(得分:2)
考虑到我的脚本的性质,有没有办法只打开一个子进程对象并使用不同的shell命令重用它?
是
#!/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()
将while True
置于try: ... finally:
内以在发生异常时执行清理。在Python 3.2+上,您可以使用with Popen(...):
代替。
除了每次打开新的子进程对象之外,它会更有效吗?
你的情况有关系吗?不要猜。测量它。
答案 3 :(得分:1)
p1.wait()
以避免僵尸。其他步骤取决于子进程的行为;如果它产生任何输出,你应该消耗输出(例如p1.read()
...但这会占用大量内存)然后才调用p1.wait()
;或者你可以等待一段时间超时,如果你认为它没有按预期工作,可以调用p1.terminate()
来终止进程,并可以调用p1.wait()
清除僵尸。或者,如果io等待你({而不是杀戮),p1.communicate(...)
会进行处理。