Bash и Unix-утилиты для Data Engineer

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.

Зачем DE Bash

Bash — повседневный инструмент DE: ad-hoc анализ логов, обработка CSV до загрузки в DWH, отладка пайплайнов на серверах, написание простых ETL-скриптов. На собесе обязательно дадут задачу: «есть лог из миллионов строк, найди топ-10 IP по числу запросов» — ожидают цепочку из 4-5 утилит за 30 секунд.

Главная боль без Bash — DE пишет 50 строк Python там, где нужно grep | awk | sort | uniq -c | sort -rn | head. На проде такие скрипты живут вечно и тратят время команды.

Pipes и редиректы

cmd1 | cmd2          # stdout cmd1 → stdin cmd2
cmd > file           # stdout → файл (перезапись)
cmd >> file          # stdout → файл (добавление)
cmd 2> err.log       # stderr → файл
cmd > out 2>&1       # stdout + stderr в один файл
cmd > /dev/null      # выкинуть stdout
cmd | tee file       # stdout в файл И в следующий pipe

Тонкость: cmd1 | cmd2 запускает обе команды параллельно (cmd2 не ждёт окончания cmd1). На больших данных это даёт streaming.

grep, awk, sed

grep — поиск по regex.

grep "ERROR" app.log               # строки с ERROR
grep -i "error" app.log            # case-insensitive
grep -v "DEBUG" app.log            # инверсия
grep -E "(ERROR|WARN)" app.log     # extended regex
grep -c "ERROR" app.log            # count
grep -A 3 "ERROR" app.log          # +3 строки после
grep -B 3 "ERROR" app.log          # +3 строки до
grep -r "TODO" src/                # рекурсивно по директории

awk — работа с колоночными данными. Дефолтный разделитель — пробел/таб.

awk '{print $1}' file              # первая колонка
awk -F',' '{print $2}' file.csv    # разделитель = запятая
awk '$3 > 100 {print $0}' file     # фильтр + вывод всей строки
awk '{sum += $2} END {print sum}'  # сумма второй колонки
awk -F',' 'NR>1 {a[$1]++} END {for (k in a) print k, a[k]}' file.csv  # уникальные счётчики

sed — потоковое редактирование.

sed 's/old/new/g' file             # замена (g = global, all occurrences)
sed -n '5,10p' file                # строки 5-10
sed -i 's/foo/bar/g' file          # in-place (Linux)
sed -i '' 's/foo/bar/g' file       # in-place (macOS)

cut, sort, uniq

cut — извлечь колонки.

cut -d',' -f1,3 file.csv           # 1-я и 3-я колонка csv
cut -c1-10 file                    # символы 1-10 каждой строки

sort — сортировка.

sort file                           # по возрастанию
sort -r file                        # обратно
sort -n file                        # числовая
sort -k 2 -n file                   # по 2-й колонке, числовая
sort -t',' -k 3 -nr file.csv        # csv, 3-я колонка, число, обратно
sort -u file                        # уникальные (sort + dedup)

uniq — удаление дубликатов подряд идущих строк (требует sorted вход).

sort file | uniq                    # уникальные
sort file | uniq -c                 # с count
sort file | uniq -c | sort -rn      # топ по частоте

Классическая комбинация — топ N в логе:

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

jq для JSON

Bash сам по себе плохо работает с JSON. Стандартный инструмент — jq.

jq '.user.name' file.json          # достать поле
jq '.[]' file.json                 # элементы массива
jq '.users[] | {id, name}' file.json   # выбор полей
jq -r '.name' file.json            # raw output (без кавычек)
jq 'select(.age > 30)' users.json
jq '.[] | "\(.id),\(.name)"' file.json   # CSV-формат
cat events.ndjson | jq -c 'select(.event == "click")'

-c — compact (one line per JSON), удобно для NDJSON.

cron и процессы

cron-синтаксис:

* * * * * команда
│ │ │ │ │
│ │ │ │ └── день недели (0-7, 0 и 7 = вс)
│ │ │ └──── месяц (1-12)
│ │ └────── день месяца (1-31)
│ └──────── час (0-23)
└────────── минута (0-59)
0 2 * * *      # каждый день в 02:00
*/15 * * * *   # каждые 15 минут
0 0 * * 0      # каждое воскресенье в 00:00
0 9 1 * *      # 1-го числа каждого месяца в 09:00

Процессы:

ps aux | grep python                # найти процесс
kill -9 12345                       # форс-убить процесс
nohup script.sh > log.txt 2>&1 &    # запустить в фоне, не убивать при logout
jobs                                # фоновые задачи
disown -h                           # отвязать от терминала
htop                                # интерактивный мониторинг

Сигналы: SIGTERM (15) — мягкое завершение, SIGKILL (9) — принудительное, SIGHUP (1) — перезагрузка конфига.

Частые ошибки

Парсинг CSV через awk -F','. На CSV с экранированными кавычками и запятыми внутри — сломается. Использовать csvkit/miller или Python.

Сортировать огромные файлы в RAM. sort использует disk-spill, но LC_ALL=C sort (без локали) ускоряет на порядок.

cat file | grep pattern. «Useless use of cat». Просто grep pattern file.

ls | grep pattern. Не парсить вывод ls. Использовать globbing (*.csv) или find.

Игнорировать set -euo pipefail в скриптах. Без него ошибка где-то в пайпе — скрипт продолжается с битыми данными.

Замена in-place без backup. sed -i без -i.bak — опасно. Сначала проверить на dry-run, потом backup, потом replace.

Игнорировать exit codes. cmd1 && cmd2 — следующая команда выполнится только при успехе предыдущей. || — наоборот. Без проверки exit codes цепочки молча проваливаются.

Связанные темы

FAQ

Bash или Python для скриптов DE?

Bash — для коротких скриптов из утилит (filter/transform/load пайплайны на logs). Python — для сложной логики, типизированных трансформаций, тестируемого кода. Граница условно — больше 50 строк → Python.

Чем xargs полезен?

Передаёт stdout как аргументы команде. find . -name '*.log' | xargs grep ERROR — grep по всем log-файлам. С -P N — параллельно. С -I {} — placeholder.

Что такое tee?

Разветвитель: cmd | tee file пишет stdout И в файл, И в следующий pipe. Полезно для логирования промежуточных результатов длинного pipeline.

Как ловить ошибки в bash?

set -euo pipefail в начале скрипта: e — выход на любой ошибке, u — ошибка на неопределённой переменной, o pipefail — ошибка в любом этапе pipe фейлит весь pipe.

Стоит ли учить awk глубоко?

Базовые awk '{print $N}', фильтры, агрегаты — must для DE. Полный язык awk (functions, arrays) — приятно знать, но 90% задач решаются 5-6 идиомами.

Это официальная информация?

Нет. Статья основана на man pages GNU coreutils и общей практике DevOps/DE.


Тренируйте Data Engineering — откройте тренажёр с 1500+ вопросами для собесов.