Методы выполнения команд через exec в Python для администрирования

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

Выполнение команд через exec в Python: полное руководство

Основное решение: subprocess.run (модуль subprocess)

Для выполнения внешних команд в Python наиболее эффективным и современным способом является использование класса subprocess.run. Он заменяет устаревшие os.system и os.popen, предоставляя гибкий контроль над вводом/выводом, обработку ошибок и безопасность.

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

Пример запуска команды ls в Linux:

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

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

Пояснение:

  • Первый аргумент - список строк (программа и аргументы).
  • capture_output=True перенаправляет stdout и stderr в объект результата.
  • text=True возвращает строки вместо байтов.
  • result.returncode содержит код завершения (0 - успех).

Распространённая ошибка: инъекция через shell=True

Если команда строится из внешних данных, использование shell=True крайне опасно. Злоумышленник может передать команду, которая выполнит произвольный код. Всегда предпочтительнее передавать список аргументов, избегая оболочки. Если shell=True неизбежен, необходимо тщательно экранировать входные данные с помощью shlex.quote().

import subprocess
import shlex
user_input = '; rm -rf /'  # опасная строка
safe_cmd = 'echo ' + shlex.quote(user_input)
subprocess.run(safe_cmd, shell=True)

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


Вариант 1: os.system - простейший запуск

Функция os.system(command) выполняет команду в подоболочке и возвращает код завершения. Подходит для быстрых скриптов, где не нужен вывод.

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

import os
exit_code = os.system('mkdir -p /tmp/test')
if exit_code == 0:
    print('Директория создана')
else:
    print('Ошибка, код:', exit_code)

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

  • Проблема: вывод команды напрямую попадает в терминал, его нельзя перехватить.
  • Решение: использовать subprocess или os.popen для захвата вывода.
  • Ошибка: os.system не различает stdout и stderr, код возврата 0 не гарантирует успех.

Вариант 2: os.popen - чтение вывода команды

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

import os
handle = os.popen('ls -la')
output = handle.read()
print(output)
handle.close()

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

Функция возвращает файлоподобный объект. Для записи в команду используется режим 'w'. Однако os.popen устарел, рекомендуется subprocess.Popen.

  • Проблема: отсутствие контроля над stderr.
  • Решение: использовать subprocess.Popen с параметрами stdout и stderr.

Вариант 3: subprocess.Popen - полный контроль

Как взаимодействовать с командой в реальном времени (подавать данные и читать построчно)?

import subprocess
proc = subprocess.Popen(['ping', '-c', '4', 'google.com'],
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE,
                       text=True)
for line in proc.stdout:
    print('PING:', line.strip())
proc.wait()
print('Код:', proc.returncode)

Popen не блокирует выполнение, позволяя читать потоки по мере поступления. Require вызывает wait() для ожидания завершения.

  • Проблема: deadlock при неполном чтении stdout/stderr.
  • Решение: использовать communicate() или читать оба потока в отдельных потоках.
  • Пример безопасного чтения:
proc = subprocess.Popen(['command'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = proc.communicate()
print('stdout:', out.decode() if out else '')
print('stderr:', err.decode() if err else '')

Вариант 4: семейство os.exec - замена процесса

Функции os.exec* (execl, execvp и др.) заменяют текущий процесс новой программой. Они не создают дочерний процесс и не возвращают управление в Python.

Как полностью передать управление другой программе (например, для запуска демона)?

import os
print('До exec')
os.execvp('nano', ['nano', '/tmp/test.txt'])
print('Этот код никогда не выполнится')
# После exec процесс Python завершается и заменяется nano
  • Проблема: после exec нельзя вернуться к исходному Python-коду.
  • Используется в сценариях-обёртках (wrapper) или для перезапуска скрипта с новыми аргументами.
  • Важно: exec не порождает дочерний процесс, поэтому PID остаётся прежним.

Вариант 5: exec() - выполнение Python-кода (не системных команд)

Встроенная функция exec() выполняет динамический Python-код, переданный в виде строки. Это не системная команда, но часто путается с os.exec.

Как выполнить произвольный Python-код из строки?

code = '''
def hello():
    print("Привет из exec!")
hello()
'''
exec(code)
# Вывод: Привет из exec!
  • Опасность: exec может выполнить вредоносный код, если источник не доверенный.
  • Рекомендация: избегать exec с пользовательскими данными, использовать eval с осторожностью или безопасные альтернативы (например, ast.literal_eval).

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

Ниже приведены нетривиальные сценарии выполнения команд через subprocess и другие методы.

1. Асинхронное выполнение с помощью asyncio

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

Пример
import asyncio

async def run_cmd(cmd):
    proc = await asyncio.create_subprocess_exec(
        *cmd,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE
    )
    stdout, stderr = await proc.communicate()
    return proc.returncode, stdout.decode(), stderr.decode()

async def main():
    code, out, err = await run_cmd(['ping', '-c', '2', '8.8.8.8'])
    print(f'Код: {code}')
    print(f'Вывод: {out}')

asyncio.run(main())

Результат:

Код: 0
Вывод: PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=118 time=12.3 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=12.1 ms

--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms

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

Как передать строку в stdin внешней программы?

Пример
import subprocess
# Пример: шифрование строки через openssl
plain = b"secret data"
proc = subprocess.Popen(
    ['openssl', 'enc', '-aes-256-cbc', '-base64', '-pass', 'pass:mypassword'],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
)
out, err = proc.communicate(input=plain)
print('Зашифровано:', out.decode())
print('Ошибки:', err.decode() if err else 'нет')

Результат:

Зашифровано: U2FsdGVkX1+... (зависит от данных)
Ошибки: нет

3. Запуск с изменённым окружением (env)

Как передать собственные переменные окружения, не затрагивая внешнюю среду?

Пример
import subprocess
import os

my_env = {**os.environ, 'MY_VAR': 'hello'}
proc = subprocess.run(
    ['printenv', 'MY_VAR'],
    env=my_env,
    capture_output=True,
    text=True
)
print('MY_VAR =', proc.stdout.strip())

Результат:

MY_VAR = hello

4. Перенаправление вывода в файл

Как сохранить stdout и stderr в разные файлы?

Пример
import subprocess

with open('stdout.log', 'w') as out_f, open('stderr.log', 'w') as err_f:
    subprocess.run(
        ['ls', '/nonexistent'],
        stdout=out_f,
        stderr=err_f,
        text=True
    )
print('Вывод сохранён в файлы stdout.log и stderr.log')

Содержимое stderr.log:

ls: cannot access '/nonexistent': No such file or directory

5. Установка тайм-аута выполнения

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

Пример
import subprocess
try:
    proc = subprocess.run(['sleep', '10'], timeout=3)
    print('Команда завершилась вовремя')
except subprocess.TimeoutExpired:
    print('Команда прервана по тайм-ауту')

Результат:

Команда прервана по тайм-ауту

6. Запуск команды с повышенными правами (sudo) с неинтерактивным паролем

Как выполнить команду от root, передав пароль через stdin?

Пример
import subprocess
password = 'my_sudo_pass'
cmd = ['sudo', '-S', 'whoami']
proc = subprocess.run(cmd, input=password + '\n', capture_output=True, text=True)
print(proc.stdout.strip())  # выведет 'root' при успехе

Внимание: хранение пароля в коде небезопасно, лучше использовать sudo с NOPASSWD или expect.

Выполнение команды через exec в Python - comments

En
Python exec command (python)