Выполнение cmd команд в Python через 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), поэтому его можно декодировать заранее.