5 способов выполнения системных команд в Python: полное руководство

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Программисты и разработчики, желающие улучшить свои навыки в 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. Этот метод существует с ранних версий языка и работает практически идентично вызову команды напрямую из терминала.

Базовый синтаксис предельно прост:

Python
Скопировать код
import os
exit_code = os.system('ls -la')
print(f"Команда завершилась с кодом: {exit_code}")

Однако у этого метода есть серьёзные ограничения:

  • Вы получаете только код возврата, но не сам вывод команды
  • Нет прямого способа перехватить stdout или stderr
  • Отсутствует контроль над процессом выполнения
  • Повышенные риски безопасности при использовании с пользовательским вводом

Для захвата вывода приходится использовать перенаправление в файл и последующее чтение:

Python
Скопировать код
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():

Python
Скопировать код
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:

Python
Скопировать код
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}")

Чтение вывода процесса в реальном времени:

Python
Скопировать код
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):

Python
Скопировать код
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. Она запускает команду и возвращает файловый объект для чтения или записи.

Python
Скопировать код
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:

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:

Python
Скопировать код
# 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 там, где это возможно. Овладение этими инструментами откроет перед вами новый уровень системной автоматизации.

Загрузка...