Mở tài liệu với ứng dụng HĐH mặc định trong Python, cả trong Windows và Mac OS


125

Tôi cần có khả năng mở một tài liệu bằng ứng dụng mặc định của nó trong Windows và Mac OS. Về cơ bản, tôi muốn làm điều tương tự xảy ra khi bạn nhấp đúp vào biểu tượng tài liệu trong Explorer hoặc Finder. Cách tốt nhất để làm điều này trong Python là gì?


9
Có một vấn đề cho việc này được đưa vào thư viện tiêu chuẩn trong trình theo dõi Python từ năm 2008: bug.python.org/su3177
Ram Rachum

Câu trả lời:


77

openstartlà những thứ thông dịch lệnh cho Mac OS / X và Windows tương ứng, để làm điều này.

Để gọi chúng từ Python, bạn có thể sử dụng subprocessmô-đun hoặc os.system().

Dưới đây là những cân nhắc về việc sử dụng gói nào:

  1. Bạn có thể gọi họ qua os.system, hoạt động, nhưng ...

    Thoát: os.system chỉ hoạt động với tên tệp không có bất kỳ khoảng trắng hoặc ký tự đại diện hệ vỏ nào khác trong tên đường dẫn (ví dụ A:\abc\def\a.txt), nếu không, những tên này cần phải được thoát. Có shlex.quotecác hệ thống tương tự Unix, nhưng không có gì thực sự chuẩn cho Windows. Có thể xem thêm python, windows: phân tích dòng lệnh bằng shlex

    • MacOS / X: os.system("open " + shlex.quote(filename))
    • Windows: os.system("start " + filename)nơi nói đúng filenamecũng nên được thoát.
  2. Bạn cũng có thể gọi họ qua subprocessmô-đun, nhưng ...

    Đối với Python 2.7 trở lên, chỉ cần sử dụng

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

    Trong Python 3.5+, bạn có thể sử dụng tương đối phức tạp hơn một chút nhưng cũng linh hoạt hơn

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

    Nếu bạn cần tương thích hoàn toàn với Python 2.4, bạn có thể sử dụng subprocess.call()và thực hiện kiểm tra lỗi của riêng mình:

    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
    

    Bây giờ, những lợi thế của việc sử dụng là subprocessgì?

    • Bảo mật: Về lý thuyết, điều này an toàn hơn, nhưng trên thực tế chúng ta cần thực thi một dòng lệnh theo cách này hay cách khác; trong cả hai môi trường, chúng ta cần môi trường và dịch vụ để giải thích, nhận đường dẫn, v.v. Trong cả hai trường hợp, chúng tôi không thực thi văn bản tùy ý, vì vậy nó không có 'filename ; rm -rf /'vấn đề "nhưng bạn có thể gõ " cố hữu và nếu tên tệp có thể bị hỏng, sử dụng subprocess.callsẽ cung cấp cho chúng tôi ít sự bảo vệ hơn.
    • Xử lý lỗi: Nó thực sự không cung cấp cho chúng tôi bất kỳ phát hiện lỗi nào nữa, chúng tôi vẫn phụ thuộc vào một retcodetrong hai trường hợp; nhưng hành vi đưa ra một ngoại lệ rõ ràng trong trường hợp có lỗi chắc chắn sẽ giúp bạn nhận thấy nếu có lỗi (mặc dù trong một số trường hợp, việc truy nguyên có thể không hữu ích hơn là chỉ đơn giản là bỏ qua lỗi).
    • Sinh ra một quy trình con (không chặn) : Chúng ta không cần chờ quá trình con, vì chúng ta bằng cách phát biểu vấn đề bắt đầu một quy trình riêng.

    Để phản đối "Nhưng subprocessđược ưa thích." Tuy nhiên, os.system()không bị phản đối, và theo một nghĩa nào đó, đây là công cụ đơn giản nhất cho công việc đặc biệt này. Kết luận: sử dụng os.system()do đó cũng là một câu trả lời đúng.

    Một nhược điểm rõ rệt là startlệnh Windows yêu cầu bạn vượt qua trong shell=Trueđó phủ nhận hầu hết các lợi ích của việc sử dụng subprocess.


2
Tùy thuộc vào nơi filenamexuất hiện, đây là một ví dụ hoàn hảo về lý do tại sao os.system () không an toàn và xấu. quá trình con là tốt hơn.
Devin Jeanpierre

6
Câu trả lời của Nick có vẻ tốt với tôi. Không có gì cản trở. Giải thích những điều sử dụng các ví dụ sai không dễ dàng chính đáng.
Devin Jeanpierre

2
Nó kém an toàn và kém linh hoạt hơn so với sử dụng quy trình con. Điều đó nghe có vẻ sai đối với tôi.
Devin Jeanpierre

8
Tất nhiên nó quan trọng. Đó là sự khác biệt giữa một câu trả lời tốt và một câu trả lời tồi (hoặc một câu trả lời khủng khiếp). Các tài liệu cho os.system () tự nói "Sử dụng mô đun quy trình con." Cần gì hơn nữa? Đó là sự phản đối đủ cho tôi.
Devin Jeanpierre

20
Tôi cảm thấy hơi miễn cưỡng khi bắt đầu lại cuộc thảo luận này, nhưng tôi nghĩ phần "Cập nhật sau" hoàn toàn sai. Vấn đề với os.system()là nó sử dụng shell (và bạn không thực hiện bất kỳ shell nào thoát ra ở đây, vì vậy Bad Things sẽ xảy ra đối với các tên tệp hoàn toàn hợp lệ xảy ra để chứa các siêu ký tự shell). Lý do tại sao subprocess.call()được ưa thích là bạn có tùy chọn bỏ qua trình bao bằng cách sử dụng subprocess.call(["open", filename]). Điều này hoạt động cho tất cả các tên tệp hợp lệ và không giới thiệu lỗ hổng shell-shell ngay cả đối với tên tệp không đáng tin cậy.
Sven Marnach

150

Sử dụng subprocessmô-đun có sẵn trên Python 2.4+, không os.system(), vì vậy bạn không phải đối phó với thoát vỏ.

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

Các dấu ngoặc kép là vì subprocess.call()muốn một chuỗi là đối số đầu tiên của nó, vì vậy chúng tôi đang sử dụng một bộ dữ liệu ở đây. Trên các hệ thống Linux với Gnome cũng có một gnome-openlệnh thực hiện điều tương tự, nhưng xdg-openlà tiêu chuẩn Free Desktop Foundation và hoạt động trên các môi trường máy tính để bàn Linux.


5
Sử dụng 'start' trong sub process.call () không hoạt động trên Windows - start không thực sự là một tệp thực thi.
Tomas Sedovic

4
nitpick: trên tất cả linuxen (và tôi đoán hầu hết các BSD) bạn nên sử dụng xdg-open- linux.die.net/man/1/xdg-open
gnud

6
bắt đầu trên Windows là một lệnh shell, không phải là một lệnh thực thi. Bạn có thể sử dụng sub process.call (('start', filepath), shell = True), mặc dù nếu bạn đang thực thi trong shell, bạn cũng có thể sử dụng os.system.
Peter Graham

Tôi chạy xdg-open test.pyvà nó mở hộp thoại tải firefox cho tôi. Chuyện gì vậy? Tôi đang dùng manjaro linux.
Jason

1
@Jason Nghe có vẻ như xdg-opencấu hình của bạn bị lẫn lộn, nhưng đó không thực sự là thứ chúng tôi có thể khắc phục trong một nhận xét. Có thể xem unix.stackexchange.com/questions / 3880 / Cách
tripleee

44

Tôi thích:

os.startfile(path, 'open')

Lưu ý rằng mô-đun này hỗ trợ tên tệp có khoảng trắng trong thư mục và tệp của họ, vd

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

( tài liệu python ) 'open' không cần phải thêm (nó là mặc định). Các tài liệu đề cập cụ thể rằng điều này giống như nhấp đúp vào biểu tượng của tệp trong Windows Explorer.

Giải pháp này chỉ là cửa sổ.


Cảm ơn. Tôi đã không nhận thấy sự sẵn có, vì các tài liệu đã được thêm vào đoạn cuối cùng. Trong hầu hết các phần khác, ghi chú sẵn có chiếm dòng riêng của nó.
DrBloodmoney

Trên Linux vì một số lý do, thay vì gây ra lỗi, startfilechức năng thậm chí không tồn tại, điều đó có nghĩa là người dùng sẽ nhận được thông báo lỗi khó hiểu về một chức năng bị thiếu. Bạn có thể muốn kiểm tra nền tảng để tránh điều này.
cz

39

Chỉ để hoàn thiện (không có trong câu hỏi), xdg-open sẽ làm tương tự trên Linux.


6
+1 Thông thường, người trả lời không nên trả lời các câu hỏi không được hỏi, nhưng trong trường hợp này tôi nghĩ rằng nó rất phù hợp và hữu ích cho toàn bộ cộng đồng SO.
demongolem

đã tìm kiếm điều này
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
Huh, tôi không biết về startfile. Sẽ thật tuyệt nếu phiên bản Mac và Linux của Python chọn ngữ nghĩa tương tự.
Nick

3
Lỗi python có liên quan: bug.python.org/su3177 - cung cấp một bản vá đẹp và nó có thể được chấp nhận =)
gnud

Lệnh xdg-open cho linux
TheTechRobo36414519

21

Nếu bạn phải sử dụng một phương pháp heuristic, bạn có thể xem xét webbrowser.
Đó là thư viện chuẩn và mặc dù tên của nó, nó cũng sẽ cố gắng mở tệp:

Lưu ý rằng trên một số nền tảng, cố gắng mở tên tệp bằng chức năng này, có thể hoạt động và khởi động chương trình liên kết của hệ điều hành. Tuy nhiên, điều này không được hỗ trợ cũng không di động. ( Tham khảo )

Tôi đã thử mã này và nó hoạt động tốt trong Windows 7 và Ubuntu Natty:

import webbrowser
webbrowser.open("path_to_file")

Mã này cũng hoạt động tốt trong Windows XP Professional, sử dụng Internet Explorer 8.


3
Theo như tôi có thể nói, đây là câu trả lời tốt nhất. Có vẻ đa nền tảng và không cần kiểm tra nền tảng nào đang được sử dụng hoặc nhập os, nền tảng.
cảnh sát

2
@jonathanrocher: Tôi thấy hỗ trợ Mac trong mã nguồn . Nó sử dụng open locationở đó sẽ hoạt động nếu bạn đưa đường dẫn dưới dạng url hợp lệ.
jfs

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 "Lưu ý rằng trên một số nền tảng, cố gắng mở một tên tập tin sử dụng [webbrowser.open (url)], có thể làm việc và bắt đầu chương trình liên quan của hệ điều hành. Tuy nhiên , cái này không được hỗ trợ hay di động. "
nyanpasu64

6

Nếu bạn muốn đi subprocess.call()theo con đường, nó sẽ giống như thế này trên Windows:

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

Bạn không thể chỉ sử dụng:

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

bởi vì start không phải là một thực thi mà là một lệnh của cmd.exechương trình. Những công việc này:

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

nhưng chỉ khi không có khoảng trắng trong FILE_NAME.

Trong khi subprocess.callphương thức en trích dẫn đúng các tham số, startlệnh có một cú pháp khá lạ, trong đó:

start notes.txt

làm một cái gì đó khác hơn:

start "notes.txt"

Chuỗi trích dẫn đầu tiên nên đặt tiêu đề của cửa sổ. Để làm cho nó hoạt động với không gian, chúng ta phải làm:

start "" "my notes.txt"

đó là những gì mã trên đầu làm.


5

Bắt đầu không hỗ trợ tên đường dẫn dài và khoảng trắng. Bạn phải chuyển đổi nó thành 8.3 đường dẫn tương thích.

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 )

Tệp phải tồn tại để hoạt động với lệnh gọi API.


1
Cách giải quyết khác là để cho nó một tiêu đề trong dấu ngoặc kép, ví dụ:start "Title" "C:\long path to\file.avi"
user3364825

3

Tôi khá muộn với rất nhiều, nhưng đây là một giải pháp sử dụng api windows. Điều này luôn mở ứng dụng liên quan.

import ctypes

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

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

Rất nhiều hằng số ma thuật. Số không đầu tiên là hwnd của chương trình hiện tại. Có thể bằng không. Hai số không khác là các tham số tùy chọn (tham số và thư mục). 5 == SW_SHOW, nó chỉ định cách thực thi ứng dụng. Đọc tài liệu API ShellExecute để biết thêm thông tin.


1
Làm thế nào nó so sánh với os.startfile(file)?
jfs

2

trên mac os bạn có thể gọi 'open'

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

điều này sẽ mở tệp bằng TextEdit hoặc bất kỳ ứng dụng nào được đặt làm mặc định cho kiểu tệp này


2

Nếu bạn muốn chỉ định ứng dụng để mở tệp trên Mac OS X, hãy sử dụng: os.system("open -a [app name] [file name]")


2

Trên windows 8.1, bên dưới đã hoạt động trong khi các cách đã cho khác bị subprocess.calllỗi với đường dẫn có khoảng trắng trong đó.

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

Bằng cách sử dụng câu trả lời này và câu trả lời khác trước đây, đây là một mã nội tuyến hoạt động trên nhiều nền tảng.

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')trong Windows là tốt vì khi không gian tồn tại trong thư mục, os.system('start', path_name)không thể mở ứng dụng một cách chính xác và khi i18n tồn tại trong thư mục, os.systemcần thay đổi unicode thành codec của bàn điều khiển trong Windows.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.