Preciso abrir um documento usando seu aplicativo padrão no Windows e Mac OS. Basicamente, quero fazer o mesmo que acontece quando você clica duas vezes no ícone do documento no Explorer ou no Finder. Qual é a melhor maneira de fazer isso no Python?
Preciso abrir um documento usando seu aplicativo padrão no Windows e Mac OS. Basicamente, quero fazer o mesmo que acontece quando você clica duas vezes no ícone do documento no Explorer ou no Finder. Qual é a melhor maneira de fazer isso no Python?
Respostas:
open
e start
são coisas de interpretador de comandos para Mac OS / X e Windows, respectivamente, para fazer isso.
Para chamá-los do Python, você pode usar subprocess
module ou os.system()
.
Aqui estão algumas considerações sobre qual pacote usar:
Você pode chamá-los via os.system
, o que funciona, mas ...
Escapando: os.system
funciona apenas com nomes de arquivos que não possuem espaços ou outros metacaracteres de shell no nome do caminho (por exemplo A:\abc\def\a.txt
), ou eles precisam ser escapados. Existe shlex.quote
para sistemas tipo Unix, mas nada realmente padrão para o Windows. Talvez veja também python, windows: analisando linhas de comando com shlex
os.system("open " + shlex.quote(filename))
os.system("start " + filename)
onde se fala corretamente também filename
deve ser evitado.Você também pode chamá-los via subprocess
módulo, mas ...
Para Python 2.7 e mais recente, basta usar
subprocess.check_call(['open', filename])
No Python 3.5+, você pode usar de forma equivalente o um pouco mais complexo, mas também um pouco mais versátil
subprocess.run(['open', filename], check=True)
Se você precisar ser compatível desde o Python 2.4, poderá usar subprocess.call()
e implementar sua própria verificação de erro:
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
Agora, quais são as vantagens de usar subprocess
?
'filename ; rm -rf /'
" e, se o nome do arquivo puder ser corrompido, o uso de subprocess.call
uma proteção adicional é reduzida.retcode
dois casos; mas o comportamento de gerar uma exceção explicitamente no caso de um erro certamente o ajudará a perceber se houver uma falha (embora em alguns cenários, um retorno de retorno possa não ser mais útil do que simplesmente ignorar o erro).À objeção "Mas subprocess
é preferível". No entanto, os.system()
não é preterido e, de certa forma, é a ferramenta mais simples para esse trabalho em particular. Conclusão: usar os.system()
é, portanto, também uma resposta correta.
Uma desvantagem acentuada é que o start
comando do Windows exige que você passe, o shell=True
que nega a maioria dos benefícios do uso subprocess
.
filename
vem a forma, este é um exemplo perfeito de por que o os.system () é inseguro e ruim. subprocesso é melhor.
os.system()
é que ele usa o shell (e você não está escapando aqui, então Bad Things acontecerá para nomes de arquivos perfeitamente válidos que contenham meta-caracteres do shell). O motivo pelo qual subprocess.call()
é preferido é que você tem a opção de ignorar o shell usando subprocess.call(["open", filename])
. Isso funciona para todos os nomes de arquivos válidos e não introduz uma vulnerabilidade de injeção de shell, mesmo para nomes de arquivos não confiáveis.
Use o subprocess
módulo disponível no Python 2.4+, não os.system()
, para que você não precise lidar com o escape de 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))
Os parênteses duplos são porque subprocess.call()
deseja uma sequência como seu primeiro argumento, então estamos usando uma tupla aqui. Nos sistemas Linux com Gnome, também existe um gnome-open
comando que faz a mesma coisa, mas xdg-open
é o padrão Free Desktop Foundation e funciona em ambientes de desktop Linux.
xdg-open
- linux.die.net/man/1/xdg-open
xdg-open test.py
e ele abriu a caixa de diálogo de download do firefox para mim. O que há de errado? Estou no manjaro linux.
xdg-open
configuração está confusa, mas isso não é algo que possamos solucionar em um comentário. Talvez veja unix.stackexchange.com/questions/36380/…
Eu prefiro:
os.startfile(path, 'open')
Observe que este módulo suporta nomes de arquivos com espaços em suas pastas e arquivos, por exemplo
A:\abc\folder with spaces\file with-spaces.txt
( docs python ) 'open' não precisa ser adicionado (é o padrão). Os documentos mencionam especificamente que isso é como clicar duas vezes no ícone de um arquivo no Windows Explorer.
Esta solução é apenas para Windows.
startfile
função nem existe, o que significa que os usuários receberão uma mensagem de erro confusa sobre uma função ausente. Você pode querer verificar a plataforma para evitar isso.
Apenas para completar (não estava em questão), o xdg-open fará o mesmo no Linux.
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])
Se você precisar usar um método heurístico, considere webbrowser
.
É uma biblioteca padrão e, apesar do nome, também tentaria abrir arquivos:
Observe que em algumas plataformas, tentar abrir um nome de arquivo usando esta função, pode funcionar e iniciar o programa associado ao sistema operacional. No entanto, isso não é suportado nem portátil. ( Referência )
Eu tentei esse código e funcionou bem no Windows 7 e no Ubuntu Natty:
import webbrowser
webbrowser.open("path_to_file")
Esse código também funciona bem no Windows XP Professional, usando o Internet Explorer 8.
open location
lá que deve funcionar se você der o caminho como um URL válido.
import webbrowser webbrowser.open("file:///Users/nameGoesHere/Desktop/folder/file.py")
Se você quiser seguir o subprocess.call()
caminho, deve ficar assim no Windows:
import subprocess
subprocess.call(('cmd', '/C', 'start', '', FILE_NAME))
Você não pode simplesmente usar:
subprocess.call(('start', FILE_NAME))
porque start
não é um executável, mas um comando do cmd.exe
programa. Isso funciona:
subprocess.call(('cmd', '/C', 'start', FILE_NAME))
mas somente se não houver espaços no FILE_NAME.
Enquanto o subprocess.call
método en cita os parâmetros corretamente, o start
comando tem uma sintaxe bastante estranha, onde:
start notes.txt
faz algo diferente de:
start "notes.txt"
A primeira string entre aspas deve definir o título da janela. Para fazê-lo funcionar com espaços, precisamos fazer:
start "" "my notes.txt"
que é o que o código na parte superior faz.
O Start não suporta nomes de caminhos longos e espaços em branco. Você precisa convertê-lo em caminhos compatíveis com 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 )
O arquivo precisa existir para funcionar com a chamada da API.
start "Title" "C:\long path to\file.avi"
Estou muito atrasado para o lote, mas aqui está uma solução usando a API do Windows. Isso sempre abre o aplicativo associado.
import ctypes
shell32 = ctypes.windll.shell32
file = 'somedocument.doc'
shell32.ShellExecuteA(0,"open",file,0,0,5)
Muitas constantes mágicas. O primeiro zero é o hwnd do programa atual. Pode ser zero. Os outros dois zeros são parâmetros opcionais (parâmetros e diretório). 5 == SW_SHOW, especifica como executar o aplicativo. Leia os documentos da API ShellExecute para obter mais informações.
os.startfile(file)
?
Se você deseja especificar o aplicativo para abrir o arquivo no Mac OS X, use este:
os.system("open -a [app name] [file name]")
No Windows 8.1, abaixo funcionou, enquanto outras formas dadas com subprocess.call
falha no caminho possuem espaços.
subprocess.call('cmd /c start "" "any file path with spaces"')
Utilizando essas e outras respostas anteriores, aqui está um código embutido que funciona em várias plataformas.
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))