jq — самая популярная утилита для обработки JSON из командной строки, написана на C и имеет свой собственный синтаксис для работы с JSON.
Однако, обрабатывать JSON в командной строке не нужно очень часто, а когда потребность возникает, приходится мучиться с незнакомым языком программирования.
Так и появилась идея написать fx с простым и понятным синтаксисом, который никогда не забудешь. А какой язык программирования знают все? Правильно — JavaScript.
fx принимает в качестве аргумента код на JavaScript, содержащий анонимную функцию, и вызывает её, подставляя в качестве аргумента JSON, полученный из stdin. То, что функция вернёт, будет выведено в stdout. Все. Больше никаких сложных и непонятных конструкций.
Сравните два решения одной и той же задачи на jq и на fx:
jq '[.order[] as $x | .data[$x]]'
fx 'input => input.order.map(x => obj.data[x])'
Чуть более многословно? Да, это же просто обычный JavaScript.
Если fx вообще не передать аргументов, то JSON будет отформатирован и выведен:
$ echo '{"key":"value"}' | fx
{
"key": "value"
}
Если код не содержит param =>
, то переданный код будет автоматически преобразован в анонимную функцию и this
будет содержать переданный JSON объект:
$ echo '{"foo": [{"bar": "value"}]}' | fx 'this.foo[0].bar'
"value"
fx можно передать несколько аргументов/анонимных функций, они будут применены поочерёдно к JSON:
$ echo '{"foo": [{"bar": "value"}]}' | fx 'x => x.foo' 'this[0]' 'this.bar'
"value"
Если код содержит ключевое слово yield
, созданная анонимная функция будет содержать "развернутый" генератор (пример из generator-expression):
$ cat data.json | fx 'for (let user of this) \
if (user.login.startsWith("a")) yield user'
$ echo '["a", "b"]' | fx 'yield* this'
[
"a",
"b"
]
Это позволяет описывать очень простые выборки в сложных запросах.
Кстати, модифицировать JSON с fx тоже очень просто:
$ echo '{"count": 0}' | fx '{...this, count: 1}'
{
"count": 1
}
А также можно использовать любой npm пакет, если поставить его глобально:
$ npm install -g lodash
$ cat package.json | fx 'require("lodash").keys(this.dependencies)'
Для некоторых важно, что jq всего лишь один бинарник (~2mb). Так вот fx тоже имеет отдельные бинарники.
Весят они немного больше (~30mb), но если вам это не критично, и стоит nodejs, то поставить fx можно с помощью npm:
npm install -g fx
А что по производительности? Давайте замерим с помощью hyperfine:
$ curl 'https://api.github.com/repos/stedolan/jq/commits' > data.json
$ hyperfine --warmup 3 "jq ..." "fx ..."
Benchmark #1: cat data.json | jq '[.[] | {message: .commit.message, name: .commit.committer.name}]'
Time (mean ± ?): 10.7 ms ± 0.9 ms [User: 8.7 ms, System: 3.6 ms]
Range (min … max): 9.0 ms … 14.9 ms
Benchmark #2: cat data.json | fx 'this.map(({commit}) => ({message: commit.message, name: commit.committer.name}))'
Time (mean ± ?): 159.6 ms ± 4.4 ms [User: 127.4 ms, System: 28.4 ms]
Range (min … max): 153.0 ms … 170.0 ms
На порядок меньше. А все дело в том, что fx использует eval
для запуска кода из аргументов (и вообще весь код fx <70 строк кода). Если для вас важна скорость обработки, не используйте fx. Во всех остальных случаях — fx отличный выбор.
Надеюсь, эта утилита кому-нибудь пригодится. Спасибо за внимание.
Комментарии (15)
playnet
30.01.2018 11:28«Так и появилась идея написать fx с простым и понятным синтаксисом, который никогда не забудешь. А какой язык программирования знают все? Правильно — JavaScript. „
Опасная фраза, особенно в виде “знают все». Только за нее может минусов прилететь…
welcomerooot
30.01.2018 11:58Пожалуйста, хватит тащить JS туда, где он не нужен.
alexey-m-ukolov
30.01.2018 13:25+1Кому-то не нужен, кому-то нужен, в чём проблема? Никого же силком не тащат, а для тех, кто с консолью на вы, но вынужден решать какую-то срочную задачу и знает js, вполне себе удобный инструмент может быть.
akamensky
31.01.2018 05:40Мне кажется те, кто «с консолью на вы», не должны решать срочных задач в консоли, потому что в 99% случаев (appx/bome) это заканчивается поисками тех кто с консолью работает каждый день чтобы починить не работающий сервис.
alexey-m-ukolov
31.01.2018 13:16+1Конечно, в продакшене — да. Но бывают и другие окружения совершенно не требовательные к компетенциям работающего. И если такие люди будут использовать хорошо им знакомый язык в рамках малознакомой системы, вероятность падения только уменьшится.
Мне сам подход непонятен — «инструмента не должно существовать, потому что он написан на языке, который мне не нравится». Кому-то он может быть полезен, а те, кому нравятся другие спокойно могут их использовать. Чем больше разнообразных инструментов доступно, тем лучше.
potan
31.01.2018 00:42А он где-то нужен? Ну кроме как в качестве ассемблера для компиляции других языков в веб-приложения.
dimcha
30.01.2018 14:53+1fx написана на js и требует среды исполнения. На сервак nodejs тащить как-то стремно. Вот если-бы вы fx на Си написали и использовали embedded js engine, типа Duktape, цены утиле было-бы гораздо больше.
Elfet Автор
30.01.2018 15:51+1У fx есть независимый бинарники: https://github.com/antonmedv/fx/releases
Просто качайте и используйте, никаких зависимостей.TheKnight
30.01.2018 19:41Там внутри статически слинкованная нода и все зависимости, я правильно понимаю?
ALexhha
30.01.2018 21:53Так и появилась идея написать fx с простым и понятным синтаксисом, который никогда не забудешь.
я конечно извиняюсь, но для кого этот синтаксис будет простым и понятным- для js'еров? Например, для меня он не простой и не понятный
Для некоторых важно, что jq всего лишь один бинарник (~2mb). Так вот fx тоже имеет отдельные бинарники. Весят они немного больше (~30mb)
<зануда mode=on>Ну как бы не немного, а на порядок<зануда mode=off>
и вообще весь код fx <70 строк кода
это все хорошо, но при этом бинарник весит 35 Мб
Во всех остальных случаях — fx отличный выбор.
очень спорное утверждение
P.S.
как ни странно, но в плане работы с JSON мне нравится aws cli с его --query, который поддерживает JMESPath
tgz
31.01.2018 09:46Лучше бы к jq приделали yaml, чем изобретать велосипед со страпоном вместо седла.
KIVagant
31.01.2018 16:37Ну… тут конечно набросились выше, но я считаю, что автор сделал хорошее дело. Да, на продакшн я бы это не ставил. Даже в докер-контейнер затаскивать как временное решение для дебага — не захочется, особенно если ещё и с нодой сверху. Но если бы я писал много и часто на nodejs, то иметь такой же синтаксис для разбора выхлопа json локально в процессе разработки — вай нот? В общем, плюсую. Уверен, если популяризировать, то найдутся благодарные пользователи.
Self_Perfection
Я наивно полагал, что самая популярная утилита для работы с json из командной строки это jshon. Вдвое быстрее jq да и мне как человеку, для которого наиболее привычный язык — шелл скриптинг, синтаксис команд кажется наиболее внятным:
Функциональности у jshon вроде меньше, но не помню ситуаций, чтобы мне её не хватало.