Subprocess в Python: запуск команды, вывод и тайм-аут

Subprocess в Python: запуск команды, вывод и тайм-аут
Содержание статьи

Для обычного запуска внешней команды из Python начните с subprocess.run: передайте аргументы списком, задайте тайм-аут и явно решите, считать ли ненулевой код завершения ошибкой. В практикуме Mentoring.digital используем дочерний Python, поэтому пример не зависит от наличия сторонней утилиты.

Как получить stdout и stderr раздельно

capture_output=True собирает оба потока, а text=True возвращает строки. Кодировку выбирайте по договорённости с запускаемой программой. В примере вывод состоит из ASCII-символов и явно указан UTF-8.

import subprocess
import sys

command = [
    sys.executable, "-c",
    "import sys; print('ready'); print('notice', file=sys.stderr)",
]

try:
    result = subprocess.run(
        command, capture_output=True,
        text=True, encoding="utf-8",
        check=True, timeout=3,
    )
except subprocess.CalledProcessError as exc:
    print("exit:", exc.returncode)
    print("stderr:", exc.stderr)
except subprocess.TimeoutExpired:
    print("child process exceeded the time limit")
else:
    print(result.stdout.strip())  # ready
    print(result.stderr.strip())  # notice

Сообщение в stderr само по себе не означает неудачу. Некоторые программы пишут туда предупреждения при успешном коде завершения. Решение в этом примере основано на returncode, а содержимое потока помогает понять причину.

Что меняют check и timeout

При check=True ненулевой код вызывает CalledProcessError. В исключении доступны код и перехваченные потоки. Без check тот же запуск вернёт результат, и код нужно проверить самостоятельно.

При превышении timeout функция run завершает дочерний процесс, дожидается его и поднимает TimeoutExpired. Создание процесса на некоторых платформах нельзя прервать этим таймером. Поэтому параметр не является строгой гарантией общего времени вызова. Ошибка запуска, например отсутствие исполняемого файла, относится к OSError и требует отдельной обработки в рабочем приложении.

Как избежать случайного выполнения команд оболочки

По умолчанию shell=False. Передавайте исполняемый файл и каждый аргумент отдельным элементом списка. Не склеивайте пользовательский ввод с командной строкой и не включайте shell=True только ради привычного синтаксиса терминала.

Перехваченный вывод хранится в памяти. Для большого или непрерывного потока выберите чтение по мере поступления либо перенаправление в файл. run с capture_output удобен для ограниченного результата, например ответа небольшой служебной команды.

Упражнение с тремя исходами

Сначала выполните пример. Затем замените код дочернего процесса на import sys; sys.exit(2), а потом на import time; time.sleep(5). Проверьте, какая ветка срабатывает в каждом случае. Не используйте для упражнения рабочие утилиты, изменяющие файлы или данные.

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

Похожие статьи по данной теме