我正在 Python 中使用
subprocess.Popen
启动多个子进程。
我想检查一个这样的过程是否已经完成。我找到了两种检查子流程状态的方法,但这两种方法似乎都强制该流程完成。
一种是使用 process.communicate()
并打印返回码,如下所述:在 Python 中使用 subprocess.Popen 检查进程状态。
另一种方法是简单地调用 process.wait()
并检查它是否返回 0。
有没有办法检查进程是否仍在运行而不等待它完成(如果是)?
问题:...一种检查进程是否仍在运行的方法...
你可以这样做:
p = subprocess.Popen(...
"""
A None value indicates that the process hasn't terminated yet.
"""
poll = p.poll()
if poll is None:
# p.subprocess is alive
Python » 3.6.1 文档 popen-objects
使用Python测试:3.4.2
正在做
myProcessIsRunning = poll() is None
正如主要答案所建议的,是检查进程是否正在运行的推荐方法和最简单的方法。 (它也适用于 jython)
如果您手头没有流程实例,请检查它。 然后使用操作系统TaskList/Ps进程。
在 Windows 上,我的命令将如下所示:
filterByPid = "PID eq %s" % pid
pidStr = str(pid)
commandArguments = ['cmd', '/c', "tasklist", "/FI", filterByPid, "|", "findstr", pidStr ]
这本质上与以下命令行执行相同的操作:
cmd /c "tasklist /FI "PID eq 55588" | findstr 55588"
在 Linux 上,我使用以下方法进行完全相同的操作:
pidStr = str(pid)
commandArguments = ['ps', '-p', pidStr ]
ps 命令将返回错误代码 0 / 1,具体取决于是否找到进程。在 Windows 上,您需要查找字符串命令。
这与以下堆栈溢出线程中讨论的方法相同:
注意: 如果您使用这种方法,请记住将命令调用包装在 try/ except 中:
try:
foundRunningProcess = subprocess.check_output(argumentsArray, **kwargs)
return True
except Exception as err:
return False
注意,如果您使用 VS Code 并使用纯 Python 和 Jython 进行开发,请务必小心。 在我的环境中,我有一种错觉,认为 poll() 方法不起作用,因为我怀疑必须已经结束的进程确实正在运行。 这个过程启动了Wildfly。在我要求 Wildfly 停止之后,shell 仍在等待用户“按任意键继续......”。
为了完成这个过程,在纯Python中,以下代码正在运行:
process.stdin.write(os.linesep)
在 jython 上,我必须修复此代码,如下所示:
print >>process.stdin, os.linesep
有了这个差异,这个过程确实完成了。 jython.poll() 开始告诉我该过程确实已完成。
正如其他答案所建议的那样,
None
是当子进程尚未返回代码时“返回代码”的设计占位符。
returncode 属性的文档支持了这一点(强调我的):
子返回代码,由
和poll()
设置(由wait()
间接设置)。communicate()
值表示 进程尚未终止。None
负值 -N 表示子进程被信号 N 终止(仅限 POSIX)。
出现此
None
值的一个有趣的地方是使用 timeout
参数进行 wait 或 communicate 时。
如果进程在超时秒后没有终止,则会引发
异常。TimeoutExpired
如果您捕获该异常并检查 returncode 属性,它确实会是
None
import subprocess
with subprocess.Popen(['ping','127.0.0.1']) as p:
try:
p.wait(timeout=3)
except subprocess.TimeoutExpired:
assert p.returncode is None
如果您查看子流程的源代码,您可以看到引发的异常。 https://github.com/python/cpython/blob/47be7d0108b4021ede111dbd15a095c725be46b7/Lib/subprocess.py#L1930-L1931
如果您在该源中搜索
self.returncode is
,您会发现许多用途,库作者依靠 None
返回代码设计来推断应用程序是否正在运行。 returncode
属性是 initialized 到 None
,并且仅在少数位置发生变化,这是调用 _handle_exitstatus
传递实际返回代码的主要流程。
如果你不介意安装 comtypes(纯 Python),你可以使用这个(针对 Python 3.10 / Windows 10 进行测试):
import subprocess
from time import perf_counter
import comtypes.client
def is_process_alive(pid):
WMI = comtypes.client.CoGetObject("winmgmts:")
processes = WMI.InstancesOf("Win32_Process")
if [True for pp in processes if pp.Properties_("ProcessID").Value == pid]: # checks all pids there are
return True
return False
p = subprocess.Popen("ping 8.8.8.8", shell=True)
start = perf_counter()
while is_process_alive(p.pid):
print(f"{p.pid} is alive", end="\r")
print(f"{p.pid} is not alive - execution time: {perf_counter()-start}")
start = perf_counter()
p = subprocess.Popen("ping 8.8.8.8", shell=False)
while is_process_alive(p.pid):
print(f"{p.pid} is alive", end="\r")
print(f"{p.pid} is not alive - execution time: {perf_counter()-start}")
#output
Pinging 8.8.8.8 with 32 bytes of data:
Reply from 8.8.8.8: bytes=32 time=9ms TTL=119
Reply from 8.8.8.8: bytes=32 time=11ms TTL=119
Reply from 8.8.8.8: bytes=32 time=9ms TTL=119
Reply from 8.8.8.8: bytes=32 time=10ms TTL=119
Ping statistics for 8.8.8.8:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 9ms, Maximum = 11ms, Average = 9ms
18404 is not alive - execution time: 3.170662900000025
Pinging 8.8.8.8 with 32 bytes of data:
Reply from 8.8.8.8: bytes=32 time=10ms TTL=119
Reply from 8.8.8.8: bytes=32 time=9ms TTL=119
Reply from 8.8.8.8: bytes=32 time=10ms TTL=119
Reply from 8.8.8.8: bytes=32 time=9ms TTL=119
Ping statistics for 8.8.8.8:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 9ms, Maximum = 10ms, Average = 9ms
6788 is not alive - execution time: 3.1898495000023104
为了让它在多个平台上工作,我使用这个:
# -------------------
def _raw_run_cmd(self, cmd):
self._log_line(f'running "{cmd}" ...')
shell = True
if OsSpecific.os_name == 'win':
cmd = ['c:/msys64/usr/bin/bash', '-i', '-c', cmd]
proc = subprocess.Popen(cmd,
shell=shell,
bufsize=0,
universal_newlines=True,
stdin=None,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
lineno = 1
lines = []
while True:
line = proc.stdout.readline()
print(f'@=@= line="{line}"') <== for debug only
if proc.poll() is not None:
break
if line:
line = line.rstrip()
lines.append(line)
self._log_lineno(lineno, line)
lineno += 1
proc.wait()
rc = proc.returncode
return rc, lines
在 Ubuntu 22.04 和 macOS (python3.10) 上,我检查了空行 (""),效果很好:
@=@= line=" do_ut: overall rc=0 # <= handles empty lines with \n
"
@@@@ 164] do_ut: overall rc=0
@=@= line="" # <= get only 1 empty line
但是在 Windows 上(在 MSYS2 中运行 python3),我得到了虚假的空行,因此进程循环退出得太快了。
@=@= line=" do_ut: overall rc=0 # <= handles empty lines with \n
"
@@@@ 164] do_ut: overall rc=0
@=@= line="" # <= get multiple empty lines
@=@= line=""
@=@= line=""
@=@= line=""
@=@= line=""
@=@= line=""
@=@= line=""
@=@= line=""
@=@= line=""
@=@= line=""
@=@= line=""
这表明即使进程没有任何内容, proc.poll() 仍然可以返回 None 。我认为 proc.wait() 可以处理这种情况,但脚本挂起等待一些未知的事情。
无论如何,上述内容适用于所有 3 个平台。
您可以使用 subprocess.check_output 来查看您的输出。
试试这个代码:
import subprocess
subprocess.check_output(['your command here'], shell=True, stderr=subprocess.STDOUT)
希望这有帮助!