Выполнение cmd команд в Python через subprocess

Раздел: ОС -> Использование subprocess

Основные методы запуска команд cmd из Python

Самый надежный и современный способ

Использование модуля subprocess с функцией run(). Этот метод позволяет выполнить команду, получить её вывод, код возврата и управлять потоками ввода/вывода. Он предпочтителен для большинства задач.

import subprocess

# Простой запуск команды (список аргументов, без shell)
result = subprocess.run(['cmd', '/c', 'dir', '/b'], capture_output=True, text=True)
print(result.stdout)
print(result.returncode)

Python запустить cmd (запуск команд cmd из python)

Пояснение: cmd /c dir /b запускает командную строку и выполняет dir /b. Параметр capture_output=True перехватывает stdout и stderr, text=True возвращает строки, а не байты. Код возврата 0 означает успех.

Типичные проблемы:

  • Кодировка: Если в выводе есть кириллица, может потребоваться encoding='cp866' или 'utf-8' в зависимости от системы.
  • Безопасность: Передача команды строкой через shell=True опасна (shell injection). Лучше использовать список аргументов.
  • Блокировка: Если команда долгая, программа зависнет. Используйте timeout или Popen для асинхронности.

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

Используйте subprocess.call() (устаревший) или os.system(). Подходит для простых команд, где результат не нужен.

import os
os.system('mkdir test_folder')

Проблема: Нет доступа к выводу, код возврата не всегда корректен на Windows.

Как выполнить команду и получить только stdout?

Функция subprocess.check_output() возвращает stdout, но выбрасывает исключение при ошибке.

import subprocess
try:
    output = subprocess.check_output(['cmd', '/c', 'echo', 'Привет'], text=True, encoding='cp866')
    print(output)
except subprocess.CalledProcessError as e:
    print('Ошибка:', e)

Ошибка: Если команда завершилась с ненулевым кодом, возникает исключение. Нужно обрабатывать.

Как запустить интерактивную команду (например, ping с постоянным выводом)?

Используйте subprocess.Popen для потокового чтения вывода в реальном времени.

import subprocess

proc = subprocess.Popen(['ping', '8.8.8.8', '-t'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='cp866')
for line in iter(proc.stdout.readline, ''):
    print(line.strip())
    # прервать по условию
    if 'Превышен интервал' in line:
        proc.terminate()
        break

Проблема: Чтение построчно может блокировать, если буфер не сброшен. Используйте bufsize=1 или universal_newlines=True.

Как задать таймаут для выполнения команды?

subprocess.run() поддерживает параметр timeout.

import subprocess

try:
    result = subprocess.run(['cmd', '/c', 'ping', '8.8.8.8', '-n', '10'], capture_output=True, text=True, timeout=5)
    print(result.stdout)
except subprocess.TimeoutExpired:
    print('Команда превысила время ожидания')

Ошибка: При превышении таймаута процесс не завершается автоматически. Нужно вызвать proc.kill() при использовании Popen.

Расширенные примеры использования subprocess для команд cmd

Пример 1. Передача аргументов и работа с переменными окружения

Пример
import subprocess
import os

# Устанавливаем переменную окружения для дочернего процесса
env = os.environ.copy()
env['MY_VAR'] = 'test_value'

result = subprocess.run(
    ['cmd', '/c', 'echo %MY_VAR%'],
    capture_output=True,
    text=True,
    env=env
)
print('Вывод:', result.stdout.strip())  # test_value
Вывод: test_value

Пояснение: Переменные окружения передаются через параметр env. Важно копировать os.environ, чтобы не потерять стандартные переменные.

Пример 2. Чтение вывода по мере поступления (потоковый ping)

Пример
import subprocess
import sys

proc = subprocess.Popen(
    ['ping', '127.0.0.1', '-n', '4'],
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    text=True,
    bufsize=1,
    universal_newlines=True
)

for line in proc.stdout:
    print(line, end='')
    sys.stdout.flush()

proc.wait()
print('\nКод возврата:', proc.returncode)
Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
...
Код возврата: 0

Пояснение: bufsize=1 и universal_newlines=True обеспечивают построчную буферизацию. Можно обрабатывать данные в реальном времени.

Пример 3. Передача данных на stdin команды

Пример
import subprocess

proc = subprocess.Popen(
    ['find', '/i', 'error'],  # ищем строки с 'error' в stdin
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True
)

out, err = proc.communicate(input='Line1\nError found\nLine3\n')
print('Отфильтровано:', out.strip())  # Error found
Отфильтровано: Error found

Пояснение: communicate() отправляет строку на stdin, после чего ожидает завершения процесса. Полезно для фильтрации или передачи конфигурации.

Пример 4. Запуск cmd с правами администратора (повышение привилегий)

Пример
import subprocess
import sys

# Создаём команду, которая запускает скрипт с повышенными правами
command = f'runas /user:Administrator "python {sys.argv[0]}" '
# На практике нужно передать пароль или использовать UAC
# Пример с UAC через PowerShell
cmd = ['powershell', 'Start-Process', 'cmd', '-Verb', 'RunAs', '-ArgumentList', '/c echo Admin']
result = subprocess.run(cmd, capture_output=True, text=True)
print(result.stdout)

Пояснение: Прямой запуск с правами администратора требует взаимодействия с UAC. Приведённый пример использует PowerShell. Альтернатива – использовать shell32.ShellExecuteW через ctypes.

Пример 5. Команда с долгим выполнением и принудительное завершение

Пример
import subprocess
import time

proc = subprocess.Popen(['cmd', '/c', 'ping', '8.8.8.8', '-t'], stdout=subprocess.PIPE, text=True)
time.sleep(3)  # даём поработать
proc.terminate()  # или proc.kill() для SIGKILL
out, _ = proc.communicate()
print('Вывод до завершения:', out[:200])
print('Код возврата:', proc.returncode)  # обычно 1 при terminate
Вывод до завершения: Pinging 8.8.8.8 with 32 bytes of data:
Reply from 8.8.8.8: bytes=32 time=12ms TTL=117
...
Код возврата: 1

Пояснение: terminate() посылает сигнал SIGTERM (на Windows эквивалент – WM_CLOSE). communicate() считывает оставшийся вывод и закрывает потоки.

Пример 6. Обработка ошибок и получение stderr

Пример
import subprocess

try:
    result = subprocess.run(
        ['cmd', '/c', 'dir', 'nonexistent'],
        capture_output=True,
        text=True,
        check=True  # выбросит исключение при ненулевом коде
    )
except subprocess.CalledProcessError as e:
    print('Код ошибки:', e.returncode)
    print('stderr:', e.stderr.strip())
    print('stdout:', e.stdout.strip())
Код ошибки: 1
stderr: ╨в╨░╨║╨╛╨╣ ╨┐╨░╨┐╨║╨╕ ╨╜╨╡╤В
stdout:

Пояснение: Параметр check=True упрощает отлов ошибок. Вывод stderr может быть в системной кодировке (cp866), поэтому его можно декодировать заранее.

Запуск команд cmd из Python - comments

En
Python запустить cmd (python)