เปิดเอกสารด้วยแอปพลิเคชัน OS เริ่มต้นใน Python ทั้งใน Windows และ Mac OS


127

ฉันต้องสามารถเปิดเอกสารโดยใช้แอปพลิเคชันเริ่มต้นใน Windows และ Mac OS โดยพื้นฐานแล้วฉันต้องการทำสิ่งเดียวกันกับที่เกิดขึ้นเมื่อคุณดับเบิลคลิกที่ไอคอนเอกสารใน Explorer หรือ Finder วิธีที่ดีที่สุดในการทำ Python คืออะไร?


9
มีปัญหาสำหรับสิ่งนี้ที่จะรวมอยู่ในไลบรารีมาตรฐานในตัวติดตาม Python ตั้งแต่ปี 2008: bugs.python.org/issue3177
Ram Rachum

คำตอบ:


77

openและstartเป็นตัวแปลคำสั่งสำหรับ Mac OS / X และ Windows ตามลำดับเพื่อทำสิ่งนี้

หากต้องการเรียกใช้จาก Python คุณสามารถใช้subprocessโมดูลหรือos.system().

ข้อควรพิจารณาในการใช้แพ็คเกจมีดังนี้

  1. คุณสามารถโทรหาพวกเขาผ่านทางos.systemซึ่งใช้งานได้ แต่ ...

    การ หลีกเลี่ยง:os.systemใช้ได้เฉพาะกับชื่อไฟล์ที่ไม่มีช่องว่างหรือเมตาอักขระเชลล์อื่น ๆ ในชื่อพา ธ (เช่นA:\abc\def\a.txt) มิฉะนั้นจะต้องใช้ Escape มี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()จึงเป็นคำตอบที่ถูกต้องเช่นกัน

    ข้อเสียที่ทำเครื่องหมายไว้คือstartคำสั่งของWindows ต้องการให้คุณส่งผ่านshell=Trueซึ่งจะปฏิเสธประโยชน์ส่วนใหญ่ของการใช้งานsubprocessซึ่งขัดแย้งมากที่สุดของประโยชน์ของการใช้


2
filenameนี่คือตัวอย่างที่สมบูรณ์แบบว่าทำไม os.system () จึงไม่ปลอดภัยและไม่ปลอดภัยทั้งนี้ขึ้นอยู่กับที่มา กระบวนการย่อยจะดีกว่า
Devin Jeanpierre

6
คำตอบของ Nick ดูดีสำหรับฉัน ไม่มีอะไรขวางทาง การอธิบายสิ่งต่าง ๆ โดยใช้ตัวอย่างที่ไม่ถูกต้องไม่ใช่เรื่องที่สมเหตุสมผล
Devin Jeanpierre

2
มีความปลอดภัยน้อยกว่าและมีความยืดหยุ่นน้อยกว่าการใช้กระบวนการย่อย นั่นฟังดูผิดสำหรับฉัน
Devin Jeanpierre

8
แน่นอนว่ามันสำคัญ ความแตกต่างระหว่างคำตอบที่ดีและคำตอบที่ไม่ดี (หรือคำตอบที่แย่มาก) เอกสารสำหรับ os.system () พูดว่า "ใช้โมดูลกระบวนการย่อย" ต้องการอะไรอีก? นั่นเพียงพอแล้วสำหรับฉัน
Devin Jeanpierre

20
ฉันรู้สึกลังเลเล็กน้อยที่จะเริ่มการสนทนานี้ใหม่ แต่ฉันคิดว่าส่วน "อัปเดตในภายหลัง" ทำให้เกิดข้อผิดพลาดอย่างสิ้นเชิง ปัญหาos.system()คือมันใช้เชลล์ (และคุณไม่ได้ทำเชลล์หนีที่นี่สิ่งที่ไม่ดีจะเกิดขึ้นกับชื่อไฟล์ที่ถูกต้องอย่างสมบูรณ์ซึ่งมีเมตา - อักขระของเชลล์) เหตุผลที่เป็นที่ต้องการคือการที่คุณสามารถเลือกที่จะข้ามเปลือกโดยใช้subprocess.call() subprocess.call(["open", filename])สิ่งนี้ใช้ได้กับชื่อไฟล์ที่ถูกต้องทั้งหมดและไม่ทำให้เกิดช่องโหว่การแทรกเชลล์แม้กระทั่งสำหรับชื่อไฟล์ที่ไม่น่าเชื่อถือ
Sven Marnach

151

ใช้subprocessโมดูลที่มีอยู่ใน Python 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()ต้องการลำดับเป็นอาร์กิวเมนต์แรกดังนั้นเราจึงใช้ทูเพิลที่นี่ บนระบบ Linux ที่มี Gnome ยังมีgnome-openคำสั่งที่ทำสิ่งเดียวกัน แต่xdg-openเป็นมาตรฐาน Free Desktop Foundation และใช้ได้กับสภาพแวดล้อมเดสก์ท็อป Linux


5
การใช้ 'start' ใน subprocess.call () ไม่สามารถใช้งานได้บน Windows - start ไม่ใช่ปฏิบัติการจริงๆ
Tomas Sedovic

4
nitpick: ใน linuxen ทั้งหมด (และฉันเดาว่า BSD ส่วนใหญ่) คุณควรใช้xdg-open- linux.die.net/man/1/xdg-open
gnud

6
start บน Windows เป็นคำสั่งเชลล์ไม่ใช่ปฏิบัติการ คุณสามารถใช้ subprocess.call (('start', filepath), shell = True) แม้ว่าคุณจะดำเนินการในเชลล์คุณก็อาจใช้ os.system ได้เช่นกัน
Peter Graham

ฉันวิ่งxdg-open test.pyและเปิดกล่องโต้ตอบดาวน์โหลด firefox ให้ฉัน มีอะไรผิดปกติ ฉันใช้ manjaro linux
Jason

1
@ Jason ดูเหมือนว่าxdg-openการกำหนดค่าของคุณจะสับสน แต่นั่นไม่ใช่สิ่งที่เราสามารถแก้ไขได้ในความคิดเห็น อาจจะดูunix.stackexchange.com/questions/36380/…
tripleee

44

ฉันชอบ:

os.startfile(path, 'open')

โปรดทราบว่าโมดูลนี้รองรับชื่อไฟล์ที่มีช่องว่างในโฟลเดอร์และไฟล์เช่น

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

( python docs ) ไม่จำเป็นต้องเพิ่ม 'open' (เป็นค่าเริ่มต้น) เอกสารกล่าวถึงโดยเฉพาะว่าสิ่งนี้เหมือนกับการดับเบิลคลิกที่ไอคอนของไฟล์ใน Windows Explorer

โซลูชันนี้ใช้เฉพาะ Windows เท่านั้น


ขอบคุณ ฉันไม่สังเกตเห็นความพร้อมใช้งานเนื่องจากเอกสารได้ต่อท้ายย่อหน้าสุดท้าย ในส่วนอื่น ๆ ส่วนใหญ่หมายเหตุความพร้อมใช้งานจะอยู่ในบรรทัดของตัวเอง
DrBloodmoney

ใน Linux ด้วยเหตุผลบางประการแทนที่จะเพิ่มข้อผิดพลาดstartfileฟังก์ชันนี้ไม่มีอยู่ด้วยซ้ำซึ่งหมายความว่าผู้ใช้จะได้รับข้อความแสดงข้อผิดพลาดที่ทำให้สับสนเกี่ยวกับฟังก์ชันที่หายไป คุณอาจต้องการตรวจสอบแพลตฟอร์มเพื่อหลีกเลี่ยงปัญหานี้
cz

39

เพื่อความสมบูรณ์ (ไม่ได้อยู่ในคำถาม) xdg-openจะทำเช่นเดียวกันบน Linux


6
+1 โดยปกติแล้วผู้ตอบไม่ควรตอบคำถามที่ไม่ได้ถาม แต่ในกรณีนี้ฉันคิดว่ามันมีความเกี่ยวข้องและเป็นประโยชน์มากสำหรับชุมชน SO โดยรวม
demongolem

กำลังมองหาสิ่งนี้
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
ฉันไม่รู้เกี่ยวกับ startfile คงจะดีไม่น้อยถ้า Python เวอร์ชัน Mac และ Linux เลือกใช้ความหมายที่คล้ายกัน
นิค

3
ข้อผิดพลาด python ที่เกี่ยวข้อง: bugs.python.org/issue3177 - จัดเตรียมแพทช์ที่ดีและอาจได้รับการยอมรับ =)
gnud

xdg-open command สำหรับ linux
TheTechRobo36414519

21

webbrowserหากคุณมีการใช้วิธีการแก้ปัญหาคุณอาจพิจารณา
เป็นไลบรารีมาตรฐานและแม้จะมีชื่อ แต่ก็ยังพยายามเปิดไฟล์:

โปรดทราบว่าในบางแพลตฟอร์มการพยายามเปิดชื่อไฟล์โดยใช้ฟังก์ชันนี้อาจใช้งานได้และเริ่มโปรแกรมที่เกี่ยวข้องกับระบบปฏิบัติการ อย่างไรก็ตามสิ่งนี้ไม่ได้รับการสนับสนุนหรือพกพา ( อ้างอิง )

ฉันลองใช้รหัสนี้และใช้งานได้ดีใน Windows 7 และ Ubuntu Natty:

import webbrowser
webbrowser.open("path_to_file")

รหัสนี้ยังใช้งานได้ดีใน Windows XP Professional โดยใช้ Internet Explorer 8


3
เท่าที่ฉันสามารถบอกได้นี่เป็นคำตอบที่ดีที่สุด ดูเหมือนข้ามแพลตฟอร์มและไม่จำเป็นต้องตรวจสอบว่าแพลตฟอร์มใดใช้งานอยู่หรือนำเข้าระบบปฏิบัติการแพลตฟอร์ม
polandeer

2
@jonathanrocher: ผมเห็นการสนับสนุน Mac ในรหัสที่มา ใช้open locationที่นั่นควรใช้งานได้หากคุณให้เส้นทางเป็น URL ที่ถูกต้อง
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 "โปรดทราบว่าในบางแพลตฟอร์มการพยายามเปิดชื่อไฟล์โดยใช้ [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.callmethod enอ้างถึงพารามิเตอร์อย่างถูกต้อง แต่startคำสั่งมีไวยากรณ์ที่ค่อนข้างแปลกโดยที่:

start notes.txt

ทำอย่างอื่นที่ไม่ใช่:

start "notes.txt"

สตริงที่ยกมาชุดแรกควรตั้งชื่อของหน้าต่าง เพื่อให้มันใช้งานได้กับช่องว่างเราต้องทำ:

start "" "my notes.txt"

ซึ่งเป็นสิ่งที่โค้ดด้านบนทำ


5

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


1
วิธีแก้ปัญหาอีกประการหนึ่งคือตั้งชื่อเรื่องในเครื่องหมายคำพูดเช่นstart "Title" "C:\long path to\file.avi"
user3364825

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

3

os.startfile(path, 'open')ภายใต้ Windows เป็นสิ่งที่ดีเนื่องจากเมื่อมีช่องว่างในไดเรกทอรีos.system('start', path_name)ไม่สามารถเปิดแอปได้อย่างถูกต้องและเมื่อมี i18n อยู่ในไดเรกทอรีos.systemจำเป็นต้องเปลี่ยน Unicode เป็นตัวแปลงสัญญาณของคอนโซลใน Windows


ทำงานได้อย่างสมบูรณ์ใน Python 3!
luca76

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))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.