Sh – управление shell-командами

онлайн тренажер по питону
Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое библиотека Sh

Во многих проектах на Python возникает необходимость вызывать shell-команды: управлять файлами, запускать системные процессы, взаимодействовать с установленными CLI-инструментами. Использовать os.system или subprocess можно, но они громоздки и требуют ручной обработки аргументов, ошибок и вывода.

Библиотека Sh предоставляет чистый, питонизированный способ вызывать shell-команды как обычные функции Python. Она значительно упрощает интеграцию внешних утилит, делает код читаемым и легко отлаживаемым.

Преимущества использования Sh

Библиотека Sh обладает рядом важных преимуществ перед стандартными методами выполнения системных команд:

Читаемость и простота синтаксиса - команды выглядят как обычные функции Python, что делает код более понятным и поддерживаемым.

Автоматическое управление ошибками - библиотека автоматически обрабатывает коды возврата и выбрасывает исключения при ошибках.

Безопасность - защита от инъекций команд благодаря правильному экранированию аргументов.

Гибкость - поддержка пайплайнов, фонового выполнения, потокового ввода-вывода и многих других возможностей.

Установка и подключение

Установка библиотеки выполняется через pip:

pip install sh

Для подключения в коде достаточно одного импорта:

import sh

Основные принципы работы

Sh автоматически маппит имя любой shell-команды на Python-функцию. Это позволяет обращаться к ним естественным образом:

print(sh.ls("-l"))

Каждый вызов возвращает объект, содержащий stdout, который может быть преобразован в строку, список строк, байты и другие форматы.

Базовое использование

Работа с файлами

# Список файлов
print(sh.ls("-lah"))

# Создание файла
sh.touch("file.txt")

# Копирование файла
sh.cp("file.txt", "copy.txt")

# Удаление файла
sh.rm("copy.txt")

# Создание директории
sh.mkdir("test_dir")

# Перемещение файла
sh.mv("file.txt", "test_dir/")

Работа с текстом

# Вывод содержимого файла
content = sh.cat("file.txt")
print(str(content))

# Поиск в файлах
matches = sh.grep("python", "*.py")
for match in matches:
    print(match.strip())

# Подсчет строк
line_count = sh.wc("-l", "file.txt")
print(f"Количество строк: {line_count.strip()}")

Работа с аргументами и опциями

Sh поддерживает различные способы передачи аргументов команд:

Позиционные аргументы

sh.ls("--color", "auto", "/home/user")

Именованные аргументы

# Длинные опции
sh.ls(color="auto", all=True, human_readable=True)

# Короткие опции
sh.ls(a=True, l=True, h=True)

Комбинированный подход

# Смешанное использование
sh.find("/home", name="*.py", type="f")

Обработка вывода команд

Получение результата как строки

output = sh.ls()
print(str(output))

Построчная обработка

for line in output:
    print(line.strip())

Работа с байтами

raw_output = sh.ls()
bytes_data = raw_output.stdout

Разделение на список строк

lines = str(sh.ls()).split('\n')
for line in lines:
    if line.strip():
        print(line)

Управление ошибками

Библиотека Sh предоставляет мощные механизмы для обработки ошибок:

Базовая обработка ошибок

try:
    sh.rm("non_existent.txt")
except sh.ErrorReturnCode_1 as e:
    print("Ошибка:", e.stderr.decode())

Обработка различных кодов возврата

try:
    result = sh.command_that_might_fail()
except sh.ErrorReturnCode as e:
    print(f"Команда завершилась с кодом: {e.exit_code}")
    print(f"Stderr: {e.stderr.decode()}")
    print(f"Stdout: {e.stdout.decode()}")

Игнорирование определенных кодов ошибок

# Разрешить коды возврата 0 и 1
sh.grep("pattern", "file.txt", _ok_code=[0, 1])

Интеграция с пайплайнами

Sh поддерживает мощные возможности пайплайнов Unix:

Простой пайплайн

result = sh.grep("python", _in=sh.ls("-l"))
print(result)

Цепочка команд

# Эквивалент: ls -la | grep ".py" | wc -l
count = sh.wc("-l", _in=sh.grep(".py", _in=sh.ls("-la")))
print(f"Количество Python файлов: {count.strip()}")

Использование оператора пайпа

# Альтернативный синтаксис
result = (sh.ls("-la") | sh.grep(".py") | sh.wc("-l"))
print(result)

Потоковый ввод и вывод

Передача данных в команду

# Передача строки
sh.cat(_in="Привет из Python\n")

# Передача файла
with open("input.txt", "r") as f:
    sh.sort(_in=f)

Чтение в реальном времени

# Чтение лога в реальном времени
proc = sh.tail("-f", "log.txt", _iter=True)
for line in proc:
    print(line.strip())
    # Добавить логику обработки

Перенаправление вывода

# Запись в файл
sh.ls("-la", _out="output.txt")

# Добавление к файлу
sh.echo("New line", _out="output.txt", _append=True)

Работа с пользовательскими командами

Создание команды по пути

my_tool = sh.Command("/usr/local/bin/mycli")
result = my_tool("--run", path="/tmp")

Проверка наличия команды

if sh.which("docker"):
    print("Docker установлен")
    docker_version = sh.docker("--version")
    print(docker_version)
else:
    print("Docker не найден")

Параллельное выполнение

Фоновое выполнение

# Запуск в фоне
p = sh.sleep(10, _bg=True)
print("Выполняется...")
# Делаем другие задачи
p.wait()  # Ждем завершения

Управление процессами

# Получение PID
proc = sh.long_running_command(_bg=True)
print(f"PID процесса: {proc.pid}")

# Проверка статуса
if proc.is_alive():
    print("Процесс еще выполняется")

# Принудительное завершение
proc.kill()

Работа с окружением

Изменение рабочей директории

with sh.pushd("/tmp"):
    sh.touch("test.txt")
    files = sh.ls()
    print(files)
# Автоматически возвращаемся в предыдущую директорию

Управление переменными окружения

with sh.env(MY_VAR="123", DEBUG="true"):
    result = sh.printenv("MY_VAR")
    print(result)

Запуск из другой директории

# Выполнить команду из указанной директории
sh.ls(_cwd="/home/user/projects")

Расширенные возможности

Timeout для команд

try:
    sh.sleep(10, _timeout=2)
except sh.TimeoutException:
    print("Команда превысила лимит времени")

Работа с TTY

# Для команд, требующих терминал
sh.sudo("apt", "update", _tty_in=True)

Декодирование вывода

# Автоматическое декодирование в строку
result = sh.ls(_decode=True)
print(type(result))  # <class 'str'>

Таблица основных методов и параметров

Метод/Параметр Описание Пример использования
sh.<command>() Вызов системной команды sh.ls("-la")
sh.Command(path) Создание команды по пути sh.Command("/bin/custom")
sh.which(cmd) Поиск команды в PATH sh.which("python3")
sh.pushd(path) Смена директории (контекст) with sh.pushd("/tmp"):
sh.env(**vars) Изменение окружения with sh.env(VAR="val"):
_in Передача входных данных sh.cat(_in="text")
_out Перенаправление вывода sh.ls(_out="file.txt")
_err Перенаправление ошибок sh.cmd(_err="errors.txt")
_bg Фоновое выполнение sh.sleep(10, _bg=True)
_timeout Ограничение времени sh.cmd(_timeout=30)
_cwd Рабочая директория sh.ls(_cwd="/home")
_ok_code Допустимые коды возврата sh.grep("pat", _ok_code=[0,1])
_tty_in Использование TTY для ввода sh.sudo("cmd", _tty_in=True)
_tty_out Использование TTY для вывода sh.cmd(_tty_out=True)
_iter Итерация по строкам for line in sh.ls(_iter=True):
_decode Авто-декодирование sh.ls(_decode=True)
_err_to_out Объединение stderr в stdout sh.cmd(_err_to_out=True)
_append Добавление к файлу sh.echo("text", _out="f", _append=True)
_long_opts Длинные опции словарем sh.cmd(_long_opts={"--opt": "val"})
.stdout Получение stdout result.stdout
.stderr Получение stderr result.stderr
.exit_code Код возврата result.exit_code
.pid PID процесса proc.pid
.wait() Ожидание завершения proc.wait()
.kill() Принудительное завершение proc.kill()
.is_alive() Проверка статуса proc.is_alive()

Безопасность и совместимость

Поддерживаемые платформы

Linux - полная поддержка всех возможностей macOS - полная поддержка Unix-команд Windows - ограниченная поддержка через WSL или CMD

Преимущества безопасности

Библиотека Sh обеспечивает высокий уровень безопасности по сравнению с альтернативами:

  • Автоматическое экранирование аргументов предотвращает инъекции
  • Безопасная передача параметров без риска выполнения произвольного кода
  • Изоляция процессов и контроль их жизненного цикла

Сравнение с другими методами

Метод Уровень API Безопасность Читаемость Поддержка ошибок Гибкость
os.system Низкий Низкая Низкая Нет Низкая
subprocess Средний Средняя Средняя Да Средняя
sh Высокий Высокая Отличная Да Высокая

Практические примеры использования

DevOps автоматизация

import sh

def deploy_application():
    # Обновление кода
    sh.git("pull", "origin", "main")
    
    # Установка зависимостей
    sh.pip("install", "-r", "requirements.txt")
    
    # Запуск тестов
    try:
        sh.pytest("tests/")
    except sh.ErrorReturnCode as e:
        print(f"Тесты не прошли: {e.stderr.decode()}")
        return False
    
    # Перезапуск сервиса
    sh.systemctl("restart", "myapp")
    return True

Мониторинг системы

def system_health_check():
    # Проверка свободного места
    disk_usage = sh.df("-h")
    print("Использование диска:")
    print(disk_usage)
    
    # Проверка памяти
    memory = sh.free("-h")
    print("Использование памяти:")
    print(memory)
    
    # Проверка загрузки CPU
    cpu_load = sh.uptime()
    print("Загрузка системы:")
    print(cpu_load)

Работа с Git

def git_operations():
    # Получение статуса репозитория
    try:
        status = sh.git("status", "--porcelain")
        if status.strip():
            print("Есть незакоммиченные изменения")
        else:
            print("Рабочая директория чистая")
    except sh.ErrorReturnCode:
        print("Не Git репозиторий")
    
    # Получение последних коммитов
    commits = sh.git("log", "--oneline", "-5")
    print("Последние 5 коммитов:")
    print(commits)

Обработка распространенных ошибок

Команда не найдена

try:
    sh.nonexistent_command()
except sh.CommandNotFound as e:
    print(f"Команда не найдена: {e}")

Неправильные аргументы

try:
    sh.ls("--invalid-option")
except sh.ErrorReturnCode as e:
    print(f"Неверная опция: {e.stderr.decode()}")

Превышение времени ожидания

try:
    sh.long_command(_timeout=10)
except sh.TimeoutException:
    print("Команда превысила лимит времени")

Лучшие практики

Обработка ошибок

Всегда обрабатывайте исключения при вызове команд, которые могут завершиться с ошибкой:

try:
    result = sh.risky_command()
except sh.ErrorReturnCode as e:
    logger.error(f"Команда завершилась с ошибкой: {e}")
    # Обработка ошибки

Использование контекстных менеджеров

Для операций, требующих изменения окружения, используйте контекстные менеджеры:

with sh.pushd("/project"):
    with sh.env(ENV="production"):
        sh.make("deploy")

Валидация команд

Проверяйте наличие команд перед использованием:

if not sh.which("docker"):
    raise RuntimeError("Docker не установлен")

Часто задаваемые вопросы

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

try:
    sh.test_command()
    exit_code = 0
except sh.ErrorReturnCode as e:
    exit_code = e.exit_code

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

sh.sudo("systemctl", "restart", "nginx", _tty_in=True)

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

text = """
Line 1
Line 2
Line 3
"""
sh.cat(_in=text)

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

lines = str(sh.ls("-1")).strip().split('\n')

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

with sh.env(MY_VAR="value"):
    sh.command_that_uses_env()

Можно ли использовать sh в многопоточных приложениях?

Да, но с осторожностью. Каждый вызов команды создает отдельный процесс, что безопасно для параллельного выполнения.

Ограничения и альтернативы

Ограничения

  • Работает только в Unix-подобных системах (Linux, macOS)
  • Ограниченная поддержка Windows
  • Не подходит для команд, требующих интерактивного ввода
  • Может быть медленнее прямого использования subprocess для простых случаев

Альтернативы

Для случаев, когда sh не подходит, рассмотрите:

  • subprocess - для более низкоуровневого контроля
  • plumbum - альтернативная библиотека с похожим API
  • fabric - для удаленного выполнения команд
  • invoke - для создания CLI-приложений

Заключение

Sh - это высокоуровневая библиотека для вызова системных команд в Python, обеспечивающая лаконичный и читаемый синтаксис. Она идеально подходит для скриптов автоматизации, работы с DevOps-инструментами и интеграции CLI-интерфейсов в Python-приложения.

Основные преимущества библиотеки:

  • Интуитивный питонический API
  • Мощные возможности обработки ошибок
  • Поддержка пайплайнов и потокового ввода-вывода
  • Безопасное выполнение команд
  • Гибкость в настройке выполнения

Благодаря этим возможностям и минимальному синтаксису, Sh делает работу с системными командами по-настоящему питонизированной и эффективной.

Новости