Мне нужно иметь возможность открыть документ с помощью приложения по умолчанию в Windows и Mac OS. По сути, я хочу сделать то же самое, что происходит, когда вы дважды щелкаете значок документа в Проводнике или Средстве поиска. Каков наилучший способ сделать это в Python?
Открыть документ с приложением по умолчанию для ОС на Python, как в Windows, так и в Mac OS
Ответ 1
open
и start
- вещи интерпретатора команд для Mac OS/X и Windows соответственно, чтобы сделать это.
Чтобы вызвать их из Python, вы можете использовать модуль subprocess
или os.system()
.
Вот соображения о том, какой пакет использовать:
-
Вы можете вызвать их через
os.system
, которая работает, но...Экранирование:
os.system
работает только с именами файлов, которые не имеют пробелов или других метасимволов оболочки в имени пути (например,A:\abc\def\a.txt
), иначе они должны быть экранированы. Для Unix-подобных систем естьshlex.quote
, но для Windows нет ничего стандартного. Может быть, увидеть также Python, Windows: синтаксический анализ командной строки с SHLEX- MacOS/X:
os.system("open " + shlex.quote(filename))
- Windows:
os.system("start " + filename)
где также следует экранировать правильно произносимоеfilename
.
- MacOS/X:
-
Вы также можете вызвать их через модуль
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
Используйте модуль 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()
тем, что subprocess.call()
хочет получить последовательность в качестве первого аргумента, поэтому здесь мы используем кортеж. В системах Linux с Gnome есть также команда gnome-open
которая делает то же самое, но xdg-open
является стандартом Free Desktop Foundation и работает во всех средах Linux.
Ответ 3
Я предпочитаю:
os.startfile(path, 'open')
Обратите внимание, что этот модуль поддерживает имена файлов, которые имеют пробелы в своих папках и файлах, например.
A:\abc\folder with spaces\file with-spaces.txt
(python docs) 'open' не нужно добавлять (это значение по умолчанию). В документах упоминается, что это похоже на двойной щелчок на значке файла в проводнике Windows.
Это решение - только окна.
Ответ 4
Просто для полноты (в этом не было вопроса) xdg-open будет делать то же самое в Linux.
Ответ 5
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])
Ответ 6
Если вам нужно использовать эвристический метод, вы можете рассмотреть webbrowser
.
Это стандартная библиотека и, несмотря на название, она также пытается открыть файлы:
Обратите внимание, что на некоторых платформах попытка открыть имя файла с помощью этой функции может сработать и запустить операционную систему, связанную с программой. Однако это не поддерживается и не переносимо. (Ссылка)
Я попробовал этот код, и он отлично работал в Windows 7 и Ubuntu Natty:
import webbrowser
webbrowser.open("path_to_file")
Этот код также отлично работает в Windows XP Professional, используя Internet Explorer 8.
Ответ 7
Если вы хотите перейти 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.call
метод en корректно цитирует параметры, команда start
имеет довольно странный синтаксис, где:
start notes.txt
делает что-то еще:
start "notes.txt"
В первой цитируемой строке следует указать заголовок окна. Чтобы заставить его работать с пробелами, мы должны сделать:
start "" "my notes.txt"
что и делает код сверху.
Ответ 8
В начало не поддерживаются длинные имена путей и пробелы. Вы должны преобразовать его в 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.
Ответ 9
Я довольно поздно для партии, но вот решение, использующее 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 docs для получения дополнительной информации.
Ответ 10
os.startfile(путь, 'open') под окнами хорош, потому что, когда в каталоге существуют пробелы, os.system('start', path_name) не может правильно открыть приложение и когда i18n существует в каталоге, os.system необходимо изменить юникод на кодек консоли в Windows.
Ответ 11
на mac os вы можете вызвать 'open'
import os
os.popen("open myfile.txt")
это откроет файл с помощью TextEdit или любое другое приложение будет установлено по умолчанию для этого типа файлов
Ответ 12
Если вы хотите указать приложение для открытия файла в Mac OS X, используйте это:
os.system("open -a [app name] [file name]")
Ответ 13
В окнах 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))