Bash pipes и Unix-инструменты для Data Engineer

Проверь себя · 1/3разбор после ответа
У вас есть таблицы users(email) и marketing_signups(email). В одной системе email сохраняется как Ivan@Example.com, в другой — ivan@example.com. Какой подход в JOIN чаще всего решает проблему без изменения данных в таблицах?

Зачем разбирать на собесе

Командная строка — рабочий инструмент DE: посмотреть логи на сервере, быстро проверить выгрузку, посчитать что-то по 10-гигабайтному CSV, не заливая его в базу. На собесе это проверяют вопросами вроде «как найти топ-N значений в большом файле» или «как посчитать количество ошибок в логе, не открывая его целиком».

Ключевая идея, которую хотят услышать, — потоковая обработка. Unix-инструменты (grep, awk, sort, sed, jq) читают данные построчно и не держат весь файл в памяти, поэтому справляются с файлами, которые не влезли бы в RAM. Ответ «загружу в pandas» на файле в десятки гигабайт — красный флаг: pandas прочитает всё в память и упадёт, а конвейер из пайпов пройдёт файл потоком.

Пайпы и перенаправление

Пайп (|) передаёт вывод одной команды на вход другой — так из простых утилит собирают конвейер обработки.

cat large.log | grep "ERROR" | wc -l
# посчитать количество строк с ERROR в логе

Здесь grep отбирает строки с ошибкой, а wc -l их считает — данные идут потоком, файл целиком в память не грузится. (Строго говоря, cat тут лишний — grep "ERROR" large.log сделает то же, но с пайпом нагляднее.)

Перенаправление потоков — базовый минимум, который стоит помнить:

  • > — записать вывод в файл (перезаписав его).
  • >> — дописать вывод в конец файла.
  • < — подать файл на вход команды.
  • 2> — перенаправить поток ошибок (stderr) отдельно от вывода.
  • 2>&1 — слить stderr в stdout, чтобы обработать их вместе.

awk — обработка по полям

awk — мощный построчный процессор, который разбивает строку на поля и работает с ними. Незаменим для колоночных данных (CSV, TSV, логи).

# вывести вторую колонку
awk '{print $2}' file.csv

# для CSV задать разделитель запятой
awk -F',' '{print $1, $3}' data.csv

# отфильтровать строки, где третье поле больше 100
awk -F',' '$3 > 100 {print $0}' data.csv

# просуммировать третью колонку
awk -F',' '{sum += $3} END {print sum}' data.csv

# посчитать количество строк по значению первой колонки (аналог GROUP BY)
awk -F',' '{counts[$1]++} END {for (k in counts) print k, counts[k]}' data.csv

По сути awk заменяет GROUP BY и агрегаты для быстрых разовых скриптов: $1, $2 — это поля строки, блок END выполняется один раз после прохода по всему файлу. На собесе группировку по полю через ассоциативный массив (counts[$1]++) любят как проверку, понимаете ли вы, что агрегацию можно сделать без базы.

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

sed (stream editor) редактирует поток построчно: замена, удаление и вставка строк без открытия файла в редакторе.

# заменить все вхождения old на new (флаг g — по всей строке)
sed 's/old/new/g' file > new.txt

# удалить строки, содержащие pattern
sed '/pattern/d' file

# вывести только строки с 10-й по 20-ю
sed -n '10,20p' file

Чаще всего sed берут для массовых подстановок в выгрузках (поправить разделитель, вырезать префикс) и для вырезания диапазона строк из большого файла.

sort и uniq

# сортировка по третьей колонке как числу (-n), разделитель — запятая
sort -t',' -k3 -n data.csv

# частотный топ значений первой колонки
cut -d',' -f1 data.csv | sort | uniq -c | sort -rn | head

# найти строки-дубликаты
sort file | uniq -d

Важная деталь: uniq схлопывает только соседние одинаковые строки, поэтому перед ним почти всегда ставят sort. Классический конвейер sort | uniq -c | sort -rn | head — это «топ по частоте»: отсортировать, посчитать повторы (-c), отсортировать по счётчику по убыванию (-rn) и взять начало. Сокращение sort -u эквивалентно sort | uniq и работает быстрее, потому что делает всё за один проход.

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

jq — обработка JSON

jq — это «awk для JSON»: он парсит JSON и позволяет доставать и преобразовывать поля прямо в пайпе. Критичен для ответов API и файлов формата NDJSON (по одному JSON-объекту на строку).

# достать email всех пользователей
cat data.json | jq '.users[].email'

# отфильтровать объекты по условию
jq '.users[] | select(.age > 30)' file.json

# оставить только нужные поля
jq '{email, age}' file.json

# посчитать количество элементов массива
jq '.users | length' file.json

На собесе jq всплывает в задачах «распарсить ответ API и вытащить нужные поля» — знать хотя бы .field, [] (итерация по массиву) и select(...) (фильтр) стоит.

xargs

xargs превращает вывод одной команды в аргументы другой — нужен, когда команда принимает аргументы, а не поток на stdin (например, rm).

# удалить все найденные .tmp-файлы
find . -name '*.tmp' | xargs rm

# запустить обработку в 4 параллельных процесса
ls *.json | xargs -P 4 -I {} python process.py {}

Два полезных флага: -P N задаёт число параллельных процессов (простой способ распараллелить пакетную обработку файлов), -I {} — плейсхолдер, куда подставится каждый аргумент. Про распараллеливание через xargs -P на собесе спрашивают как про дешёвый способ ускорить обработку пачки файлов без написания кода.

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

uniq без предварительного sort. uniq схлопывает только соседние строки. Без сортировки одинаковые, но разбросанные по файлу строки не схлопнутся — и подсчёт uniq -c будет неверным.

Грузить большой файл в память вместо потоковой обработки. «Открою в pandas» на файле в десятки гигабайт кончается OOM. Пайп из grep/awk/sort пройдёт файл потоком.

Забыть -F в awk для CSV. По умолчанию awk разбивает строку по пробелам, а не по запятой. Для CSV нужен -F',', иначе $1 захватит не то поле.

Наивный парсинг CSV с кавычками и запятыми внутри полей. awk -F',' ломается на значениях вида "Иванов, Иван". Для «грязных» CSV берут инструмент, понимающий кавычки (csvkit, python -c), а не просто разделитель.

Путать > и >>. > перезаписывает файл, >> дописывает. Ошибка при работе с логами затирает данные. А sed 's///' file > file и вовсе обнулит файл — писать надо в новый и потом переименовать.

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

FAQ

Как посчитать топ-N частых значений в большом файле?

Классический конвейер: cut -d',' -f1 file.csv | sort | uniq -c | sort -rn | head. Он вырезает нужную колонку, сортирует, считает повторы, сортирует по счётчику по убыванию и берёт начало. Всё это потоково, файл целиком в память не грузится.

Когда brать awk, а когда Python?

awk/sed/jq хороши для быстрых разовых задач и когда файл слишком велик для памяти: они читают поток построчно. Python берут, когда логика сложнее (парсинг «грязного» CSV с кавычками, обращения к API, ветвления), нужна читаемость и поддержка, или требуются библиотеки. Для одноразового подсчёта пайп быстрее написать, для повторяемого пайплайна лучше код.

Почему uniq не убирает все дубликаты?

Потому что uniq сравнивает только соседние строки. Одинаковые строки, разбросанные по файлу, он не увидит. Нужно сначала отсортировать: sort file | uniq (или короче sort -u file).

Как обработать 10 ГБ CSV без базы?

Потоковыми утилитами. Фильтрация — grep/awk, агрегация по полю — awk с ассоциативным массивом (counts[$1]++), частоты — sort | uniq -c. Они не держат файл в памяти. Если нужен полноценный SQL по файлу — можно взять DuckDB, тоже без поднятия сервера.

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

Нет. Статья основана на стандартном поведении Unix/Bash-инструментов (GNU coreutils, awk, sed, jq) и типичных вопросах собеседований Data Engineer.


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