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 без фильтра)