
Чаще всего в работе датасаентиста мне приходится перегонять данные из одного представления в другое, агрегировать, приводить к одинаковой гранулярности и чистить данные, загружать, выгружать, анализировать, форматировать и присылать результаты (которые в общем-то тоже данные в каком-то виде). С данными всегда что-то не так и их нужно шустро гонять туда и обратно больше всего в этом мне помогают классические юниксовые утилиты и небольшие, но гордые тулзы: вот о них-то мы сегодня и поговорим.
И сегодня будет подборка с примерами и ситуациями, в которых мне приходится их использовать. Все описанное здесь и ниже это настоящий субъективный опыт и конечно же он у всех разный, но возможно кому-то он будет полезен.
Tools learn the tools все написанное субъективно и основано исключительно на личном опыте: помогло мне может быть поможет и вам.
Zsh и oh-my-zsh после всех этих лет в Баше!
Как сейчас помню, мне было 17 лет и я установил линукс. Терминал и Баш. И как-то всегда баш был частью процесса и олицетворял для меня собственно работу в терминале. Спустя 12 лет после окончания PhD я попал в компанию, где был вводный документ и мне впервые в руки попался мак и я решил ему следовать.
И о чудо! Удобный переход по папкам, человеческое автодополнение, индикатор git, темы, плагины, поддержка виртуальной среды для python и тд сижу теперь в терминале и не нарадуюсь!

Ставим zsh, как вы обычно ставите все и переходим к oh-my-zsh (по сути это народная сборка рецептов, которые работают из коробки и добавили поддержку плагинов, тем и тд). Взять можно тут. А также можно поставить тему (ну например). Вот тут неплохое демо возможностей. Взято из вот этой статьи.
Pipelines

Одна из самых прекрасных конструкций терминала это pipeline. Упрощенно говоря, он позволяет соединять выходы одной команды с входами другой, пример простого применения, который буквально взят из одной задачи, которой я занимался два дня назад.
Нужно было смоделировать задачу на одном языке для решения комбинаторных задач, запускалось все из терминала и выдавалось в абсолютно нечитаемом виде из текста, поставив простой значок | соединили входы выходы и сделали поддержку форматирования:
| python.py format.py
Более интересная и каждодневная задача оценить какой-нибудь параметр или характеристику по отгруженным данным, как правило это ряд быстрых проверок, что нужные величины где-то на сервере с данными ведут себя хорошо например мы хотим понять, что у нас с парсером и смотрим сколько во всех json файлах собрано уникальных групп этот параметр должен естественно адекватно расти во времени:
cat data/*groups* | jq .group | uniq | wc -l
Мы подробнее поговорим о каждом из них, но общая идея уже понятна:
- cat (сокр. от concatenate) печатает содержимое файлов со словом group в названии из папки data/
- jq выдирает из json поле group
- uniq оставляет только уникальные группы
- wc с ключом -l считает строчки, т.е количество групп
И сейчас мы подробнее посмотрим на wc.
WC маленькая, но гордая утилитка для подсчета строк, слов и тд.
wc умеет быстро считать слова, строчки, буквы, байты и максимальную длину строчки, и все с помощью простых ключей:
-
bytes
-
chars
-
words
-
lines
-
max-line-length
Кажется, что это тривиально, но оказывается невероятно часто нужно и удобно.
Повседневное использование, быстро оценим сколько у нас каких данных собрано (тут одна строка одна запись):

Подробнее тут.
Ack/grep
Про них написаны тысячи мануалов и статей, но не могу не упомянуть выдирают текст регулярками и своим языком запросов по соответствию образцу. В целом мне кажется ack более дружелюбным и простым в использовании из коробки, поэтому будет он тут:
Пример: быстренько находим вхождение слова (ключ -w) ga2m (тип модели), без учета регистра (ключ -i) в файлах исходниках питона:

JQ парсим json в командной строке
Документация.
JQ это прямо-таки grep/ack для json (хотя и с оттенком sed и awk о последнем далее) по сути простой парсер json и json line в командной строке, но иногда бывает чрезвычайно удобным как-то пришлось парсить архив wikidata в формате bz2 он весит порядка 100ГБ и где-то 0.5TB uncompressed.
Из него нужно было выдрать соответствие между несколькими полями, что получилось сделать очень просто на машине практически без нагрузки на CPU и память, вот собственно та самая команда, которую я использовал:
bzcat data/latest-all.json.bz2 | jq stream 'select((.[0][1] == "sitelinks" and (.[0][2]=="enwiki" or .[0][2] =="ruwiki") and .[0][3] =="title") or .[0][1] == "id")' | python3 scripts/post_process.py "output.csv"
Это был по сути весь пайплайн, который создавал нужный mapping, как мы видим все работало в режиме потока:
- bzcat читал часть архива и отдавал jq
- jq с ключом stream сразу выдавал результат и передавал его постпроцессору (так же как и с самым первым примером) на питоне
- внутри постпроцессор это простая машина состояний
Итого сложный пайплайн работающий в режиме потока на больших данных (0.5TB), без существенных ресурсов и сделан из простого пайплайна и пары тулзов. Определенно рекомендую глянуть на досуге.
fzf fuzzy finder
Удобнейшая вещь (особенно внутри вима): быстро ищет по файлам, что удобно на большом проекте особенно, когда их у вас несколько. Как правило нужно для быстрого поиска файлов по определенному слову в большом проекте: у меня происходило погружение в новый проект, в который входит несколько крупных репозиториев и в качестве вводного задания мне нужно было добавить одну простую модель в ассортимент доступных в системе и мне нужно было быстро находить свои файлы по ключевому слову ga2m и работать по аналогии с другими блоками кода быстро редактировать то одно, то другое тут fzf очень хорошо приходит на помощь:

Ссылка на репозиторий.
AWK
Название происходит от первых букв создателей Aho, Weinberger и Kernighan: по сути скриптовый язык обработки текстово-табличных данных он приминяет шаблоны трансформации к каждой строке файла
Как правило идеально подходит для быстрых разовых трансформаций, например, у нас были собранный руками датасет в виде tsv, а процессор принимал на вход jsonl причем ожидал доп поле theme, которого не было в исходном файле (нужного для некоторых вещей, которые были не критичны для текущих подсчетов) итого, был написан простой однострочник:
cat groups.tsv | awk '{ printf "{\"group\": \"%s\", \"theme\": \"manual\" }\n", $1 }' > group.jsonl
По сути он брал файлик и каждую строчку заворачивал json с нужными полями.
Ссылка с tutorial.
wget универсальный command line downloader
Регулярно скрипты и пайплайн должны что-то откуда-то подтянуть и качнуть и wget не подводит: умеет докачивать, авторизовываться, proxies, cookies да еще и помимо http(s) умеет в ftp.
Швейцарский нож в скачивании.
HSTR поиск по истории команд, с человеческим лицом
Сommand history: hstr
Регулярно мне приходится искать что-то в истории команд:
- Это уже приходилось делать
- А с каким ключам запускается Х?
- А вот этот кусок можно и переиспользовать
Поэтому мне довольно критично иметь хороший и удобный поиск по истории команд, пока hstr полностью со своей задачей справляется:

Полезное, но не вошедшее
В финале я бы упомянул полезными но тянущими на отдельную статью темы полезно глянуть:
- Связку ssh + tmux + vim (neo, plugins, etc)
- Базовые знания command line git + git hooks
- Data pipeline construction make/just
- Python virtual environments
- Docker