我正在尝试检测安装程序何时从Python脚本中执行。具体来说,该应用程序是Oracle 10gR2数据库。目前我正在使用Popen的子进程模块。理想情况下,我只是使用wait()方法等待安装完成执行,但是,记录的命令实际上生成子进程来处理实际安装。以下是失败代码的示例代码:
import subprocess
OUI_DATABASE_10GR2_SUBPROCESS = ['sudo',
'-u',
'oracle',
os.path.join(DATABASE_10GR2_TMP_PATH,
'database',
'runInstaller'),
'-ignoreSysPrereqs',
'-silent',
'-noconfig',
'-responseFile '+ORACLE_DATABASE_10GR2_SILENT_RESPONSE]
oracle_subprocess = subprocess.Popen(OUI_DATABASE_10GR2_SUBPROCESS)
oracle_subprocess.wait()
此处有一个类似的问题:Killing a subprocess including its children from python,但所选答案并未解决子问题,而是指示用户直接调用应用程序等待。我正在寻找一个等待子进程的所有子进程的特定解决方案。如果有不确定数量的子流程怎么办?我将选择解决等待所有子进程完成的问题的答案。
故障更清晰:子进程在wait()命令之后继续执行,因为该命令只等待顶级进程(在这种情况下它是'sudo')。以下是此问题中已知子进程的简单图表: Python子进程模块 - > Sudo - > runInstaller - > java - > (未知)
答案 0 :(得分:3)
好的,这是一个仅适用于Unix的技巧。它类似于这个问题的答案之一:Ensuring subprocesses are dead on exiting Python program。我们的想法是创建一个新的流程组。然后,您可以等待组中的所有进程终止。
pid = os.fork()
if pid == 0:
os.setpgrp()
oracle_subprocess = subprocess.Popen(OUI_DATABASE_10GR2_SUBPROCESS)
oracle_subprocess.wait()
os._exit(0)
else:
os.waitpid(-pid)
我没有测试过这个。它创建了一个额外的子流程作为流程组的领导者,但避免这种情况(我认为)相当复杂。
我发现这个网页也很有帮助。 http://code.activestate.com/recipes/278731-creating-a-daemon-the-python-way/
答案 1 :(得分:2)
你可以使用os.waitpid并将pid设置为-1,这将等待当前进程的所有子进程,直到它们完成:
import os
import sys
import subprocess
proc = subprocess.Popen([sys.executable,
'-c',
'import subprocess;'
'subprocess.Popen("sleep 5", shell=True).wait()'])
pid, status = os.waitpid(-1, 0)
print pid, status
这是分叉的不同子流程pstree <pid>
的结果:
python───python───sh───sleep
希望这有助于:)
答案 2 :(得分:2)
查看以下链接http://www.oracle-wiki.net/startdocsruninstaller,其中详细说明了可用于runInstaller命令的标志。
这个标志肯定适用于11gR2,但是我没有10g数据库为这个版本打包的runInstaller试用这个标志。
此致
答案 3 :(得分:1)
我看的每个地方似乎都说在一般情况下不可能解决这个问题。我创建了一个名为“pidmon”的库,它结合了Windows和Linux的一些答案,可能会做你需要的。
我打算把它清理干净并把它放在github上,可能叫做'pidmon'或类似的东西。如果/我什么时候发布,我会发布一个链接。
编辑:可在http://github.com/dbarnett/python-pidmon处找到。
我创建了一个特殊的waitpid
函数,它接受一个graft_func
参数,这样你就可以松散地定义当你不是直接孩子时想要等待的进程:
import pidmon
pidmon.waitpid(oracle_subprocess.pid, recursive=True,
graft_func=(lambda p: p.name == '???' and p.parent.pid == ???))
或者,作为一个猎枪方法,只要等待自waitpid
调用再次停止以来开始的任何进程,请执行:
import pidmon
pidmon.waitpid(oracle_subprocess.pid, graft_func=(lambda p: True))
请注意,这仍然在Windows上几乎没有经过测试,在Windows上看起来很慢(但是我在github上提到它很容易分叉吗?)。这至少应该让你开始,如果它对你有用,我对如何优化它有很多想法。