>>> from env_helper import info; info()
待更新

5.13. 从Python启动其他程序

利用内建的 subprocess 模块中的 Popen() 函数,Python 程序可以启动计算机中的其他程序( Popen() 函数名中的P 表示 process ,进程)。如果你打开了一个应用程序 的多个实例,每个实例都是同一个程序的不同进程。例如, 如果你同时打开了Web浏览器的多个窗口,每个窗口都是 Web 浏览器程序的不同进程。

每个进程可以有多个线程。不像线程,进程无法直接读写另一个进程的变量。 如果你认为多线程程序是多个手指在追踪源代码,那么同一个程序 打开多个进程就像有一个朋友拿着程序源代码的独立副本。 你们都独立地执行相同的程序。

如果想在 Python 脚本中启动一个外部程序,就将该程序的文件名传递给 subprocess.Popen() (在Windows 中,右键点击该应用程序的开始 菜单项,然后选择“属性”,查看应用程序的文件名。在 OS X 上, 按住 Ctrl 键单击该应用程序并选择

“显示包内容”,找到可执行文件的路径)。Popen()函数随后将立即返回。 请记住,启动的程序和你的Python程序不在同一线程中运行。

在Windows计算机上,在交互式环境中输入以下代码:

import subprocess
subprocess.Popen( 'C:\\Windows\\System32\\calc.exe')

UbuntuLinux 上,可以输入以下代码:

import subprocess
subprocess.Popen('/usr/bin/gnome-calculator')

在 OS X 上,过程稍有不同。参见15.8.5节“用默认应用程序打开文件”。 返回值是一个 Popen 对象,它有两个有用的方法: poll()wait()

可以认为 poll() 方法是问你的朋友,她是否执行完毕 你给她的代码。如果这个进程在 poll() 调用时仍在运行, poll() 方法就返回None 。如果该程序已经终止, 它会返回该进程的整数退出代码。退出代码用于说明进程是 无错终止(退出代码为0),还是一个错误导致进程终止 (退出代码非零,通常为1,但可能根据程序而不同)。

wait()方法就像是等着你的朋友执行完她的代码,然后你 继续执行你的代码。 wait()方法将阻塞,直到启动的进程终止。 如果你希望你的程序暂停,直到用户完成与其他程序,这非常有用。 wait()的返回值是进程的整数退出代码。

在Windows上,在交互环境中输入以下代码。请注意, wait() 的调用将阻塞,直到退出启动的计算器程序。

calcProc =subprocess.Popen('c:\\Windows\\System32\\calc.exe')
calcProc.poll() == None
calcProc.wait()
calcProc.poll()

这里,我们打开了计算器程序。在它仍在运行时,我们检查 polio 是否返回None 。它应该返回 None, 因为该进程仍在运行。然后,我们关闭计算器程序,并对 已终止的进程调用wait()wait()poll() 现在返回0,说明该进程终止且无错。

5.13.1. Popen() 传递命令行参数

Popen()创建进程时,可以向进程传递命令行参数。 要做到这一点,向Popen()传递一个列表,作为唯一的参数。 该列表中的第一个字符串是要启动的程序的可执 行文件名,所有后续的字符串将是该程序启动时, 传递给该程序的命令行参数。实际上,这个列表将 作为被启动程序的sys.argv的值。

大多数具有图形用户界面(GUI)的应用程序, 不像基于命令行或基于终端的程序那样尽可能地使用命令行 参数。但大多数 GUI 应用程序将接受一个参数,表示 应用程序启动时立即打开的文件。例如,如果你使用的是 Windows,创建一个简单的文本文件 C:hello.txt , 然后在交互式环境中输入以下代码:

subprocess.Popen( ['C:\\Windows\\notepad.exe','C:\\hello.txt'])

这不仅会启动记事本应用程序, 也会让它立即打开 C:hello.txt 。

5.13.2. Task Schedulerlaunchdcron

如果你精通计算机,可能知道Windows上的 Task SchedulerOS X 上的launchd, 或 Linux 上的 cron 调度程序。这些工具文档齐全, 而且可靠,它们都允许你安排应用程序在特定的时间启动。 如果想更多地了解它们,http://nostarch.com/automatestuff/ 找到教程的链接。

利用操作系统内置的调度程序,你不必自己写时钟检查代码 来安排你的程序。但是,如果只需要程序稍作停顿,就用 time.sleep() 函数。或者不使用操作系统的调度程序, 代码可以循环直到特定的日期和时间,每次循环时调用 time.sleep(1)

5.13.3. 用 Python 打开网站

webbrowser.open() 函数可以从程序启动 Web 浏览器, 打开指定的网站,而不是用 subprocess.Popen() 打开 浏览器应用程序。详细内容参见第11章的“项目:利用 webbrowser 模块的maplt.py一节。

5.13.4. 运行其他 Python 脚本

可以在Python中启动另一个Python脚本,就像任何其他的 应用程序一样。只需向 Popen() 传入python.exe 可执行文件,并将想运行的. py 脚本的文件名作为它的参 数。例如,下面代码将运行第1章的 hello.py 脚本:

subprocess.Popen( ['C:\\python34\\python.exe', 'hello.py'])

Popen() 传入一个列表,其中包含 Python 可执行 文件的路径字符串,以及脚本文件名的字符串。如果要启动的 脚本需要命令行参数,就将它们添加列表中,放在脚本文件名 后面。在 Windows 上, Python 可执行文件的路径是 C:\python34\python.exe 。 在OS X上, 是/Library/Frameworks/Python.framework/Versions/3.3/bin/python3。 在Linux上,是/usr/bin/python3

不同于将Python程序导入为一个模块,如果Python程序 启动了另一个Python程序,两者将在独立的进程中运行, 不能分享彼此的变量。

5.13.5. 用默认的应用程序打开文件

双击计算机上的 .txt 文件,会自动启动与 .txt 文件扩展名 关联的应用程序。计算机上已经设置了一些这样的文件扩展名 关联。利用 Popen(),Python也可以用这种方式打开文件。

每个操作系统都有一个程序,其行为等价于双击文档文件来 打开它。在Windows上,这是 start 程序。在OS X上, 这是 open 程序。在 Ubuntu Linux 上,这是 see 程序。 在交互式环境中输入以下代码,根据操作系统,向 Popen() 传入’start''open’'see'

>>> fileObj = open('hello.txt', 'w')
>>> fileObj .write('Hello world!')
12
>>> fileObj.close()
>>> import subprocess
>>> subprocess.Popen(['start', 'hello.txt'], shell=True)
<subprocess.Popen at 0x7f52896452e8>

这里,我们将Hello world!写入一个新的hello.txt文件。 然后调用Popen(),传入一个列表,其中包含程序名称(在这个 例子中,是Windows上的'start'),以及文件名。我们也 传入了shelHTme关键字参数,这只在Windows上需要。 操作系统知道所有的文件关联,能弄清楚应该启动哪个程序, 比如Notepad.exe,来处理hello.txt文件。

OS X上,open程序用于打开文档文件和程序。 如果你有Mac,在交互式环境中输入以下代码:

>>> subprocess.Popen(['open','/Applications/Calculator.app/'])
<subprocess.Popen at 0x7f528e6b25c0>

计算器应用程序应该会打开。

Unix哲学

程序精心设计,能被其他程序启动,这样的程序比单独使用 它们自己的代码更强大。Unix的哲学是一组由UNIX操作系统 (现代的Linux和OS X也是基于它)的程序员建立的软件设计 原则。它认为:编写小的、目的有限的、能互操作的程序, 胜过大的、功能丰富的应用程序。

较小的程序更容易理解,通过能够互操作,它们可以是更强大的 应用程序的构建块。智能手机应用程序也遵循这种方式。如果你的 餐厅应用程序需要显示一间咖啡店的方位,开发者不必重新发明 轮子,编写自己的地图代码。餐厅应用程序只是启动一个地图应 用程序,同时传入咖啡店的地址,就像Python代码调用 一个函数,并传入参数一样。

你在本书中编写的Python程序大多符合 Unix 哲学, 尤其是在一个重要的方面:它们使用命令行参数,而不是 input() 函数调用。 如果程序需要的所有信息都可以事先提供,最好是用命令行参数传入这些信息, 而不是等待用户键入它。这样,命令行参数可以由人类用户健入, 也可以由另一个程序提供。这种互操作的方式,让你的程序可以 作为另一个程序的部分而复用。

唯一的例外是,你不希望口令作为命令行参数传入, 因为命令行可能记录它们,作为命令历史功能的一部分。 在需要输入口令时,程序应该调用 input() 函数。

在这方面,有一本著名的书《UNIX编程艺术》,可以深入阅读。