Search
Write a publication
Pull to refresh

Bash vs Python: битва, где нет проигравших

Level of difficultyEasy
Reading time4 min
Views8K

Привет, Хабр! В мире автоматизации часто возникает вопрос: писать скрипт на 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.

Tags:
Hubs:
+1
Comments41

Articles