Python subprocess与shell=True:重定向和平台无关的子进程终止

6
我在使用Python subprocess模块时遇到了麻烦(该模块应该是一个统一/跨平台的抽象,但是不要让我开始 :))。
所以我想要的简单事情是:
  • 启动外部(stdio)应用程序(可能使用subprocess),其中我使用shell样式的重定向(例如'./myapp > stdout_log > stderr_log')
  • 基本上我想要执行一个shell命令行,因此我必须为subprocess.Popen()指定shell=True(否则命令行中的重定向将无法工作)
  • 我想以异步方式启动此命令行(因此它作为独立子进程运行,但我的Python进程不会等待其完成)
  • (我的父Python进程偶尔会查看子进程的日志以提取信息,但这与问题无关)
  • 如果我的父Python进程决定,它应该能够终止此子进程。
现在,我的主要问题是:
  • 我基本上被迫使用shell=True,以使重定向起作用
  • 在父python进程中处理子进程的stdout/stderr不是一个选项,因为我找不到以非等待方式执行此操作的功能(而父python进程必须在子进程运行时执行其他任务)
  • 如果使用shell=True,则subprocess.kill()仅会终止shell而不是子进程
  • 我需要一种可靠的子进程终止方法,适用于任何平台(但至少适用于linux和windows)

我希望我表述得足够清楚。感谢您提供的任何提示/建议 - 我刚刚花了一整天时间研究subprocess,并且我认为它远非跨平台或简单 :( (但也许只是我)

更新(2010-10-13):

如果启动子进程(即使shell=False),则subprocess.Popen.kill()函数仅会杀死该子进程(因此如果有任何“孙子”进程,它们将不会被终止。)

我读到关于使用preexec_fn参数在所有子进程中设置sid的方法,但这只适用于Unix系统:超时一个子进程


4个回答

4

我最近也遇到了类似的情况,我创建了一个使用shell=True的python子进程,并且后来需要将其终止。然而,由于 shell 参数的存在,'真实' 进程是 shell 的子进程,即主进程的孙子进程。杀死子 shell 进程并不会自动终止孙子进程。

在我的顶级 python 脚本中:

childProcess = subprocess.Popen('python other.py', shell=True)

为了杀掉 shell 和 "真正的工作" 子进程,我做了以下操作:
subprocess.call("ps -ef | awk '$3 == \"" + str(childProcess.pid) + "\" {print $2}' | xargs kill -9", shell=True)
childProcess.kill()

第一行代码会杀掉所有子进程(基于ps -ef的父子关系),第二行代码会杀掉子进程本身。有趣的是,在Mac OSX上这不是必须的,因为在Mac上孙子进程会接管原始的子shell进程ID。

4
上次我遇到类似的情况时,发现最简单且实际上唯一的解决方法是启动一个线程来处理子进程。你可以采用不同的方法,比如解析 shell 风格命令的管道并在 Python 代码中执行它们(由于阻塞而无法使用),这将同时解决杀死进程的问题。基本上,线程封装似乎是可行的方法。
遗憾的是,我对 Windows 平台上的 subprocess 的经验非常有限,因为它有很多自己的小问题。subprocess 在各方面都有很多缺陷,尽管存在着它应该替代的 popen、popen2 等模块,但它必须做得还不错。

是的,我可能会采用线程方式,因为我不想在这条路上浪费更多时间...谢谢你的提示。如果有任何安慰的话,在Linux上的子进程也有它自己的怪癖。 :( - riviera

3

回答一些问题:


2
逐个解决你的问题:

我基本上被迫使用shell=True,才能让重定向起作用。

你不能只是使用 stdoutstderr 参数吗?

out_log = open("stdout_log", "w")
err_log = open("stderr_log", "w")
subproc = subprocess.popen(..., stdout=out_log, stderr=err_log, ...)

在父Python进程中处理子进程的标准输出/错误流不是一个选项,因为我找不到以非等待方式执行此操作的功能(而父Python进程在子进程运行时必须执行其他任务)。
这是由于Windows造成的。在类Unix操作系统上,您只需使用“select”模块即可。Windows只能在套接字上选择,而不能在文件上进行选择。
如果使用shell=True,则subprocess.kill()仅终止shell而不终止子进程。
因为当shell=True时,shell就是子进程,而命令是它的子进程。
我需要一种可靠的子进程终止方法,适用于任何平台(但至少适用于Linux和Windows)。
可靠的子进程终止方法是否存在于Windows中?据我所知,即使任务管理器的“结束任务”也不是100%可靠的。在Linux中,您可能无法杀死通过崩溃的驱动程序打开文件的进程。
正如Stigma所说,对于Windows支持,您需要使用线程作为子进程代理。此外,您应该尝试使用shell=False运行,如果无法,请详细说明原因。

因为 shell=True,所以 shell 是子进程,而命令是它的子进程。 这是正确的,我知道这一点,但我期望有进程层次结构终止(或至少有一个选项)。 如果 subprocess 应该是一个“高级”,平台无关的模块,那么这将是预期的行为。 (?) - riviera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接