启动多个Shell进程
我想用Python同时在多个独立的终端窗口中启动一个命令。请问有什么好的方法吗?现在我正在尝试使用subprocess模块中的popen,这个方法可以用来执行一个命令,但对多个命令就不行了。
提前谢谢你们。
编辑:
这是我现在的做法:
from subprocess import*
Popen('ant -Dport='+str(5555)+ ' -Dhost='+GetIP()+ ' -DhubURL=http://192.168.1.113:4444 -Denvironment=*firefox launch-remote-control $HOME/selenium-grid-1.0.8', shell=True)
对我来说,问题在于这个方法会在终端中启动一个Java进程,而我希望这个进程能够一直运行下去。另外,我还想在多个不同的进程中多次运行类似的命令。
3 个回答
0
这里有一个简单的阻塞队列的版本。你可以用 collections.deque 之类的东西让它变得更好,或者用 Twisted 的 deferreds 之类的更高级的东西。这个版本有一些不太好的地方:
- 会阻塞
- 终止信号可能不会传递下去
根据自己的需要调整吧!
import logging
basicConfig = dict(level=logging.INFO, format='%(process)s %(asctime)s %(lineno)s %(levelname)s %(name)s %(message)s')
logging.basicConfig(**basicConfig)
logger = logging.getLogger({"__main__":None}.get(__name__, __name__))
import subprocess
def wait_all(list_of_Popens,sleep_time):
""" blocking wait for all jobs to return.
Args:
list_of_Popens. list of possibly opened jobs
Returns:
list_of_Popens. list of possibly opened jobs
Side Effect:
block until all jobs complete.
"""
jobs = list_of_Popens
while None in [j.returncode for j in jobs]:
for j in jobs: j.poll()
logger.info("not all jobs complete, sleeping for %i", last_sleep)
time.sleep(sleep_time)
return jobs
jobs = [subprocess.Popen('sleep 1'.split()) for x in range(10)]
jobs = wait_all(jobs)
1
我能想到的简单方法是让Python使用 Popen
来启动一个类似于下面的脚本:
gnome-terminal --window -e 'ant -Dport=5555 -Dhost=$IP1 -DhubURL=http://192.168.1.113:4444 -Denvironment=*firefox launch-remote-control $HOME/selenium-grid-1.0.8' &
disown
gnome-terminal --window -e 'ant -Dport=5555 -Dhost=$IP2 -DhubURL=http://192.168.1.113:4444 -Denvironment=*firefox launch-remote-control $HOME/selenium-grid-1.0.8' &
disown
# etc. ...
其实有一种完全用Python实现的方法,但那样写起来很麻烦,只能在类Unix操作系统上运行,而且我没时间把代码写出来。基本上,subprocess.Popen
不支持这种方式,因为它假设你要么等着子进程完成,要么和子进程互动,或者监控子进程。它不支持“就启动它,别再管我”的情况。
在类Unix操作系统中,通常是这样做的:
- 使用
fork
来创建一个子进程 - 让这个子进程再
fork
一个自己的子进程 - 让这个孙子进程把输入输出重定向到
/dev/null
,然后用某个exec
函数来启动你真正想要启动的进程(这部分可能可以用Popen
来实现) - 子进程退出。
- 现在,祖父进程和孙子进程之间没有联系,所以如果孙子进程结束了,你不会收到
SIGCHLD
信号,而如果祖父进程结束了,也不会把所有的孙子进程都杀掉。
我可能在细节上有些不准确,但大概就是这个意思。在 bash
中,后台运行(&
)和 disown
的操作应该能实现同样的效果。
1
这个过程只要在运行,就应该保持打开。如果你想同时启动多个这样的过程,只需把它放在一个线程里。
这段代码没有经过测试,但你应该能理解大概意思:
class PopenThread(threading.Thread):
def __init__(self, port):
threading.Thread.__init__(self)
self.port=port
def run(self):
Popen('ant -Dport='+str(self.port)+ ' -Dhost='+GetIP()+
' -DhubURL=http://192.168.1.113:4444'
' -Denvironment=*firefox launch-remote-control'
' $HOME/selenium-grid-1.0.8', shell=True)
if '__main__'==__name__:
PopenThread(5555).start()
PopenThread(5556).start()
PopenThread(5557).start()
补充说明:下面提到的双重分叉方法:https://stackoverflow.com/a/3765162/450517,是启动守护进程的正确方式,也就是一个不会通过标准输入输出进行通信的长时间运行的过程。