Buka dokumen dengan aplikasi OS default dalam Python, baik di Windows maupun Mac OS

126

Saya harus dapat membuka dokumen menggunakan aplikasi standarnya di Windows dan Mac OS. Pada dasarnya, saya ingin melakukan hal yang sama yang terjadi ketika Anda mengklik dua kali pada ikon dokumen di Explorer atau Finder. Apa cara terbaik untuk melakukan ini dengan Python?

Abdullah Jibaly
sumber
9
Ada masalah untuk ini untuk dimasukkan dalam pustaka standar dalam pelacak Python mulai 2008: bugs.python.org/issue3177
Ram Rachum

Jawaban:

77

opendan starthal-hal interpreter perintah untuk Mac OS / X dan Windows masing-masing, untuk melakukan ini.

Untuk memanggil mereka dari Python, Anda bisa menggunakan subprocessmodul atau os.system().

Berikut adalah pertimbangan paket mana yang akan digunakan:

  1. Anda dapat memanggil mereka melalui os.system, yang berfungsi, tetapi ...

    Melarikan diri: os.system hanya berfungsi dengan nama file yang tidak memiliki spasi atau karakter meta shell lain di pathname (mis. A:\abc\def\a.txt), Atau ini harus diloloskan. Ada shlex.quoteuntuk sistem mirip Unix, tetapi tidak ada yang benar-benar standar untuk Windows. Mungkin lihat juga python, windows: parsing baris perintah dengan shlex

    • MacOS / X: os.system("open " + shlex.quote(filename))
    • Windows: di os.system("start " + filename)mana berbicara dengan benar filenameharus lolos juga.
  2. Anda juga dapat memanggil mereka melalui subprocessmodul, tetapi ...

    Untuk Python 2.7 dan yang lebih baru, cukup gunakan

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

    Dalam Python 3.5+ Anda dapat menggunakan cara yang sedikit lebih rumit tetapi juga agak lebih setara

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

    Jika Anda harus kompatibel kembali ke Python 2.4, Anda dapat menggunakan subprocess.call()dan mengimplementasikan pemeriksaan kesalahan Anda sendiri:

    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

    Sekarang, apa keuntungan menggunakan subprocess?

    • Keamanan: Secara teori, ini lebih aman, tetapi sebenarnya kita perlu mengeksekusi baris perintah dengan satu atau lain cara; di kedua lingkungan, kita membutuhkan lingkungan dan layanan untuk menafsirkan, mendapatkan jalur, dan sebagainya. Dalam kedua kasus kami mengeksekusi teks arbitrer, sehingga tidak memiliki masalah "tetapi Anda dapat mengetik 'filename ; rm -rf /'" yang melekat , dan jika nama file dapat rusak, menggunakan subprocess.callmemberi kami sedikit perlindungan tambahan.
    • Penanganan kesalahan: Sebenarnya tidak memberi kami deteksi kesalahan lagi, kami masih bergantung pada retcodedalam kedua kasus; tetapi perilaku untuk secara eksplisit meningkatkan pengecualian dalam kasus kesalahan pasti akan membantu Anda memperhatikan jika ada kegagalan (meskipun dalam beberapa skenario, traceback mungkin sama sekali tidak lebih membantu daripada mengabaikan kesalahan).
    • Menumbuhkan subproses (non-blocking) : Kami tidak perlu menunggu proses anak, karena kami dengan pernyataan masalah memulai proses terpisah.

    Untuk keberatan "Tetapi subprocesslebih disukai." Namun, os.system()tidak usang, dan dalam beberapa hal alat paling sederhana untuk pekerjaan khusus ini. Kesimpulan: menggunakan os.system()karena itu juga merupakan jawaban yang benar.

    Kerugian yang ditandai adalah bahwa startperintah Windows mengharuskan Anda untuk lulus shell=Trueyang meniadakan sebagian besar manfaat menggunakan subprocess.

Charlie Martin
sumber
2
Tergantung dari mana filenamebentuknya, ini adalah contoh sempurna mengapa os.system () tidak aman dan buruk. subproses lebih baik.
Devin Jeanpierre
6
Jawaban Nick tampak baik bagi saya. Tidak ada yang menghalangi. Menjelaskan hal-hal menggunakan contoh yang salah tidak mudah dibenarkan.
Devin Jeanpierre
2
Ini kurang aman dan kurang fleksibel daripada menggunakan subproses. Kedengarannya salah bagi saya.
Devin Jeanpierre
8
Tentu saja itu penting. Perbedaan antara jawaban yang baik dan jawaban yang buruk (atau jawaban yang mengerikan). Dokumen untuk os.system () sendiri mengatakan "Gunakan modul subproses." Apa lagi yang dibutuhkan? Itu cukup mencela bagi saya.
Devin Jeanpierre
20
Saya merasa agak enggan untuk memulai kembali diskusi ini, tetapi saya pikir bagian "Pembaruan nanti" sepenuhnya salah. Masalahnya os.system()adalah bahwa ia menggunakan shell (dan Anda tidak melakukan shell melarikan diri di sini, sehingga Hal Buruk akan terjadi untuk nama file yang valid sempurna yang mengandung karakter meta shell). Alasan mengapa subprocess.call()lebih disukai adalah bahwa Anda memiliki opsi untuk memotong shell dengan menggunakan subprocess.call(["open", filename]). Ini berfungsi untuk semua nama file yang valid, dan tidak memperkenalkan kerentanan injeksi shell bahkan untuk nama file yang tidak terpercaya.
Sven Marnach
151

Gunakan subprocessmodul yang tersedia di Python 2.4+, tidak os.system(), jadi Anda tidak harus berurusan dengan shell escape.

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

Tanda kurung ganda karena subprocess.call()menginginkan urutan sebagai argumen pertama, jadi kami menggunakan tuple di sini. Pada sistem Linux dengan Gnome ada juga gnome-openperintah yang melakukan hal yang sama, tetapi xdg-openmerupakan standar Free Desktop Foundation dan bekerja di seluruh lingkungan desktop Linux.

Nick
sumber
5
Menggunakan 'start' di subprocess.call () tidak berfungsi di Windows - start sebenarnya tidak dapat dieksekusi.
Tomas Sedovic
4
nitpick: pada semua linuxen (dan saya kira sebagian besar BSD) Anda harus menggunakan xdg-open- linux.die.net/man/1/xdg-open
gnud
6
mulai pada Windows adalah perintah shell, bukan yang dapat dieksekusi. Anda dapat menggunakan subprocess.call (('start', filepath), shell = True), meskipun jika Anda mengeksekusi di shell Anda mungkin juga menggunakan sistem os.s.
Peter Graham
Saya berlari xdg-open test.pydan membuka dialog unduhan firefox untuk saya. Apa yang salah? Saya di manjaro linux.
Jason
1
@Jason Kedengarannya seperti xdg-openkonfigurasi Anda bingung, tapi itu bukan sesuatu yang bisa kami atasi dalam komentar. Mungkin lihat unix.stackexchange.com/questions/36380/…
tripleee
44

Saya lebih memilih:

os.startfile(path, 'open')

Perhatikan bahwa modul ini mendukung nama file yang memiliki spasi di folder dan file mereka misalnya

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

( python docs ) 'terbuka' tidak harus ditambahkan (ini adalah default). Dokumen secara khusus menyebutkan bahwa ini seperti mengklik dua kali pada ikon file di Windows Explorer.

Solusi ini hanya untuk windows.

DrBloodmoney
sumber
Terima kasih. Saya tidak memperhatikan ketersediaannya, karena dokumen sudah ditambahkan ke paragraf terakhir. Di sebagian besar bagian lain, catatan ketersediaan menempati barisnya sendiri.
DrBloodmoney
Di Linux untuk beberapa alasan, alih-alih memunculkan kesalahan, startfilefungsi itu bahkan tidak ada, yang berarti bahwa pengguna akan mendapatkan pesan kesalahan yang membingungkan tentang fungsi yang hilang. Anda mungkin ingin memeriksa platform untuk menghindari ini.
cz
39

Hanya untuk kelengkapan (bukan dalam pertanyaan), xdg-open akan melakukan hal yang sama di Linux.

dF.
sumber
6
+1 Biasanya, responden tidak boleh menjawab pertanyaan yang tidak diajukan, tetapi dalam kasus ini saya pikir ini sangat relevan dan membantu bagi komunitas SO secara keseluruhan.
demongolem
sedang mencari ini
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])
nosklo
sumber
2
Hah, saya tidak tahu tentang startfile. Akan lebih baik jika versi Mac dan Linux dari Python mengambil semantik serupa.
Nick
3
Bug python yang relevan: bugs.python.org/issue3177 - berikan tambalan yang bagus, dan mungkin bisa diterima =)
gnud
xdg-open command untuk linux
TheTechRobo36414519
21

Jika Anda harus menggunakan metode heuristik, Anda dapat mempertimbangkan webbrowser.
Ini perpustakaan standar dan meskipun namanya juga akan mencoba untuk membuka file:

Perhatikan bahwa pada beberapa platform, mencoba membuka nama file menggunakan fungsi ini, dapat bekerja dan memulai program terkait sistem operasi. Namun, ini tidak didukung atau portabel. ( Referensi )

Saya mencoba kode ini dan berfungsi baik di Windows 7 dan Ubuntu Natty:

import webbrowser
webbrowser.open("path_to_file")

Kode ini juga berfungsi dengan baik di Windows XP Professional, menggunakan Internet Explorer 8.

etuardu
sumber
3
Sejauh yang saya tahu, ini adalah jawaban terbaik. Tampaknya lintas platform dan tidak perlu memeriksa platform mana yang digunakan atau mengimpor os, platform.
Polandia
2
@jonathanrocher: Saya melihat dukungan Mac dalam kode sumber . Itu menggunakan open locationada yang harus bekerja jika Anda memberikan path sebagai url yang valid.
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 "Perhatikan bahwa pada beberapa platform, mencoba untuk membuka nama file menggunakan [webbrowser.open (url)], dapat bekerja dan memulai program terkait sistem operasi. Namun , ini tidak didukung atau portabel. "
nyanpasu64
6

Jika Anda ingin melakukannya subprocess.call(), seharusnya terlihat seperti ini di Windows:

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

Anda tidak bisa hanya menggunakan:

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

karena start bukan merupakan executable tetapi perintah cmd.exeprogram. Ini bekerja:

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

tetapi hanya jika tidak ada spasi di FILE_NAME.

Sementara subprocess.callmetode en mengutip parameter dengan benar, startperintah tersebut memiliki sintaks yang agak aneh, di mana:

start notes.txt

melakukan sesuatu selain:

start "notes.txt"

String yang dikutip pertama harus mengatur judul jendela. Untuk membuatnya bekerja dengan spasi, kita harus melakukan:

start "" "my notes.txt"

yang dilakukan oleh kode di atas.

Tomas Sedovic
sumber
5

Mulai tidak mendukung nama jalur panjang dan spasi putih. Anda harus mengubahnya menjadi 8,3 jalur yang kompatibel.

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 )

File harus ada agar dapat berfungsi dengan panggilan API.

bFloch
sumber
1
Solusi lain adalah dengan memberinya judul dalam tanda kutip, misalnyastart "Title" "C:\long path to\file.avi"
user3364825
3

Saya cukup terlambat ke tempat parkir, tapi di sini ada solusi menggunakan windows api. Ini selalu membuka aplikasi terkait.

import ctypes

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

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

Banyak konstanta sihir. Nol pertama adalah jumlah program saat ini. Bisa nol. Dua nol lainnya adalah parameter opsional (parameter dan direktori). 5 == SW_SHOW, ini menentukan cara menjalankan aplikasi. Baca dokumen ShellExecute API untuk info lebih lanjut.

George
sumber
1
bagaimana itu dibandingkan os.startfile(file)?
jfs
2

di mac os Anda dapat memanggil 'open'

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

ini akan membuka file dengan TextEdit, atau aplikasi apa pun yang ditetapkan sebagai default untuk tipe file ini

lvinvinny
sumber
2

Jika Anda ingin menentukan aplikasi untuk membuka file dengan di Mac OS X, gunakan ini: os.system("open -a [app name] [file name]")


sumber
2

Pada windows 8.1, di bawah ini telah berfungsi sementara cara lain yang diberikan dengan subprocess.callgagal dengan path memiliki spasi di dalamnya.

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

Dengan memanfaatkan jawaban ini dan yang lain sebelumnya, inilah kode sebaris yang berfungsi pada banyak platform.

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))
Ch.Idea
sumber
2

os.startfile(path, 'open')di bawah Windows adalah baik karena ketika ruang ada di direktori, os.system('start', path_name)tidak dapat membuka aplikasi dengan benar dan ketika i18n ada di direktori, os.systemperlu mengubah unicode ke codec konsol di Windows.

BearPy
sumber