Открыть документ с приложением по умолчанию для ОС на Python, как в Windows, так и в Mac OS

Мне нужно иметь возможность открыть документ с помощью приложения по умолчанию в Windows и Mac OS. По сути, я хочу сделать то же самое, что происходит, когда вы дважды щелкаете значок документа в Проводнике или Средстве поиска. Каков наилучший способ сделать это в Python?

Ответ 1

open и start - вещи интерпретатора команд для Mac OS/X и Windows соответственно, чтобы сделать это.

Чтобы вызвать их из Python, вы можете использовать модуль subprocess или os.system().

Вот соображения о том, какой пакет использовать:

  1. Вы можете вызвать их через 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.
  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

Используйте модуль 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))