Apri documento con l'applicazione del sistema operativo predefinita in Python, sia in Windows che in Mac OS


126

Devo essere in grado di aprire un documento utilizzando la sua applicazione predefinita in Windows e Mac OS. Fondamentalmente, voglio fare la stessa cosa che succede quando si fa doppio clic sull'icona del documento in Explorer o Finder. Qual è il modo migliore per farlo in Python?


9
C'è stato un problema per questo da includere nella libreria standard nel tracker Python del 2008: bugs.python.org/issue3177
Ram Rachum

Risposte:


77

opene startsono cose da interprete di comandi per Mac OS / X e Windows rispettivamente, per fare questo.

Per chiamarli da Python, puoi usare subprocessmodule o os.system().

Ecco alcune considerazioni su quale pacchetto usare:

  1. Puoi chiamarli via os.system, che funziona, ma ...

    Escap: os.system funziona solo con nomi di file che non hanno spazi o altri metacaratteri della shell nel nome del percorso (ad es. A:\abc\def\a.txt), Altrimenti devono essere salvati. Esiste shlex.quoteper sistemi simili a Unix, ma nulla di veramente standard per Windows. Forse vedi anche Python, Windows: analisi delle righe di comando con shlex

    • MacOS / X: os.system("open " + shlex.quote(filename))
    • Windows: os.system("start " + filename)dove filenamedovrebbe essere evitato anche il corretto parlare .
  2. Puoi anche chiamarli tramite il subprocessmodulo, ma ...

    Per Python 2.7 e versioni successive, utilizzare semplicemente

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

    In Python 3.5+ puoi usare equivalentemente il leggermente più complesso ma anche un po 'più versatile

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

    Se devi essere compatibile con Python 2.4, puoi utilizzare subprocess.call()e implementare il tuo controllo degli errori:

    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
    

    Ora, quali sono i vantaggi dell'utilizzo subprocess?

    • Sicurezza: in teoria, questo è più sicuro, ma in realtà abbiamo bisogno di eseguire una riga di comando in un modo o nell'altro; in entrambi gli ambienti, abbiamo bisogno dell'ambiente e dei servizi per interpretare, ottenere percorsi e così via. In nessuno dei due casi stiamo eseguendo un testo arbitrario, quindi non ha un 'filename ; rm -rf /'problema intrinseco "ma puoi digitare " e, se il nome del file può essere corrotto, l'uso subprocess.callci offre una protezione aggiuntiva.
    • Gestione degli errori: in realtà non ci dà più il rilevamento degli errori, dipendiamo comunque retcodein entrambi i casi; ma il comportamento di sollevare esplicitamente un'eccezione in caso di errore ti aiuterà sicuramente a notare se si verifica un errore (sebbene in alcuni scenari, un traceback potrebbe non essere affatto più utile che semplicemente ignorare l'errore).
    • Genera un sottoprocesso (non bloccante) : non abbiamo bisogno di attendere il processo figlio, dato che stiamo per affermazione del problema che avvia un processo separato.

    All'obiezione "Ma subprocessè preferito". Tuttavia, os.system()non è deprecato ed è in qualche modo lo strumento più semplice per questo particolare lavoro. Conclusione: l'utilizzo os.system()è quindi anche una risposta corretta.

    Uno svantaggio evidente è che il startcomando Windows richiede di passare, il shell=Trueche annulla la maggior parte dei vantaggi dell'utilizzo subprocess.


2
A seconda della filenameforma, questo è un esempio perfetto del perché os.system () è insicuro e dannoso. il sottoprocesso è migliore.
Devin Jeanpierre,

6
La risposta di Nick mi andava bene. Niente si è messo in mezzo. Spiegare cose usando esempi sbagliati non è facilmente giustificabile.
Devin Jeanpierre,

2
È meno sicuro e meno flessibile rispetto all'utilizzo del sottoprocesso. Mi sembra sbagliato.
Devin Jeanpierre,

8
Certo che importa. È la differenza tra una buona risposta e una cattiva risposta (o una risposta terribile). I documenti per os.system () dicono "Usa il modulo di sottoprocesso". Cosa serve di più? Questa è deprecazione abbastanza per me.
Devin Jeanpierre,

20
Mi sento un po 'riluttante a riavviare questa discussione, ma penso che la sezione "Aggiornamento successivo" abbia sbagliato completamente. Il problema os.system()è che utilizza la shell (e non si sta eseguendo alcuna evasione dalla shell qui, quindi accadranno cose cattive per nomi di file perfettamente validi che contengono metacaratteri di shell). Il motivo per cui subprocess.call()si preferisce è che hai la possibilità di bypassare la shell usando subprocess.call(["open", filename]). Funziona con tutti i nomi di file validi e non presenta una vulnerabilità di iniezione shell anche per nomi di file non attendibili.
Sven Marnach,

151

Usa il subprocessmodulo disponibile su Python 2.4+, non os.system(), quindi non devi occuparti dell'escaping della 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))

Le doppie parentesi sono perché subprocess.call()vuole una sequenza come primo argomento, quindi qui stiamo usando una tupla. Sui sistemi Linux con Gnome esiste anche un gnome-opencomando che fa la stessa cosa, ma xdg-openè lo standard Free Desktop Foundation e funziona su ambienti desktop Linux.


5
L'uso di 'start' in subprocess.call () non funziona su Windows - start non è realmente un eseguibile.
Tomas Sedovic,

4
nitpick: su tutti Linux (e suppongo che la maggior parte dei BSD) dovresti usare xdg-open- linux.die.net/man/1/xdg-open
gnud

6
avviare su Windows è un comando shell, non un eseguibile. Puoi usare subprocess.call (('start', filepath), shell = True), sebbene se stai eseguendo in una shell potresti anche usare os.system.
Peter Graham,

Ho corso xdg-open test.pye mi ha aperto la finestra di dialogo per il download di Firefox. Cosa c'è che non va? Sono su Manjaro Linux.
Jason,

1
@Jason Sembra che la tua xdg-openconfigurazione sia confusa, ma non è proprio qualcosa che possiamo risolvere in un commento. Forse vedi unix.stackexchange.com/questions/36380/…
tripleee

44

Preferisco:

os.startfile(path, 'open')

Si noti che questo modulo supporta nomi di file con spazi nelle loro cartelle e file, ad es

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

( python docs ) 'open' non deve essere aggiunto (è il valore predefinito). I documenti menzionano specificamente che è come fare doppio clic sull'icona di un file in Esplora risorse.

Questa soluzione è solo per Windows.


Grazie. Non ho notato la disponibilità, poiché i documenti sono stati aggiunti all'ultimo paragrafo. Nella maggior parte delle altre sezioni, la nota di disponibilità occupa una propria riga.
DrBloodmoney,

Su Linux per qualche motivo, piuttosto che generare un errore, la startfilefunzione non esiste nemmeno, il che significa che gli utenti riceveranno un messaggio di errore confuso su una funzione mancante. Potresti voler controllare la piattaforma per evitarlo.
CZ

39

Solo per completezza (non era nella domanda), xdg-open farà lo stesso su Linux.


6
+1 Di solito, i rispondenti non dovrebbero rispondere a domande che non sono state poste, ma in questo caso penso che sia molto pertinente e utile per l'intera comunità SO.
demongolem,

cercavo questo
nurettin il

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
Eh, non sapevo di startfile. Sarebbe bello se le versioni Mac e Linux di Python acquisissero una semantica simile.
Nick,

3
Rilevante bug di Python: bugs.python.org/issue3177 - fornisce una bella patch, e potrebbe essere accettato =)
gnud,

comando xdg-open per linux
TheTechRobo36414519

21

Se devi usare un metodo euristico, potresti prendere in considerazione webbrowser.
È una libreria standard e nonostante il suo nome proverebbe anche ad aprire i file:

Si noti che su alcune piattaforme, tentando di aprire un nome file utilizzando questa funzione, potrebbe funzionare e avviare il programma associato al sistema operativo. Tuttavia, questo non è né supportato né portatile. ( Riferimento )

Ho provato questo codice e ha funzionato bene in Windows 7 e Ubuntu Natty:

import webbrowser
webbrowser.open("path_to_file")

Questo codice funziona anche bene in Windows XP Professional, utilizzando Internet Explorer 8.


3
Per quanto ne so, questa è di gran lunga la risposta migliore. Sembra multipiattaforma e non è necessario controllare quale piattaforma è in uso o importare sistema operativo, piattaforma.
polandeer

2
@jonathanrocher: vedo il supporto Mac nel codice sorgente . Usa open locationlì che dovrebbe funzionare se dai il percorso come un URL valido.
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 "Nota che su alcune piattaforme, il tentativo di aprire un nome file utilizzando [webbrowser.open (url)], potrebbe funzionare e avviare il programma associato al sistema operativo. Tuttavia , questo non è né supportato né portatile. "
nyanpasu64,

6

Se vuoi seguire la subprocess.call()strada, dovrebbe apparire così su Windows:

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

Non puoi semplicemente usare:

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

perché start non è un eseguibile ma un comando del cmd.exeprogramma. Questo funziona:

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

ma solo se non ci sono spazi in FILE_NAME.

Mentre subprocess.callmethod en cita correttamente i parametri, il startcomando ha una sintassi piuttosto strana, dove:

start notes.txt

fa qualcosa di diverso da:

start "notes.txt"

La prima stringa tra virgolette dovrebbe impostare il titolo della finestra. Per farlo funzionare con gli spazi, dobbiamo fare:

start "" "my notes.txt"

che è ciò che fa il codice in alto.


5

L'inizio non supporta nomi di percorsi lunghi e spazi bianchi. Devi convertirlo in percorsi compatibili 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 )

Il file deve esistere per funzionare con la chiamata API.


1
Un'altra soluzione consiste nel dargli un titolo tra virgolette, ad esstart "Title" "C:\long path to\file.avi"
User3364825,

3

Sono abbastanza tardi, ma ecco una soluzione che utilizza Windows API. Questo apre sempre l'applicazione associata.

import ctypes

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

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

Molte costanti magiche. Il primo zero è l'hwnd del programma corrente. Può essere zero. Gli altri due zeri sono parametri opzionali (parametri e directory). 5 == SW_SHOW, specifica come eseguire l'app. Leggi i documenti API ShellExecute per maggiori informazioni.


1
come si confronta os.startfile(file)?
jfs,

2

su mac os puoi chiamare 'open'

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

questo aprirebbe il file con TextEdit o qualsiasi app sia impostata come predefinita per questo tipo di file


2

Se vuoi specificare l'app con cui aprire il file su Mac OS X, usa questo: os.system("open -a [app name] [file name]")


2

Su Windows 8.1, di seguito hanno funzionato mentre altri modi dati con subprocess.callerrori con percorso contengono spazi.

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

Utilizzando questa e altre risposte prima, ecco un codice integrato che funziona su più piattaforme.

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')in Windows è utile perché quando esistono spazi nella directory, os.system('start', path_name)non è possibile aprire correttamente l'app e quando i18n esiste nella directory, è os.systemnecessario modificare l'unicode nel codec della console in Windows.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.