Имя файла стало командой: передаём subprocess аргументы списком

Python-утилита вызывает внешний конвертер и передаёт путь к файлу. Первый вариант собирает одну строку с командой. Путь содержит пробел — программа получает два аргумента. Автор добавляет кавычки вручную, потом сталкивается с символами, которые оболочка интерпретирует по своим правилам. Простая задача передачи данных превращается в поддержку синтаксиса командной строки.

Если возможности оболочки не нужны, используйте subprocess.run со списком аргументов и стандартным shell=False. Каждый элемент списка представляет отдельный аргумент. Пробел внутри пути остаётся частью значения. Для проверки возьмём дочерний Python: он напечатает полученный аргумент, поэтому видно, что именно дошло до программы.

Аргументы без синтаксиса shell

Исполняемый файл выбирается через sys.executable. Так дочерний процесс использует тот же интерпретатор, что родитель, а не случайную команду python из PATH. text=True и encoding задают строковый результат. check=True превращает ненулевой статус в CalledProcessError, а timeout ограничивает ожидание завершения.

import subprocess
import sys

filename = "report 2026;draft.txt"
result = subprocess.run(
    [sys.executable, "-c", "import sys; print(sys.argv[1])", filename],
    capture_output=True,
    text=True,
    encoding="utf-8",
    check=True,
    timeout=5,
)
assert result.stdout.strip() == filename
assert result.stderr == ""
print(result.stdout.strip())

Вывод, статус и тайм-аут

Запустите файл на Python 3.12. Он напечатает report 2026;draft.txt целиком. Точка с запятой остаётся частью аргумента, поскольку оболочка не участвует. Для второго сценария замените код ребёнка на import sys; sys.exit(3): run поднимет CalledProcessError с returncode 3. Это проверяет, что вызывающая программа не принимает отказ конвертера за успех.

Передача списком не исправляет интерпретацию аргументов самой целевой программой. Например, путь, начинающийся с дефиса, некоторые утилиты воспринимают как опцию. Если инструмент поддерживает разделитель --, используйте его по документации. Для Windows batch-файлов поведение запуска имеет особенности; пример относится к обычному исполняемому Python, а не к cmd-сценарию.

capture_output хранит весь вывод в памяти. Для небольшого результата это удобно. Большой экспорт лучше направлять в открытый файл либо читать поток с продуманным ограничением. Не используйте PIPE как обещание, что память расходоваться не будет: run собирает оба захваченных потока перед возвратом.

На границе внешней программы полезно определить три исхода: успешный ответ, ненулевой статус, превышение времени. Для каждого добавьте отдельный тест и сохраните stderr, нужный для диагностики. Не нужно возвращать пустую строку на все ошибки. Явный список аргументов решает передачу данных, а понятная обработка статуса делает вызов полноценной частью приложения.

Когда локальный пример работает, полезно разобрать соседние механизмы языка и их взаимодействие. В курсе «Python для профессионалов» есть практика по итераторам, генераторам, декораторам, asyncio, метапрограммированию и профилированию. Эти темы помогают понимать время жизни объектов, управление выполнением и причины поведения прикладной программы.

Посмотреть программу и практику курса.

Документация: официальный источник по теме.

Другие задачи по теме: Типы, тесты и поведение программы.

Продолжить на практике

  • Python для профессионалов ↗

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