有没有办法检查子进程是否仍在运行?

问题描述 投票:0回答:6

我正在 Python 中使用

subprocess.Popen
启动多个子进程。 我想检查一个这样的过程是否已经完成。我找到了两种检查子流程状态的方法,但这两种方法似乎都强制该流程完成。 一种是使用
process.communicate()
并打印返回码,如下所述:在 Python 中使用 subprocess.Popen 检查进程状态。 另一种方法是简单地调用
process.wait()
并检查它是否返回 0。

有没有办法检查进程是否仍在运行而不等待它完成(如果是)?

python subprocess
6个回答
160
投票

问题:...一种检查进程是否仍在运行的方法...

你可以这样做:

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


10
投票

正在做

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 上,您需要查找字符串命令。

这与以下堆栈溢出线程中讨论的方法相同:

在JAVA中使用PID验证进程是否正在运行

注意: 如果您使用这种方法,请记住将命令调用包装在 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() 开始告诉我该过程确实已完成。


3
投票

正如其他答案所建议的那样,

None
是当子进程尚未返回代码时“返回代码”的设计占位符。

returncode 属性的文档支持了这一点(强调我的):

子返回代码,由

poll()
wait()
设置(由
communicate()
间接设置)。
None
值表示 进程尚未终止

负值 -N 表示子进程被信号 N 终止(仅限 POSIX)。

出现此

None
值的一个有趣的地方是使用
timeout
参数进行 waitcommunicate 时。

如果进程在超时秒后没有终止,则会引发

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
传递实际返回代码的主要流程。


0
投票

如果你不介意安装 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

0
投票

为了让它在多个平台上工作,我使用这个:

    # -------------------
    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 个平台。


-3
投票

您可以使用 subprocess.check_output 来查看您的输出。

试试这个代码:

import subprocess
subprocess.check_output(['your command here'], shell=True, stderr=subprocess.STDOUT)

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.