Привет, Хабр! В мире автоматизации часто возникает вопрос: писать скрипт на Bash или на Python? Оба инструмента подходят отлично, но принципиально разные. Bash - больше про "скоропись", для системных задач, где важна скорость и краткость. Python же - универсальный язык, который намного лучше справляется со сложной логикой и структурами данных. Но когда лучше выбрать один, а когда - другой?

Некоторые задачи в Bash решаются одной строкой, когда же на Python потребуется десяток строк кода. При усложнении сценария - Bash превращается в головоломку из awk, sed и прочих, что значительно усложняет поддержку. В данной статье сравним подходы и определим, когда и какой язык лучше использовать.

Введение

Python - универсальный язык, но иногда Bash справится с задачей в разы быстрее и лаконичнее. Особенно в данных ситуациях:

  • Работа с файлами (поиск, замена, обработка)

  • Вызов системных утилит (grep, awk, sed, find и тд.)

  • Интеграция с системными утилитами (systemctl, top и тд.)

  • Конвейерная обработка данных (пайплайны, перенаправление вывода)

Но и у Bash есть слабые места: он не очень хорошо работает со сложными структурами данных (JSON, XML), сложно масштабируется, а код быстро становится нечитаемым.

Анализ: когда выигрывает Bash, а когда Python незаменим

1. Обработка логов: сравнение подходов

Bash:

grep ' 500 ' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -5

Преимущества:

  • Быстрая скорость обработки (особенно ощутимо на больших файлах)

  • Минимальное потребление памяти (потоковая обработка)

Недостатки:

  • Хрупкость (зависимость от формата логов)

  • Сложность доработки (например, если нужно добавить фильтр по дате)

  • Плохая читаемость при усложнении логики

Python:

from collections import defaultdict
import re

log_file = "/var/log/nginx/access.log"
ip_counts = defaultdict(int)
log_pattern = re.compile(r'^(\S+) \S+ \S+ \[(.*?)\] "(.*?)" (\d+)')

with open(log_file) as f:
    for line in f:
        match = log_pattern.match(line)
        if match and match.group(4) == "500":
            ip = match.group(1)
            ip_counts[ip] += 1

top_ips = sorted(ip_counts.items(), key=lambda x: x[1], reverse=True)[:5]
for ip, count in top_ips:
    print(f"{count}\t{ip}")

Преимущества:

  • Четкая структура парсинга через регулярные выражения

  • Легкость добавления дополнительной логики (фильтрация по дате, URL и т.д.)

  • Возможность обработки некорректных строк

  • Лучшая читаемость кода

Недостатки:

  • Больший объем кода

  • Требуется больше памяти для обработки

  • Ощутимо медленнее на очень больших файлах

Вывод: для разовых задач на знакомых логах Bash явно предпочтительнее. Для сложного же анализа с большей гибкостью - Python.

2. Массовое переименование файлов

Bash:

# Безопасная версия с проверками
find . -maxdepth 1 -name "*.txt" -print0 | while IFS= read -r -d '' file; do
    new_name="${file// /_}"
    [ "$file" != "$new_name" ] && mv -- "$file" "$new_name"
done

Нюансы:

  • Обработка файлов с пробелами и спецсимволами через -print0

  • Защита от переименования в то же имя

  • Поддержка рекурсивного поиска через find

Python:

import os
from pathlib import Path

for item in Path('.').glob('*.txt'):
    new_name = item.name.replace(' ', '_')
    if new_name != item.name:
        item.rename(item.with_name(new_name))

Нюансы:

  • Не переименовывает файлы без изменений

  • Использует современный pathlib вместо os

  • Кросс-платформенность

Производительность (возможно незначительное отклонение +-0,1с):

  • На 1000 файлах: Bash - 0,2с, Python - 0,5с

  • На 10000 файлах: Bash - 2,1с, Python - 4,8с

Вывод: для простых случаев Bash в разы быстрее, но Python предлагает более надежное решение для сложных сценариев.

3. Мониторинг диска

Bash:

#!/bin/bash

threshold=90
recipient="admin@example.com"
partition="/"

usage=$(df --output=pcent "$partition" | tail -1 | tr -d '% ')
message="Disk usage on $(hostname) for $partition: ${usage}%"

if [ "$usage" -ge "$threshold" ]; then
    echo "$message" | mail -s "Disk Alert" "$recipient"
    # Дополнительно: логирование и система повторов
    logger -t disk_alert "$message"
fi

Python:

import smtplib
import socket
from email.mime.text import MIMEText
from shutil import disk_usage

def send_alert(usage):
    host = socket.gethostname()
    msg = MIMEText(f"Disk usage on {host}: {usage}%")
    msg['Subject'] = 'Disk Alert'
    msg['From'] = 'monitoring@example.com'
    msg['To'] = 'admin@example.com'
    
    with smtplib.SMTP('smtp.example.com') as server:
        server.send_message(msg)

def main():
    threshold = 90
    partition = '/'
    
    total, used, _ = disk_usage(partition)
    usage_percent = (used / total) * 100
    
    if usage_percent >= threshold:
        send_alert(round(usage_percent, 1))
        # Дополнительные функции:
        # - Логирование
        # - Очередь сообщений
        # - Повторы при ошибках

if __name__ == '__main__':
    main()

Критерии выбора:

Критерий

Bash

Python

Скорость разработки

Быстрее для простых случаев

Требует больше кода

Поддержка

Сложнее поддерживать

Легче развивать

Надежность

Хрупкий

Более устойчивый

Возможности

Ограниченные

Полноценные приложения

Портируемость

Только Unix

Кроссплатформенный

4. Обработка CSV-файлов

Для полноты картины поставим задачу: посчитать среднее значение по второму столбцу CSV

Bash:

awk -F',' 'NR>1 {sum+=$2; count++} END {print sum/count}' data.csv

Python:

import pandas as pd

df = pd.read_csv('data.csv')
print(df.iloc[:, 1].mean())

Сравнение:

  • Bash: 0.8с при файле в 100МВ

  • Python: 1.2с, но с поддержкой:

    1. Пропущенных значений

    2. Разных форматов CSV

    3. Дополнительной аналитики

5. Производительность: тесты на реальных данных

Домашний тестовый стенд:

  • Ubuntu 20.04

  • Intel Core i7-10750H

  • 32GB RAM

  • SSD NVMe

Результаты:

Операция

Bash

Python

Относительная разница

Поиск в логах (1GB)

1.2с

3.8с

Python в 3.2 раза медленнее

Обработка 10k файлов

2.1с

5.4с

Python в 2.6 раза медленнее

Анализ CSV (100MB)

0.8с

1.2с

Python в 1.5 раза медленнее

Сложная ETL-задача

-

4.2с

Bash не справился

Итоги

Использовать Bash в случаях, когда:

  1. Требуется быстрое решение для одноразовой задачи

  2. Работа с текстовыми потоками и системными утилитами

  3. Необходима максимальная производительность на простых операциях

  4. Скрипт будет выполняться только в Unix-окружении

Выбирать Python, когда:

  1. Требуется сложная логика обработки данных

  2. Важна читаемость кода

  3. Необходима кроссплатформенная совместимость

  4. Предполагается дальнейшее развитие скрипта

  5. Планируется работа со структурированными данными (JSON, XML, CSV)

Со своей же стороны добавлю. Я часто использую "гибридный" подход. Комбинирование языков даёт максимальную эффективность: Bash - для быстрой предобработки данных, Python - для сложного анализа.

P.S. Я веду свою группу в Телеграмм, буду рад видеть всех, кому интересен процесс написания скриптов и автоматизация в мире IT.

Комментарии (9)


  1. bolk
    18.07.2025 13:36

    У «bash и ко» есть интересное достоинство, про которое почему-то все забывают — процессы, связанные через pipe, могут прекрасно утилизировать все ядра. Никакой gil этому не помешает.


  1. apevzner
    18.07.2025 13:36

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


  1. skymal4ik
    18.07.2025 13:36

    Избегаю скриптов на баше кроме разве что совсем простых на пару строк) из-за его, bash-a, синтаксиса. Простите за сквернословие, но назвать его могу только ублюдским.

    Массивы, присваивание где нельзя ставить пробел между переменной и равно, все эти ne le ge , циклы - как будто против человечества всё это писалось. Понимаю, что наследие, традиции, ограничения, но прямо раздражает :)


  1. vadimr
    18.07.2025 13:36

    Не очень корректное сравнение. Python никак не мешает использовать сложные командные строки. Поэтому Python является скорее надстройкой над bash, чем альтернативой.


  1. shoytov
    18.07.2025 13:36

    У Python есть один явный минус в сравнении с bash - это виртуальное окружение. Далеко не все скрипты можно "вот так взять и просто написать" без использования сторонних библиотек, а для этого нужно создавать виртуальное окружение, прописывать полный путь к интерпретатору в этом окружении... То есть речи о переносимости "из коробки" тут быть не может.


    1. skymal4ik
      18.07.2025 13:36

      Часть библиотек есть в репозиториях ОС, иногда проще ставить их, чем возиться с виртуальными окружениями. Понятно, что не всё есть и версии старее, но иногда этого достаточно.


      1. shoytov
        18.07.2025 13:36

        нет, дорогой друг, ставить какой-то пакет питона в "стандартное" окружение ОС - это путь в никуда, если вы собираетесь прожить с этой системой хоть пару лет (со всеми обновлениями безопасности и т.д,) Никогда! Повторю, НИКОГДА, не нужно ставить какие-то пакеты в окружение python вашего дистрибутива. Нет, я не выпендриваюсь, честно, просто это - опыт.
        P.S. python - мой рабочий инструмент более 10 лет, я его искренне люблю и лелею, но нет - для задач переносимой автоматизации он подходит едва ли...


  1. LexD1
    18.07.2025 13:36

    Bash — командная оболочка (UNIX, GNU/Linux, MacOS).

    Python — высокоуровневый язык программирования.

    Сравнение кажется мне не особо корректным. Разным задачам — разные инструменты.

    P.S. Но заголовки в стиле "versus" нравятся, хоть и маркетинг.

    P.P.S. «Bash vs Golang» # ?


  1. Ninil
    18.07.2025 13:36

    Согласен с предыдущем коментарием.
    ПыСы

    • уже было на хабре

    • по всей видимости эффект от чатаЖПТ и иже с ними + "эффект ваАйТишников" уже провел к тому, что появилось много людей, не видящих абсурдности тем, подобных теме этой статьи.