Python subprocess使用shell=True:重定向和跨平台进程终止
我在使用Python的subprocess模块时遇到了一些困难(据我所知,这个模块应该是一个统一的、跨平台的抽象工具,但我不想多说这个 :))。
我想做的简单事情如下:
- 启动一个外部应用程序(可能用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 个回答
针对一些问题的解答:
如果你想处理在子进程中运行的命令的标准输出和输入,你需要使用 "shell = true"。
具体的解决方案可能会根据你想做的事情而有所不同。不过,对于那些需要长时间运行的命令的输出处理,有一些有用的解决办法:
- 你可以使用轮询(poll),可以参考这个讨论: 获取实时输出使用子进程
- 查看以下内容以实现与子进程的异步交互: http://code.activestate.com/recipes/576957-asynchronous-subprocess-using-asyncore/
- 下面这个对子进程的扩展也可以支持异步操作: http://code.google.com/p/subprocdev/
- http://pypi.python.org/pypi/drainers/0.0.3
- 在SO上还有其他有用的讨论: python子进程带有超时和大输出(>64K)
- 如建议的那样,你可以结合使用线程。
Popen实例提供了一个pid属性,表示子进程的进程ID,当使用shell=True时。你应该能够收集到命令的pid,并用os.kill()来使用它。
我最近也遇到了类似的情况。我创建了一个使用 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
的父子关系),第二行代码则是杀掉子进程。
有趣的是,这在 Ubuntu 上是必要的,但在 Mac OSX 上并不是绝对必要,因为在 Mac 上,孙子进程似乎会接管原来的子 shell 进程的 ID。
上次我遇到类似的情况时,发现最简单(几乎是唯一)的解决办法就是启动一个线程来处理你的子进程。用这种方法你可以有不同的选择,比如解析类似命令行的管道,然后在Python代码中执行这些命令(不过你说这样不行,因为会阻塞),这样也能解决你遇到的杀死进程的问题。总的来说,使用线程封装似乎是个不错的选择。
可惜我对subprocess
的经验都是在Windows平台上,Windows有很多自己独特的小毛病。subprocess
似乎有很多缺陷,不过考虑到它有popen
、popen2
等模块来替代,应该也算是能勉强用吧。