Python 3: команды для системного администратора

Раздел: Системное администрирование -> Выполнение команд и системные вызовы

Методы выполнения системных команд в Python 3

В системном администрировании часто требуется запускать внешние программы, обрабатывать их вывод и управлять процессами. Библиотеки Python 3 предоставляют несколько способов решения этой задачи. Самым эффективным и рекомендуемым является модуль subprocess, а для простых сценариев подходят более старые функции. Каждый вариант имеет свои цели, преимущества и ограничения.

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

Модуль subprocess.run() появился в Python 3.5 как универсальная замена устаревшим функциям. Он позволяет запускать команду, ожидать её завершения, получать stdout, stderr и код возврата. Рекомендуется использовать shell=False для безопасности.

import subprocess

result = subprocess.run(['ls', '-l', '/tmp'], capture_output=True, text=True)
print('Код возврата:', result.returncode)
print('stdout:', result.stdout)
print('stderr:', result.stderr)

Python execute command line (выполнение команд командной строки из python)

Пояснение: список аргументов ['ls', '-l', '/tmp'] исключает интерпретацию shell, параметр capture_output=True захватывает вывод, text=True возвращает строки вместо байтов.

Возможные проблемы и их решение:

  • Команда не найдена - вызовет FileNotFoundError. Решение: проверить путь или использовать which перед запуском.
  • Команда зависла - добавить параметр timeout. Например: subprocess.run(..., timeout=5)
  • Кодировка вывода - если text=True не подходит, можно указать encoding='utf-8' или работать с байтами.

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

Функция os.system() из модуля os выполняет команду через системный shell. Она проста, но не даёт доступа к stdout/stderr и коду возврата (только числовой статус). Подходит для быстрых одноразовых команд.

import os

return_code = os.system('mkdir /tmp/new_folder')
if return_code == 0:
    print('Папка создана')
else:
    print('Ошибка, код:', return_code)

Python exec command (выполнение команды через exec в python)

Пояснение: команда передается строкой, вызывается /bin/sh. Результат только код возврата (0 - успех, ненулевой - ошибка).

Возможные проблемы: Уязвимость к shell injection при подстановке пользовательских данных. Решение - использовать subprocess с shell=False. Кроме того, нет контроля над выводом и временем выполнения.

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

Функция subprocess.call() запускает команду и блокирует выполнение до завершения, возвращая код возврата. Она менее гибка, чем run(), но полезна, когда результат не нужен.

import subprocess

return_code = subprocess.call(['rm', '-rf', '/tmp/old_dir'])
if return_code == 0:
    print('Удаление выполнено')
else:
    print('Ошибка удаления')

Cmd commands python (команды cmd в python)

Пояснение: call принимает те же аргументы, что и run, но не захватывает вывод (хотя можно указать stdout/stderr).

Типичные ошибки: Если команда требует много времени, программа зависнет. Для таймаута используйте run() с timeout. call не поддерживает прямое указание timeout.

Как получить только stdout команды с проверкой успешности?

Функция subprocess.check_output() возвращает стандартный вывод команды. Если код возврата не 0, выбрасывается исключение CalledProcessError. Удобно, когда нужно прочитать результат и гарантировать успех.

import subprocess

try:
    output = subprocess.check_output(['df', '-h', '/home'], text=True)
    print('Вывод:', output)
except subprocess.CalledProcessError as e:
    print('Ошибка:', e.returncode, e.output)

Python 3 commands (команды python 3)

Пояснение: text=True аналогично run(). В блоке except e.output содержит stdout (даже при ошибке).

Проблемы: Невозможно одновременно захватить и stdout, и stderr. Для этого используйте run() с capture_output=True. check_output также не имеет встроенного таймаута.

Как выполнить команду с непрерывным потоковым выводом или асинхронно?

Класс subprocess.Popen предоставляет максимальную гибкость. Он запускает процесс и возвращает объект, через который можно взаимодействовать: читать/писать в stdin, stdout, stderr, завершать процесс, ожидать. Полезно для долгих команд или интерактивного общения.

import subprocess

proc = subprocess.Popen(['ping', '-c', '4', 'google.com'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
try:
    stdout, stderr = proc.communicate(timeout=10)
    print('stdout:', stdout)
    print('stderr:', stderr)
    print('Код возврата:', proc.returncode)
except subprocess.TimeoutExpired:
    proc.kill()
    stdout, stderr = proc.communicate()
    print('Процесс завершен по таймауту')

Пояснение: PIPE перенаправляет потоки, communicate() ждет завершения и возвращает данные. Таймаут предотвращает зависание.

Сложности: Нужно правильно закрывать потоки, избегать deadlock при больших объемах данных. Рекомендуется всегда вызывать communicate() или явно закрывать PIPE. Для асинхронного выполнения используйте asyncio.create_subprocess_exec.

Расширенные примеры работы с системными командами

1. Цепочка команд и передача данных между ними

Использование subprocess.Popen для эмуляции пайпа shell без shell:

Пример
import subprocess

# Эквивалент ls | grep py
p1 = subprocess.Popen(['ls', '-la'], stdout=subprocess.PIPE, text=True)
p2 = subprocess.Popen(['grep', 'py'], stdin=p1.stdout, stdout=subprocess.PIPE, text=True)
p1.stdout.close()  # закрываем поток, чтобы p1 мог завершиться
output, _ = p2.communicate()
print(output)
drwxr-xr-x 2 user user 4096 мар 12 12:00 my_script.py

2. Запуск команды с таймаутом и обработка сигналов

Пример
import subprocess
import signal

# Команда может зависнуть, таймаут 5 секунд
proc = subprocess.Popen(['sleep', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
try:
    stdout, stderr = proc.communicate(timeout=5)
except subprocess.TimeoutExpired:
    proc.send_signal(signal.SIGTERM)  # мягкое завершение
    stdout, stderr = proc.communicate()
    print('Процесс завершен принудительно')
print('Код возврата:', proc.returncode)
Процесс завершен принудительно
Код возврата: -15

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

Пример
import subprocess

# Передача текста в bc (калькулятор)
proc = subprocess.Popen(['bc', '-l'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
stdout, _ = proc.communicate('2+2\nquit\n')
print('Результат:', stdout.strip())
Результат: 4

4. Запуск команды с изменением рабочего каталога и окружения

Пример
import subprocess
import os

my_env = os.environ.copy()
my_env['MY_VAR'] = 'hello'

result = subprocess.run(
    ['printenv', 'MY_VAR'],
    cwd='/tmp',
    env=my_env,
    capture_output=True,
    text=True
)
print('Вывод:', result.stdout)
Вывод: hello

5. Асинхронный запуск команд с asyncio

Пример
import asyncio

async def run_command():
    proc = await asyncio.create_subprocess_exec(
        'ping', '-c', '2', '127.0.0.1',
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE
    )
    stdout, stderr = await proc.communicate()
    print('Результат:')
    print(stdout.decode())

asyncio.run(run_command())
Результат:
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.036 ms
...

6. Использование shlex для безопасного разбора командной строки

Пример
import subprocess
import shlex

# Вместо строки с shell=True лучше разобрать
cmd = 'ls -la | grep py'
args = shlex.split(cmd)
print('Аргументы:', args)
# Но shlex не обрабатывает пайпы - только для простых команд
# Правильно: использовать Popen с пайпом
result = subprocess.run(shlex.split('ls -la'), capture_output=True, text=True)
print(result.stdout[:100])
Аргументы: ['ls', '-la', '|', 'grep', 'py']
(вывод ls без фильтра)

Команды Python 3 - comments

En
Python 3 commands (python)