从Python中单独启动另一个程序

8

我正试图从Python中运行一个外部的、独立的程序。通常情况下不会有问题,但这个程序是一个游戏,并且内置了Python解释器。当我使用subprocess.Popen时,它会启动另一个程序,但是在原始程序的Python实例下执行,所以它们共享第一个Python控制台。我可以很好地结束第一个程序,但我宁愿拥有单独的控制台(主要是因为我将控制台隐藏起来,但当我使用subprocess.POpen从Python启动程序时,它会被显示出来)。

如果我能让第二个程序完全独立地启动,就像我只是“双击它”一样,那就太好了。此外,os.system不起作用,因为我想要跨平台兼容性,而os.system只适用于Windows。


相关:一个稍微复杂的问题如何从批处理文件中保持多个cmd窗口打开 - jfs
2个回答

7
我希望我可以完全地启动第二个程序,就像我刚双击它一样。截至Python 2.7和3.3,Python 没有跨平台的方法来实现这一点。未来可能会添加新的shutil.open 方法(可能不是以这个名字),请参见http://bugs.python.org/issue3177了解详情。但在那之前,您需要为每个关心的平台编写自己的代码。
幸运的是,你想做的事情比shutil.open 最终希望提供的更简单、更少一般性,这意味着编码并不那么困难:
  • 在 OS X 上,有一个名为 open 的命令可以实现你想要的功能:“open 命令打开一个文件(或目录或 URL),就像你双击文件图标一样。”因此,你只需 popen open /Applications/MyGame.app
  • 在 Windows 上,相应的命令是 start,但不幸的是,这是 cmd.exe shell 的一部分而不是独立的程序。幸运的是,Python 自带了一个函数 os.startfile 可以实现同样的功能,所以只需 os.startfile(r'C:\Program Files\MyGame\MyGame.exe')
  • 在兼容 FreeDesktop 的 *nix 系统上(包括大多数现代 linux 发行版等),有一个非常类似的命令称为 xdg-open:“xdg-open 在用户首选应用程序中打开文件或 URL。”同样地,只需 popen xdg-open /usr/local/bin/mygame
  • 如果你期望在其他平台上运行,你需要进行一些研究,找到最佳的替代方案。否则,在 Mac 和 Windows 以外的任何情况下,我会尝试 popen xdg-open,如果失败就抛出错误。

请参考http://pastebin.com/XVp46f7X(未经测试)的示例。

请注意,这只适用于可以双击在Finder/Explorer/Nautilus等中启动的程序。例如,如果您尝试启动'./script.py',根据您的设置,它可能只会在文本编辑器中打开脚本。

此外,在OS X上,您需要运行.app包而不是其中的UNIX可执行文件。(在某些情况下,启动UNIX可执行文件——无论是在.app包内还是独立运行——都可能有效,但不要指望它。)

同时,请记住,以这种方式启动程序与从命令行运行程序不同——特别是,它将继承其环境、当前目录/驱动器等信息,而不是从终端会话中继承。如果您需要更多控制子进程,则需要查看openxdg-openos.startfile的文档和/或想出不同的解决方案。

最后,仅仅因为open/xdg-open/os.startfile执行成功,并不意味着游戏已经启动成功。例如,如果它在创建窗口之前启动并崩溃,你仍然会认为它是成功的。

您可以在PyPI上寻找符合您需求的库。http://pypi.python.org/pypi/desktop看起来是一个可能的选择。

或者您可以查看问题3177中的补丁,并选择您最喜欢的补丁。据我所知,它们都是纯Python编写的,您可以轻松地将添加的函数放入自己的模块中,而不是放在osshutil中。

作为一个快速的hack,你或许可以(滥用)webbrowser.open。"请注意,在某些平台上,尝试使用此函数打开文件名可能会启动操作系统关联的程序。然而,这既不被支持也不是可移植的。" 特别地,如果我没记错的话,它在OS X 10.5+上不起作用。然而,我认为将文件名制作成file: URL实际上在OS X和Windows上都有效,并且对于大多数但不是全部配置的Linux也有效。如果是这样,它或许足够用于一个快速脚本。只需记住它没有被记录为有效,可能会对一些用户造成问题,可能会在未来出现问题,它被Python开发人员明确地视为滥用,所以我不会指望它用于更严肃的事情。并且,与上述更正确的方法一样,它也会有启动'script.py'或'Foo.app/Contents/MacOS/foo'、传递环境变量等相同的问题。
你提到的几乎所有其他内容都是不相关和错误的:
通常情况下这不是问题,但该程序是一个游戏,并且内置了Python解释器。如果游戏是从C代码写入stdout,它也会做同样的事情。
当我使用subprocess.Popen时,它会启动一个单独的程序,但是在原始程序的Python实例下执行。
不会的。它会启动一个全新的进程,其嵌入式Python解释器是全新的Python实例。例如,您可以通过运行与游戏嵌入版本不同的Python版本来验证这一点。
它们不会共享第一个Python控制台。它们可能共享相同的tty/cmd窗口,但这不是同一回事。
我可以很好地结束第一个程序,但我更愿意拥有单独的控制台(主要是因为我让控制台初始处于隐藏状态,但是当我使用subprocess.POpen从Python启动程序时,它会显示)。

你可以将子进程的标准输出和错误输出导入到一个日志文件中,这样你就可以从父进程的输出中单独查看它们。但我认为这与你实际关心的内容无关。

此外,os.system不起作用,因为我想实现跨平台兼容性,而这只在Windows上可用。

错了;os.system 可以在“Unix、Windows”上使用——这可能是你关心的所有平台。然而,它不起作用,因为它在脚本的子shell中运行子程序,使用相同的tty。(还有很多其他问题,例如阻塞直到子进程完成。)


1
似乎webbrowser模块可以自动选择xdg-open、open、startfile()。虽然不明显如何使用它们来运行Python脚本,例如,xdg-open script.py打开编辑器,而在shell中运行./script.py - jfs
好的观点。我认为./script.py的情况在这里并不相关,因为OP明确地尝试打开一个游戏,就像他双击它一样,但是仍然值得一提。此外,webbrowser.open绝对是一种hack,但值得讨论。所以,我已经更新了答案。 - abarnert
谢谢提供这些信息,都很值得一看。很奇怪没有跨平台的方法来启动另一个独立的程序。我想我得为每个操作系统使用特殊情况。无论如何,非常感谢! - SolarLune
如果你读了3177号问题中的评论,你就能大致了解为什么它还没有被实现。设计比最低公共分母稍微灵活一点的东西,要处理错误等等,都很困难。看起来我们最终会得到一些东西,但除非你不打算在Python 3.4或3.5之前发布你的代码,否则现在对你没有帮助... - abarnert

2
当我使用subprocess.Popen时,它会启动单独的程序,但是在原始程序的Python实例下执行...
不正确。
这就是问题的关键。如果你想在另一个控制台中运行它,那么你必须运行另一个控制台并告诉它来运行你的程序。
我追求跨平台兼容性...
很抱歉,没有跨平台的方法可以做到这一点。您需要运行适合该平台的控制台/终端。

真的吗?那很奇怪,因为我正在运行的游戏是使用带有Python的引擎制作的(不是用Python编写的),所以运行游戏会启动一个Python控制台实例。如果我同时运行两个游戏实例,每个实例都有自己独立的Python控制台。没有办法只调用一个单独的程序来运行吗? - SolarLune
2
这一切都是正确的,但这是一个有些令人绝望和泄气的答案。在OP可能关心的所有平台上做到这一点并不难,而且你绝对不想通过找到合适的终端程序并运行它来实现这一点。(首先,在*nix平台上,这甚至不需要打开一个新的终端窗口。) - abarnert

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