Python subprocess: запуск внешних команд и процессов

10 сентября 2026
Автор

Антон Ларичев

Что такое subprocess и зачем он нужен

Модуль subprocess входит в стандартную библиотеку Python и предназначен для запуска внешних программ и системных команд из Python-кода. С его помощью можно вызывать утилиты командной строки, запускать скрипты на других языках, выполнять системные команды и управлять потоками ввода-вывода дочерних процессов.

До появления subprocess разработчики использовали os.system(), os.popen() и модуль commands. Эти инструменты имели серьёзные ограничения: плохой контроль над потоками ввода-вывода, сложности с обработкой ошибок и проблемы безопасности. Модуль subprocess заменяет все эти устаревшие подходы и предоставляет единый, мощный интерфейс.

Курс по теме

Основы Python — курс

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

subprocess.run() — основной способ запуска команд

Функция subprocess.run() появилась в Python 3.5 и является рекомендуемым способом запуска внешних команд в большинстве случаев.

Простой запуск команды

import subprocess

result = subprocess.run(['ls', '-la'])
print(result.returncode)  # 0 означает успех

Первый аргумент — список, где первый элемент это исполняемый файл, а остальные — аргументы. Такой способ передачи аргументов безопаснее, чем передача строки целиком.

import subprocess

# Запуск с аргументами
result = subprocess.run(['echo', 'Hello, World!'])
# Выведет: Hello, World!

Захват вывода команды

По умолчанию вывод команды идёт прямо в терминал. Чтобы захватить его в переменную, используется параметр capture_output=True или явное указание stdout=subprocess.PIPE.

import subprocess

result = subprocess.run(
    ['ls', '-la'],
    capture_output=True,
    text=True  # декодировать вывод как строку, а не bytes
)

print(result.stdout)   # стандартный вывод
print(result.stderr)   # стандартный поток ошибок
print(result.returncode)  # код возврата

Параметр text=True (или его алиас encoding='utf-8') автоматически декодирует байты в строку. Без него result.stdout будет объектом типа bytes.

import subprocess

# Без text=True
result = subprocess.run(['echo', 'test'], capture_output=True)
print(type(result.stdout))  # <class 'bytes'>
print(result.stdout)        # b'test\n'

# С text=True
result = subprocess.run(['echo', 'test'], capture_output=True, text=True)
print(type(result.stdout))  # <class 'str'>
print(result.stdout)        # test

Коды возврата и обработка ошибок

Каждый процесс завершается с кодом возврата: 0 означает успех, любое другое значение — ошибку. Параметр check=True заставляет subprocess.run() выбрасывать исключение subprocess.CalledProcessError при ненулевом коде возврата.

import subprocess

try:
    result = subprocess.run(
        ['cat', 'nonexistent_file.txt'],
        capture_output=True,
        text=True,
        check=True
    )
except subprocess.CalledProcessError as e:
    print(f'Команда завершилась с ошибкой: {e.returncode}')
    print(f'Stderr: {e.stderr}')

Без check=True нужно проверять returncode вручную:

import subprocess

result = subprocess.run(['cat', 'nonexistent_file.txt'], capture_output=True, text=True)

if result.returncode != 0:
    print(f'Ошибка: {result.stderr}')
else:
    print(result.stdout)

Таймаут выполнения

Параметр timeout ограничивает время выполнения команды. При превышении лимита выбрасывается subprocess.TimeoutExpired.

import subprocess

try:
    result = subprocess.run(
        ['sleep', '10'],
        timeout=3  # секунды
    )
except subprocess.TimeoutExpired:
    print('Команда превысила лимит времени')

Передача данных на stdin

Можно передать данные в стандартный ввод процесса через параметр input.

import subprocess

result = subprocess.run(
    ['grep', 'error'],
    input='INFO: starting\nERROR: something failed\nINFO: done\n',
    capture_output=True,
    text=True
)

print(result.stdout)  # ERROR: something failed

subprocess.Popen — расширенный контроль над процессом

Класс subprocess.Popen предоставляет более низкоуровневый интерфейс и используется там, где subprocess.run() недостаточно гибок: асинхронное взаимодействие с процессом, потоковое чтение вывода, работа с несколькими процессами одновременно.

Базовое использование Popen

import subprocess

# Запускаем процесс
process = subprocess.Popen(
    ['ping', '-c', '3', 'google.com'],
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True
)

# Ждём завершения и получаем вывод
stdout, stderr = process.communicate()
print(stdout)
print('Код возврата:', process.returncode)

Потоковое чтение вывода

Когда процесс долго работает и нужно обрабатывать вывод по мере поступления, используют итерацию по stdout.

import subprocess

process = subprocess.Popen(
    ['tail', '-f', '/var/log/syslog'],
    stdout=subprocess.PIPE,
    text=True
)

try:
    for line in process.stdout:
        print(f'Получено: {line}', end='')
except KeyboardInterrupt:
    process.terminate()
    process.wait()

Неблокирующий запуск

Popen не блокирует выполнение скрипта — процесс работает параллельно.

import subprocess
import time

# Запускаем процесс в фоне
process = subprocess.Popen(['sleep', '5'])

print('Процесс запущен, продолжаем работу...')
time.sleep(2)

# Проверяем, завершился ли процесс
if process.poll() is None:
    print('Процесс ещё работает')
else:
    print(f'Процесс завершился с кодом: {process.returncode}')

# Ждём завершения
process.wait()
print('Готово')

process.poll() возвращает None, если процесс ещё выполняется, или код возврата, если завершился.

Работа с каналами (pipes)

Каналы позволяют передавать вывод одной команды на вход другой — аналог оператора | в командной строке.

Цепочка команд

import subprocess

# Эквивалент: ps aux | grep python
ps = subprocess.Popen(
    ['ps', 'aux'],
    stdout=subprocess.PIPE
)

grep = subprocess.Popen(
    ['grep', 'python'],
    stdin=ps.stdout,
    stdout=subprocess.PIPE,
    text=True
)

# Закрываем stdout ps, чтобы grep получил EOF при завершении ps
ps.stdout.close()

output, _ = grep.communicate()
print(output)

Использование subprocess.DEVNULL

Для подавления вывода без его захвата используется subprocess.DEVNULL.

import subprocess

# Запускаем команду, игнорируя весь вывод
result = subprocess.run(
    ['make', 'build'],
    stdout=subprocess.DEVNULL,
    stderr=subprocess.DEVNULL
)

Параметр shell=True и безопасность

Параметр shell=True позволяет передавать команду строкой и использовать возможности оболочки: перенаправление, переменные окружения, wildcards.

import subprocess

# С shell=True можно передавать строку и использовать возможности shell
result = subprocess.run(
    'ls -la | grep .py',
    shell=True,
    capture_output=True,
    text=True
)
print(result.stdout)

Почему shell=True опасен

Если в команду подставляются данные от пользователя, shell=True открывает возможность для инъекции команд.

import subprocess

# ОПАСНО: никогда не делайте так с пользовательскими данными
user_input = 'file.txt; rm -rf /'
result = subprocess.run(
    f'cat {user_input}',
    shell=True  # выполнит и cat, и rm -rf /
)

# БЕЗОПАСНО: передавайте аргументы списком
user_input = 'file.txt; rm -rf /'
result = subprocess.run(
    ['cat', user_input],  # rm -rf / будет именем файла, а не командой
    capture_output=True
)

Правило простое: используйте shell=True только с полностью контролируемыми строками. Если аргументы поступают извне — всегда передавайте их списком.

Переменные окружения

Дочерний процесс наследует окружение родителя. Изменить или дополнить его можно через параметр env.

import subprocess
import os

# Дополняем текущее окружение
env = os.environ.copy()
env['MY_VAR'] = 'hello'
env['PATH'] = '/custom/bin:' + env['PATH']

result = subprocess.run(
    ['printenv', 'MY_VAR'],
    capture_output=True,
    text=True,
    env=env
)
print(result.stdout)  # hello

Если передать в env словарь без копирования текущего окружения, дочерний процесс получит только то, что вы указали явно.

Рабочая директория

Параметр cwd задаёт рабочую директорию для запускаемого процесса.

import subprocess

result = subprocess.run(
    ['ls'],
    cwd='/tmp',
    capture_output=True,
    text=True
)
print(result.stdout)  # содержимое /tmp

Практические примеры

Запуск Git-команд

import subprocess

def git_status(repo_path):
    result = subprocess.run(
        ['git', 'status', '--short'],
        cwd=repo_path,
        capture_output=True,
        text=True,
        check=True
    )
    return result.stdout.strip()

def git_log(repo_path, count=10):
    result = subprocess.run(
        ['git', 'log', '--oneline', f'-{count}'],
        cwd=repo_path,
        capture_output=True,
        text=True,
        check=True
    )
    return result.stdout.strip().splitlines()

status = git_status('/path/to/repo')
print('Изменённые файлы:')
print(status)

commits = git_log('/path/to/repo', count=5)
print('Последние коммиты:')
for commit in commits:
    print(commit)

Конвертация файлов с помощью ffmpeg

import subprocess
from pathlib import Path

def convert_video(input_path, output_path, codec='libx264'):
    cmd = [
        'ffmpeg',
        '-i', str(input_path),
        '-c:v', codec,
        '-y',  # перезаписать без вопроса
        str(output_path)
    ]

    result = subprocess.run(
        cmd,
        capture_output=True,
        text=True
    )

    if result.returncode != 0:
        raise RuntimeError(f'ffmpeg завершился с ошибкой:\n{result.stderr}')

    return output_path

convert_video('input.avi', 'output.mp4')

Проверка доступности хоста

import subprocess

def is_host_reachable(host, count=1, timeout=2):
    result = subprocess.run(
        ['ping', '-c', str(count), '-W', str(timeout), host],
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL
    )
    return result.returncode == 0

hosts = ['google.com', '192.168.1.1', 'nonexistent.local']
for host in hosts:
    status = 'доступен' if is_host_reachable(host) else 'недоступен'
    print(f'{host}: {status}')

Параллельный запуск нескольких процессов

import subprocess

urls = [
    'https://example.com',
    'https://httpbin.org/get',
    'https://api.github.com',
]

# Запускаем все процессы одновременно
processes = [
    subprocess.Popen(
        ['curl', '-s', '-o', '/dev/null', '-w', '%{http_code}', url],
        stdout=subprocess.PIPE,
        text=True
    )
    for url in urls
]

# Ждём завершения каждого и выводим результат
for url, process in zip(urls, processes):
    stdout, _ = process.communicate()
    print(f'{url}: HTTP {stdout.strip()}')

Управление жизненным циклом процесса

Класс Popen предоставляет методы для управления запущенным процессом.

import subprocess
import signal

process = subprocess.Popen(['long_running_script.py'])

# Отправить сигнал SIGTERM (мягкое завершение)
process.terminate()

# Дождаться завершения с таймаутом
try:
    process.wait(timeout=5)
except subprocess.TimeoutExpired:
    # Если процесс не завершился — принудительное завершение
    process.kill()
    process.wait()

print('Код возврата:', process.returncode)

Разница между terminate() и kill():

  • terminate() отправляет SIGTERM — процесс может обработать сигнал и завершиться корректно
  • kill() отправляет SIGKILL — немедленное принудительное завершение без возможности обработки

Контекстный менеджер для Popen

Объект Popen поддерживает протокол контекстного менеджера, что гарантирует корректное закрытие дескрипторов.

import subprocess

with subprocess.Popen(
    ['python3', '-c', 'import sys; sys.stdout.write("hello\\n"); sys.stdout.flush()'],
    stdout=subprocess.PIPE,
    text=True
) as process:
    stdout, _ = process.communicate()
    print(stdout)
# После блока with файловые дескрипторы закрыты

Типичные ошибки

FileNotFoundError

Выбрасывается, если исполняемый файл не найден.

import subprocess

try:
    subprocess.run(['nonexistent_program'])
except FileNotFoundError:
    print('Программа не найдена. Проверьте PATH или установите её.')

Взаимная блокировка (deadlock)

При использовании Popen напрямую с PIPE можно получить дедлок: родительский процесс ждёт данных от дочернего, а дочерний не может записать данные, потому что буфер заполнен.

import subprocess

# ОПАСНО: может привести к дедлоку при большом выводе
process = subprocess.Popen(
    ['command_with_lots_of_output'],
    stdout=subprocess.PIPE
)
process.wait()  # дедлок, если буфер stdout переполнится

# ПРАВИЛЬНО: используйте communicate()
process = subprocess.Popen(
    ['command_with_lots_of_output'],
    stdout=subprocess.PIPE
)
stdout, _ = process.communicate()  # буферизует весь вывод безопасно

Метод communicate() читает весь вывод в память и только потом возвращает управление — он защищает от дедлока, но при очень больших объёмах данных может занять много памяти.

Сравнение subprocess.run() и subprocess.Popen()

Задача subprocess.run() subprocess.Popen()
Запуск и ожидание завершения Да Через .wait() или .communicate()
Захват вывода capture_output=True stdout=PIPE
Неблокирующий запуск Нет Да
Потоковое чтение вывода Нет Да
Проверка кода возврата check=True .returncode после завершения
Простота использования Высокая Средняя

Выбирайте subprocess.run() для большинства задач и Popen только тогда, когда нужен тонкий контроль над жизненным циклом процесса или потоковая обработка вывода.

Заключение

Модуль subprocess — это мощный инструмент для интеграции Python-кода с внешними программами и системными утилитами. Ключевые принципы работы с ним: использовать subprocess.run() для типовых задач, передавать аргументы списком для безопасности, всегда обрабатывать возможные ошибки и использовать Popen только когда требуется более тонкое управление процессом.

Освоить Python с нуля и разобраться в работе стандартной библиотеки можно на курсе Python для разработчиков от PurpleSchool.

Стрелочка влевоuv — современный менеджер пакетов PythonВеб-скрапинг с Scrapy в PythonСтрелочка вправо

Постройте личный план изучения Python до уровня Middle — бесплатно!

Python — часть карты развития Backend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Все гайды по Python

Как отправлять запросы с помощью requests в PythonПочему Python выводит значение без команды printКак работает команда print в PythonВозможности Python для автоматизации задачРабота с JSON в Python на примерахPython get — методы получения данныхКак находить и исправлять ошибки в PythonРабота с данными через API и внешние сервисыСтруктура и оформление кода PythonОсновы Django с PythonПолезные приёмы в Python для повседневной работыИспользование locals в Python для отладкиИнтеграция PHP и PythonКак выполнять HTTPS-запросы в PythonFastAPI Python — быстрый старт: создание REST API с нуляКак работать с API в Python
Ввод целого числа в PythonВедение логов в PythonУдаление данных в Python с помощью removeОбработка исключений с помощью try/except в PythonФункция super() в Python — как вызвать метод родителяСоздание собственных контекстных менеджеров в PythonРабота с символами программирования PythonРабота с переменной X в PythonРабота с классами в PythonКак скачать Python на компьютерПростая программа на Python для начинающихОсновы Python для тех, кто начинаетЧто нового в Python 3Поддерживается ли Python 2 и стоит ли его использоватьPython 1 — с чего начиналась история языкаКоманда python print - полное руководство по выводу данныхПравила именования переменных в PythonПользовательские исключения в PythonОсновы Python coreОписание объектов PythonНаследование классов в Python — основы и примерыМножественное наследование в Python — примеры и MROКонтекстный менеджер with в Python — как работает и зачем нуженКомментарии в Python — однострочные, многострочные и docstringКакой Python выбрать для установкиКак вывести целое число с помощью print в PythonКак установить Python на Windows macOS и LinuxКак пользоваться консолью PythonКак получить последний элемент в PythonКак найти значение в PythonКак настроить PythonКак использовать print для строк в PythonКак работает интерпретатор PythonИнструкция по работе с PythonБлок finally в обработке исключений PythonЦелые числа в PythonАбстрактные классы в Python — ABC и abstractmethod
Загрузка данных PythonУправление проектами на GitHub с PythonСоздание веб-приложений на Flask PythonСоздание бота на PythonСоздание интерфейсов Python QTСоздание GUI в PythonСоздание игр с PygameКак работать со словарями в PythonРабота с библиотеками через Python PackagingРабота со временем в Python при помощи модуля timePython name — особенности переменнойМатематические операции в Python с модулем mathPython listing — что это и как использоватьPytest — тестирование на Python: полное руководствоОбработка изображений с OpenCV PythonNumPy в Python — основы и применение в задачахМашинное обучение с PythonИспользование Anaconda с PythonМодуль contextlib в Python — утилиты для контекстных менеджеровБиблиотеки Python и их применение в проектах
Возврат значений из функции в PythonВложенные функции в PythonСоздание собственных декораторов в PythonРабота с функцией map в PythonЦикл while в Python и примеры использованияОбработка чисел, введённых через input в PythonОсновные операторы в Python с примерамиУсловные выражения if else в Python для начинающихКак выполняется вызов функций call в PythonПродвинутые генераторы в Python — send, throw, close и корутиныПозиционные и именованные аргументы в PythonОбъявление переменных и управление областью видимости в PythonПередача аргументов по ссылке и по значению в PythonПередача аргументов через args и kwargs в PythonОсновные методы Python и примеры их использованияОператор match/case в Python 3.10+ — основы структурного сопоставленияПаттерны match/case в Python — деструктуризация, guard и вложенные шаблоныПрактические примеры match/case в Python — реальные сценарии примененияЛокальные и глобальные переменные в PythonЧасто используемые команды PythonКлючевые слова global и nonlocal в PythonКак создавать функции в PythonКак работает сборщик мусора в PythonКак работает область видимости переменных в PythonКак работает функция callable в PythonКак работает функция any и all в PythonКак проверить тип переменной в PythonКак передать функцию как аргумент в PythonКак использовать функцию isinstance в PythonКак использовать функцию filter в PythonКак использовать функцию filter в PythonКак использовать функцию eval безопасно в PythonКак использовать декораторы в PythonИзменяемые и неизменяемые типы данных в PythonГенераторы и yield в Python — как создавать и использоватьГенераторные выражения в Python — синтаксис и примерыФункции в Python и способы их вызоваФункции как объекты в PythonЧто делает функция reduce в PythonЧто такое замыкания в PythonЧто делает функция id в PythonАргументы по умолчанию в PythonАнонимные функции и lambda в PythonАлгоритмы на Python — примеры и объяснение
Запись данных в PythonУстановка pip в PythonУправление зависимостями requirement в PythonУправление библиотеками с помощью Python PackagingУдаление пробелов с помощью strip в PythonСтруктурирование кода в PythonСоздание исполняемого файла Python в exeРазбор traceback в модуле PythonРазбор site-packages в PythonРазбор Program Files в PythonРабота с Unicode кодировками в PythonРабота с системными функциями Python sysРабота с папкой AppData в PythonРабота с модулем logging в PythonРабота с каталогами в PythonРабота с CSV в PythonВиртуальная среда venv в Python — создание и настройкаКак создать простое приложение на PythonИспользование pip в Python для установки пакетовМодули в Python и организация кода в проектеИмпорт модулей в Python и правила подключенияРабота с файлами в Python пошаговоЧто делает компилятор Python и как он работаетПолучение строки из модуля PythonПодключение файлов в Python с includeПеременные среды в PythonСборка проекта с помощью packaging в PythonНастройка Python сервераИспользование Python на UbuntuИспользование консоли PythonИспользование кодировок в PythonИнициализация пакетов PythonИмпорт модулей PythonИмпорт имен в PythonСреда IDLE Python и базовые возможностиЧтение и запись TXT в PythonЧтение файлов в Python с помощью open file
Удаление элементов из списка PythonТипы данных в Python — обзор и рекомендацииОсновные операции со строками в PythonМетоды str в Python и обработка текстаСписки в Python и их ключевые методыСоздание списков данных в PythonРабота со строками и символами в PythonРабота со столбцами в PythonРабота со списком значений в PythonРабота с таблицами в Python с помощью DataFrameРабота с RFR в PythonРабота с пробелами в PythonРабота с массивами в PythonРабота с кортежами tuple PythonРабота с координатами X и Y в PythonРабота с ключами в PythonРабота с элементами данных PythonРабота с двоичными числами PythonРабота с данными в PythonРабота с данными NumPy PythonРабота с большими числами в PythonРабота с битами в PythonРабота с байтами в PythonЧто такое значение в Python и как его определитьМножества в Python и операции с нимиИспользование range в Python для цикловПроверка на четность в PythonПроверка числа в PythonПреобразование типов в PythonПреобразование списка в строку PythonПреобразование числа в строку в PythonПостроение графиков в PythonОкругление чисел в PythonОпределение индекса элемента в PythonОбъединение списков в Python с помощью zipМножества в PythonМассивы в Python и отличие от списковМассив чисел в PythonКортежи данных в PythonКак вычислить сумму чисел в PythonКак получить остаток от деления в PythonКак найти следующее число в PythonИспользование Unicode в PythonТип int в Python и его особенностиИндекс списка в PythonФункции для работы со строками в PythonЭлементы Python и способы доступа к нимДоступ к элементам массива в PythonДеление чисел в PythonРабота с данными в Python на практикеКак работать с числами в Python
Открыть базу знаний

Лучшие курсы по теме

Иконка молнииНовый
изображение курса

Основы Python

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий