Обрабатывать очень большой входной файл без излишеств - PullRequest
2 голосов
/ 03 апреля 2019

Я работаю с выводом JSON из инструмента (massdns), который отформатирован следующим образом:

{"query_name":"1eaff.example.com.","query_type":"A","resp_name":"ns02.example.com.","resp_type":"A","data":"<ip>"}
{"query_name":"1cf0e.example.com.","query_type":"A","resp_name":"ns01.example.com.","resp_type":"A","data":"<ip>"}
{"query_name":"1cf0e.example.com.","query_type":"A","resp_name":"ns02.example.com.","resp_type":"A","data":"<ip>"}
{"query_name":"1fwsjz2f4ok1ot2hh2illyd1-wpengine.example.com.","query_type":"A","resp_name":"ns01.example.com.","resp_type":"A","data":"<ip>"}
{"query_name":"1fwsjz2f4ok1ot2hh2illyd1-wpengine.example.com.","query_type":"A","resp_name":"ns02.example.com.","resp_type":"A","data":"<ip>"}
{"query_name":"1a811.example.com.","query_type":"A","resp_name":"ns01.example.com.","resp_type":"A","data":"<ip>"}

Я могу использовать jq с slurp (-s), чтобы красиво выводить результаты в нужном мне формате:

jq -s '{ a: "xxx", "b": 123, domains: map(select(.resp_type=="A") | .resp_name[:-1] ) | unique }'

Это дает строку JSON, например:

{
  "a": "xxx",
  "b": 123,
  "domains": [
    "ns01.example.com",
    "ns02.example.com"
  ]
}

(см. пример JQPlay здесь .)

Моя проблема возникает, когда мой ввод масштабируется до сотен тысяч строк (ГБ данных), в этом случае slurp становится слишком потребляющим память, и jq завершается с ошибкой.

Я обнаружил опцию --stream, которая позволяет обрабатывать большие входные данные, но я изо всех сил пытаюсь найти способ получить тот же самый выход. Есть ли способ использовать --stream (а не --slurp), чтобы получить требуемый вывод для очень большого входного файла с jq?

1 Ответ

2 голосов
/ 03 апреля 2019

--stream усложнит эту задачу, вместо этого используйте параметр --null-input/-n вместе с reduce.

{a: "xxx", b: 123}
| .domains = (reduce (inputs|select(.query_type == "A").resp_name) as $d
  ({}; . + {($d): null}) | keys_unsorted | map(.[:-1]))

Сохранение доменов в объекте в качестве ключей вместо массива делает этот сценарий еще более эффективнымс точки зрения потребления памяти и времени процессора;в jq объекты добавляются путем слияния, то есть вставки всех пар ключ-значение из обоих объектов в один объединенный объект.Если оба объекта содержат значение для одного и того же ключа, объект справа от + выигрывает. Таким образом, нет необходимости unique.

Обрезка последнего символа (.[:-1])все resp_name s также замедляют процесс, вместо этого выполнение map(.[:-1]) с результирующим массивом более эффективно.

См. это на jqplay .

...