返回

Python subprocess:可靠关闭外部程序(含示例)

windows

利用 Python subprocess 打开与关闭程序

程序自动化时,常常需要利用 Python 控制外部应用。subprocess 模块为此提供了强大的工具。一个常见的场景就是打开特定程序,并在稍后将其关闭。使用 subprocess.Popen 可以方便地打开程序,但要确保可靠地关闭它们,情况就可能变得复杂。

subprocess.Popen 可以启动一个外部程序作为子进程。当你使用 terminate()kill() 方法试图关闭这个子进程时,你会发现有些程序能被顺利关闭,而另一些则无动于衷。这背后有着深刻的机制,了解这些机制是有效控制外部程序的关键。

问题根源:进程与子进程

通常,应用程序并非都以单个进程的形式存在。有些应用(如 Firefox)会创建一个或多个子进程。 当你通过 subprocess 启动一个这样的应用,Popen 对象会关联到主进程,而不会自动关联到所有的子进程。 当调用 terminate()kill() 时,你实际操作的仅仅是主进程,那些依附其上的子进程仍然在运行,所以你会看到程序看起来并未关闭。

简单来说,当你启动 Notepad 时,通常会直接创建一个主进程。关闭这个主进程就等于关闭 Notepad。而对于 Firefox 这种复杂的应用程序,主进程只是一个“管理者”,实际承担工作的是许多个独立的子进程,因此直接杀死父进程不足以完全结束所有程序运行。

解决方案一:任务管理器

Windows 下可以通过命令行调用 taskkill 命令。 taskkill 提供更灵活的进程控制方式,它可以强制终止指定程序及其所有的子进程。这为彻底关闭程序提供了强有力的支持。

操作步骤:

  1. 找到要关闭程序的确切可执行文件名,例如 firefox.exe
  2. 利用 subprocess 执行 taskkill 命令。使用 /im 参数指定可执行文件名,使用 /f 参数强制关闭。

代码示例:

import subprocess

def close_program(program_name):
    """使用 taskkill 关闭指定名称的程序及其所有子进程."""
    try:
        subprocess.run(["taskkill", "/im", program_name, "/f"], check=True, capture_output=True)
        print(f"{program_name} 已成功关闭.")
    except subprocess.CalledProcessError as e:
        print(f"关闭 {program_name} 时发生错误: {e}")


#  示例使用:
close_program("firefox.exe")

该代码先定义一个函数close_program接收可执行文件名作为参数,并构建taskkill 命令。当执行这个函数的时候,会使用taskkill /im <可执行文件名> /f 命令强制结束所有与指定程序名有关的进程,如果操作失败会返回相应的报错信息。

解决方案二: 使用 psutil 模块

psutil 是一个跨平台的库,提供获取系统运行信息和管理进程的功能。 利用它可以方便地找到指定程序的主进程以及所有子进程,然后将其逐一关闭。

安装:

pip install psutil

操作步骤:

  1. 通过进程名称(例如 “firefox.exe”) 查找目标程序主进程。
  2. 遍历目标程序所有子进程。
  3. 逐个终止找到的进程。

代码示例:

import psutil
import os
import time

def close_program_psutil(program_name):
    """使用 psutil 关闭指定名称的程序及其所有子进程"""
    try:
       for proc in psutil.process_iter(['pid', 'name']):
           if program_name in proc.info['name']:
               parent_process = psutil.Process(proc.info['pid'])
               for child in parent_process.children(recursive=True):
                    child.terminate()
               parent_process.terminate()
       print(f"{program_name} (及相关进程)已关闭")
    except psutil.NoSuchProcess as e:
        print(f"没有找到相关的进程: {e}")


close_program_psutil("firefox.exe")

这段代码首先循环所有运行的进程,找到名称中包含我们给定的程序名称(例如”firefox.exe”)。然后找出找到的父进程及其所有的子进程,最后使用terminate函数结束它们。

额外的安全建议

  • 谨慎使用 killtaskkill: 这些操作强制终止进程,可能导致数据丢失,或影响其他正在运行的程序,确保只对已知且需要关闭的程序使用。
  • 进程名称的匹配: 注意程序的可执行文件名,有时可执行文件名称和软件本身的名称并不相同,避免错误操作导致关闭其他程序。
  • 错误处理: 添加适当的错误处理机制,防止出现无法预计的状况。 比如 try catch,让程序在出错的时候也能得到一些提醒。
  • 跨平台兼容性: 注意 taskkill 是Windows 下的命令,在其他的平台可能不可用,因此编写代码的时候需要注意平台兼容问题。可以使用psutil 这样的库以实现更好的跨平台支持。

正确地使用 Python 的 subprocess 模块控制外部程序需要对进程概念有一定了解, 针对不同的场景选择合适的方法可以确保程序操作的可靠性和安全性。 taskkill 命令和 psutil 模块, 可以应对多数情况下程序的终止。