#json #bash #grep
#json #bash #grep
Вопрос:
Я использую API, который возвращает данные JSON. В данных обычно не хватает нескольких символов в конце, поэтому технически они «похожи на JSON», поскольку они слегка искажены.
Я могу извлечь из него интересующую область, используя grep
это в моем скрипте Bash:
grep -Po '"username": *K"[^"]*"' jsonraw > jsonclean
Это работает нормально, несмотря на то, что JSON слегка усечен. Единственная проблема заключается в том, что он возвращает каждую отдельную запись, тогда как я хочу сделать ее зависимой от другой пары ключ-значение.
Например, я хочу, чтобы оно возвращало username
значение только в том случае, если activity_count
поле есть >=1
, иначе просто пропустите запись. Некоторый псевдокод для представления этого может выглядеть так:
if '"activity_count":' >=1 grep -Po '"username": *K"[^"]*"' jsonraw > jsonclean
Я понимаю, что jq
это может быть более простой альтернативой, но я предпочитаю придерживаться из grep
-за искаженного конца данных JSON и других причин.
Пример данных:
[
{"id":"37da1db11b6b4977902baa286f88bf05","activity_count":0,"blocked":false,"coverPhoto":"cb861013bdcc4e5f9e2a93394a7b4309","followed":true,"human":true,"integration":false,"joined":"20190602125229","muted":false,"name":"AV8R","rss":false,"private":false,"profilePhoto":"511d4625df2442fc9b02ab4279c28f09","subscribed":false,"username":"APALMER66","verified":false,"verifiedComments":false,"badges":[0],"score":"1.4k","interactions":259},{"id":"525f9e87bb2d4f4184d12037050afc8d","activity_count":2,"blocked":false,"coverPhoto":"b0bbb4dec22f40d6a347dfb666ff0158","followed":true,"human":true,"integration":false,"joined":"20200627154134","muted":false,"name":"DeziRay","rss":false,"private":false,"profilePhoto":"86627047425844fcbf921e53fc71d106","subscribed":false,"username":"Deziray","verified":false,"verifiedComments":false,"badges":[0],"score":"4.7k","interactions":259},
Ожидаемый результат:
Deziray
Комментарии:
1. FWIW, ответ jq, который не заботится о искаженном конце документа, возможен, если использовать режим потоковой передачи.
2. …с другой стороны, вы в порядке с Python? Для Python также доступны парсеры JSON на основе потоков, такие как pypi.org/project/jsonslicer , и было бы довольно просто создать функцию оболочки, которая обертывает короткий скрипт Python, используя один из них.
3. Я добавил образцы данных. Спасибо @CharlesDuffy за информацию о
jq
том, что я могу рассмотреть это, если нет хорошегоgrep
ответа, но было несколько других причин, по которым я хотел придерживатьсяgrep
, например, он уже присутствует в любой системе Linux, поэтому код будет работать без необходимости его установки. То же самое для Python, хотя да, мне лично нравится Python. Я просто пытаюсь сохранить скрипт свободным от зависимостей, если смогу.4. Ах — ладно, это немного сложнее, чем я думал тогда, спасибо за разъяснение.
5. …добавлен ответ jq для начала, попытаюсь узнать об ответе Python, когда у меня появится такая возможность.
Ответ №1:
Во-первых (потому что это проще), jq
ответ:
jq -nr --stream '
fromstream(1|truncate_stream(inputs))
| select(.activity_count >= 1)
| .username
' <test.json
Поскольку это работает в потоковом режиме, оно способно обрабатывать даже усеченные документы.
Комментарии:
1. Это здорово, спасибо. Если у кого-нибудь есть
grep
ответ, который все равно был бы полезен (и мне, возможно, придется переместить зеленую галочку — но пока это работает, еще раз спасибо).
Ответ №2:
Как собственная реализация Python, основанная на современной среде выполнения Python 3.x:
#!/usr/bin/env python3
import json, sys
def found_obj_cb(item):
if item.get('activity_count', 0) >= 1 and 'username' in item:
print(item['username'])
return item
try:
json.load(sys.stdin, object_hook=found_obj_cb)
except json.JSONDecodeError:
pass
… используется из оболочки как:
#!/usr/bin/env bash
json_parse_py=$(cat <<'EOF'
import json, sys
def found_obj_cb(item):
if item.get('activity_count', 0) >= 1 and 'username' in item:
print(item['username'])
return item
try:
json.load(sys.stdin, object_hook=found_obj_cb)
except json.JSONDecodeError:
pass
EOF
)
# define a shell function to wrap the Python code
json_parse() { python3 -c "$json_parse_py" "$@"; }
# actually call it, with test.json on stdin
json_parse <test.python