在Windows和Mac OS中都使用Python中的默认OS应用程序打开文档


125

我需要能够使用Windows和Mac OS中的默认应用程序打开文档。基本上,我想做的事情与您在资源管理器或Finder中双击文档图标时发生的事情相同。用Python做到这一点的最佳方法是什么?


9
从2008年起,此问题就已包含在Python跟踪器的标准库中:bugs.python.org/issue3177
Ram Rachum 2011年

Answers:


77

openstart分别是Mac OS / X和Windows的命令解释器。

要从Python调用它们,可以使用subprocessmodule或os.system()

以下是有关使用哪个软件包的注意事项:

  1. 您可以通过致电给他们os.system,该电话有效,但是...

    转义: os.system仅适用于路径名中没有空格或其他shell元字符的文件名(例如A:\abc\def\a.txt),否则需要转义。有shlex.quote针对Unix的系统,但没有Windows的真正标准。也许还会看到python,windows:用shlex解析命令行

    • MacOS / X: os.system("open " + shlex.quote(filename))
    • Windows:也应避免os.system("start " + filename)在适当的地方说话filename
  2. 您也可以通过subprocess模块调用它们,但是...

    对于Python 2.7及更高版本,只需使用

    subprocess.check_call(['open', filename])

    在Python 3.5+中,您可以等效地使用稍微更复杂但也更通用的功能

    subprocess.run(['open', filename], check=True)

    如果您需要一直兼容Python 2.4,则可以使用subprocess.call()并实现自己的错误检查:

    try:
        retcode = subprocess.call("open " + filename, shell=True)
        if retcode < 0:
            print >>sys.stderr, "Child was terminated by signal", -retcode
        else:
            print >>sys.stderr, "Child returned", retcode
    except OSError, e:
        print >>sys.stderr, "Execution failed:", e

    现在,使用的好处是subprocess什么?

    • 安全性:从理论上讲,这是更安全的方法,但是实际上我们需要以一种或另一种方式执行命令行。在这两种环境中,我们都需要环境和服务来解释,获取路径等。在这两种情况下,我们都不执行任意文本,因此它没有固有的“但您可以键入'filename ; rm -rf /'”问题,并且如果文件名可以被破坏,则使用subprocess.call不会给我们带来更多的保护。
    • 错误处理:实际上并没有给我们提供更多的错误检测功能,retcode无论哪种情况我们都取决于 但是在错误的情况下显式引发异常的行为当然可以帮助您注意到是否存在故障(尽管在某些情况下,回溯可能根本不比简单地忽略错误更有用)。
    • 产生一个(非阻塞的)子流程:我们不需要等待子流程,因为我们通过问题陈述来启动一个单独的流程。

    反对“但subprocess首选”。但是,os.system()不建议弃用它,从某种意义上说,它是完成此特定工作的最简单工具。结论:os.system()因此使用也是正确的答案。

    明显的缺点是Windows start命令要求您传递shell=True,否定了使用的大多数好处subprocess


2
根据filename形式的不同,这是为何os.system()不安全和糟糕的完美示例。子过程更好。
Devin Jeanpierre 09年

6
尼克的回答对我来说很好。一切都没有。用错误的例子来解释事情是不容易的。
Devin Jeanpierre 09年

2
与使用子流程相比,它的安全性和灵活性较差。这对我来说听起来是错误的。
Devin Jeanpierre 09年

8
当然重要。好的答案和不好的答案(或糟糕的答案)之间是有区别的。os.system()的文档本身会说“使用子流程模块”。还需要什么?这对我来说已经够用了。
Devin Jeanpierre 2009年

20
我有点不愿意重新开始讨论,但是我认为“最新更新”部分完全错误。问题os.system()在于它使用外壳程序(并且您在这里没有进行任何外壳程序转义,因此对于恰好包含外壳程序元字符的完全有效的文件名,将发生“坏事”)。subprocess.call()首选之所以是您可以选择使用绕过Shell subprocess.call(["open", filename])。这适用于所有有效的文件名,即使对于不受信任的文件名也不会引入shell注入漏洞。
Sven Marnach 2012年

150

使用subprocessPython 2.4+上可用的模块,而不要使用os.system(),因此您不必处理外壳转义。

import subprocess, os, platform
if platform.system() == 'Darwin':       # macOS
    subprocess.call(('open', filepath))
elif platform.system() == 'Windows':    # Windows
    os.startfile(filepath)
else:                                   # linux variants
    subprocess.call(('xdg-open', filepath))

双括号是因为subprocess.call()希望将序列作为其第一个参数,因此我们在这里使用元组。在具有Gnome的Linux系统上,还有一个gnome-open命令可以执行相同的操作,但是xdg-open它是Free Desktop Foundation标准,并且可以在Linux桌面环境中使用。


5
在Windows上,在subprocess.call()中使用'start'无效-start实际上不是可执行文件。
Tomas Sedovic 09年

4
鸡蛋里挑骨头:所有linuxen(我想大多数BSD系统),你应该使用xdg-open- linux.die.net/man/1/xdg-open
gnud

6
在Windows上启动是Shell命令,而不是可执行文件。您可以使用subprocess.call((''start',filepath),shell = True),尽管如果您在Shell中执行,则最好使用os.system。
彼得·格雷厄姆

我跑了xdg-open test.py,它为我打开了Firefox下载对话框。怎么了?我正在使用manjaro linux。
杰森

1
@Jason听起来您的xdg-open配置很混乱,但这并不是我们可以在注释中解决的问题。也许请看unix.stackexchange.com/questions/36380/…–
三点

44

我更喜欢:

os.startfile(path, 'open')

请注意,此模块支持在其文件夹和文件中带有空格的文件名,例如

A:\abc\folder with spaces\file with-spaces.txt

python docs)不必添加'open'(这是默认值)。文档特别提到这就像双击Windows资源管理器中的文件图标。

此解决方案仅适用于Windows。


谢谢。我没有注意到可用性,因为文档将其附加到最后一段。在大多数其他部分,可用性说明占据其自己的行。
DrBloodmoney

在Linux上,由于某种原因,该startfile功能甚至不存在,而不是引发错误,这意味着用户将收到有关缺少功能的令人困惑的错误消息。您可能要检查平台以避免这种情况。
cz

39

仅出于完整性考虑(这不是问题),xdg-open将在Linux上执行相同的操作。


6
+1通常,响应者不应回答未曾提出的问题,但在这种情况下,我认为这对整个SO社区都非常重要且有帮助。
demongolem 2012年

一直在寻找
nurettin

25
import os
import subprocess

def click_on_file(filename):
    '''Open document with default application in Python.'''
    try:
        os.startfile(filename)
    except AttributeError:
        subprocess.call(['open', filename])

2
呵呵,我对启动文件一无所知。如果Mac和Linux版本的Python获得类似的语义,那将是很好的。
尼克

3
相关的python错误:bugs.python.org/issue3177-提供一个不错的补丁,它可能会被接受=)
gnud

适用于Linux的xdg-open命令
TheTechRobo36414519

21

如果必须使用启发式方法,则可以考虑webbrowser
它是标准库,尽管有其名称,它也会尝试打开文件:

请注意,在某些平台上,尝试使用此功能打开文件名可能有效并启动操作系统的关联程序。但是,这既不支持也不是可移植的。(参考

我尝试了这段代码,它在Windows 7和Ubuntu Natty中运行良好:

import webbrowser
webbrowser.open("path_to_file")

使用Internet Explorer 8,此代码在Windows XP Professional中也可以正常工作。


3
据我所知,这是最好的答案。似乎是跨平台的,无需检查正在使用哪个平台或导入操作系统。
polandeer

2
@jonathanrocher:我在源代码中看到了Mac支持open location如果您将路径指定为有效的url,它将在此使用。
jfs 2015年

1
macOS:import webbrowser webbrowser.open("file:///Users/nameGoesHere/Desktop/folder/file.py")
Daniel Springer

3
docs.python.org/3/library/webbrowser.html#webbrowser.open “请注意,在某些平台上,尝试使用[webbrowser.open(url)]打开文件名可能有效并启动操作系统的关联程序。但是,这既不受支持,也无法移植。”
nyanpasu64

6

如果subprocess.call()要这样做,在Windows上应如下所示:

import subprocess
subprocess.call(('cmd', '/C', 'start', '', FILE_NAME))

您不能只使用:

subprocess.call(('start', FILE_NAME))

因为start 它不是可执行文件,而是cmd.exe程序的命令。这有效:

subprocess.call(('cmd', '/C', 'start', FILE_NAME))

但前提是FILE_NAME中没有空格。

尽管subprocess.call方法en正确引用了参数,但该start命令具有一种相当奇怪的语法,其中:

start notes.txt

除了:

start "notes.txt"

第一个带引号的字符串应设置窗口的标题。要使其与空格配合使用,我们必须执行以下操作:

start "" "my notes.txt"

这是最上面的代码的作用。


5

开始不支持长路径名和空格。您必须将其转换为8.3兼容路径。

import subprocess
import win32api

filename = "C:\\Documents and Settings\\user\\Desktop\file.avi"
filename_short = win32api.GetShortPathName(filename)

subprocess.Popen('start ' + filename_short, shell=True )

该文件必须存在才能与API调用一起使用。


1
另一个解决方法是给它start "Title" "C:\long path to\file.avi"
加上

3

我已经很晚了,但是这里是使用Windows API的解决方案。这总是打开关联的应用程序。

import ctypes

shell32 = ctypes.windll.shell32
file = 'somedocument.doc'

shell32.ShellExecuteA(0,"open",file,0,0,5)

很多魔术常数。第一个零是当前程序的hwnd。可以为零。另外两个零是可选参数(参数和目录)。5 == SW_SHOW,它指定如何执行应用程序。阅读 ShellExecute API文档以获取更多信息。


1
与之相比os.startfile(file)呢?
jfs

2

在Mac OS上,您可以调用“打开”

import os
os.popen("open myfile.txt")

这将使用TextEdit打开该文件,或者将此文件类型设置为默认应用程序


2

如果要指定用于在Mac OS X上打开文件的应用程序,请使用以下命令: os.system("open -a [app name] [file name]")


2

在Windows 8.1上,下面的方法已经起作用,而其他给定的方法却subprocess.call失败了,并且路径中有空格。

subprocess.call('cmd /c start "" "any file path with spaces"')

通过之前利用此答案和其他答案,这是一个内联代码,可在多个平台上工作。

import sys, os, subprocess
subprocess.call(('cmd /c start "" "'+ filepath +'"') if os.name is 'nt' else ('open' if sys.platform.startswith('darwin') else 'xdg-open', filepath))

2

os.startfile(path, 'open')在Windows下,这是一个好习惯,因为当目录中存在空格时,os.system('start', path_name)无法正确打开应用程序;当目录中存在i18n时,则os.system需要将unicode更改为Windows中控制台的编解码器。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.