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

我需要能够打开一个文档使用它的默认应用程序在 Windows 和 Mac 操作系统。基本上,我想做的事情与在 Explorer 或 Finder 中双击文档图标时发生的事情相同。在 Python 中实现这一点的最佳方法是什么?

125663 次浏览

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

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

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

  1. 你可以通过 os.system打电话给他们,这个可以,但是..。

    转义: os.system仅适用于路径名中没有任何空格或其他 shell 元字符的文件名(例如 A:\abc\def\a.txt) ,否则这些文件名需要转义。对于类 Unix 系统有 shlex.quote,但对于 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的大部分好处。

在 mac os 上,你可以拨打 open:

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

这将使用 TextEdit 打开该文件,或者将该文件类型的任何应用程序设置为默认值。

我更喜欢:

os.startfile(path, 'open')

注意,这个模块支持在文件夹和文件中使用空格的文件名,例如。

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

(巨蟒文档)不必添加“ open”(这是默认值)。文档特别提到,这就像在文件资源管理器中双击文件的图标。

此解决方案仅适用于窗口。

为了完整起见(问题不在这里) ,打开将在 Linux 上做同样的事情。

使用 Python 2.4 + 上可用的 subprocess模块,而不是 os.system(),这样就不必处理 shell 转义。

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是免费桌面基金会的标准,可以跨 Linux 桌面环境工作。

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])

如果你想指定在 Mac OS X 上打开这个文件的应用程序,可以这样做: os.system("open -a [app name] [file name]")

如果你想走 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方法 正确地引用了参数,但是 start命令有一个相当奇怪的语法,其中:

start notes.txt

除了:

start "notes.txt"

第一个带引号的字符串应该设置窗口的标题。为了让它适用于空间,我们必须这样做:

start "" "my notes.txt"

上面的代码就是这么做的。

Start 不支持长路径名和空白。必须将其转换为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 调用一起工作。

我已经迟到了,但是这里有一个使用 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 docs 获取更多信息。

如果必须使用启发式方法,可以考虑使用 webbrowser
它是一个标准的库,尽管它的名字叫做“文件库”,但它也会尝试打开文件:

注意,在某些平台上,尝试使用以下命令打开文件名 功能,可以工作和启动操作系统的关联 但是,这既不支持也不可移植。 (参考文献)

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

import webbrowser
webbrowser.open("path_to_file")

此代码在使用 Internet Explorer 8的 WindowsXP 专业版中也能正常工作。

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

在 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))

以下是尼克的回答,稍微根据 WSL 进行了调整:

import os
import sys
import logging
import subprocess


def get_platform():
if sys.platform == 'linux':
try:
proc_version = open('/proc/version').read()
if 'Microsoft' in proc_version:
return 'wsl'
except:
pass
return sys.platform


def open_with_default_app(filename):
platform = get_platform()
if platform == 'darwin':
subprocess.call(('open', filename))
elif platform in ['win64', 'win32']:
os.startfile(filename.replace('/','\\'))
elif platform == 'wsl':
subprocess.call('cmd.exe /C start'.split() + [filename])
else:                                   # linux variants
subprocess.call(('xdg-open', filename))

我想您可能需要在编辑器中打开文件。

适用于视窗

subprocess.Popen(["notepad", filename])

为了 Linux

subprocess.Popen(["text-editor", filename])

我构建了一个 小图书馆,将这里的最佳答案结合起来,以提供跨平台支持:

$ pip install universal-startfile

然后启动一个文件或 URL:

from startfile import startfile


startfile("~/Downloads/example.png")
startfile("http://example.com")

调用 open file ()函数时出错。我是跟着一个指南,但指南是写在窗口,而我在 Linux 上。所以 os.statrfile 方法对我不起作用。我通过以下方式缓解了这个问题:

导入库

import sys, os, subprocess
import tkinter
import tkinter.filedioalog as fd
import tkinter.messagebox as mb

在 lib 导入之后,我调用 subprocess 方法在基于 unix 的操作系统中打开一个文件,该文件名为“ xdg-open”,以及将要打开的文件。

def open_file():
file = fd.askopenfilename(title='Choose a file of any type', filetypes=[('All files', "*.*")])
subprocess.call(['xdg-open', file])