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

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

Ответы [ 13 ]

132 голосов
/ 12 января 2009

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

57 голосов
/ 12 января 2009

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() также является правильным ответом.

    Отмеченный недостаток заключается в том, что команда Windows start требует передачи вам shell=True, что сводит на нет большинство преимуществ использования subprocess.

33 голосов
/ 12 января 2009

Я предпочитаю:

os.startfile(path, 'open')

Обратите внимание, что этот модуль поддерживает имена файлов, которые имеют пробелы в своих папках и файлах, например

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

( python docs ) 'open' не нужно добавлять (это значение по умолчанию). В документах конкретно упоминается, что это похоже на двойной щелчок по значку файла в проводнике Windows.

Это решение только для Windows.

31 голосов
/ 12 января 2009

Просто для полноты (это не было в вопросе), xdg-open сделает то же самое в Linux.

21 голосов
/ 12 января 2009
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])
19 голосов
/ 14 июля 2012

Если вам нужно использовать эвристический метод, вы можете рассмотреть webbrowser.
Это стандартная библиотека, и, несмотря на название, она также пытается открыть файлы:

Обратите внимание, что на некоторых платформах при попытке открыть имя файла с помощью этого может работать и запускать операционную систему, связанную программа. Однако это не поддерживается и не переносимо. ( Ссылка )

Я попробовал этот код, и он отлично работал в Windows 7 и Ubuntu Natty:

import webbrowser
webbrowser.open("path_to_file")

Этот код также отлично работает в Windows XP Professional с использованием Internet Explorer 8.

4 голосов
/ 05 августа 2010

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.

4 голосов
/ 18 октября 2009

Если вы хотите пойти по пути 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 метод ru правильно цитирует параметры, команда start имеет довольно странный синтаксис, где:

start notes.txt

делает что-то еще, кроме:

start "notes.txt"

Первая строка в кавычках должна установить заголовок окна. Чтобы заставить его работать с пробелами, мы должны сделать:

start "" "my notes.txt"

что и делает код сверху.

2 голосов
/ 31 августа 2011

Я довольно поздно опаздываю, но вот решение с использованием Windows API. Это всегда открывает соответствующее приложение.

import ctypes

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

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

Множество магических констант. Первый ноль - это текущая программа. Может быть ноль. Два других нуля являются необязательными параметрами (параметры и каталог). 5 == SW_SHOW, он определяет, как выполнить приложение. Прочитайте ShellExecute API документы для получения дополнительной информации.

1 голос
/ 22 июля 2014

os.startfile (путь, 'open') под окнами хорош, потому что, когда в каталоге существуют пробелы, os.system ('start', path_name) не может правильно открыть приложение и когда i18n существует в каталоге , os.system необходимо изменить юникод на кодек консоли в Windows.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...