Запуск исполняемых файлов и открытие документов средствами Python
Запуск файлов и программ из Python: subprocess и os.startfile
Основное эффективное решение: subprocess.run
Модуль subprocess (начиная с Python 3.5) предоставляет унифицированный интерфейс для запуска внешних процессов. Функция run() блокирует выполнение до завершения запущенной программы, возвращает объект CompletedProcess, позволяющий проверить код возврата и при необходимости получить вывод.
import subprocess
# Запуск утилиты с аргументами
result = subprocess.run(['ls', '-la'], capture_output=True, text=True, encoding='utf-8')
print(result.stdout)
Python launch file (запуск файла (программы) из python (subprocess, os.startfile))
Пояснение: список строк ['ls', '-la'] – команда и её аргументы. Параметр capture_output=True забирает stdout и stderr; text=True возвращает строки, а не байты. После завершения проверяется result.returncode – 0 означает успех.
Возникающие проблемы
- Ошибка
FileNotFoundError– если программа не найдена. Решение: указывать полный путь или убедиться, что она в PATH. - Ошибка
subprocess.CalledProcessError– когдаcheck=Trueи код возврата не ноль. Можно обработать через try/except. - Проблемы с кодировкой вывода – использовать
encodingили обрабатывать байты.
Как открыть файл в программе по умолчанию, не запуская новую консоль?
Использовать os.startfile() (только Windows). Эта функция открывает файл или папку так же, как двойной клик мыши.
import os
os.startfile('document.pdf')
На Linux и macOS применяется subprocess.run(['xdg-open', 'document.pdf']) или open.
Возможные ошибки
- Файл не найден –
FileNotFoundError. Проверить существование через os.path.exists. - Отсутствие ассоциации – на Windows вызывает исключение
OSErrorс кодом 1155. Можно использоватьsubprocessс командойstart.
Как запустить процесс асинхронно, не дожидаясь его завершения?
Класс subprocess.Popen создаёт дочерний процесс в фоновом режиме. Вызов Popen.communicate() ждёт завершения, но если этого не делать – код продолжает выполняться параллельно.
import subprocess
process = subprocess.Popen(['sleep', '10']) # процесс зависнет на 10 секунд
print('Процесс запущен, PID:', process.pid)
# Можно продолжить работу
Если не вызвать process.wait() или process.communicate(), процесс останется «сиротой» после завершения Python. Может забить ресурсы. Рекомендуется сохранять объект и завершить явно (process.terminate()) или дождаться.
Как запустить команду с использованием оболочки (shell) и подстановками?
Параметр shell=True передаёт строку в системную оболочку. Подходит для сложных команд с пайпами или переменными среды.
subprocess.run('echo $HOME | wc -c', shell=True, capture_output=True, text=True)
Опасности
- Инъекции команд, если строка содержит пользовательский ввод. Никогда не использовать с непроверенными данными.
- Различия оболочек (sh, bash, cmd). Код может быть непереносим.
Как получить стандартный вывод и ошибку по отдельности?
Передайте stdout=subprocess.PIPE и stderr=subprocess.PIPE при использовании Popen, затем communicate() вернёт кортеж (stdout, stderr).
import subprocess
p = subprocess.Popen(['ls', '/nonexistent'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
print('STDOUT:', out.decode())
print('STDERR:', err.decode())
Как запустить программу с ограничением по времени?
Используйте timeout параметр в subprocess.run() (Python 3.6+). При превышении лимита выбрасывается subprocess.TimeoutExpired.
import subprocess
try:
subprocess.run(['sleep', '10'], timeout=5)
except subprocess.TimeoutExpired:
print('Процесс превысил время ожидания и был убит')
На старых версиях Python можно реализовать таймаут через Popen и process.wait(timeout=...).
Расширенные примеры и нестандартные сценарии
import subprocess, os, sys
# 1. Запуск с передачей данных через stdin и получением вывода
p = subprocess.Popen(['sort'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
out, _ = p.communicate(input='banana\napple\ncherry\n')
print('Отсортированные фрукты:')
print(out)
# Результат: apple banana cherry
Отсортированные фрукты: apple banana cherry
# 2. Запуск с изменением рабочей директории
result = subprocess.run(['pwd'], cwd='/tmp', capture_output=True, text=True)
print('Текущая папка:', result.stdout.strip())
# Результат: /tmp
# 3. Использование os.startfile для открытия URL в браузере (только Windows)
import os
os.startfile('https://python.org') # откроется в браузере по умолчанию
# 4. Запуск консольного приложения с постоянным потоком вывода (live output)
process = subprocess.Popen(['ping', '127.0.0.1', '-c', '5'], stdout=subprocess.PIPE, text=True)
for line in process.stdout:
print('PING:', line.strip())
process.wait()
PING: PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. PING: 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.017 ms ...
# 5. Запуск с пользовательским окружением (env)
my_env = os.environ.copy()
my_env['MY_VAR'] = '42'
subprocess.run(['echo', '$MY_VAR'], shell=True, env=my_env, capture_output=True, text=True)
# 6. Обработка пути с пробелами (используйте список, а не строку!)
program = r'C:\Program Files\MyApp\app.exe'
subprocess.run([program, '--arg1', 'value with space'])
# 7. Получение кода возврата и вывод при ошибке
try:
subprocess.run(['false'], check=True)
except subprocess.CalledProcessError as e:
print(f'Команда завершилась с кодом {e.returncode}')