Инструменты исследования: jq, ripgrep, awk и друзья
Третий урок раздела. Когда нужно быстро ответить на вопрос “что происходит в этих данных”, тяжёлый скрипт обычно избыточен. Набор острых CLI-инструментов отвечает за секунды и помещается в одну строку. Разница между разработчиком, который пишет на каждый вопрос скрипт на тридцать строк, и тем, кто собирает ответ конвейером из трёх утилит, это разница в темпе работы на порядок. Этот урок про то, как думать конвейером.
Идея: каждый инструмент это маленький язык под свой тип данных
Универсального инструмента нет, и это хорошо. Сила в том, что под каждый
тип данных есть острая утилита со своим крошечным языком, а конвейер |
соединяет их.
- Текст и код:
ripgrep(rg), поиск по содержимому. - Файлы по имени:
fd, поиск по дереву. - JSON:
jq, срезы и трансформации. - YAML:
yq, тот жеjqдля YAML. - Таблицы и поля:
awk, построчная обработка по колонкам. - Замены в потоке:
sed, потоковый редактор.
Думать надо так: на входе у меня данные такого-то типа, я хочу из них такой-то ответ, какая цепочка маленьких языков ведёт от одного к другому.
ripgrep: поиск, который уважает проект
rg
это grep, выросший для работы с кодовой базой. По умолчанию он рекурсивен,
уважает .gitignore (не лезет в node_modules и dist), пропускает
бинарники и работает заметно быстрее за счёт параллелизма.
rg "TODO" -t ts # только в TypeScript-файлах
rg "createUser" -A 3 -B 1 # 3 строки после и 1 до совпадения
rg -l "useEffect" # только имена файлов со совпадением
rg "v(\d+)\.(\d+)" -o # вывести только сами совпадения по regex
Флаг -t фильтрует по типу файла, -A / -B дают контекст вокруг
совпадения, -l отдаёт список файлов (удобно скормить дальше в xargs),
-o печатает только совпавшую часть. Для поиска “где вообще используется
эта функция” это первый инструмент, ещё до того, как лезть в редактор.
Файлы по имени ищет напарник
fd:
fd config найдёт все пути с config в имени, без громоздкого
find . -name '*config*'.
jq: запросы к JSON
API, логи, конфиги, всё это JSON, и
jq
это маленький язык запросов к нему. Фильтр на входе получает JSON, на
выходе отдаёт JSON, а | внутри jq соединяет фильтры так же, как | в
shell соединяет команды.
# достать поле из каждого элемента массива
curl -s api/users | jq '.[].email'
# выбрать по условию и собрать проекцию
jq '.items[] | select(.price > 100) | {name, price}' data.json
# агрегат: средняя цена
jq '[.items[].price] | add / length' data.json
# разобрать массив JSON-строк (по одному объекту на строку)
jq -s 'sort_by(.latency) | reverse | .[0:10]' logs.json
Ключевые кирпичики: .field достаёт поле, .[] разворачивает массив в
поток элементов, select(...) фильтрует, {a, b} собирает новый объект,
sort_by / group_by / add агрегируют. jq -s (slurp) собирает поток
JSON-объектов в один массив, это режим для построчных логов.
YAML обрабатывает yq
с тем же синтаксисом, что незаменимо для Helm-values и compose-файлов.
дальше после входа
Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.
Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.