fx — алтернатива jq для обработки JSON из командной строки


    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 отличный выбор.



    Надеюсь, эта утилита кому-нибудь пригодится. Спасибо за внимание.

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 19

      0
      Я наивно полагал, что самая популярная утилита для работы с json из командной строки это jshon. Вдвое быстрее jq да и мне как человеку, для которого наиболее привычный язык — шелл скриптинг, синтаксис команд кажется наиболее внятным:

      $ time for i in {1..100}; do jq '.["foo"] | .[0] | .["bar"]' <<< '{"foo": [{"bar": "value"}]}' >/dev/null; done   
      
      real    0m0,288s
      user    0m0,186s
      sys     0m0,100s
      
      $ time for i in {1..100}; do jshon -e foo -e 0 -e bar <<< '{"foo": [{"bar": "value"}]}' >/dev/null; done
      
      real    0m0,158s
      user    0m0,078s
      sys     0m0,043s


      Функциональности у jshon вроде меньше, но не помню ситуаций, чтобы мне её не хватало.
        +4
        «Так и появилась идея написать fx с простым и понятным синтаксисом, который никогда не забудешь. А какой язык программирования знают все? Правильно — JavaScript. „
        Опасная фраза, особенно в виде “знают все». Только за нее может минусов прилететь…
          +2
          К счастью, не все знаю JavaScript.
          +10
          Пожалуйста, хватит тащить JS туда, где он не нужен.
            –1
            Кому-то не нужен, кому-то нужен, в чём проблема? Никого же силком не тащат, а для тех, кто с консолью на вы, но вынужден решать какую-то срочную задачу и знает js, вполне себе удобный инструмент может быть.
              0
              Мне кажется те, кто «с консолью на вы», не должны решать срочных задач в консоли, потому что в 99% случаев (appx/bome) это заканчивается поисками тех кто с консолью работает каждый день чтобы починить не работающий сервис.
                +1
                Конечно, в продакшене — да. Но бывают и другие окружения совершенно не требовательные к компетенциям работающего. И если такие люди будут использовать хорошо им знакомый язык в рамках малознакомой системы, вероятность падения только уменьшится.

                Мне сам подход непонятен — «инструмента не должно существовать, потому что он написан на языке, который мне не нравится». Кому-то он может быть полезен, а те, кому нравятся другие спокойно могут их использовать. Чем больше разнообразных инструментов доступно, тем лучше.
              0
              А он где-то нужен? Ну кроме как в качестве ассемблера для компиляции других языков в веб-приложения.
              +2
              fx написана на js и требует среды исполнения. На сервак nodejs тащить как-то стремно. Вот если-бы вы fx на Си написали и использовали embedded js engine, типа Duktape, цены утиле было-бы гораздо больше.
                +1

                У fx есть независимый бинарники: https://github.com/antonmedv/fx/releases
                Просто качайте и используйте, никаких зависимостей.

                  0
                  Там внутри статически слинкованная нода и все зависимости, я правильно понимаю?
                0

                Ошибся веткой

                  0
                  Так и появилась идея написать fx с простым и понятным синтаксисом, который никогда не забудешь.
                  я конечно извиняюсь, но для кого этот синтаксис будет простым и понятным- для js'еров? Например, для меня он не простой и не понятный

                  Для некоторых важно, что jq всего лишь один бинарник (~2mb). Так вот fx тоже имеет отдельные бинарники. Весят они немного больше (~30mb)
                  <зануда mode=on>Ну как бы не немного, а на порядок<зануда mode=off>

                  и вообще весь код fx <70 строк кода

                  это все хорошо, но при этом бинарник весит 35 Мб

                  Во всех остальных случаях — fx отличный выбор.
                  очень спорное утверждение

                  P.S.
                  как ни странно, но в плане работы с JSON мне нравится aws cli с его --query, который поддерживает JMESPath
                    –3
                    Лучше бы к jq приделали yaml, чем изобретать велосипед со страпоном вместо седла.
                      +1
                      Ну… тут конечно набросились выше, но я считаю, что автор сделал хорошее дело. Да, на продакшн я бы это не ставил. Даже в докер-контейнер затаскивать как временное решение для дебага — не захочется, особенно если ещё и с нодой сверху. Но если бы я писал много и часто на nodejs, то иметь такой же синтаксис для разбора выхлопа json локально в процессе разработки — вай нот? В общем, плюсую. Уверен, если популяризировать, то найдутся благодарные пользователи.
                        0
                        С одной стороны, что ставить на сервер ноду, что тащить её запакованную, а потом (что так, что эдак) грузить её рантайм ради простого скрипта это страшненько. Но когда мне понадобится обработать джсон из командной строки я предпочёл бы, чтобы там стояла вот такая фиговина, чем учить впопыхах ещё одну разновидность регулярок. Всё же у яваскрипта (а особенно в ес6) очень естественная и понятная работа с именно джсоном. Это при том, что по жизни я этот язык не люблю.
                          0

                          Простите, но я вижу в результатах тестов то, что утилита fx проигрывает в производительности jq на порядок, а не наоборот.

                            0

                            ДА. Именно это я и написал. :) Эта утилита только для разработки (1ms vs 35ms погоды не сделают)

                              0

                              А! Прошу прощения, я не так воспринял смысл последнего абзаца.

                          Only users with full accounts can post comments. Log in, please.