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

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

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

I need to be able to open a document using its default application in Windows and Mac OS. Basically, I want to do the same thing that happens when you double-click on the document icon in Explorer or Finder. What is the best way to do this in Python?


回答 0

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

open and start are command-interpreter things for Mac OS/X and Windows respectively, to do this.

To call them from Python, you can either use subprocess module or os.system().

Here are considerations on which package to use:

  1. You can call them via os.system, which works, but…

    Escaping: os.system only works with filenames that don’t have any spaces or other shell metacharacters in the pathname (e.g. A:\abc\def\a.txt), or else these need to be escaped. There is shlex.quote for Unix-like systems, but nothing really standard for Windows. Maybe see also python, windows : parsing command lines with shlex

    • MacOS/X: os.system("open " + shlex.quote(filename))
    • Windows: os.system("start " + filename) where properly speaking filename should be escaped, too.
  2. You can also call them via subprocess module, but…

    For Python 2.7 and newer, simply use

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

    In Python 3.5+ you can equivalently use the slightly more complex but also somewhat more versatile

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

    If you need to be compatible all the way back to Python 2.4, you can use subprocess.call() and implement your own error checking:

    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
    

    Now, what are the advantages of using subprocess?

    • Security: In theory, this is more secure, but in fact we’re needing to execute a command line one way or the other; in either environment, we need the environment and services to interpret, get paths, and so forth. In neither case are we executing arbitrary text, so it doesn’t have an inherent “but you can type 'filename ; rm -rf /'” problem, and if the file name can be corrupted, using subprocess.call gives us little additional protection.
    • Error handling: It doesn’t actually give us any more error detection, we’re still depending on the retcode in either case; but the behavior to explicitly raise an exception in the case of an error will certainly help you notice if there is a failure (though in some scenarios, a traceback might not at all be more helpful than simply ignoring the error).
    • Spawns a (non-blocking) subprocess: We don’t need to wait for the child process, since we’re by problem statement starting a separate process.

    To the objection “But subprocess is preferred.” However, os.system() is not deprecated, and it’s in some sense the simplest tool for this particular job. Conclusion: using os.system() is therefore also a correct answer.

    A marked disadvantage is that the Windows start command requires you to pass in shell=True which negates most of the benefits of using subprocess.


回答 1

使用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桌面环境中使用。

Use the subprocess module available on Python 2.4+, not os.system(), so you don’t have to deal with shell escaping.

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

The double parentheses are because subprocess.call() wants a sequence as its first argument, so we’re using a tuple here. On Linux systems with Gnome there is also a gnome-open command that does the same thing, but xdg-open is the Free Desktop Foundation standard and works across Linux desktop environments.


回答 2

我更喜欢:

os.startfile(path, 'open')

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

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

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

此解决方案仅适用于Windows。

I prefer:

os.startfile(path, 'open')

Note that this module supports filenames that have spaces in their folders and files e.g.

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

(python docs) ‘open’ does not have to be added (it is the default). The docs specifically mention that this is like double-clicking on a file’s icon in Windows Explorer.

This solution is windows only.


回答 3

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

Just for completeness (it wasn’t in the question), xdg-open will do the same on Linux.


回答 4

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

回答 5

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

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

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

import webbrowser
webbrowser.open("path_to_file")

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

If you have to use an heuristic method, you may consider webbrowser.
It’s standard library and despite of its name it would also try to open files:

Note that on some platforms, trying to open a filename using this function, may work and start the operating system’s associated program. However, this is neither supported nor portable. (Reference)

I tried this code and it worked fine in Windows 7 and Ubuntu Natty:

import webbrowser
webbrowser.open("path_to_file")

This code also works fine in Windows XP Professional, using Internet Explorer 8.


回答 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"

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

If you want to go the subprocess.call() way, it should look like this on Windows:

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

You can’t just use:

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

because start is not an executable but a command of the cmd.exe program. This works:

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

but only if there are no spaces in the FILE_NAME.

While subprocess.call method enquotes the parameters properly, the start command has a rather strange syntax, where:

start notes.txt

does something else than:

start "notes.txt"

The first quoted string should set the title of the window. To make it work with spaces, we have to do:

start "" "my notes.txt"

which is what the code on top does.


回答 7

开始不支持长路径名和空格。您必须将其转换为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调用一起使用。

Start does not support long path names and white spaces. You have to convert it to 8.3 compatible paths.

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 )

The file has to exist in order to work with the API call.


回答 8

我已经很晚了,但是这里是使用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文档以获取更多信息。

I am pretty late to the lot, but here is a solution using the windows api. This always opens the associated application.

import ctypes

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

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

A lot of magic constants. The first zero is the hwnd of the current program. Can be zero. The other two zeros are optional parameters (parameters and directory). 5 == SW_SHOW, it specifies how to execute the app. Read the ShellExecute API docs for more info.


回答 9

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

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

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

on mac os you can call ‘open’

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

this would open the file with TextEdit, or whatever app is set as default for this filetype


回答 10

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

If you want to specify the app to open the file with on Mac OS X, use this: os.system("open -a [app name] [file name]")


回答 11

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

On windows 8.1, below have worked while other given ways with subprocess.call fails with path has spaces in it.

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

By utilizing this and other’s answers before, here’s an inline code which works on multiple platforms.

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

回答 12

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

os.startfile(path, 'open') under Windows is good because when spaces exist in the directory, os.system('start', path_name) can’t open the app correctly and when the i18n exist in the directory, os.system needs to change the unicode to the codec of the console in Windows.