Spec-Zone.ru › jq

Руководство по jq 1.8

О других версиях см. 1.8, 1.7, 1.6, 1.5, 1.4, 1.3 или версию для разработки.

Программа jq — это «фильтр»: она принимает входные данные и формирует выходные. В jq есть множество встроенных фильтров для извлечения определённого поля объекта, преобразования числа в строку и выполнения других стандартных задач.

Фильтры можно комбинировать разными способами: передавать выходные данные одного фильтра на вход другому или собирать результаты фильтра в массив.

Некоторые фильтры формируют несколько результатов. Например, один из фильтров возвращает все элементы входного массива. Если передать результат этого фильтра второму фильтру, второй фильтр выполнится для каждого элемента массива. В целом, то, что в других языках выполняется с помощью циклов и итераций, в jq можно сделать, соединяя фильтры.

Важно помнить, что у каждого фильтра есть входные и выходные данные. Даже литералы, такие как "hello" или 42, являются фильтрами: они принимают входные данные, но всегда формируют один и тот же литерал в качестве результата. Операции, объединяющие два фильтра, например сложение, обычно передают обоим один и тот же вход и объединяют результаты. Таким образом, фильтр для вычисления среднего значения можно реализовать как add / length — передать входной массив и фильтру add, и фильтру length, а затем выполнить деление.

Но мы забегаем вперёд. :) Начнём с чего-нибудь попроще:

Запуск jq

Фильтры jq выполняются над потоком данных JSON. Входные данные jq разбираются как последовательность значений JSON, разделённых пробелами, и по одному передаются указанному фильтру. Выходные данные фильтра записываются в стандартный вывод как последовательность данных JSON, разделённых символами новой строки.

Самый простой и распространённый фильтр (или программа jq) — ., оператор тождества, копирующий входные данные процессора jq в выходной поток. Поскольку процессор jq по умолчанию считывает тексты JSON из входного потока и выводит результаты в форматированном виде, основное применение программы . — проверка и форматирование входных данных. Язык программирования jq довольно богат и позволяет делать гораздо больше, чем просто проверять данные и форматировать их.

Примечание: важно учитывать правила экранирования командной оболочки. Как правило, лучше всегда заключать программу jq в кавычки (в Unix-оболочках — в одинарные), поскольку многие символы, имеющие специальное значение в jq, также являются метасимволами оболочки. Например, jq "foo" не сработает в большинстве Unix-оболочек, поскольку будет интерпретировано так же, как jq foo, что обычно приводит к ошибке, поскольку foo is not defined. В командной оболочке Windows (cmd.exe) при передаче программы jq в командной строке лучше заключать её в двойные кавычки (вместо параметра -f program-file), но тогда двойные кавычки внутри программы jq нужно экранировать обратной косой чертой. В Powershell (powershell.exe) или Powershell Core (pwsh/pwsh.exe) заключайте программу jq в одинарные кавычки, а двойные кавычки внутри программы jq экранируйте обратной косой чертой (\").

  • Unix-оболочки: jq '.["foo"]'
  • Powershell: jq '.[\"foo\"]'
  • Командная оболочка Windows: jq ".[\"foo\"]"

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

С помощью некоторых параметров командной строки можно управлять тем, как jq считывает и записывает входные и выходные данные:

  • --null-input / -n:

Не считывать входные данные. Вместо этого фильтр запускается один раз, используя null в качестве входных данных. Это полезно, если вы используете jq как простой калькулятор или для создания данных JSON с нуля.

  • --raw-input / -R:

Не разбирать входные данные как JSON. Вместо этого каждая строка текста передаётся фильтру как строка. Если использовать этот параметр вместе с --slurp, фильтру передаётся весь ввод как одна длинная строка.

  • --slurp / -s:

Вместо запуска фильтра для каждого объекта JSON во входных данных считать весь входной поток в большой массив и запустить фильтр только один раз.

  • --compact-output / -c:

По умолчанию jq выводит JSON в форматированном виде. Этот параметр позволяет получить более компактный вывод: каждый объект JSON будет размещён в одной строке.

  • --raw-output / -r:

Если результатом фильтра является строка, этот параметр выводит её непосредственно в стандартный вывод, а не в виде строки JSON в кавычках. Это может быть полезно, если фильтры jq должны взаимодействовать с системами, не использующими JSON.

  • --raw-output0:

Как -r, но jq выводит NUL вместо символа новой строки после каждого результата. Это может быть полезно, если выводимые значения могут содержать символы новой строки. Если выходное значение содержит NUL, jq завершает работу с ненулевым кодом.

  • --join-output / -j:

Как -r, но jq не выводит символ новой строки после каждого результата.

  • --ascii-output / -a:

Обычно jq выводит не-ASCII кодовые точки Unicode в кодировке UTF-8, даже если во входных данных они были представлены escape-последовательностями (например, "\u03bc"). С помощью этого параметра можно принудительно включить вывод только в ASCII, заменяя каждый символ, не входящий в ASCII, соответствующей escape-последовательностью.

  • --sort-keys / -S:

Выводить поля каждого объекта, сортируя ключи.

  • --color-output / -C и --monochrome-output / -M:

По умолчанию jq выводит JSON с подсветкой цвета, если вывод направлен в терминал. Параметр -C принудительно включает цветной вывод даже при выводе в канал или файл, а -M отключает его. Если переменная среды NO_COLOR не пуста, jq по умолчанию отключает цветной вывод, но его можно включить с помощью -C.

Цвета можно настроить с помощью переменной среды JQ_COLORS (см. ниже).

  • --tab:

Использовать табуляцию вместо двух пробелов для каждого уровня отступа.

  • --indent n:

Использовать для отступа указанное количество пробелов (не более 7).

  • --unbuffered:

Сбрасывать выходные данные после вывода каждого объекта JSON (полезно, если вы передаёте jq данные из медленного источника, а вывод jq — в другое место).

  • --stream:

Разбирать входные данные в потоковом режиме, выводя массивы из пути и конечных значений (скаляров, пустых массивов или пустых объектов). Например, "a" преобразуется в [[],"a"], а [[],"a",["b"]] — в [[0],[]], [[1],"a"] и [[2,0],"b"].

Это полезно для обработки очень больших входных данных. Используйте этот параметр вместе с фильтрацией и синтаксисом reduce и foreach, чтобы постепенно сокращать объём больших входных данных.

  • --stream-errors:

Как --stream, но для некорректных входных данных JSON выводятся значения-массивы, в которых первый элемент — ошибка, а второй — путь. Например, ["a",n] выдаёт ["Invalid literal at line 1, column 7",[1]].

Подразумевает --stream. Некорректные входные данные JSON не приводят к выводу значений ошибок при использовании --stream без --stream-errors.

  • --seq:

Использовать схему MIME-типа application/json-seq для разделения текстов JSON во входных и выходных данных jq. Это означает, что перед каждым выходным значением выводится символ ASCII RS (разделитель записей), а после каждого результата — символ ASCII LF (перевод строки). Входные тексты JSON, которые не удалось разобрать, игнорируются (с предупреждением); все последующие входные данные отбрасываются до следующего RS. В этом режиме также разбираются выходные данные jq без параметра --seq.

  • -f / --from-file:

Считывать фильтр из файла, а не из командной строки, как параметр -f в awk. В этом случае аргумент фильтра трактуется как имя файла, а не как исходный код программы.

  • -L directory / --library-path directory:

Добавить directory в начало списка путей поиска модулей. Если этот параметр указан, встроенный список путей поиска не используется. См. раздел о модулях ниже.

  • --arg name value:

Этот параметр передаёт программе jq значение в качестве предопределённой переменной. Если запустить jq с --arg foo bar, в программе будет доступна переменная $foo со значением "bar". Обратите внимание, что value будет обработано как строка, поэтому --arg foo 123 присвоит $foo значение "123".

Именованные аргументы также доступны программе jq как $ARGS.named. Если имя не является допустимым идентификатором, это единственный способ обратиться к нему.

  • --argjson name JSON-text:

Этот параметр передаёт программе jq значение в формате JSON в качестве предопределённой переменной. Если запустить jq с --argjson foo 123, в программе будет доступна переменная $foo со значением 123.

  • --slurpfile variable-name filename:

Этот параметр считывает все тексты JSON из указанного файла и связывает массив разобранных значений JSON с заданной глобальной переменной. Если запустить jq с --slurpfile foo bar, в программе будет доступна переменная $foo — массив, элементы которого соответствуют текстам в файле bar.

  • --rawfile variable-name filename:

Этот параметр считывает указанный файл и связывает его содержимое с заданной глобальной переменной. Если запустить jq с --rawfile foo bar, в программе будет доступна переменная $foo со строкой, содержимое которой совпадает с текстом в файле bar.

  • --args:

Оставшиеся аргументы являются позиционными строковыми аргументами. Они доступны программе jq как $ARGS.positional[].

  • --jsonargs:

Оставшиеся аргументы являются позиционными аргументами с текстом JSON. Они доступны программе jq как $ARGS.positional[].

  • --exit-status / -e:

Устанавливает код завершения jq в 0, если последнее выходное значение не равно ни false, ни null; в 1, если последнее выходное значение равно false или null; либо в 4, если корректный результат не был получен. Обычно jq завершается с кодом 2 при ошибке использования или системной ошибке, с кодом 3 при ошибке компиляции программы jq и с кодом 0, если программа jq выполнилась.

Ещё один способ установить код завершения — использовать встроенную функцию halt_error.

  • --binary / -b:

Пользователям Windows, работающим через WSL, MSYS2 или Cygwin, следует использовать этот параметр при запуске нативного jq.exe. В противном случае jq будет преобразовывать символы новой строки (LF) в возврат каретки с последующим переводом строки (CRLF).

  • --version / -V:

Вывести версию jq и завершить работу с кодом 0.

  • --build-configuration:

Вывести конфигурацию сборки jq и завершить работу с кодом 0. Формат и структура этих выходных данных не поддерживаются и могут измениться без предупреждения в будущих выпусках.

  • --help / -h:

Вывести справку jq и завершить работу с кодом 0.

  • --:

Завершить обработку аргументов. Оставшиеся аргументы не будут интерпретироваться как параметры.

  • --run-tests [filename]:

Запустить тесты из указанного файла или стандартного ввода. Этот параметр должен быть последним и не учитывает все предшествующие параметры. Входные данные состоят из строк-комментариев, пустых строк и строк с программой, за которыми следуют одна строка входных данных, столько строк с выходными данными, сколько ожидается (по одной на каждый результат), и завершающая пустая строка. Тесты на ошибки компиляции начинаются со строки, содержащей только %%FAIL; далее следует строка с программой для компиляции, а затем строка с сообщением об ошибке для сравнения с фактическим сообщением.

Обратите внимание: этот параметр может измениться в несовместимом с предыдущими версиями виде.

Базовые фильтры

Тождественный фильтр: .

Самый простой фильтр — это . . Этот фильтр принимает входное значение и выдает его же в качестве результата. Иными словами, это оператор тождества.

Поскольку jq по умолчанию форматирует все выходные данные с отступами, простую программу, состоящую только из ., можно использовать для форматирования вывода JSON, например, из curl.

Хотя тождественный фильтр никогда не изменяет значение на входе, обработка jq иногда может создать такое впечатление. Например, при использовании текущей реализации jq мы увидим, что выражение:

1E1234567890 | .

выдает 1.7976931348623157e+308 как минимум на одной платформе. Это происходит потому, что при разборе числа эта версия jq преобразовала его в представление с плавающей запятой двойной точности IEEE754, потеряв точность.

Способ обработки чисел в jq менялся со временем, и в будущем, вероятно, произойдут дальнейшие изменения в рамках требований соответствующих стандартов JSON. Кроме того, параметры сборки могут влиять на обработку чисел в jq.

Поэтому следующие замечания приводятся с пониманием того, что они описывают текущую версию jq и не должны рассматриваться как предписания:

(1) Любая арифметическая операция над числом, которое еще не было преобразовано в представление IEEE754 с плавающей запятой двойной точности, приводит к такому преобразованию.

(2) jq пытается сохранять исходную десятичную точность числовых литералов (если при сборке не использовался параметр --disable-decnum), однако в таких выражениях, как 1E1234567890, точность будет потеряна, если показатель степени слишком велик.

(3) Сравнения выполняются с использованием нез усеченного представления чисел с высокой точностью, если оно доступно, как показано в одном из следующих примеров.

В приведенных ниже примерах используется встроенная функция have_decnum, чтобы показать ожидаемые результаты при использовании и без использования параметра сборки --disable-decnum, а также чтобы автоматические тесты, составленные на основе этих примеров, проходили независимо от того, используется ли этот параметр.

Команда jq '.'
Ввод "Hello, world!"
Вывод "Hello, world!"
Запустить
Команда jq '.'
Ввод 0.12345678901234567890123456789
Вывод 0.12345678901234567890123456789
Запустить
Команда jq '[., tojson] == if have_decnum then [12345678909876543212345,"12345678909876543212345"] else [12345678909876543000000,"12345678909876543000000"] end'
Ввод 12345678909876543212345
Вывод true
Запустить
Команда jq '[1234567890987654321,-1234567890987654321 | tojson] == if have_decnum then ["1234567890987654321","-1234567890987654321"] else ["1234567890987654400","-1234567890987654400"] end'
Ввод null
Вывод true
Запустить
Команда jq '. < 0.12345678901234567890123456788'
Ввод 0.12345678901234567890123456789
Вывод false
Запустить
Команда jq 'map([., . == 1]) | tojson == if have_decnum then "[[1,true],[1.000,true],[1.0,true],[1.00,true]]" else "[[1,true],[1,true],[1,true],[1,true]]" end'
Ввод [1, 1.000, 1.0, 100e-2]
Вывод true
Запустить
Команда jq '. as $big | [$big, $big + 1] | map(. > 10000000000000000000000000000000) | . == if have_decnum then [true, false] else [false, false] end'
Ввод 10000000000000000000000000000001
Вывод true
Запустить

Индекс объекта по идентификатору: .foo, .foo.bar

Самый простой полезный фильтр имеет вид .foo. Если на вход подан объект JSON (также называемый словарем или хешем), .foo возвращает значение по ключу "foo", если такой ключ существует, и null в противном случае.

Фильтр вида .foo.bar эквивалентен .foo | .bar.

Синтаксис .foo работает только с простыми ключами, похожими на идентификаторы: состоящими только из буквенно-цифровых символов и знака подчеркивания и не начинающимися с цифры.

Если ключ содержит специальные символы или начинается с цифры, его нужно заключить в двойные кавычки, например: ."foo$", либо использовать .["foo$"].

Например, .["foo::bar"] и .["foo.bar"] работают, а .foo::bar — нет.

Команда jq '.foo'
Ввод {"foo": 42, "bar": "less interesting data"}
Вывод 42
Запустить
Команда jq '.foo'
Ввод {"notfoo": true, "alsonotfoo": false}
Вывод null
Запустить
Команда jq '.["foo"]'
Ввод {"foo": 42}
Вывод 42
Запустить

Необязательный индекс объекта по идентификатору: .foo?

Работает так же, как .foo, но не выдает ошибку, если . не является объектом.

Команда jq '.foo?'
Ввод {"foo": 42, "bar": "less interesting data"}
Вывод 42
Запустить
Команда jq '.foo?'
Ввод {"notfoo": true, "alsonotfoo": false}
Вывод null
Запустить
Команда jq '.["foo"]?'
Ввод {"foo": 42}
Вывод 42
Запустить
Команда jq '[.foo?]'
Ввод [1,2]
Вывод []
Запустить

Индекс объекта: .[<string>]

Также можно обращаться к полям объекта с помощью синтаксиса .["foo"] (приведенное выше .foo — это сокращенная форма записи, доступная только для строк, похожих на идентификаторы).

Индекс массива: .[<number>]

Если индекс — целое число, .[<number>] может использоваться для доступа к элементам массива. Индексы массивов начинаются с нуля, поэтому .[2] возвращает третий элемент.

Допускаются отрицательные индексы: -1 обозначает последний элемент, -2 — предпоследний и так далее.

Команда jq '.[0]'
Ввод [{"name":"JSON", "good":true}, {"name":"XML", "good":false}]
Вывод {"name":"JSON", "good":true}
Запустить
Команда jq '.[2]'
Ввод [{"name":"JSON", "good":true}, {"name":"XML", "good":false}]
Вывод null
Запустить
Команда jq '.[-2]'
Ввод [1,2,3]
Вывод 2
Запустить

Срез массива/строки: .[<number>:<number>]

Синтаксис .[<number>:<number>] можно использовать для получения подмассива или подстроки. Массив, возвращаемый выражением .[10:15], будет содержать 5 элементов: от индекса 10 включительно до индекса 15 не включительно. Любой из индексов может быть отрицательным (в этом случае отсчет ведется от конца массива) или опущенным (тогда он указывает на начало или конец массива). Индексы начинаются с нуля.

Команда jq '.[2:4]'
Ввод ["a","b","c","d","e"]
Вывод ["c", "d"]
Запустить
Команда jq '.[2:4]'
Ввод "abcdefghi"
Вывод "cd"
Запустить
Команда jq '.[:3]'
Ввод ["a","b","c","d","e"]
Вывод ["a", "b", "c"]
Запустить
Команда jq '.[-2:]'
Ввод ["a","b","c","d","e"]
Вывод ["d", "e"]
Запустить

Итератор значений массива/объекта: .[]

Если использовать синтаксис .[index], но не указывать индекс, будут возвращены все элементы массива. Запуск .[] с входным значением [1,2,3] вернет числа как три отдельных результата, а не как один массив. Фильтр вида .foo[] эквивалентен .foo | .[].

Этот синтаксис можно также использовать с объектом: он вернет все значения объекта.

Обратите внимание, что оператор итератора является генератором значений.

Команда jq '.[]'
Ввод [{"name":"JSON", "good":true}, {"name":"XML", "good":false}]
Вывод {"name":"JSON", "good":true}
{"name":"XML", "good":false}
Запустить
Команда jq '.[]'
Ввод []
Вывод нет
Запустить
Команда jq '.foo[]'
Ввод {"foo":[1,2,3]}
Вывод 1
2
3
Запустить
Команда jq '.[]'
Ввод {"a": 1, "b": 1}
Вывод 1
1
Запустить

.[]?

Работает как .[], но не выдает ошибок, если . не является массивом или объектом. Фильтр вида .foo[]? эквивалентен .foo | .[]?.

Запятая: ,

Если два фильтра разделены запятой, одно и то же входное значение передается обоим, а потоки выходных значений этих фильтров объединяются в порядке следования: сначала выводятся все результаты левого выражения, затем — правого. Например, фильтр .foo, .bar выводит поля "foo" и "bar" по отдельности.

Оператор , — один из способов создания генераторов.

Команда jq '.foo, .bar'
Ввод {"foo": 42, "bar": "something else", "baz": true}
Вывод 42
"something else"
Запустить
Команда jq '.user, .projects[]'
Ввод {"user":"stedolan", "projects": ["jq", "wikiflow"]}
Вывод "stedolan"
"jq"
"wikiflow"
Запустить
Команда jq '.[4,2]'
Ввод ["a","b","c","d","e"]
Вывод "e"
"c"
Запустить

Конвейер: |

Оператор | объединяет два фильтра, передавая выходные значения левого фильтра на вход правого. Он похож на конвейер Unix shell, если вы с ним знакомы.

Если левый фильтр выдает несколько результатов, правый будет запущен для каждого из них. Таким образом, выражение .[] | .foo извлекает поле "foo" из каждого элемента входного массива. Это декартово произведение, что может оказаться неожиданным.

Обратите внимание, что .a.b.c — то же самое, что .a | .b | .c.

Также обратите внимание, что . — это входное значение на данном этапе «конвейера», а именно в том месте, где находится выражение .. Поэтому .a | . | .b — то же самое, что .a.b, поскольку . в середине ссылается на значение, полученное в результате .a.

Команда jq '.[] | .name'
Ввод [{"name":"JSON", "good":true}, {"name":"XML", "good":false}]
Вывод "JSON"
"XML"
Запустить

Скобки

Скобки работают как оператор группировки, как и в любом обычном языке программирования.

Команда jq '(. + 2) * 5'
Ввод 1
Вывод 15
Запустить

Типы и значения

jq поддерживает тот же набор типов данных, что и JSON: числа, строки, логические значения, массивы, объекты (которые в терминологии JSON представляют собой хеши только со строковыми ключами) и «null».

Логические значения, null, строки и числа записываются так же, как в JSON. Как и всё остальное в jq, эти простые значения принимают входные данные и выдают результат: 42 — допустимое выражение jq, которое принимает входные данные, игнорирует их и вместо этого возвращает 42.

Внутри jq числа представлены приближёнными значениями двойной точности IEEE754. Любая арифметическая операция с числами, будь то литералы или результаты предыдущих фильтров, выдаёт результат с плавающей точкой двойной точности.

Однако при разборе литерала jq сохраняет исходную строку литерала. Если это значение не изменяется, оно попадает в результат в исходном виде, даже если преобразование в число двойной точности привело бы к потере точности.

Создание массивов: []

Как и в JSON, [] используется для создания массивов, например [1,2,3]. Элементами массивов могут быть любые выражения jq, включая конвейер. Все результаты, полученные всеми выражениями, собираются в один большой массив. С его помощью можно создать массив из известного количества значений (как в [.foo, .bar, .baz]) или «собрать» в массив все результаты фильтра (как в [.items[].name])

Поняв оператор «,», вы можете взглянуть на синтаксис массивов jq по-новому: выражение [1,2,3] не использует встроенный синтаксис для массивов с элементами, разделёнными запятыми, а применяет оператор [] (собрать результаты) к выражению 1,2,3 (которое выдаёт три разных результата).

Если у вас есть фильтр X, который выдаёт четыре результата, то выражение [X] выдаст один результат — массив из четырёх элементов.

Команда jq '[.user, .projects[]]'
Входные данные {"user":"stedolan", "projects": ["jq", "wikiflow"]}
Результат ["stedolan", "jq", "wikiflow"]
Запустить
Команда jq '[ .[] | . * 2]'
Входные данные [1, 2, 3]
Результат [2, 4, 6]
Запустить

Создание объектов: {}

Как и в JSON, {} используется для создания объектов (также называемых словарями или хешами), например: {"a": 42, "b": 17}.

Если ключи имеют вид идентификаторов, кавычки можно опустить, как в {a:42, b:17}. Ссылки на переменные в выражениях ключей используют значение переменной в качестве ключа. Выражения ключей, отличные от константных литералов, идентификаторов или ссылок на переменные, необходимо заключать в скобки, например {("a"+"b"):59}.

Значением может быть любое выражение (хотя, например, если оно содержит двоеточия, его может потребоваться заключить в скобки), которое применяется к входным данным выражения {} (помните: у всех фильтров есть входные данные и результат).

{foo: .bar}

создаст объект JSON {"foo": 42}, если ему на вход передать объект JSON {"bar":42, "baz":43}. Это можно использовать для выбора определённых полей объекта: если входные данные — объект с полями "user", "title", "id" и "content", а вам нужны только "user" и "title", можно написать

{user: .user, title: .title}

Поскольку это очень распространённая задача, для неё предусмотрен сокращённый синтаксис: {user, title}.

Если одно из выражений выдаёт несколько результатов, будет создано несколько словарей. Если во входных данных

{"user":"stedolan","titles":["JQ Primer", "More JQ"]}

то выражение

{user, title: .titles[]}

выдаст два результата:

{"user":"stedolan", "title": "JQ Primer"}
{"user":"stedolan", "title": "More JQ"}

Если заключить ключ в скобки, он будет вычислен как выражение. Для тех же входных данных, что и выше,

{(.user): .titles}

выдаёт

{"stedolan": ["JQ Primer", "More JQ"]}

В ссылках на переменные в качестве ключа используется значение переменной. Если значение не указано, именем ключа становится имя переменной, а её значение — значением ключа,

"f o o" as $foo | "b a r" as $bar | {$foo, $bar:$foo}

выдаёт

{"foo":"f o o","b a r":"f o o"}
Команда jq '{user, title: .titles[]}'
Входные данные {"user":"stedolan","titles":["JQ Primer", "More JQ"]}
Результат {"user":"stedolan", "title": "JQ Primer"}
{"user":"stedolan", "title": "More JQ"}
Запустить
Команда jq '{(.user): .titles}'
Входные данные {"user":"stedolan","titles":["JQ Primer", "More JQ"]}
Результат {"stedolan": ["JQ Primer", "More JQ"]}
Запустить

Рекурсивный обход: ..

Рекурсивно обходит ., выдавая каждое значение. Это то же самое, что встроенная функция recurse без аргументов (см. ниже). По замыслу она напоминает оператор XPath //. Обратите внимание: ..a не работает; вместо него используйте .. | .a. В примере ниже мы используем .. | .a?, чтобы найти все значения ключей объектов "a" в любом объекте, расположенном «ниже» ..

Это особенно полезно в сочетании с path(EXP) (см. также ниже) и оператором ?.

Команда jq '.. | .a?'
Входные данные [[{"a":1}]]
Результат 1
Запустить

Встроенные операторы и функции

Некоторые операторы jq (например, +) ведут себя по-разному в зависимости от типа своих аргументов (массивы, числа и т. д.). Однако jq никогда не выполняет неявное преобразование типов. Если вы попытаетесь прибавить строку к объекту, вы получите сообщение об ошибке и не получите результата.

Обратите внимание, что все числа преобразуются в формат чисел с плавающей точкой двойной точности IEEE754. Арифметические и логические операторы работают с этими преобразованными числами. Результаты всех таких операций также ограничены точностью двойной точности.

Единственное исключение из этого правила для чисел — снимок исходного числового литерала. Если число, изначально заданное литералом, не изменяется до конца программы, оно выводится в исходном буквальном представлении. Это касается и случаев, когда исходный литерал был бы усечён при преобразовании в число с плавающей точкой двойной точности IEEE754.

Сложение: +

Оператор + принимает два фильтра, применяет их оба к одному и тому же входному значению и складывает результаты. Значение «сложения» зависит от участвующих типов:

  • Числа складываются обычным арифметическим способом.

  • Массивы складываются путём объединения в массив большего размера.

  • Строки складываются путём объединения в строку большей длины.

  • Объекты складываются путём слияния, то есть вставки всех пар «ключ-значение» из обоих объектов в один общий объект. Если в обоих объектах есть значение с одинаковым ключом, приоритет имеет объект справа от +. (Для рекурсивного слияния используйте оператор *.)

К любому значению можно прибавить null; результатом будет другое значение без изменений.

Команда jq '.a + 1'
Входные данные {"a": 7}
Результат 8
Запустить
Команда jq '.a + .b'
Входные данные {"a": [1,2], "b": [3,4]}
Результат [1,2,3,4]
Запустить
Команда jq '.a + null'
Входные данные {"a": 1}
Результат 1
Запустить
Команда jq '.a + 1'
Входные данные {}
Результат 1
Запустить
Команда jq '{a: 1} + {b: 2} + {c: 3} + {a: 42}'
Входные данные null
Результат {"a": 42, "b": 2, "c": 3}
Запустить

Вычитание: -

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

Команда jq '4 - .a'
Входные данные {"a":3}
Результат 1
Запустить
Команда jq '. - ["xml", "yaml"]'
Входные данные ["xml", "yaml", "json"]
Результат ["json"]
Запустить

Умножение, деление, остаток от деления: *, /, %

Эти инфиксные операторы работают ожидаемым образом, если им передать два числа. Деление на ноль вызывает ошибку. x % y вычисляет остаток от деления x на y.

При умножении строки на число получается конкатенация этой строки, повторённая указанное число раз. "x" * 0 выдаёт "".

Деление одной строки на другую разбивает первую строку, используя вторую в качестве разделителей.

Умножение двух объектов выполняет их рекурсивное слияние: оно работает подобно сложению, но если в обоих объектах есть значения с одинаковым ключом и эти значения являются объектами, они объединяются по той же стратегии.

Команда jq '10 / . * 3'
Входные данные 5
Результат 6
Запустить
Команда jq '. / ", "'
Входные данные "a, b,c,d, e"
Результат ["a","b,c,d","e"]
Запустить
Команда jq '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}'
Входные данные null
Результат {"k": {"a": 0, "b": 2, "c": 3}}
Запустить
Команда jq '.[] | (1 / .)?'
Входные данные [1,0,-1]
Результат 1
-1
Запустить

abs

Встроенная функция abs определяется простейшим образом: if . < 0 then - . else . end.

Для числового входного значения это абсолютная величина. О последствиях такого определения для числового входного значения см. раздел о фильтре идентичности.

Чтобы вычислить абсолютную величину числа в формате числа с плавающей точкой, можно использовать fabs.

Команда jq 'map(abs)'
Входные данные [-10, -1.1, -1e-1]
Результат [10,1.1,1e-1]
Запустить

length

Встроенная функция length возвращает длину значения различных типов:

  • Длина строки — это количество содержащихся в ней кодовых точек Unicode (для строки, состоящей только из символов ASCII, оно совпадает с длиной её представления в байтах в формате JSON).

  • Длина числа — это его абсолютная величина.

  • Длина массива — это количество элементов.

  • Длина объекта — это количество пар «ключ-значение».

  • Длина null равна нулю.

  • Использование length для логического значения вызывает ошибку.

Команда jq '.[] | length'
Входные данные [[1,2], "string", {"a":2}, null, -5]
Результат 2
6
1
0
5
Запустить

utf8bytelength

Встроенная функция utf8bytelength выводит количество байтов, используемых для кодирования строки в UTF-8.

Команда jq 'utf8bytelength'
Входные данные "\u03bc"
Результат 2
Запустить

keys, keys_unsorted

Встроенная функция keys возвращает ключи переданного объекта в виде массива.

Ключи сортируются «по алфавиту» в порядке кодовых точек Unicode. Такой порядок не имеет особого смысла ни для какого языка, но для любых двух объектов с одинаковым набором ключей он будет одинаковым независимо от настроек локали.

Если keys получает массив, она возвращает допустимые индексы этого массива: целые числа от 0 до длины минус 1.

Функция keys_unsorted работает так же, как keys, но если на входе объект, его ключи не сортируются, а располагаются примерно в порядке добавления.

Команда jq 'keys'
Входные данные {"abc": 1, "abcd": 2, "Foo": 3}
Результат ["Foo", "abc", "abcd"]
Запустить
Команда jq 'keys'
Входные данные [42,3,35]
Результат [0,1,2]
Запустить

has(key)

Встроенная функция has возвращает, содержит ли входной объект заданный ключ или есть ли во входном массиве элемент с заданным индексом.

has($key) действует так же, как проверка того, входит ли $key в массив, возвращаемый keys, хотя has работает быстрее.

Команда jq 'map(has("foo"))'
Входные данные [{"foo": 42}, {}]
Результат [true, false]
Запустить
Команда jq 'map(has(2))'
Входные данные [[0,1], ["a","b","c"]]
Результат [false, true]
Запустить

in

Встроенная функция in возвращает, содержится ли входной ключ в заданном объекте или соответствует ли входной индекс элементу заданного массива. По сути, это инвертированная версия has.

Команда jq '.[] | in({"foo": 42})'
Входные данные ["foo", "bar"]
Результат true
false
Запустить
Команда jq 'map(in([0,1]))'
Входные данные [2, 0]
Результат [false, true]
Запустить

map(f), map_values(f)

Для любого фильтра f функции map(f) и map_values(f) применяют f к каждому значению входного массива или объекта, то есть к значениям .[].

Если не возникает ошибок, map(f) всегда выводит массив, тогда как map_values(f) выводит массив, если на входе массив, и объект, если на входе объект.

Если на входе map_values(f) находится объект, выходной объект имеет те же ключи, что и входной, за исключением ключей, значения которых при передаче в f не дают никаких результатов.

Основное различие между map(f) и map_values(f) заключается в том, что первая просто формирует массив из всех значений ($x|f) для каждого значения $x во входном массиве или объекте, тогда как map_values(f) использует только first($x|f).

В частности, для объекта на входе map_values(f) создаёт выходной объект, по очереди рассматривая значение first(.[$k]|f) для каждого ключа $k входного объекта. Если это выражение не даёт результатов, соответствующий ключ удаляется; в противном случае выходной объект содержит это значение по ключу $k.

Вот несколько примеров, иллюстрирующих поведение map и map_values при применении к массивам. Во всех случаях предполагается, что на входе находится [1]:

map(.+1)          #=>  [2]
map(., .)         #=>  [1,1]
map(empty)        #=>  []

map_values(.+1)   #=>  [2]
map_values(., .)  #=>  [1]
map_values(empty) #=>  []

map(f) эквивалентно [.[] | f], а map_values(f) эквивалентно .[] |= f.

Именно так они и реализованы.

Команда jq 'map(.+1)'
Входные данные [1,2,3]
Результат [2,3,4]
Запустить
Команда jq 'map_values(.+1)'
Входные данные {"a": 1, "b": 2, "c": 3}
Результат {"a": 2, "b": 3, "c": 4}
Запустить
Команда jq 'map(., .)'
Входные данные [1,2]
Результат [1,1,2,2]
Запустить
Команда jq 'map_values(. // empty)'
Входные данные {"a": null, "b": true, "c": false}
Результат {"b":true}
Запустить

pick(pathexps)

Выводит проекцию входного объекта или массива, заданную указанной последовательностью выражений пути, так что если p является одной из этих спецификаций, то (. | p) будет вычисляться в то же значение, что и (. | pick(pathexps) | p). Для массивов не следует использовать отрицательные индексы и спецификации .[m:n].

Команда jq 'pick(.a, .b.c, .x)'
Входные данные {"a": 1, "b": {"c": 2, "d": 3}, "e": 4}
Результат {"a":1,"b":{"c":2},"x":null}
Запустить
Команда jq 'pick(.[2], .[0], .[0])'
Входные данные [1,2,3,4]
Результат [1,null,3]
Запустить

path(path_expression)

Выводит представления заданного выражения пути в виде массивов в .. Результатами являются массивы строк (ключей объектов) и/или чисел (индексов массивов).

Выражения пути — это выражения jq, например .a, а также .[]. Существует два типа выражений пути: те, которые могут точно совпасть, и те, которые не могут. Например, .a.b.c — это выражение пути с точным совпадением, а .a[].b — нет.

path(exact_path_expression) создаёт представление выражения пути в виде массива, даже если этот путь не существует в ., при условии что . — это null, массив или объект.

path(pattern) создаёт представления в виде массивов для путей, соответствующих pattern, если эти пути существуют в ..

Обратите внимание, что выражения пути ничем не отличаются от обычных выражений. Выражение path(..|select(type=="boolean")) выводит все пути к логическим значениям в . и только эти пути.

Команда jq 'path(.a[0].b)'
Входные данные null
Результат ["a",0,"b"]
Запустить
Команда jq '[path(..)]'
Входные данные {"a":[{"b":1}]}
Результат [[],["a"],["a",0],["a",0,"b"]]
Запустить

del(path_expression)

Встроенная функция del удаляет ключ и соответствующее ему значение из объекта.

Команда jq 'del(.foo)'
Входные данные {"foo": 42, "bar": 9001, "baz": 42}
Результат {"bar": 9001, "baz": 42}
Запустить
Команда jq 'del(.[1, 2])'
Входные данные ["foo", "bar", "baz"]
Результат ["foo"]
Запустить

getpath(PATHS)

Встроенная функция getpath выводит значения в ., найденные по каждому пути в PATHS.

Команда jq 'getpath(["a","b"])'
Ввод null
Вывод null
Запустить
Команда jq '[getpath(["a","b"], ["a","c"])]'
Ввод {"a":{"b":0, "c":1}}
Вывод [0, 1]
Запустить

setpath(PATHS; VALUE)

Встроенная функция setpath устанавливает PATHS в . в значение VALUE.

Команда jq 'setpath(["a","b"]; 1)'
Ввод null
Вывод {"a": {"b": 1}}
Запустить
Команда jq 'setpath(["a","b"]; 1)'
Ввод {"a":{"b":0}}
Вывод {"a": {"b": 1}}
Запустить
Команда jq 'setpath([0,"a"]; 1)'
Ввод null
Вывод [{"a":1}]
Запустить

delpaths(PATHS)

Встроенная функция delpaths удаляет PATHS из .. PATHS должен быть массивом путей, где каждый путь — массив строк и чисел.

Команда jq 'delpaths([["a","b"]])'
Ввод {"a":{"b":1},"x":{"y":2}}
Вывод {"a":{},"x":{"y":2}}
Запустить

to_entries, from_entries, with_entries(f)

Эти функции преобразуют объект в массив пар «ключ-значение» и обратно. Если to_entries передан объект, то для каждой k: v записи во входных данных выходной массив содержит {"key": k, "value": v}.

from_entries выполняет обратное преобразование, а with_entries(f) — это сокращённая запись для to_entries | map(f) | from_entries, полезная для выполнения операции над всеми ключами и значениями объекта. from_entries принимает в качестве ключей "key", "Key", "name", "Name", "value" и "Value".

Команда jq 'to_entries'
Ввод {"a": 1, "b": 2}
Вывод [{"key":"a", "value":1}, {"key":"b", "value":2}]
Запустить
Команда jq 'from_entries'
Ввод [{"key":"a", "value":1}, {"key":"b", "value":2}]
Вывод {"a": 1, "b": 2}
Запустить
Команда jq 'with_entries(.key |= "KEY_" + .)'
Ввод {"a": 1, "b": 2}
Вывод {"KEY_a": 1, "KEY_b": 2}
Запустить

select(boolean_expression)

Функция select(f) возвращает входные данные без изменений, если f возвращает для них true, и не выдаёт результат в противном случае.

Она полезна для фильтрации списков: [1,2,3] | map(select(. >= 2)) даст вам [2,3].

Команда jq 'map(select(. >= 2))'
Ввод [1,5,3,0,7]
Вывод [5,3,7]
Запустить
Команда jq '.[] | select(.id == "second")'
Ввод [{"id": "first", "val": 1}, {"id": "second", "val": 2}]
Вывод {"id": "second", "val": 2}
Запустить

arrays, objects, iterables, booleans, numbers, normals, finites, strings, nulls, values, scalars

Эти встроенные функции выбирают только входные данные, которые являются массивами, объектами, итерируемыми значениями (массивами или объектами), логическими значениями, числами, обычными числами, конечными числами, строками, null, ненулевыми значениями и неитерируемыми значениями соответственно.

Команда jq '.[]|numbers'
Ввод [[],{},1,"foo",null,true,false]
Вывод 1
Запустить

empty

empty не возвращает результатов. Вообще никаких. Даже null.

Иногда это бывает полезно. Вы поймёте, когда это понадобится :)

Команда jq '1, empty, 2'
Ввод null
Вывод 1
2
Запустить
Команда jq '[1,2,empty,3]'
Ввод null
Вывод [1,2,3]
Запустить

error, error(message)

Выдаёт ошибку с входным значением или с сообщением, переданным в качестве аргумента. Ошибки можно перехватывать с помощью try/catch; см. ниже.

Команда jq 'try error catch .'
Ввод "error message"
Вывод "error message"
Запустить
Команда jq 'try error("invalid value: \(.)") catch .'
Ввод 42
Вывод "invalid value: 42"
Запустить

halt

Останавливает программу jq, не выдавая дальнейших результатов. jq завершится с кодом возврата 0.

halt_error, halt_error(exit_code)

Останавливает программу jq, не выдавая дальнейших результатов. Входные данные будут выведены в stderr как необработанный вывод (т. е. строки будут без двойных кавычек), без оформления и даже без символа новой строки.

Указанный exit_code (по умолчанию 5) будет кодом возврата jq.

Например, "Error: something went wrong\n"|halt_error(1).

$__loc__

Создаёт объект с ключами "file" и "line", значениями которых являются имя файла и номер строки, где встречается $__loc__.

Команда jq 'try error("\($__loc__)") catch .'
Ввод null
Вывод "{\"file\":\"<top-level>\",\"line\":1}"
Запустить

paths, paths(node_filter)

paths выводит пути ко всем элементам входных данных (за исключением пустого списка, который представляет . само по себе).

paths(f) выводит пути к значениям, для которых f равно true. То есть paths(type == "number") выводит пути ко всем числовым значениям.

Команда jq '[paths]'
Ввод [1,[[],{"a":2}]]
Вывод [[0],[1],[1,0],[1,1],[1,1,"a"]]
Запустить
Команда jq '[paths(type == "number")]'
Ввод [1,[[],{"a":2}]]
Вывод [[0],[1,1,"a"]]
Запустить

add, add(generator)

Фильтр add принимает на вход массив и выдаёт его элементы, сложенные вместе. В зависимости от типов элементов входного массива это может означать арифметическое суммирование, конкатенацию или объединение — правила такие же, как для оператора + (описанного выше).

Если на вход подан пустой массив, add возвращает null.

add(generator) работает с указанным генератором, а не с входными данными.

Команда jq 'add'
Ввод ["a","b","c"]
Вывод "abc"
Запустить
Команда jq 'add'
Ввод [1, 2, 3]
Вывод 6
Запустить
Команда jq 'add'
Ввод []
Вывод null
Запустить
Команда jq 'add(.[].a)'
Ввод [{"a":3}, {"a":5}, {"b":6}]
Вывод 8
Запустить

any, any(condition), any(generator; condition)

Фильтр any принимает на вход массив логических значений и выдаёт true, если хотя бы один элемент массива равен true.

Если на вход подан пустой массив, any возвращает false.

Форма any(condition) применяет заданное условие к элементам входного массива.

Форма any(generator; condition) применяет заданное условие ко всем результатам указанного генератора.

Команда jq 'any'
Ввод [true, false]
Вывод true
Запустить
Команда jq 'any'
Ввод [false, false]
Вывод false
Запустить
Команда jq 'any'
Ввод []
Вывод false
Запустить

all, all(condition), all(generator; condition)

Фильтр all принимает на вход массив логических значений и выдаёт true, если все элементы массива равны true.

Форма all(condition) применяет заданное условие к элементам входного массива.

Форма all(generator; condition) применяет заданное условие ко всем результатам указанного генератора.

Если на вход подан пустой массив, all возвращает true.

Команда jq 'all'
Ввод [true, false]
Вывод false
Запустить
Команда jq 'all'
Ввод [true, true]
Вывод true
Запустить
Команда jq 'all'
Ввод []
Вывод true
Запустить

flatten, flatten(depth)

Фильтр flatten принимает на вход массив вложенных массивов и выдаёт плоский массив, в котором все массивы внутри исходного массива рекурсивно заменены их значениями. Ему можно передать аргумент, задающий количество уровней вложенности для уплощения.

flatten(2) похож на flatten, но обрабатывает только два уровня вложенности.

Команда jq 'flatten'
Ввод [1, [2], [[3]]]
Вывод [1, 2, 3]
Запустить
Команда jq 'flatten(1)'
Ввод [1, [2], [[3]]]
Вывод [1, 2, [3]]
Запустить
Команда jq 'flatten'
Ввод [[]]
Вывод []
Запустить
Команда jq 'flatten'
Ввод [{"foo": "bar"}, [{"foo": "baz"}]]
Вывод [{"foo": "bar"}, {"foo": "baz"}]
Запустить

range(upto), range(from; upto), range(from; upto; by)

Функция range формирует диапазон чисел. range(4; 10) формирует 6 чисел: от 4 (включительно) до 10 (не включительно). Числа выводятся по отдельности. Используйте [range(4; 10)], чтобы получить диапазон в виде массива.

Форма с одним аргументом генерирует числа от 0 до заданного числа с шагом 1.

Форма с двумя аргументами генерирует числа от from до upto с шагом 1.

Форма с тремя аргументами генерирует числа от from до upto с шагом by.

Команда jq 'range(2; 4)'
Входные данные null
Результат 2
3
Запустить
Команда jq '[range(2; 4)]'
Входные данные null
Результат [2,3]
Запустить
Команда jq '[range(4)]'
Входные данные null
Результат [0,1,2,3]
Запустить
Команда jq '[range(0; 10; 3)]'
Входные данные null
Результат [0,3,6,9]
Запустить
Команда jq '[range(0; 10; -1)]'
Входные данные null
Результат []
Запустить
Команда jq '[range(0; -5; -1)]'
Входные данные null
Результат [0,-1,-2,-3,-4]
Запустить

floor

Функция floor возвращает округлённое вниз значение числового аргумента.

Команда jq 'floor'
Входные данные 3.14159
Результат 3
Запустить

sqrt

Функция sqrt возвращает квадратный корень числового аргумента.

Команда jq 'sqrt'
Входные данные 9
Результат 3
Запустить

tonumber

Функция tonumber разбирает входные данные как число. Она преобразует строки в правильном формате в соответствующие числа, оставляет числа без изменений и выдаёт ошибку для всех остальных входных данных.

Команда jq '.[] | tonumber'
Входные данные [1, "1"]
Результат 1
1
Запустить

toboolean

Функция toboolean разбирает входные данные как логическое значение. Она преобразует строки в правильном формате в соответствующие логические значения, оставляет логические значения без изменений и выдаёт ошибку для всех остальных входных данных.

Команда jq '.[] | toboolean'
Входные данные ["true", "false", true, false]
Результат true
false
true
false
Запустить

tostring

Функция tostring выводит входные данные в виде строки. Строки остаются без изменений, а все остальные значения кодируются в формате JSON.

Команда jq '.[] | tostring'
Входные данные [1, "1", [1]]
Результат "1"
"1"
"[1]"
Запустить

type

Функция type возвращает тип аргумента в виде строки. Типом может быть null, boolean, number, string, array или object.

Команда jq 'map(type)'
Входные данные [0, false, [], {}, null, "hello"]
Результат ["number", "boolean", "array", "object", "null", "string"]
Запустить

infinite, nan, isinfinite, isnan, isfinite, isnormal

Некоторые арифметические операции могут возвращать бесконечные значения и значения «не число» (NaN). Встроенная функция isinfinite возвращает true, если её входное значение бесконечно. Встроенная функция isnan возвращает true, если её входное значение — NaN. Встроенная функция infinite возвращает положительное бесконечное значение. Встроенная функция nan возвращает NaN. Встроенная функция isnormal возвращает true, если её входное значение является нормальным числом.

Обратите внимание, что деление на ноль вызывает ошибку.

В настоящее время большинство арифметических операций с бесконечностями, значениями NaN и субнормальными числами не вызывают ошибок.

Команда jq '.[] | (infinite * .) < 0'
Входные данные [-1, 1]
Результат true
false
Запустить
Команда jq 'infinite, nan | type'
Входные данные null
Результат "number"
"number"
Запустить

sort, sort_by(path_expression)

Функция sort сортирует входные данные, которые должны быть массивом. Значения сортируются в следующем порядке:

  • null
  • false
  • true
  • числа
  • строки в алфавитном порядке (по значению кодовой точки Unicode)
  • массивы в лексикографическом порядке
  • объекты

Правило сортировки объектов немного сложнее: сначала они сравниваются по наборам ключей (как массивы, отсортированные по порядку), а если ключи совпадают, то значения сравниваются по каждому ключу.

sort_by можно использовать для сортировки по определённому полю объекта или по результату применения любого фильтра jq. sort_by(f) сравнивает два элемента, сравнивая результаты применения f к каждому элементу. Если f выдаёт несколько значений, сначала сравниваются первые значения, затем вторые, если первые равны, и так далее.

Команда jq 'sort'
Входные данные [8,3,null,6]
Результат [null,3,6,8]
Запустить
Команда jq 'sort_by(.foo)'
Входные данные [{"foo":4, "bar":10}, {"foo":3, "bar":10}, {"foo":2, "bar":1}]
Результат [{"foo":2, "bar":1}, {"foo":3, "bar":10}, {"foo":4, "bar":10}]
Запустить
Команда jq 'sort_by(.foo, .bar)'
Входные данные [{"foo":4, "bar":10}, {"foo":3, "bar":20}, {"foo":2, "bar":1}, {"foo":3, "bar":10}]
Результат [{"foo":2, "bar":1}, {"foo":3, "bar":10}, {"foo":3, "bar":20}, {"foo":4, "bar":10}]
Запустить

group_by(path_expression)

group_by(.foo) принимает массив, группирует элементы с одинаковым полем .foo в отдельные массивы и формирует из этих массивов элементы более крупного массива, отсортированного по значению поля .foo.

Вместо .foo можно использовать любое выражение jq, а не только обращение к полю. Порядок сортировки такой же, как описано выше для функции sort.

Команда jq 'group_by(.foo)'
Входные данные [{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]
Результат [[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]
Запустить

min, max, min_by(path_exp), max_by(path_exp)

Находит минимальный или максимальный элемент входного массива.

Функции min_by(path_exp) и max_by(path_exp) позволяют указать конкретное поле или свойство для сравнения. Например, min_by(.foo) находит объект с наименьшим значением поля foo.

Команда jq 'min'
Входные данные [5,4,2,7]
Результат 2
Запустить
Команда jq 'max_by(.foo)'
Входные данные [{"foo":1, "bar":14}, {"foo":2, "bar":3}]
Результат {"foo":2, "bar":3}
Запустить

unique, unique_by(path_exp)

Функция unique принимает массив и формирует массив с теми же элементами в отсортированном порядке, удаляя дубликаты.

Функция unique_by(path_exp) оставляет только один элемент для каждого значения, полученного при применении аргумента. Её можно представить как создание массива, в который берётся по одному элементу из каждой группы, сформированной функцией group.

Команда jq 'unique'
Входные данные [1,2,5,3,5,3,1,3]
Результат [1,2,3,5]
Запустить
Команда jq 'unique_by(.foo)'
Входные данные [{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}]
Результат [{"foo": 1, "bar": 2}, {"foo": 4, "bar": 5}]
Запустить
Команда jq 'unique_by(length)'
Входные данные ["chunky", "bacon", "kitten", "cicada", "asparagus"]
Результат ["bacon", "chunky", "asparagus"]
Запустить

reverse

Эта функция разворачивает массив в обратном порядке.

Команда jq 'reverse'
Входные данные [1,2,3,4]
Результат [4,3,2,1]
Запустить

contains(element)

Фильтр contains(b) выдаёт true, если b полностью содержится во входных данных. Строка B содержится в строке A, если B является подстрокой A. Массив B содержится в массиве A, если каждый элемент B содержится в каком-либо элементе A. Объект B содержится в объекте A, если все значения B содержатся в значениях A с теми же ключами. Считается, что значения всех остальных типов содержатся друг в друге, если они равны.

Команда jq 'contains("bar")'
Входные данные "foobar"
Результат true
Запустить
Команда jq 'contains(["baz", "bar"])'
Входные данные ["foobar", "foobaz", "blarp"]
Результат true
Запустить
Команда jq 'contains(["bazzzzz", "bar"])'
Входные данные ["foobar", "foobaz", "blarp"]
Результат false
Запустить
Команда jq 'contains({foo: 12, bar: [{barp: 12}]})'
Входные данные {"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}
Результат true
Запустить
Команда jq 'contains({foo: 12, bar: [{barp: 15}]})'
Входные данные {"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}
Результат false
Запустить

indices(s)

Выводит массив, содержащий индексы в ., по которым встречается s. Входные данные могут быть массивом; в этом случае, если s — массив, то выводятся индексы, по которым все элементы . совпадают с элементами s.

Команда jq 'indices(", ")'
Входные данные "a,b, cd, efg, hijk"
Результат [3,7,12]
Запустить
Команда jq 'indices(1)'
Входные данные [0,1,2,1,3,1,4]
Результат [1,3,5]
Запустить
Команда jq 'indices([1,2])'
Входные данные [0,1,2,3,1,4,2,5,1,2,6,7]
Результат [1,8]
Запустить

index(s), rindex(s)

Выводит индекс первого (index) или последнего (rindex) вхождения s во входных данных.

Команда jq 'index(", ")'
Входные данные "a,b, cd, efg, hijk"
Результат 3
Выполнить
Команда jq 'index(1)'
Входные данные [0,1,2,1,3,1,4]
Результат 1
Выполнить
Команда jq 'index([1,2])'
Входные данные [0,1,2,3,1,4,2,5,1,2,6,7]
Результат 1
Выполнить
Команда jq 'rindex(", ")'
Входные данные "a,b, cd, efg, hijk"
Результат 12
Выполнить
Команда jq 'rindex(1)'
Входные данные [0,1,2,1,3,1,4]
Результат 5
Выполнить
Команда jq 'rindex([1,2])'
Входные данные [0,1,2,3,1,4,2,5,1,2,6,7]
Результат 8
Выполнить

inside

Фильтр inside(b) возвращает true, если входные данные полностью содержатся в b. По сути, это инвертированная версия contains.

Команда jq 'inside("foobar")'
Входные данные "bar"
Результат true
Выполнить
Команда jq 'inside(["foobar", "foobaz", "blarp"])'
Входные данные ["baz", "bar"]
Результат true
Выполнить
Команда jq 'inside(["foobar", "foobaz", "blarp"])'
Входные данные ["bazzzzz", "bar"]
Результат false
Выполнить
Команда jq 'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})'
Входные данные {"foo": 12, "bar": [{"barp": 12}]}
Результат true
Выполнить
Команда jq 'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})'
Входные данные {"foo": 12, "bar": [{"barp": 15}]}
Результат false
Выполнить

startswith(str)

Выводит true, если . начинается с заданной строковой аргумента.

Команда jq '[.[]|startswith("foo")]'
Входные данные ["fo", "foo", "barfoo", "foobar", "barfoob"]
Результат [false, true, false, true, false]
Выполнить

endswith(str)

Выводит true, если . заканчивается заданным строковым аргументом.

Команда jq '[.[]|endswith("foo")]'
Входные данные ["foobar", "barfoo"]
Результат [false, true]
Выполнить

combinations, combinations(n)

Выводит все комбинации элементов массивов во входном массиве. Если задан аргумент n, выводит все комбинации из n повторений входного массива.

Команда jq 'combinations'
Входные данные [[1,2], [3, 4]]
Результат [1, 3]
[1, 4]
[2, 3]
[2, 4]
Выполнить
Команда jq 'combinations(2)'
Входные данные [0, 1]
Результат [0, 0]
[0, 1]
[1, 0]
[1, 1]
Выполнить

ltrimstr(str)

Выводит входное значение, удалив заданную строку-префикс, если оно с неё начинается.

Команда jq '[.[]|ltrimstr("foo")]'
Входные данные ["fo", "foo", "barfoo", "foobar", "afoo"]
Результат ["fo","","barfoo","bar","afoo"]
Выполнить

rtrimstr(str)

Выводит входное значение, удалив заданную строку-суффикс, если оно ею заканчивается.

Команда jq '[.[]|rtrimstr("foo")]'
Входные данные ["fo", "foo", "barfoo", "foobar", "foob"]
Результат ["fo","","bar","foobar","foob"]
Выполнить

trimstr(str)

Выводит входное значение, удалив заданную строку с обоих концов, если оно начинается или заканчивается ею.

Команда jq '[.[]|trimstr("foo")]'
Входные данные ["fo", "foo", "barfoo", "foobarfoo", "foob"]
Результат ["fo","","bar","bar","b"]
Выполнить

trim, ltrim, rtrim

trim удаляет пробельные символы в начале и в конце.

ltrim удаляет только пробельные символы в начале (слева).

rtrim удаляет только пробельные символы в конце (справа).

К пробельным символам относятся обычные " ", "\n" "\t", "\r", а также все символы в базе данных символов Unicode, обладающие свойством пробельного символа. Обратите внимание, что определение пробельных символов может измениться в будущем.

Команда jq 'trim, ltrim, rtrim'
Входные данные " abc "
Результат "abc"
"abc "
" abc"
Выполнить

explode

Преобразует входную строку в массив числовых значений кодовых точек символов этой строки.

Команда jq 'explode'
Входные данные "foobar"
Результат [102,111,111,98,97,114]
Выполнить

implode

Обратная функция для explode.

Команда jq 'implode'
Входные данные [65, 66, 67]
Результат "ABC"
Выполнить

split(str)

Разбивает входную строку по аргументу-разделителю.

split также может разбивать строку по совпадениям с регулярным выражением при вызове с двумя аргументами (см. раздел о регулярных выражениях ниже).

Команда jq 'split(", ")'
Входные данные "a, b,c,d, e, "
Результат ["a","b,c,d","e",""]
Выполнить

join(str)

Объединяет входной массив элементов, используя аргумент в качестве разделителя. Это обратная функция для split: то есть применение split("foo") | join("foo") к любой входной строке возвращает эту же строку.

Числа и логические значения во входных данных преобразуются в строки. Значения null обрабатываются как пустые строки. Массивы и объекты во входных данных не поддерживаются.

Команда jq 'join(", ")'
Входные данные ["a","b,c,d","e"]
Результат "a, b,c,d, e"
Выполнить
Команда jq 'join(" ")'
Входные данные ["a",1,2.3,true,null,false]
Результат "a 1 2.3 true false"
Выполнить

ascii_downcase, ascii_upcase

Выводит копию входной строки, в которой буквенные символы (a–z и A–Z) преобразованы в указанный регистр.

Команда jq 'ascii_upcase'
Входные данные "useful but not for é"
Результат "USEFUL BUT NOT FOR é"
Выполнить

while(cond; update)

Функция while(cond; update) позволяет многократно применять обновление к ., пока cond не станет false.

Обратите внимание, что while(cond; update) определена внутри как рекурсивная функция jq. Рекурсивные вызовы внутри while не используют дополнительную память, если update выдаёт не более одного результата для каждого входного значения. См. раздел «Дополнительные темы» ниже.

Команда jq '[while(.<100; .*2)]'
Входные данные 1
Результат [1,2,4,8,16,32,64]
Выполнить

repeat(exp)

Функция repeat(exp) позволяет многократно применять выражение exp к ., пока не возникнет ошибка.

Обратите внимание, что repeat(exp) определена внутри как рекурсивная функция jq. Рекурсивные вызовы внутри repeat не используют дополнительную память, если exp выдаёт не более одного результата для каждого входного значения. См. раздел «Дополнительные темы» ниже.

Команда jq '[repeat(.*2, error)?]'
Входные данные 1
Результат [2]
Выполнить

until(cond; next)

Функция until(cond; next) позволяет многократно применять выражение next, сначала к ., а затем к его собственному результату, пока cond не станет истинным. Например, с её помощью можно реализовать функцию вычисления факториала (см. ниже).

Обратите внимание, что until(cond; next) определена внутри как рекурсивная функция jq. Рекурсивные вызовы внутри until() не используют дополнительную память, если next выдаёт не более одного результата для каждого входного значения. См. раздел «Дополнительные темы» ниже.

Команда jq '[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]'
Входные данные 4
Результат 24
Выполнить

recurse(f), recurse, recurse(f; condition)

Функция recurse(f) позволяет выполнять поиск в рекурсивной структуре и извлекать интересующие данные со всех уровней. Предположим, что входные данные представляют файловую систему:

{"name": "/", "children": [
  {"name": "/bin", "children": [
    {"name": "/bin/ls", "children": []},
    {"name": "/bin/sh", "children": []}]},
  {"name": "/home", "children": [
    {"name": "/home/stephen", "children": [
      {"name": "/home/stephen/jq", "children": []}]}]}]}

Теперь предположим, что нужно извлечь имена всех имеющихся файлов. Необходимо получить .name, .children[].name, .children[].children[].name и так далее. Это можно сделать следующим образом:

recurse(.children[]) | .name

При вызове без аргумента recurse эквивалентна recurse(.[]?).

recurse(f) идентична recurse(f; true) и может использоваться без опасений по поводу глубины рекурсии.

recurse(f; condition) — это генератор, который сначала выводит ., а затем по очереди выводит .|f, .|f|f, .|f|f|f, ... пока вычисленное значение удовлетворяет условию. Например, для генерации всех целых чисел, по крайней мере в теории, можно написать recurse(.+1; true).

Рекурсивные вызовы в recurse не будут использовать дополнительную память, если f выдаёт не более одного результата для каждого входного значения.

Команда jq 'recurse(.foo[])'
Входные данные {"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]}
Результат {"foo":[{"foo":[]},{"foo":[{"foo":[]}]}]}
{"foo":[]}
{"foo":[{"foo":[]}]}
{"foo":[]}
Выполнить
Команда jq 'recurse'
Входные данные {"a":0,"b":[1]}
Результат {"a":0,"b":[1]}
0
[1]
1
Выполнить
Команда jq 'recurse(. * .; . < 20)'
Входные данные 2
Результат 2
4
16
Выполнить

walk(f)

Функция walk(f) рекурсивно применяет f к каждому компоненту входной сущности. При встрече массива f сначала применяется к его элементам, а затем к самому массиву; при встрече объекта f сначала применяется ко всем его значениям, а затем к самому объекту. На практике f обычно проверяет тип входных данных, как показано в следующих примерах. В первом примере показана полезность обработки элементов массива массивов до обработки самого массива. Во втором примере показано, как можно рассматривать для изменения все ключи всех объектов во входных данных.

Команда jq 'walk(if type == "array" then sort else . end)'
Входные данные [[4, 1, 7], [8, 5, 2], [3, 6, 9]]
Результат [[1,4,7],[2,5,8],[3,6,9]]
Запустить
Команда jq 'walk( if type == "object" then with_entries( .key |= sub( "^_+"; "") ) else . end )'
Входные данные [ { "_a": { "__b": 2 } } ]
Результат [{"a":{"b":2}}]
Запустить

have_literal_numbers

Эта встроенная функция возвращает true, если конфигурация сборки jq включает поддержку сохранения литералов входных чисел.

have_decnum

Эта встроенная функция возвращает true, если jq был собран с «decnum» — текущей реализацией числового серверного компонента jq, сохраняющей литералы.

$JQ_BUILD_CONFIGURATION

Эта привязка встроенной функции показывает конфигурацию сборки исполняемого файла jq. Её значение не имеет строго заданного формата, но можно ожидать, что оно будет содержать как минимум аргументы командной строки ./configure и в будущем может быть дополнено строками версий использованных средств сборки.

Обратите внимание, что это значение можно переопределить в командной строке с помощью --arg и связанных параметров.

$ENV, env

$ENV — это объект, представляющий переменные среды в том виде, в каком они были заданы при запуске программы jq.

env выводит объект, представляющий текущую среду jq.

В настоящее время встроенной функции для задания переменных среды нет.

Команда jq '$ENV.PAGER'
Входные данные null
Результат "less"
Запустить
Команда jq 'env.PAGER'
Входные данные null
Результат "less"
Запустить

transpose

Транспонирует, возможно, неоднородную матрицу (массив массивов). Недостающие элементы строк заполняются значениями null, поэтому результат всегда будет прямоугольным.

Команда jq 'transpose'
Входные данные [[1], [2,3]]
Результат [[1,2],[null,3]]
Запустить

bsearch(x)

bsearch(x) выполняет двоичный поиск x во входном массиве. Если входные данные отсортированы и содержат x, bsearch(x) вернёт индекс элемента в массиве; в противном случае, если массив отсортирован, будет возвращено значение (-1 - ix), где ix — позиция вставки, при которой массив останется отсортированным после вставки x. Если массив не отсортирован, bsearch(x) вернёт целое число, которое, вероятно, не представляет интереса.

Команда jq 'bsearch(0)'
Входные данные [0,1]
Результат 0
Запустить
Команда jq 'bsearch(0)'
Входные данные [1,2,3]
Результат -1
Запустить
Команда jq 'bsearch(4) as $ix | if $ix < 0 then .[-(1+$ix)] = 4 else . end'
Входные данные [1,2,3]
Результат [1,2,3,4]
Запустить

Интерполяция строк: \(exp)

Внутри строки можно поместить выражение в круглые скобки после обратной косой черты. Результат выражения будет подставлен в строку.

Команда jq '"The input was \(.), which is one less than \(.+1)"'
Входные данные 42
Результат "The input was 42, which is one less than 43"
Запустить

Преобразование в/из JSON

Встроенные функции tojson и fromjson соответственно выводят значения в виде текстов JSON и разбирают тексты JSON в значения. Встроенная функция tojson отличается от tostring тем, что tostring возвращает строки без изменений, тогда как tojson кодирует строки как строки JSON.

Команда jq '[.[]|tostring]'
Входные данные [1, "foo", ["foo"]]
Результат ["1","foo","[\"foo\"]"]
Запустить
Команда jq '[.[]|tojson]'
Входные данные [1, "foo", ["foo"]]
Результат ["1","\"foo\"","[\"foo\"]"]
Запустить
Команда jq '[.[]|tojson|fromjson]'
Входные данные [1, "foo", ["foo"]]
Результат [1,"foo",["foo"]]
Запустить

Форматирование строк и экранирование

Синтаксис @foo используется для форматирования и экранирования строк; он полезен при создании URL-адресов, документов на таких языках, как HTML или XML, и т. д. @foo можно использовать как самостоятельный фильтр; доступны следующие способы экранирования:

  • @text:

Вызывает tostring; подробности см. в описании этой функции.

  • @json:

Сериализует входные данные в формате JSON.

  • @html:

Выполняет экранирование HTML/XML, заменяя символы <>&'" соответствующими сущностями &lt;, &gt;, &amp;, &apos;, &quot;.

  • @uri:

Выполняет процентное кодирование, заменяя все зарезервированные символы URI последовательностью %XX.

  • @urid:

Обратная операция для @uri: выполняет процентное декодирование, заменяя все последовательности %XX соответствующими символами URI.

  • @csv:

Входные данные должны быть массивом; он преобразуется в CSV, строки заключаются в кавычки, а кавычки экранируются повторением.

  • @tsv:

Входные данные должны быть массивом; он преобразуется в TSV (значения, разделённые табуляцией). Каждый входной массив выводится отдельной строкой. Поля разделяются одной табуляцией (ASCII 0x09). Входные символы перевода строки (ASCII 0x0a), возврата каретки (ASCII 0x0d), табуляции (ASCII 0x09) и обратная косая черта (ASCII 0x5c) выводятся как escape-последовательности \n, \r, \t, \\ соответственно.

  • @sh:

Входные данные экранируются для использования в командной строке POSIX-оболочки. Если входные данные — массив, результатом будет последовательность строк, разделённых пробелами.

  • @base64:

Входные данные преобразуются в base64 согласно RFC 4648.

  • @base64d:

Обратная операция для @base64: входные данные декодируются согласно RFC 4648. Примечание\: если декодированная строка не является UTF-8, результаты не определены.

Этот синтаксис можно полезно сочетать с интерполяцией строк. После токена @foo можно указать строковый литерал. Содержимое строкового литерала не будет экранировано. Однако все интерполяции внутри этого строкового литерала будут экранированы. Например,

@uri "https://www.google.com/search?q=\(.search)"

для входных данных {"search":"what is jq?"} будет выведен следующий результат:

"https://www.google.com/search?q=what%20is%20jq%3F"

Обратите внимание, что косые черты, вопросительный знак и другие символы в URL не экранируются, поскольку они входят в строковый литерал.

Команда jq '@html'
Входные данные "This works if x < y"
Результат "This works if x &lt; y"
Запустить
Команда jq '@sh "echo \(.)"'
Входные данные "O'Hara's Ale"
Результат "echo 'O'\\''Hara'\\''s Ale'"
Запустить
Команда jq '@base64'
Входные данные "This is a message"
Результат "VGhpcyBpcyBhIG1lc3NhZ2U="
Запустить
Команда jq '@base64d'
Входные данные "VGhpcyBpcyBhIG1lc3NhZ2U="
Результат "This is a message"
Запустить

Даты

jq предоставляет базовые возможности для работы с датами с помощью нескольких высокоуровневых и низкоуровневых встроенных функций. Все эти функции работают исключительно со временем в UTC.

Встроенная функция fromdateiso8601 разбирает дату и время в формате ISO 8601 и преобразует их в количество секунд, прошедших с эпохи Unix (1970-01-01T00:00:00Z). Встроенная функция todateiso8601 выполняет обратное преобразование.

Встроенная функция fromdate разбирает строки даты и времени. В настоящее время fromdate поддерживает только строки даты и времени в формате ISO 8601, но в будущем будет пытаться разбирать строки и в других форматах.

Встроенная функция todate является псевдонимом для todateiso8601.

Встроенная функция now возвращает текущее время в секундах, прошедших с эпохи Unix.

Также предоставлены низкоуровневые интерфейсы jq к функциям времени библиотеки C: strptime, strftime, strflocaltime, mktime, gmtime и localtime. Форматы строк, используемые функциями strptime и strftime, описаны в документации вашей операционной системы. Примечание: эти интерфейсы могут быть нестабильными в jq, особенно в части функций локализации.

Встроенная функция gmtime принимает количество секунд, прошедших с эпохи Unix, и возвращает представление времени по Гринвичу в виде массива чисел («разложенное время»): год, месяц (с нуля), день месяца (с единицы), час дня, минута часа, секунда минуты, день недели и день года — все значения начинаются с единицы, если не указано иное. В некоторых системах номер дня недели может быть неверным для дат до 1 марта 1900 года или после 31 декабря 2099 года.

Встроенная функция localtime работает как gmtime, но использует настройки местного часового пояса.

Встроенная функция mktime принимает представления времени в виде «разложенного времени», выводимые функциями gmtime и strptime.

Встроенная функция strptime(fmt) разбирает входные строки, соответствующие аргументу fmt. Результат представлен в виде «разложенного времени», который принимает функция mktime и выводит функция gmtime.

Встроенная функция strftime(fmt) форматирует время (GMT) с использованием заданного формата. Функция strflocaltime выполняет то же самое, но использует настройки местного часового пояса.

Форматы строк для strptime и strftime описаны в типичной документации библиотек C. Форматная строка для даты и времени ISO 8601 — "%Y-%m-%dT%H:%M:%SZ".

На некоторых системах jq может не поддерживать часть или все эти возможности работы с датами. В частности, спецификаторы %u и %j для strptime(fmt) не поддерживаются в macOS.

Команда jq 'fromdate'
Входные данные "2015-03-05T23:51:47Z"
Результат 1425599507
Запустить
Команда jq 'strptime("%Y-%m-%dT%H:%M:%SZ")'
Входные данные "2015-03-05T23:51:47Z"
Результат [2015,2,5,23,51,47,4,63]
Запустить
Команда jq 'strptime("%Y-%m-%dT%H:%M:%SZ")|mktime'
Входные данные "2015-03-05T23:51:47Z"
Результат 1425599507
Запустить

Операторы в стиле SQL

jq предоставляет несколько операторов в стиле SQL.

  • INDEX(stream; index_expression):

Эта встроенная функция создаёт объект, ключи которого вычисляются с помощью заданного выражения индекса, применённого к каждому значению из заданного потока.

  • JOIN($idx; stream; idx_expr; join_expr):

Эта встроенная функция объединяет значения из заданного потока с заданным индексом. Ключи индекса вычисляются путём применения заданного выражения индекса к каждому значению из заданного потока. Для получения каждого результата массив из значения потока и соответствующего значения индекса передаётся заданному выражению объединения.

  • JOIN($idx; stream; idx_expr):

То же, что и JOIN($idx; stream; idx_expr; .).

  • JOIN($idx; idx_expr):

Эта встроенная функция объединяет входной . с заданным индексом, применяя заданное выражение индекса к . для вычисления ключа индекса. Операция объединения описана выше.

  • IN(s):

Эта встроенная функция выводит true, если . встречается в заданном потоке; в противном случае она выводит false.

  • IN(source; s):

Эта встроенная функция выводит true, если какое-либо значение исходного потока встречается во втором потоке; в противном случае она выводит false.

builtins

Возвращает список всех встроенных функций в формате name/arity. Поскольку функции с одинаковым именем, но разной арностью считаются разными функциями, в списке будут присутствовать all/0, all/1 и all/2.

Условные выражения и сравнения

==, !=

Выражение 'a == b' вернёт 'true', если результаты вычисления a и b равны (то есть представляют эквивалентные значения JSON), и 'false' в противном случае. В частности, строки никогда не считаются равными числам. При проверке равенства объектов JSON порядок ключей не имеет значения. Если вы знакомы с JavaScript, обратите внимание, что == в jq похож на === в JavaScript — оператор «строгого равенства».

!= означает «не равно», а 'a != b' возвращает значение, противоположное 'a == b'

Команда jq '. == false'
Входные данные null
Результат false
Запустить
Команда jq '. == {"b": {"d": (4 + 1e-20), "c": 3}, "a":1}'
Входные данные {"a":1, "b": {"c": 3, "d": 4}}
Результат true
Запустить
Команда jq '.[] == 1'
Входные данные [1, 1.0, "1", "banana"]
Результат true
true
false
false
Запустить

if-then-else-end

if A then B else C end действует так же, как B, если A возвращает значение, отличное от false или null, и так же, как C в противном случае.

if A then B end эквивалентно if A then B else . end. Иными словами, ветвь else необязательна; если она отсутствует, это равносильно .. Это также относится к elif без завершающей ветви else.

Проверка на false или null — более простое понятие «истинности», чем в JavaScript или Python, но это означает, что иногда условие придётся задавать явнее. Например, нельзя проверить, что строка пуста, с помощью if .name then A else B end; вместо этого понадобится что-то вроде if .name == "" then A else B end.

Если условие A возвращает несколько результатов, то B вычисляется один раз для каждого результата, отличного от false или null, а C — один раз для каждого значения false или null.

В конструкцию if можно добавить дополнительные случаи с помощью синтаксиса elif A then B.

Команда jq 'if . == 0 then "zero" elif . == 1 then "one" else "many" end'
Входные данные 2
Результат "many"
Запустить

>, >=, <=, <

Операторы сравнения >, >=, <=, < возвращают, соответственно, результат проверки того, больше ли левый аргумент правого, больше ли он или равен ему, меньше ли он или равен ему либо меньше ли он.

Порядок сравнения такой же, как описанный выше для sort.

Команда jq '. < 5'
Входные данные 2
Результат true
Запустить

and, or, not

jq поддерживает стандартные логические операторы and, or, not. Они используют то же стандартное понятие истинности, что и выражения if: false и null считаются «ложными значениями», а все остальные значения — «истинными».

Если операнд одного из этих операторов возвращает несколько результатов, сам оператор вернёт результат для каждого входного значения.

not на самом деле является встроенной функцией, а не оператором, поэтому её вызывают как фильтр, к которому можно передавать данные через конвейер, а не с помощью специального синтаксиса, как в .foo and .bar | not.

Эти три оператора возвращают только значения true и false, поэтому они пригодны только для настоящих логических операций, а не для распространённого в Perl/Python/Ruby идиоматического выражения «значение, которое может быть null, или значение по умолчанию». Если вам нужна такая форма «или» для выбора одного из двух значений, а не для вычисления условия, см. оператор // ниже.

Команда jq '42 and "a string"'
Входные данные null
Результат true
Запустить
Команда jq '(true, false) or false'
Входные данные null
Результат true
false
Запустить
Команда jq '(true, true) and (true, false)'
Входные данные null
Результат true
false
true
false
Запустить
Команда jq '[true, false | not]'
Входные данные null
Результат [false, true]
Запустить

Альтернативный оператор: //

Оператор // возвращает все значения левой части, которые не являются ни false, ни null. Если левая часть не возвращает значений, отличных от false или null, то // возвращает все значения правой части.

Фильтр вида a // b возвращает все результаты a, которые не равны false или null. Если a не возвращает результатов либо возвращает только false или null, то a // b возвращает результаты b.

Это удобно для задания значений по умолчанию: .foo // 1 вернёт 1, если во входных данных нет элемента .foo. Это похоже на то, как иногда используется or в Python (оператор or в jq предназначен исключительно для логических операций).

Примечание: some_generator // defaults_here — не то же самое, что some_generator | . // defaults_here. Последний возвращает значения по умолчанию для всех значений левой части, которые не являются false или null, тогда как первый этого не делает. Правила приоритета операторов могут сбивать с толку. Например, в false, 1 // 2 левая часть // — это 1, а не false, 1: false, 1 // 2 разбирается так же, как false, (1 // 2). В (false, null, 1) | . // 42 левая часть // — это ., которое всегда возвращает только одно значение, тогда как в (false, null, 1) // 42 левая часть — генератор трёх значений; поскольку он возвращает значение, отличное от false и null, значение по умолчанию 42 не возвращается.

Команда jq 'empty // 42'
Входные данные null
Результат 42
Запустить
Команда jq '.foo // 42'
Входные данные {"foo": 19}
Результат 19
Запустить
Команда jq '.foo // 42'
Входные данные {}
Результат 42
Запустить
Команда jq '(false, null, 1) // 42'
Входные данные null
Результат 1
Запустить
Команда jq '(false, null, 1) | . // 42'
Входные данные null
Результат 42
42
1
Запустить

try-catch

Ошибки можно перехватывать с помощью try EXP catch EXP. Сначала выполняется первое выражение, а если оно завершается с ошибкой, второе выполняется с сообщением об ошибке. Результат обработчика, если он есть, выводится так, как если бы это был результат выражения, которое нужно было выполнить.

Форма try EXP использует empty в качестве обработчика исключения.

Команда jq 'try .a catch ". is not an object"'
Входные данные true
Результат ". is not an object"
Запустить
Команда jq '[.[]|try .a]'
Входные данные [{}, true, {"a":1}]
Результат [null, 1]
Запустить
Команда jq 'try error("some exception") catch .'
Входные данные true
Результат "some exception"
Запустить

Выход из управляющих конструкций

Удобный способ использовать try/catch — выходить из управляющих конструкций, таких как reduce, foreach, while и т. д.

Например:

# Repeat an expression until it raises "break" as an
# error, then stop repeating without re-raising the error.
# But if the error caught is not "break" then re-raise it.
try repeat(exp) catch if .=="break" then empty else error

В jq предусмотрен синтаксис именованных лексических меток для «прерывания» или «перехода (назад) к»:

label $out | ... break $out ...

Выражение break $label_name заставляет программу вести себя так, как если бы ближайшая расположенная слева label $label_name вернула empty.

Связь между break и соответствующей label определяется лексически: метка должна быть «видна» из точки прерывания.

Например, чтобы выйти из reduce:

label $out | reduce .[] as $item (null; if .==false then break $out else ... end)

Следующая программа jq выдаёт синтаксическую ошибку:

break $out

поскольку метка $out не видна.

Подавление ошибок / оператор необязательности: ?

Оператор ?, используемый в форме EXP?, является сокращением для try EXP.

Команда jq '[.[] | .a?]'
Входные данные [{}, true, {"a":1}]
Результат [null, 1]
Запустить
Команда jq '[.[] | tonumber?]'
Входные данные ["1", "invalid", "3", 4]
Результат [1, 3, 4]
Запустить

Регулярные выражения

jq использует библиотеку регулярных выражений Oniguruma, как и PHP, TextMate, Sublime Text и т. д., поэтому здесь мы сосредоточимся на особенностях jq.

Oniguruma поддерживает несколько вариантов регулярных выражений, поэтому важно знать, что jq использует вариант «Perl NG» (Perl с именованными группами).

Фильтры регулярных выражений jq определены таким образом, что их можно использовать с одним из следующих шаблонов:

STRING | FILTER(REGEX)
STRING | FILTER(REGEX; FLAGS)
STRING | FILTER([REGEX])
STRING | FILTER([REGEX, FLAGS])

где:

  • STRING, REGEX и FLAGS — это строки jq, к которым применяется интерполяция строк jq;
  • REGEX после интерполяции строк должен быть допустимым регулярным выражением;
  • FILTER — это один из test, match или capture, как описано ниже.

Поскольку REGEX должен вычисляться в строку JSON, некоторые символы, необходимые для составления регулярного выражения, нужно экранировать. Например, регулярное выражение \s, обозначающее пробельный символ, следует записать как "\\s".

FLAGS — это строка, состоящая из одного или нескольких поддерживаемых флагов:

  • g — глобальный поиск (найти все совпадения, а не только первое)
  • i — поиск без учёта регистра
  • m — многострочный режим (. будет соответствовать символам новой строки)
  • n — игнорировать пустые совпадения
  • p — включены режимы s и m
  • s — однострочный режим (^ -> \A, $ -> \Z)
  • l — искать самые длинные возможные совпадения
  • x — расширенный формат регулярных выражений (игнорировать пробелы и комментарии)

Чтобы найти пробельный символ с флагом x, используйте \s, например:

jq -n '"a b" | test("a\\sb"; "x")'

Обратите внимание, что некоторые флаги также можно указать внутри REGEX, например:

jq -n '("test", "TEst", "teST", "TEST") | test("(?i)te(?-i)st")'

вычисляется в: true, true, false, false.

test(val), test(regex; flags)

Подобно match, но возвращает не объекты совпадений, а только true или false, указывая, соответствует ли входное значение регулярному выражению.

Команда jq 'test("foo")'
Ввод "foo"
Вывод true
Запустить
Команда jq '.[] | test("a b c # spaces are ignored"; "ix")'
Ввод ["xabcd", "ABC"]
Вывод true
true
Запустить

match(val), match(regex; flags)

match выводит объект для каждого найденного совпадения. У совпадений есть следующие поля:

  • offset — смещение в кодовых точках UTF-8 от начала входной строки
  • length — длина совпадения в кодовых точках UTF-8
  • string — строка, с которой найдено совпадение
  • captures — массив объектов, представляющих группы захвата.

У объектов групп захвата есть следующие поля:

  • offset — смещение в кодовых точках UTF-8 от начала входной строки
  • length — длина этой группы захвата в кодовых точках UTF-8
  • string — захваченная строка
  • name — имя группы захвата (или null, если она не имеет имени)

Для групп захвата, которым не удалось найти совпадение, возвращается смещение -1

Команда jq 'match("(abc)+"; "g")'
Ввод "abc abc"
Вывод {"offset": 0, "length": 3, "string": "abc", "captures": [{"offset": 0, "length": 3, "string": "abc", "name": null}]}
{"offset": 4, "length": 3, "string": "abc", "captures": [{"offset": 4, "length": 3, "string": "abc", "name": null}]}
Запустить
Команда jq 'match("foo")'
Ввод "foo bar foo"
Вывод {"offset": 0, "length": 3, "string": "foo", "captures": []}
Запустить
Команда jq 'match(["foo", "ig"])'
Ввод "foo bar FOO"
Вывод {"offset": 0, "length": 3, "string": "foo", "captures": []}
{"offset": 8, "length": 3, "string": "FOO", "captures": []}
Запустить
Команда jq 'match("foo (?<bar123>bar)? foo"; "ig")'
Ввод "foo bar foo foo foo"
Вывод {"offset": 0, "length": 11, "string": "foo bar foo", "captures": [{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]}
{"offset": 12, "length": 8, "string": "foo foo", "captures": [{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}
Запустить
Команда jq '[ match("."; "g")] | length'
Ввод "abc"
Вывод 3
Запустить

capture(val), capture(regex; flags)

Собирает именованные захваты в объект JSON, используя имя каждого захвата в качестве ключа, а найденную строку — в качестве соответствующего значения.

Команда jq 'capture("(?<a>[a-z]+)-(?<n>[0-9]+)")'
Ввод "xyzzy-14"
Вывод { "a": "xyzzy", "n": "14" }
Запустить

scan(regex), scan(regex; flags)

Выводит поток неперекрывающихся подстрок входной строки, соответствующих регулярному выражению с учётом заданных флагов. Если совпадений нет, поток пуст. Чтобы получить все совпадения для каждой входной строки, используйте идиому [ expr ], например [ scan(regex) ]. Если регулярное выражение содержит группы захвата, фильтр выводит поток массивов, каждый из которых содержит захваченные строки.

Команда jq 'scan("c")'
Ввод "abcdefabc"
Вывод "c"
"c"
Запустить
Команда jq 'scan("(a+)(b+)")'
Ввод "abaabbaaabbb"
Вывод ["a","b"]
["aa","bb"]
["aaa","bbb"]
Запустить

split(regex; flags)

Разбивает входную строку при каждом совпадении с регулярным выражением.

Для обратной совместимости при вызове с одним аргументом split разбивает строку по подстроке, а не по регулярному выражению.

Команда jq 'split(", *"; null)'
Ввод "ab,cd, ef"
Вывод ["ab","cd","ef"]
Запустить

splits(regex), splits(regex; flags)

Они дают те же результаты, что и соответствующие им версии split, но возвращают поток, а не массив.

Команда jq 'splits(", *")'
Ввод "ab,cd, ef, gh"
Вывод "ab"
"cd"
"ef"
"gh"
Запустить
Команда jq 'splits(",? *"; "n")'
Ввод "ab,cd ef, gh"
Вывод "ab"
"cd"
"ef"
"gh"
Запустить

sub(regex; tostring), sub(regex; tostring; flags)

Выводит строку, полученную заменой первого совпадения регулярного выражения во входной строке на tostring после интерполяции. tostring должна быть строкой jq или потоком таких строк, каждая из которых может содержать ссылки на именованные захваты. Именованные захваты фактически передаются в tostring в виде объекта JSON (созданного с помощью capture), поэтому ссылка на захваченную переменную с именем «x» будет выглядеть так: "\(.x)".

Команда jq 'sub("[^a-z]*(?<x>[a-z]+)"; "Z\(.x)"; "g")'
Ввод "123abc456def"
Вывод "ZabcZdef"
Запустить
Команда jq '[sub("(?<a>.)"; "\(.a|ascii_upcase)", "\(.a|ascii_downcase)") ]'
Ввод "aB"
Вывод ["AB","aB"]
Запустить

gsub(regex; tostring), gsub(regex; tostring; flags)

gsub подобен sub, но все неперекрывающиеся совпадения регулярного выражения заменяются на tostring после интерполяции. Если второй аргумент является потоком строк jq, gsub выдаст соответствующий поток строк JSON.

Команда jq 'gsub("(?<x>.)[^a]*"; "+\(.x)-")'
Ввод "Abcabc"
Вывод "+A-+a-"
Запустить
Команда jq '[gsub("p"; "a", "b")]'
Ввод "p"
Вывод ["a","b"]
Запустить

Расширенные возможности

Переменные совершенно необходимы в большинстве языков программирования, но в jq они отнесены к «расширенным возможностям».

В большинстве языков переменные — единственный способ передавать данные. Если вы вычислили значение и хотите использовать его несколько раз, нужно сохранить его в переменной. Чтобы передать значение в другую часть программы, эта часть программы должна определить переменную (параметр функции, член объекта и т. п.), в которую можно поместить данные.

В jq также можно определять функции, хотя эта возможность в основном используется для определения стандартной библиотеки jq (многие функции jq, например map и select, на самом деле написаны на jq).

В jq есть операторы свёртки, которые очень мощны, но несколько сложны. И снова: в основном они используются внутри jq для определения полезных частей стандартной библиотеки.

Возможно, поначалу это не очевидно, но jq целиком построен на генераторах (да, как и во многих других языках). Для работы с генераторами предусмотрены некоторые вспомогательные средства.

Доступна минимальная поддержка ввода-вывода (помимо чтения JSON из стандартного ввода и записи JSON в стандартный вывод).

Наконец, имеется система модулей и библиотек.

Оператор связывания переменной / символического связывания: ... as $identifier | ...

В jq у всех фильтров есть входные и выходные данные, поэтому вручную связывать части программы для передачи значения от одной части к другой не нужно. Многие выражения, например a + b, передают входные данные двум разным подвыражениям (в данном случае a и b получают одни и те же входные данные), поэтому переменные обычно не нужны, чтобы использовать значение дважды.

Например, для вычисления среднего значения массива чисел в большинстве языков требуется несколько переменных — как минимум одна для хранения массива и, возможно, по одной для каждого элемента или для счётчика цикла. В jq достаточно написать add / length: выражение add получает массив и возвращает его сумму, а выражение length получает массив и возвращает его длину.

Таким образом, в jq обычно можно решить большинство задач более простым способом, чем с помощью определения переменных. И всё же иногда переменные упрощают работу, поэтому jq позволяет определять их с помощью expression as $variable. Имена всех переменных начинаются с $. Вот немного менее элегантный вариант примера с вычислением среднего значения массива:

length as $array_length | add / $array_length

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

Предположим, у нас есть массив публикаций в блоге с полями «author» и «title», а также другой объект, сопоставляющий имена пользователей-авторов с их настоящими именами. Наши входные данные выглядят так:

{"posts": [{"title": "First post", "author": "anon"},
           {"title": "A well-written article", "author": "person1"}],
 "realnames": {"anon": "Anonymous Coward",
               "person1": "Person McPherson"}}

Мы хотим получить публикации, в поле автора которых указано настоящее имя, например:

{"title": "First post", "author": "Anonymous Coward"}
{"title": "A well-written article", "author": "Person McPherson"}

Мы используем переменную $names для хранения объекта с настоящими именами, чтобы позднее обращаться к нему при поиске имён пользователей-авторов:

.realnames as $names | .posts[] | {title, author: $names[.author]}

Выражение exp as $x | ... означает: для каждого значения выражения exp выполнить оставшуюся часть конвейера с исходными входными данными и присвоить $x это значение. Таким образом, as работает примерно как цикл foreach.

Подобно тому как {foo} удобно использовать вместо {foo: .foo}, {$foo} удобно использовать вместо {foo: $foo}.

Несколько переменных можно объявить одним выражением as, задав шаблон, соответствующий структуре входных данных (это называется «деструктуризацией»):

. as {realnames: $names, posts: [$first, $second]} | ...

Объявления переменных в шаблонах массивов (например, . as [$first, $second]) связываются с элементами массива по порядку, начиная с элемента с индексом ноль. Если для элемента шаблона массива нет значения с соответствующим индексом, переменной будет присвоено null.

Область видимости переменных распространяется на оставшуюся часть выражения, в котором они определены, поэтому

.realnames as $names | (.posts[] | {title, author: $names[.author]})

будет работать, а

(.realnames as $names | .posts[]) | {title, author: $names[.author]}

не будет.

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

Команда jq '.bar as $x | .foo | . + $x'
Входные данные {"foo":10, "bar":200}
Результат 210
Выполнить
Команда jq '. as $i|[(.*2|. as $i| $i), $i]'
Входные данные 5
Результат [10,5]
Выполнить
Команда jq '. as [$a, $b, {c: $c}] | $a + $b + $c'
Входные данные [2, 3, {"c": 4, "d": 5}]
Результат 9
Выполнить
Команда jq '.[] as [$a, $b] | {a: $a, b: $b}'
Входные данные [[0], [0, 1], [2, 1, 0]]
Результат {"a":0,"b":null}
{"a":0,"b":1}
{"a":2,"b":1}
Выполнить

Оператор альтернативной деструктуризации: ?//

Оператор альтернативной деструктуризации позволяет кратко описать деструктуризацию входных данных, которые могут иметь одну из нескольких форм.

Предположим, у нас есть API, который возвращает список ресурсов и связанных с ними событий, а нам нужно получить user_id и временную метку первого события для каждого ресурса. API (неуклюже преобразованный из XML) оборачивает события в массив только в том случае, если у ресурса несколько событий:

{"resources": [{"id": 1, "kind": "widget", "events": {"action": "create", "user_id": 1, "ts": 13}},
               {"id": 2, "kind": "widget", "events": [{"action": "create", "user_id": 1, "ts": 14}, {"action": "destroy", "user_id": 1, "ts": 15}]}]}

Мы можем просто обработать это структурное изменение с помощью оператора альтернативной деструктуризации:

.resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$user_id, $ts}]} | {$user_id, $kind, $id, $ts}

Или, если мы не уверены, является ли входное значение массивом или объектом:

.[] as [$id, $kind, $user_id, $ts] ?// {$id, $kind, $user_id, $ts} | ...

Необязательно, чтобы каждая альтернатива определяла одни и те же переменные, но все именованные переменные будут доступны последующему выражению. Переменным, которым не соответствует успешная альтернатива, будет присвоено значение null:

.resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$first_user_id, $first_ts}]} | {$user_id, $first_user_id, $kind, $id, $ts, $first_ts}

Кроме того, если последующее выражение возвращает ошибку, оператор альтернатив попытается выполнить следующую привязку. Ошибки, возникающие в последней альтернативе, передаются дальше.

[[3]] | .[] as [$a] ?// [$b] | if $a != null then error("err: \($a)") else {$a,$b} end
Команда jq '.[] as {$a, $b, c: {$d, $e}} ?// {$a, $b, c: [{$d, $e}]} | {$a, $b, $d, $e}'
Входные данные [{"a": 1, "b": 2, "c": {"d": 3, "e": 4}}, {"a": 1, "b": 2, "c": [{"d": 3, "e": 4}]}]
Результат {"a":1,"b":2,"d":3,"e":4}
{"a":1,"b":2,"d":3,"e":4}
Выполнить
Команда jq '.[] as {$a, $b, c: {$d}} ?// {$a, $b, c: [{$e}]} | {$a, $b, $d, $e}'
Входные данные [{"a": 1, "b": 2, "c": {"d": 3, "e": 4}}, {"a": 1, "b": 2, "c": [{"d": 3, "e": 4}]}]
Результат {"a":1,"b":2,"d":3,"e":null}
{"a":1,"b":2,"d":null,"e":4}
Выполнить
Команда jq '.[] as [$a] ?// [$b] | if $a != null then error("err: \($a)") else {$a,$b} end'
Входные данные [[3]]
Результат {"a":null,"b":3}
Выполнить

Определение функций

Фильтру можно присвоить имя с помощью синтаксиса «def»:

def increment: . + 1;

После этого increment можно использовать как фильтр, подобно встроенной функции (так определены многие встроенные функции). Функция может принимать аргументы:

def map(f): [.[] | f];

Аргументы передаются как фильтры (функции без аргументов), а не как значения. Один и тот же аргумент можно использовать несколько раз с разными входными данными (здесь f выполняется для каждого элемента входного массива). Аргументы функции больше похожи на обратные вызовы, чем на аргументы-значения. Это важно понимать. Рассмотрим следующий пример:

def foo(f): f|f;
5|foo(.*2)

Результат будет равен 20, потому что f — это .*2, а при первом вызове f значение . будет равно 5, а при втором — 10 (5 * 2), поэтому результат будет равен 20. Аргументы функций — это фильтры, а фильтрам при вызове нужны входные данные.

Если при определении простых функций вам нужно поведение аргументов-значений, можно просто использовать переменную:

def addvalue(f): f as $f | map(. + $f);

Или воспользоваться сокращённой записью:

def addvalue($f): ...;

При любом из этих определений addvalue(.foo) добавит поле .foo текущих входных данных к каждому элементу массива. Обратите внимание: при вызове addvalue(.[]) часть map(. + $f) будет вычисляться один раз для каждого значения из . в месте вызова.

Допускается несколько определений с одним и тем же именем функции. Каждое переопределение заменяет предыдущее для того же количества аргументов, но только в ссылках из функций (или основной программы), расположенных после переопределения. См. также раздел об областях видимости ниже.

Команда jq 'def addvalue(f): . + [f]; map(addvalue(.[0]))'
Входные данные [[1,2],[10,20]]
Результат [[1,2,1], [10,20,10]]
Выполнить
Команда jq 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])'
Входные данные [[1,2],[10,20]]
Результат [[1,2,1,2], [10,20,1,2]]
Выполнить

Области видимости

В jq есть два типа символов: привязки значений (также называемые «переменными») и функции. Для обоих типов действует лексическая область видимости: выражения могут ссылаться только на символы, определённые «слева» от них. Единственное исключение: функции могут ссылаться сами на себя, что позволяет создавать рекурсивные функции.

Например, в следующем выражении есть привязка, которая видна «справа» от себя, ... | .*3 as $times_three | [. + $times_three] | ..., но не «слева». Теперь рассмотрим выражение ... | (.*3 as $times_three | [. + $times_three]) | ...: здесь привязка $times_three не видна после закрывающей скобки.

isempty(exp)

Возвращает true, если exp не выдаёт результатов, и false в противном случае.

Команда jq 'isempty(empty)'
Входные данные null
Результат true
Выполнить
Команда jq 'isempty(.[])'
Входные данные []
Результат true
Выполнить
Команда jq 'isempty(.[])'
Входные данные [1,2,3]
Результат false
Выполнить

limit(n; expr)

Функция limit извлекает до n результатов из expr.

Команда jq '[limit(3; .[])]'
Входные данные [0,1,2,3,4,5,6,7,8,9]
Результат [0,1,2]
Выполнить

skip(n; expr)

Функция skip пропускает первые n результатов из expr.

Команда jq '[skip(3; .[])]'
Входные данные [0,1,2,3,4,5,6,7,8,9]
Результат [3,4,5,6,7,8,9]
Выполнить

first(expr), last(expr), nth(n; expr)

Функции first(expr) и last(expr) извлекают соответственно первое и последнее значения из expr.

Функция nth(n; expr) извлекает n-е значение, выданное expr. Обратите внимание, что nth(n; expr) не поддерживает отрицательные значения n.

Команда jq '[first(range(.)), last(range(.)), nth(5; range(.))]'
Входные данные 10
Результат [0,9,5]
Выполнить
Команда jq '[first(empty), last(empty), nth(5; empty)]'
Входные данные null
Результат []
Выполнить

first, last, nth(n)

Функции first и last извлекают первое и последнее значения из любого массива в ..

Функция nth(n) извлекает n-е значение любого массива в ..

Команда jq '[range(.)]|[first, last, nth(5)]'
Входные данные 10
Результат [0,9,5]
Выполнить

reduce

Синтаксис reduce позволяет объединить все результаты выражения, накапливая их в одном итоговом значении. Форма записи: reduce EXP as $var (INIT; UPDATE). Для примера передадим [1,2,3] этому выражению:

reduce .[] as $item (0; . + $item)

Для каждого результата, выдаваемого .[], выполняется . + $item, чтобы накопить текущую сумму, начиная с 0 в качестве входного значения. В этом примере .[] выдаёт результаты 1, 2 и 3, поэтому эффект аналогичен выполнению чего-то вроде следующего:

0 | 1 as $item | . + $item |
    2 as $item | . + $item |
    3 as $item | . + $item
Команда jq 'reduce .[] as $item (0; . + $item)'
Входные данные [1,2,3,4,5]
Результат 15
Выполнить
Команда jq 'reduce .[] as [$i,$j] (0; . + $i * $j)'
Входные данные [[1,2],[3,4],[5,6]]
Результат 44
Выполнить
Команда jq 'reduce .[] as {$x,$y} (null; .x += $x | .y += [$y])'
Входные данные [{"x":"a","y":1},{"x":"b","y":2},{"x":"c","y":3}]
Результат {"x":"abc","y":[1,2,3]}
Выполнить

foreach

Синтаксис foreach похож на reduce, но предназначен для создания limit и свёрток, выдающих промежуточные результаты.

Форма записи: foreach EXP as $var (INIT; UPDATE; EXTRACT). Для примера передадим [1,2,3] этому выражению:

foreach .[] as $item (0; . + $item; [$item, . * 2])

Как и при использовании синтаксиса reduce, . + $item выполняется для каждого результата, выдаваемого .[], но [$item, . * 2] выполняется для каждого промежуточного значения. В этом примере промежуточными значениями являются 1, 3 и 6, поэтому выражение foreach выдаёт [1,2], [2,6] и [3,12]. Таким образом, эффект аналогичен выполнению чего-то вроде следующего:

0 | 1 as $item | . + $item | [$item, . * 2],
    2 as $item | . + $item | [$item, . * 2],
    3 as $item | . + $item | [$item, . * 2]

Если EXTRACT не указано, используется тождественный фильтр. То есть промежуточные значения выводятся без изменений.

Команда jq 'foreach .[] as $item (0; . + $item)'
Входные данные [1,2,3,4,5]
Результат 1
3
6
10
15
Выполнить
Команда jq 'foreach .[] as $item (0; . + $item; [$item, . * 2])'
Входные данные [1,2,3,4,5]
Результат [1,2]
[2,6]
[3,12]
[4,20]
[5,30]
Выполнить
Команда jq 'foreach .[] as $item (0; . + 1; {index: ., $item})'
Входные данные ["foo", "bar", "baz"]
Результат {"index":1,"item":"foo"}
{"index":2,"item":"bar"}
{"index":3,"item":"baz"}
Выполнить

Рекурсия

Как описано выше, recurse использует рекурсию, и любая функция jq может быть рекурсивной. Встроенная функция while также реализована с помощью рекурсии.

Хвостовые вызовы оптимизируются, если выражение слева от рекурсивного вызова выдаёт своё последнее значение. На практике это означает, что выражение слева от рекурсивного вызова не должно выдавать более одного результата для каждого входного значения.

Например:

def recurse(f): def r: ., (f | select(. != null) | r); r;

def while(cond; update):
  def _while:
    if cond then ., (update | _while) else empty end;
  _while;

def repeat(exp):
  def _repeat:
    exp, _repeat;
  _repeat;

Генераторы и итераторы

Некоторые операторы и функции jq на самом деле являются генераторами, поскольку для каждого входного значения они могут выдавать ноль, одно или несколько значений — так же, как можно ожидать от генераторов в других языках программирования. Например, .[] генерирует все значения входных данных (которыми должны быть массив или объект), range(0; 10) генерирует целые числа от 0 до 10 и так далее.

Даже оператор запятой является генератором: сначала он генерирует значения, создаваемые выражением слева от запятой, а затем — значения, создаваемые выражением справа от неё.

Встроенная функция empty — это генератор, который не выдаёт результатов. Встроенная функция empty возвращается к предыдущему выражению-генератору.

Любые функции jq могут быть генераторами, если использовать встроенные генераторы. Новые генераторы также можно создавать, используя только рекурсию и оператор запятой. Если рекурсивные вызовы находятся в «хвостовой позиции», генератор будет эффективным. В примере ниже рекурсивный вызов _range самой себя находится в хвостовой позиции. В примере показаны три продвинутые темы: хвостовая рекурсия, создание генераторов и вспомогательные функции.

Команда jq 'def range(init; upto; by): def _range: if (by > 0 and . < upto) or (by < 0 and . > upto) then ., ((.+by)|_range) else empty end; if init == upto then empty elif by == 0 then init else init|_range end; range(0; 10; 3)'
Ввод null
Вывод 0
3
6
9
Запустить
Команда jq 'def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; [while(.<100; .*2)]'
Ввод 1
Вывод [1,2,4,8,16,32,64]
Запустить

Математика

В настоящее время jq поддерживает числа с плавающей точкой двойной точности (64-разрядные) по стандарту IEEE754.

Помимо простых арифметических операторов, таких как +, jq также предоставляет большинство стандартных математических функций из математической библиотеки C. Математические функции C, принимающие один аргумент (например, sin()), доступны в jq как функции без аргументов. Математические функции C, принимающие два аргумента (например, pow()), доступны в jq как функции с двумя аргументами, которые игнорируют .. Математические функции C, принимающие три аргумента, доступны в jq как функции с тремя аргументами, которые игнорируют ..

Наличие стандартных математических функций зависит от наличия соответствующих функций в операционной системе и математической библиотеке C. Недоступные математические функции будут определены, но при вызове вызовут ошибку.

Математические функции C с одним аргументом: acos acosh asin asinh atan atanh cbrt ceil cos cosh erf erfc exp exp10 exp2 expm1 fabs floor gamma j0 j1 lgamma log log10 log1p log2 logb nearbyint rint round significand sin sinh sqrt tan tanh tgamma trunc y0 y1.

Математические функции C с двумя аргументами: atan2 copysign drem fdim fmax fmin fmod frexp hypot jn ldexp modf nextafter nexttoward pow remainder scalb scalbln yn.

Математические функции C с тремя аргументами: fma.

Дополнительные сведения об этих функциях см. в руководстве по вашей системе.

Ввод-вывод

В настоящее время поддержка ввода-вывода в jq минимальна и в основном позволяет управлять моментом чтения входных данных. Для этого предусмотрены две встроенные функции: input и inputs. Они читают из тех же источников (например, stdin и файлов, указанных в командной строке), что и сам jq. Эти две встроенные функции и собственные операции чтения jq можно чередовать. Обычно их используют вместе с параметром нулевого ввода -n, чтобы предотвратить неявное чтение одного входного значения.

Минимальные возможности вывода обеспечивают две встроенные функции: debug и stderr. (Помните, что выходные значения программы jq всегда выводятся как тексты JSON в stdout.) Встроенная функция debug может иметь поведение, зависящее от приложения, например в исполняемых файлах, которые используют C API libjq, но не являются самим исполняемым файлом jq. Встроенная функция stderr выводит входное значение в необработанном виде в stderr без дополнительного оформления, даже без символа новой строки.

Большинство встроенных функций jq референциально прозрачны и при применении к постоянным входным данным выдают постоянные и повторяемые потоки значений. Это не относится к встроенным функциям ввода-вывода.

input

Выдаёт одно новое входное значение.

Обратите внимание: при использовании input обычно необходимо запускать jq с параметром командной строки -n, иначе будет потеряно первое значение.

echo 1 2 3 4 | jq '[., input]' # [1,2] [3,4]

inputs

Выдаёт по одному все оставшиеся входные значения.

Эта функция главным образом полезна для свёртки входных данных программы. Обратите внимание: при использовании inputs обычно необходимо запускать jq с параметром командной строки -n, иначе будет потеряно первое значение.

echo 1 2 3 | jq -n 'reduce inputs as $i (0; . + $i)' # 6

debug, debug(msgs)

Эти два фильтра подобны ., но дополнительно выводят одно или несколько сообщений в stderr.

Сообщение, создаваемое фильтром debug, имеет следующий вид:

["DEBUG:",<input-value>]

где <input-value> — компактное представление входного значения. В будущем этот формат может измениться.

Фильтр debug(msgs) определён как (msgs | debug | empty), ., что обеспечивает большую гибкость в формировании сообщения и позволяет создавать многострочные отладочные сообщения.

Например, выражение:

1 as $x | 2 | debug("Entering function foo with $x == \($x)", .) | (.+1)

выдаст значение 3, а в stderr будут записаны следующие две строки:

["DEBUG:","Entering function foo with $x == 1"]
["DEBUG:",2]

stderr

Выводит входное значение в stderr в необработанном компактном виде без дополнительного оформления, даже без символа новой строки.

input_filename

Возвращает имя файла, входные данные которого в данный момент обрабатываются. Обратите внимание: эта функция может работать некорректно, если jq запущен не в локали UTF-8.

input_line_number

Возвращает номер строки входных данных, которая в данный момент обрабатывается.

Потоковая обработка

С параметром --stream jq может разбирать входные тексты в потоковом режиме, позволяя программам jq начинать обработку больших текстов JSON сразу, не дожидаясь завершения разбора. Если размер одного текста JSON составляет 1 ГБ, потоковая обработка позволит обрабатывать его гораздо быстрее.

Однако работать с потоковой обработкой непросто: программа jq будет получать на вход [<path>, <leaf-value>] (а также несколько других форм).

Для упрощения работы с потоками предусмотрено несколько встроенных функций.

В приведённых ниже примерах используется потоковая форма ["a",["b"]], а именно [[0],"a"],[[1,0],"b"],[[1,0]],[[1]].

Потоковые формы включают [<path>, <leaf-value>] (обозначает скалярное значение, пустой массив или пустой объект) и [<path>] (обозначает конец массива или объекта). В будущих версиях jq, запущенных с параметрами --stream и --seq, могут выводиться дополнительные формы, например ["error message"], если не удаётся разобрать входной текст.

truncate_stream(stream_expression)

Принимает число на вход и удаляет соответствующее количество элементов пути слева из выходных данных заданного потокового выражения.

Команда jq 'truncate_stream([[0],"a"],[[1,0],"b"],[[1,0]],[[1]])'
Ввод 1
Вывод [[0],"b"]
[[0]]
Запустить

fromstream(stream_expression)

Выдаёт значения, соответствующие выходным данным потокового выражения.

Команда jq 'fromstream(1|truncate_stream([[0],"a"],[[1,0],"b"],[[1,0]],[[1]]))'
Ввод null
Вывод ["b"]
Запустить

tostream

Встроенная функция tostream выводит входное значение в потоковой форме.

Команда jq '. as $dot|fromstream($dot|tostream)|.==$dot'
Ввод [0,[1,{"a":1},{"b":2}]]
Вывод true
Запустить

Присваивание

Присваивание в jq работает немного иначе, чем в большинстве языков программирования. jq не различает ссылки на объекты и их копии: два объекта или массива либо равны, либо нет; понятия «тот же объект» или «не тот же объект» здесь не существует.

Если объект содержит два поля-массива, .foo и .bar, и к .foo добавить что-либо, то .bar не увеличится, даже если ранее было задано .bar = .foo. Если вы привыкли программировать на таких языках, как Python, Java, Ruby или JavaScript, можно представить, что перед присваиванием jq выполняет полное глубокое копирование каждого объекта (на самом деле ради производительности этого не происходит, но в целом идея именно такова).

Это означает, что в jq невозможно создать циклические значения (например, массив, первым элементом которого является он сам). Это сделано намеренно и гарантирует, что любое значение, которое может создать программа jq, можно представить в JSON.

Слева от всех операторов присваивания в jq находятся выражения пути. Справа (RHS) задаются значения, которые нужно присвоить путям, указанным выражениями пути слева (LHS).

Значения в jq всегда неизменяемы. Внутри присваивание работает с помощью свёртки, вычисляя новые значения-замены для ., к которым применены все необходимые присваивания ., а затем выводя изменённое значение. Это можно понять на следующем примере: {a:{b:{c:1}}} | (.a.b|=3), .. Он выведет {"a":{"b":3}} и {"a":{"b":{"c":1}}}, поскольку последнее подвыражение, ., видит исходное значение, а не изменённое.

Большинству пользователей следует применять операторы присваивания с модификацией, например |= или +=, а не =.

Обратите внимание, что левая часть операторов присваивания ссылается на значение в .. Поэтому $var.foo = 1 не сработает ожидаемым образом ($var.foo не является допустимым или полезным выражением пути в .); вместо этого используйте $var | .foo = 1.

Также обратите внимание, что .a,.b=0 не задаёт .a и .b, тогда как (.a,.b)=0 задаёт оба значения.

Присваивание с обновлением: |=

Это оператор «обновления» |=. Справа от него указывается фильтр, который вычисляет новое значение присваиваемого свойства ., применяя это выражение к старому значению. Например, (.foo, .bar) |= .+1 создаст объект, в котором поле foo содержит значение поля foo входного объекта плюс 1, а поле bar — значение поля bar входного объекта плюс 1.

Слева может находиться любое общее выражение пути; см. path().

Обратите внимание, что левая часть |= ссылается на значение в .. Поэтому $var.foo |= . + 1 не сработает ожидаемым образом ($var.foo не является допустимым или полезным выражением пути в .); вместо этого используйте $var | .foo |= . + 1.

Если правая часть не выдаёт значений (то есть empty), путь левой части будет удалён, как при использовании del(path).

Если правая часть выдаёт несколько значений, будет использовано только первое (ПРИМЕЧАНИЕ О СОВМЕСТИМОСТИ: в jq 1.5 и более ранних версиях использовалось только последнее значение).

Команда jq '(..|select(type=="boolean")) |= if . then 1 else 0 end'
Ввод [true,false,[5,true,[true,[false]],false]]
Вывод [1,0,[5,1,[1,[0]],0]]
Запустить

Арифметическое присваивание с обновлением: +=, -=, *=, /=, %=, //=

В jq есть несколько операторов вида a op= b, каждый из которых эквивалентен a |= . op b. Так, += 1 можно использовать для увеличения значений; он равнозначен |= . + 1.

Команда jq '.foo += 1'
Ввод {"foo": 42}
Вывод {"foo": 43}
Запустить

Простое присваивание: =

Это оператор простого присваивания. В отличие от остальных операторов, правая часть (RHS) получает те же входные данные, что и левая часть (LHS), а не значение по пути LHS; кроме того, используются все значения, выданные RHS (как показано ниже).

Если RHS выражения = выдаёт несколько значений, jq для каждого такого значения присвоит его путям в левой части, а затем выведет изменённое значение .. Например, (.a,.b) = range(2) выдаёт {"a":0,"b":0}, а затем {"a":1,"b":1}. Формы присваивания «с обновлением» (см. выше) так не работают.

Следующий пример должен показать разницу между = и |=:

Передайте программам входные данные {"a": {"b": 10}, "b": 20}

.a = .b

и

.a |= .b

В первом случае поле a входного значения получит значение поля b этого же входного значения, и результатом будет {"a": 20, "b": 20}. Во втором случае поле a входного значения получит значение поля b поля a, и результатом будет {"a": 10, "b": 20}.

Команда jq '.a = .b'
Ввод {"a": {"b": 10}, "b": 20}
Вывод {"a":20,"b":20}
Запустить
Команда jq '.a |= .b'
Ввод {"a": {"b": 10}, "b": 20}
Вывод {"a":10,"b":20}
Запустить
Команда jq '(.a, .b) = range(3)'
Ввод null
Вывод {"a":0,"b":0}
{"a":1,"b":1}
{"a":2,"b":2}
Запустить
Команда jq '(.a, .b) |= range(3)'
Ввод null
Вывод {"a":0,"b":0}
Запустить

Сложные присваивания

В jq для левой части присваивания разрешено гораздо больше вариантов, чем в большинстве языков. Мы уже рассмотрели простое обращение к полю в левой части; неудивительно, что обращение к элементам массива работает так же хорошо:

.posts[0].title = "JQ Manual"

Может оказаться неожиданным, что выражение слева может выдавать несколько результатов, указывающих на разные места входного документа:

.posts[].comments |= . + ["this is great"]

В этом примере к массиву «comments» каждого сообщения во входных данных добавляется строка «this is great» (входные данные — объект с полем «posts», содержащим массив сообщений).

Когда jq встречает присваивание вида «a = b», он записывает «путь», по которому была выбрана часть входного документа при выполнении a. Затем этот путь используется, чтобы определить, какую часть входных данных изменить при выполнении присваивания. В левой части знака равенства можно использовать любой фильтр: присваивание будет выполнено по всем путям, которые он выбирает из входных данных.

Это очень мощная операция. Предположим, что мы хотим добавить комментарий к публикациям в блоге, используя те же входные данные «blog», что и выше. На этот раз нужно оставить комментарии только к публикациям автора «stedolan». Найти их можно с помощью описанной ранее функции «select»:

.posts[] | select(.author == "stedolan")

Пути, полученные в результате этой операции, указывают на каждую публикацию автора «stedolan», и к каждой из них можно добавить комментарий так же, как и раньше:

(.posts[] | select(.author == "stedolan") | .comments) |=
    . + ["terrible."]

Комментарии

В фильтрах jq можно писать комментарии, используя #.

Символ # (не являющийся частью строки) начинает комментарий. Все символы от # до конца строки игнорируются.

Если перед концом строки стоит нечётное количество символов обратной косой черты, следующая строка также считается частью комментария и игнорируется.

Например, следующий код выводит [1,3,4,7]

[
  1,
  # foo \
  2,
  # bar \\
  3,
  4, # baz \\\
  5, \
  6,
  7
  # comment \
    comment \
    comment
]

Продолжение комментария на следующей строке с помощью обратной косой черты может пригодиться при написании «шебанга» для скрипта jq:

#!/bin/sh --
# total - Output the sum of the given arguments (or stdin)
# usage: total [numbers...]
# \
exec jq --args -MRnf -- "$0" "$@"

$ARGS.positional |
reduce (
  if . == []
    then inputs
    else .[]
  end |
  . as $dot |
  try tonumber catch false |
  if not or isnan then
    @json "total: Invalid number \($dot).\n" | halt_error(1)
  end
) as $n (0; . + $n)

Строка exec считается комментарием для jq, поэтому она игнорируется. Однако sh её не игнорирует, поскольку в sh обратная косая черта в конце строки не продолжает комментарий. Благодаря этому приёму, если скрипт запущен как total 1 2, будет запущен /bin/sh -- /path/to/total 1 2, а затем sh запустит exec jq --args -MRnf -- /path/to/total 1 2, заменив себя интерпретатором jq с указанными параметрами (-M, -R, -n, --args), который обработает текущий файл ($0) с аргументами ($@), переданными sh.

Модули

В jq есть система библиотек/модулей. Модули — это файлы, имена которых заканчиваются на .jq.

Модули, импортируемые программой, ищутся в пути поиска по умолчанию (см. ниже). Директивы import и include позволяют импортирующему коду изменить этот путь.

К путям в пути поиска применяются различные подстановки.

Для путей, начинающихся с ~/, вместо ~ подставляется домашний каталог пользователя.

Для путей, начинающихся с $ORIGIN/, вместо $ORIGIN подставляется каталог, в котором находится исполняемый файл jq.

Для путей, начинающихся с ./, или путей, являющихся ., вместо . подставляется путь к включающему файлу. Для программ верхнего уровня, переданных в командной строке, используется текущий каталог.

В директивах импорта можно указать дополнительный путь поиска, к которому добавляется путь по умолчанию.

Путь поиска по умолчанию — это путь поиска, указанный в параметре командной строки -L, или, если он не указан, ["~/.jq", "$ORIGIN/../lib/jq", "$ORIGIN/../lib"].

Элементы пути поиска, равные null или пустой строке, завершают обработку пути поиска.

Зависимость с относительным путём foo/bar будет искаться в foo/bar.jq и foo/bar/bar.jq в указанном пути поиска. Это позволяет размещать модули в каталоге вместе, например, с файлами системы контроля версий, файлами README и т. д., а также использовать модули, состоящие из одного файла.

Последовательные компоненты с одинаковыми именами не допускаются во избежание неоднозначности (например, foo/foo).

Например, при -L$HOME/.jq модуль foo можно найти в $HOME/.jq/foo.jq и $HOME/.jq/foo/foo.jq.

Если .jq существует в домашнем каталоге пользователя и является файлом (а не каталогом), он автоматически загружается в основную программу.

import RelativePathString as NAME [<metadata>];

Импортирует модуль, найденный по указанному пути относительно каталога в пути поиска. К строке относительного пути добавляется суффикс .jq. К символам модуля добавляется префикс NAME::.

Необязательные метаданные должны быть константным выражением jq. Они должны представлять собой объект с ключами вроде homepage и т. д. В настоящее время jq использует только ключ/значение search из метаданных. Метаданные также доступны пользователям через встроенную функцию modulemeta.

Ключ search в метаданных, если он присутствует, должен иметь строковое значение или значение-массив (массив строк); это путь поиска, который добавляется в начало пути поиска верхнего уровня.

include RelativePathString [<metadata>];

Импортирует модуль, найденный по указанному пути относительно каталога в пути поиска, как если бы он был включён непосредственно в это место. К строке относительного пути добавляется суффикс .jq. Символы модуля импортируются в пространство имён вызывающего кода, как если бы содержимое модуля было включено напрямую.

Необязательные метаданные должны быть константным выражением jq. Они должны представлять собой объект с ключами вроде homepage и т. д. В настоящее время jq использует только ключ/значение search из метаданных. Метаданные также доступны пользователям через встроенную функцию modulemeta.

import RelativePathString as $NAME [<metadata>];

Импортирует JSON-файл, найденный по указанному пути относительно каталога в пути поиска. К строке относительного пути добавляется суффикс .json. Данные файла будут доступны как $NAME::NAME.

Необязательные метаданные должны быть константным выражением jq. Они должны представлять собой объект с ключами вроде homepage и т. д. В настоящее время jq использует только ключ/значение search из метаданных. Метаданные также доступны пользователям через встроенную функцию modulemeta.

Ключ search в метаданных, если он присутствует, должен иметь строковое значение или значение-массив (массив строк); это путь поиска, который добавляется в начало пути поиска верхнего уровня.

module <metadata>;

Эта директива полностью необязательна. Для корректной работы она не требуется. Её единственное назначение — предоставлять метаданные, которые можно прочитать с помощью встроенной функции modulemeta.

Метаданные должны быть константным выражением jq. Они должны представлять собой объект с ключами вроде homepage. В настоящее время jq не использует эти метаданные, но они доступны пользователям через встроенную функцию modulemeta.

modulemeta

Принимает имя модуля и выводит метаданные модуля в виде объекта: импорты модуля (включая метаданные) представлены в виде массива в значении ключа deps, а определённые в модуле функции — в виде массива в значении ключа defs.

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

Цвета

Чтобы настроить альтернативные цвета, задайте в переменной окружения JQ_COLORS список разделённых двоеточиями неполных управляющих последовательностей терминала, например "1;31", в следующем порядке:

  • цвет для null
  • цвет для false
  • цвет для true
  • цвет для чисел
  • цвет для строк
  • цвет для массивов
  • цвет для объектов
  • цвет для ключей объектов

Цветовая схема по умолчанию совпадает со схемой, задаваемой значением JQ_COLORS="0;90:0;39:0;39:0;39:0;32:1;39:1;39:1;34".

Это не руководство по управляющим последовательностям VT100/ANSI. Однако каждая из этих спецификаций цвета должна состоять из двух чисел, разделённых точкой с запятой, где первое число — одно из следующих:

  • 1 (яркий)
  • 2 (приглушённый)
  • 4 (подчёркнутый)
  • 5 (мигающий)
  • 7 (инверсный)
  • 8 (скрытый)

а второе — одно из следующих:

  • 30 (чёрный)
  • 31 (красный)
  • 32 (зелёный)
  • 33 (жёлтый)
  • 34 (синий)
  • 35 (пурпурный)
  • 36 (голубой)
  • 37 (белый)

© 2012 Stephen Dolan
Licensed under the Creative Commons Attribution 3.0 license
https://jqlang.org/manual/v1.8/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API