5 способов выполнения системных команд в Python: полное руководство
Для кого эта статья:
- Программисты и разработчики, желающие улучшить свои навыки в Python
- Системные администраторы и DevOps-инженеры, занимающиеся автоматизацией процессов
Студенты и начинающие разработчики, ищущие практическое руководство по выполнению системных команд в Python
Python — идеальный инструмент для системной автоматизации, способный управлять процессами операционной системы изнутри ваших скриптов. Когда стандартные библиотеки языка не справляются с задачей, умение запускать команды оболочки и обрабатывать их вывод становится бесценным навыком. В этой статье я разберу пять различных способов выполнения системных команд в Python — от простейших до продвинутых методов с тонким контролем над процессами. 🐍 Эти техники значительно расширят ваш инструментарий автоматизации и помогут создавать более гибкие решения.
Хотите полностью овладеть Python и системной автоматизацией? На курсе Обучение Python-разработке от Skypro вы не только изучите базовые и продвинутые методы работы с системными командами, но и освоите полный стек технологий для создания мощных автоматизированных решений. Наши студенты уже автоматизируют рутинные процессы на своих рабочих местах, экономя десятки часов еженедельно. Ваш путь к профессиональной Python-разработке начинается здесь!
Зачем нужно выполнять команды оболочки через Python
Запуск системных команд из Python открывает поистине безграничные возможности для автоматизации. Представьте: вам больше не нужно переключаться между командной строкой и скриптами — Python становится единой точкой управления всеми процессами.
Существует три ключевых сценария, когда запуск команд оболочки через Python становится незаменимым:
- Автоматизация системного администрирования — мониторинг системных ресурсов, обслуживание серверов, автоматические бэкапы и развёртывание приложений
- Интеграция с существующими инструментами командной строки — когда готовые CLI-утилиты решают специфические задачи эффективнее, чем чистый Python
- Управление параллельными процессами — запуск и контроль множественных системных операций одновременно
Михаил Дорофеев, DevOps-инженер
Однажды я столкнулся с необходимостью ежедневно контролировать использование дискового пространства на 50+ серверах. Ручная проверка занимала около двух часов. Я написал Python-скрипт, который подключался к каждому серверу через SSH, выполнял команду
df -h, анализировал вывод и отправлял уведомления при заполнении диска более чем на 80%.Скрипт использовал subprocess для локальных операций и библиотеку paramiko для SSH-подключений:
PythonСкопировать кодimport subprocess import paramiko import smtplib def check_local_disk(): result = subprocess.run(['df', '-h'], capture_output=True, text=True) # Анализ локального диска def check_remote_disk(server): client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(server, username='admin') stdin, stdout, stderr = client.exec_command('df -h') output = stdout.read().decode() # Анализ удалённого дискаТеперь вместо двух часов ежедневно процесс занимает 30 секунд работы скрипта. За год это сэкономило более 700 часов моего времени!
Стоит помнить о потенциальных проблемах при выполнении системных команд:
| Проблема | Описание | Решение |
|---|---|---|
| Безопасность | Инъекции команд при использовании пользовательского ввода | Использование аргументов в виде списка вместо строк |
| Кросс-платформенность | Команды могут отличаться на разных ОС | Проверка платформы через os.name или sys.platform |
| Производительность | Процессы оболочки потребляют ресурсы | Использование нативных Python-решений где возможно |
| Обработка ошибок | Системные команды могут завершаться с ошибкой | Проверка кодов возврата и перехват исключений |

Метод 1: os.system() — простой способ запуска команд
Модуль os.system() — самый простой способ выполнить команду оболочки в Python. Этот метод существует с ранних версий языка и работает практически идентично вызову команды напрямую из терминала.
Базовый синтаксис предельно прост:
import os
exit_code = os.system('ls -la')
print(f"Команда завершилась с кодом: {exit_code}")
Однако у этого метода есть серьёзные ограничения:
- Вы получаете только код возврата, но не сам вывод команды
- Нет прямого способа перехватить stdout или stderr
- Отсутствует контроль над процессом выполнения
- Повышенные риски безопасности при использовании с пользовательским вводом
Для захвата вывода приходится использовать перенаправление в файл и последующее чтение:
import os
import tempfile
# Создаем временный файл
temp = tempfile.NamedTemporaryFile(delete=False)
temp.close()
# Перенаправляем вывод во временный файл
os.system(f'ls -la > {temp.name}')
# Читаем результат
with open(temp.name, 'r') as f:
output = f.read()
print(output)
# Удаляем временный файл
os.unlink(temp.name)
Несмотря на архаичность, os.system() может быть полезен в следующих случаях:
- Быстрое прототипирование скриптов
- Простые сценарии, где не требуется анализ вывода
- Запуск команд, которые взаимодействуют с пользователем через терминал
- Ситуации, когда вывод команды нужно сразу показать пользователю без обработки
Метод 2: subprocess.run() — современный подход к выполнению команд
Модуль subprocess, введенный в Python 2.4 и значительно улучшенный в Python 3.5, предоставляет гораздо более мощный механизм для выполнения системных команд. Метод subprocess.run() — рекомендуемый способ для большинства задач, связанных с выполнением команд оболочки. 🔄
Базовое использование subprocess.run():
import subprocess
# Простой вызов
result = subprocess.run(['ls', '-la'])
print(f"Код возврата: {result.returncode}")
# Захват вывода
result = subprocess.run(['ls', '-la'], capture_output=True, text=True)
print(f"Stdout: {result.stdout}")
print(f"Stderr: {result.stderr}")
Ключевые преимущества subprocess.run():
- Безопасное выполнение команд с аргументами (через список)
- Простой захват stdout и stderr
- Возможность установки таймаута выполнения
- Контроль кодировки вывода
- Возможность передачи входных данных процессу
Алексей Сорокин, Data Scientist
В одном из проектов по анализу данных я столкнулся с необходимостью обрабатывать огромные CSV-файлы (более 20 ГБ). Python справлялся медленно, а инструмент командной строки csvkit работал намного эффективнее для некоторых операций.
Я создал конвейер обработки данных, где Python оркестрировал работу нескольких утилит csvkit:
PythonСкопировать кодimport subprocess import pandas as pd import tempfile def process_large_csv(input_file): # Шаг 1: Фильтрация данных с помощью csvgrep with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp: subprocess.run([ 'csvgrep', '-c', 'status', '-m', 'COMPLETED', input_file ], stdout=temp, text=True, check=True) temp_path = temp.name # Шаг 2: Агрегация данных с помощью csvsql result = subprocess.run([ 'csvsql', '--query', 'SELECT region, COUNT(*) as count FROM stdin GROUP BY region', temp_path ], capture_output=True, text=True, check=True) # Шаг 3: Анализ результатов в Python df = pd.read_csv(pd.StringIO(result.stdout)) return dfЭта комбинация Python и инструментов командной строки сократила время обработки с нескольких часов до 15 минут. Я никогда бы не достиг такой производительности, используя только Python или только командные инструменты.
Полезные параметры subprocess.run():
| Параметр | Описание | Пример использования |
|---|---|---|
| shell | Выполнение команды через оболочку (опасно с пользовательским вводом) | subprocess.run('ls -la | grep ".py"', shell=True) |
| timeout | Максимальное время выполнения в секундах | subprocess.run(['sleep', '10'], timeout=5) |
| input | Данные для отправки в stdin процесса | subprocess.run(['grep', 'pattern'], input='text to search', text=True) |
| env | Переменные окружения для процесса | subprocess.run(['script.sh'], env={'DEBUG': '1'}) |
| cwd | Рабочая директория процесса | subprocess.run(['make'], cwd='/path/to/project') |
| check | Вызов исключения при ненулевом коде возврата | subprocess.run(['grep', 'pattern', 'file.txt'], check=True) |
В Python 3.7+ появился параметр capture_output, заменяющий необходимость указывать stdout=subprocess.PIPE и stderr=subprocess.PIPE отдельно.
Метод 3: subprocess.Popen() для продвинутого контроля процессов
Класс subprocess.Popen представляет низкоуровневый интерфейс, который лежит в основе subprocess.run(). Он обеспечивает максимальную гибкость и контроль над выполняемыми процессами. Если subprocess.run() похож на высокоуровневую функцию с разумными значениями по умолчанию, то Popen — это конструктор, дающий вам полный контроль. 🛠️
Основные преимущества subprocess.Popen:
- Асинхронное выполнение команд — процесс запускается, но Python продолжает выполнение
- Возможность взаимодействия с процессом во время его работы
- Доступ к потокам ввода/вывода в реальном времени
- Управление жизненным циклом процесса (принудительное завершение, проверка статуса)
- Создание конвейеров из нескольких процессов (аналог пайпов в shell)
Базовый пример использования Popen:
import subprocess
# Запуск процесса асинхронно
process = subprocess.Popen(['ls', '-la'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True)
# Продолжение выполнения Python-кода
print("Процесс запущен, Python продолжает работать")
# Когда нужен результат – получаем его
stdout, stderr = process.communicate()
print(f"Результат: {stdout}")
print(f"Ошибки: {stderr}")
print(f"Код возврата: {process.returncode}")
Чтение вывода процесса в реальном времени:
import subprocess
import sys
# Запуск длительного процесса с выводом в реальном времени
process = subprocess.Popen(['ping', 'google.com'],
stdout=subprocess.PIPE,
text=True)
# Чтение и обработка вывода построчно
while True:
line = process.stdout.readline()
if not line:
break
print(f"Получено: {line.strip()}")
sys.stdout.flush() # Принудительный вывод в консоль
# Ожидание завершения процесса
process.wait()
print(f"Процесс завершен с кодом: {process.returncode}")
Создание конвейера процессов (аналог shell pipe):
import subprocess
# Создаем первый процесс (аналог "ls -la")
p1 = subprocess.Popen(['ls', '-la'], stdout=subprocess.PIPE, text=True)
# Создаем второй процесс, принимающий вывод первого (аналог "| grep py")
p2 = subprocess.Popen(['grep', 'py'],
stdin=p1.stdout,
stdout=subprocess.PIPE,
text=True)
# Закрываем stdout первого процесса, чтобы не блокировать EOF
p1.stdout.close()
# Получаем результат после всей обработки
output, err = p2.communicate()
print(f"Найденные Python файлы:\n{output}")
Наиболее распространенные сценарии использования subprocess.Popen:
- Длительные процессы, требующие мониторинга
- Параллельное выполнение нескольких команд
- Создание сложных конвейеров обработки данных
- Интерактивное взаимодействие с процессом (чтение/запись)
- Системные сервисы и демоны
Метод 4 и 5: os.popen() и библиотеки sh/fabric для специфических задач
Помимо основных методов, существуют альтернативные подходы, которые могут быть более удобны для определённых сценариев использования. Рассмотрим os.popen() и сторонние библиотеки sh и fabric. 🔧
Метод 4: os.popen()
Функция os.popen() представляет собой компромисс между простотой os.system() и функциональностью subprocess. Она запускает команду и возвращает файловый объект для чтения или записи.
import os
# Выполнение команды и получение вывода как файлового объекта
stream = os.popen('ls -la')
output = stream.read()
print(output)
# Проверка кода возврата
return_code = stream.close()
print(f"Код возврата: {return_code}")
Ключевые особенности os.popen():
- Более простой синтаксис для захвата вывода по сравнению с os.system()
- Позволяет обрабатывать вывод как поток (можно читать построчно)
- Ограниченные возможности по обработке ошибок
- Устаревший метод, но все еще полезный для быстрых скриптов
Метод 5: Библиотеки sh и fabric
Для тех, кто регулярно работает с системными командами, стандартные методы Python могут казаться многословными. Сторонние библиотеки предлагают более элегантный синтаксис и дополнительные возможности.
Библиотека sh позволяет вызывать системные команды как функции Python:
# pip install sh
import sh
# Вызов команд как функций
output = sh.ls("-la")
print(output)
# Поддержка конвейеров
py_files = sh.grep(sh.ls("-la"), "py")
print(py_files)
# Асинхронное выполнение
process = sh.ls("-la", _bg=True)
# Делаем что-то еще
result = process.wait()
Библиотека fabric специализируется на автоматизации выполнения команд как локально, так и на удалённых серверах через SSH:
# pip install fabric
from fabric import Connection
# Локальное выполнение
from fabric import Connection
c = Connection("localhost")
result = c.run("ls -la")
print(f"Stdout: {result.stdout}")
# Удаленное выполнение
remote = Connection("user@server")
result = remote.run("df -h")
print(f"Дисковое пространство: {result.stdout}")
# Выполнение с sudo
result = remote.sudo("apt update")
Сравнение методов выполнения команд оболочки:
| Метод | Простота использования | Функциональность | Безопасность | Лучше всего подходит для |
|---|---|---|---|---|
| os.system() | Высокая | Низкая | Низкая | Быстрых скриптов, вывод в консоль |
| subprocess.run() | Средняя | Высокая | Высокая | Большинства задач, одиночных команд |
| subprocess.Popen() | Низкая | Очень высокая | Высокая | Сложных сценариев, конвейеров, асинхронных задач |
| os.popen() | Высокая | Средняя | Средняя | Простых задач с обработкой вывода |
| sh/fabric | Высокая | Высокая | Высокая | Регулярной автоматизации, удаленного управления |
Ключевые рекомендации при выборе метода:
- Используйте subprocess.run() как основной метод для большинства задач
- Применяйте subprocess.Popen() когда нужен тонкий контроль над процессами
- Рассмотрите sh для кратких скриптов автоматизации с чистым синтаксисом
- Выбирайте fabric для задач, связанных с удалённым администрированием
- Избегайте os.system() в продакшн-коде из-за ограниченной функциональности и потенциальных проблем безопасности
Выбор правильного метода выполнения системных команд в Python напрямую влияет на безопасность, гибкость и производительность ваших скриптов. Subprocess.run() подходит для большинства повседневных задач, обеспечивая баланс между удобством и функциональностью. Для сложных сценариев с асинхронным взаимодействием незаменим subprocess.Popen(). Если вы часто работаете с удалёнными серверами, инвестируйте время в изучение fabric. Независимо от выбранного метода, помните о безопасности при обработке пользовательского ввода — всегда используйте списки аргументов вместо shell=True там, где это возможно. Овладение этими инструментами откроет перед вами новый уровень системной автоматизации.