Regex с командой sed для анализа текста json

15

У меня есть этот текст JSON:

{
    "buildStatus" : {
        "status" : "ERROR",
        "conditions" : [{
                "status" : "OK",
                "metricKey" : "bugs"
            }, {
                "status" : "ERROR",
                "metricKey" : "test_success_density"
            }, {
                "status" : "OK",
                "metricKey" : "vulnerabilities"
            }
        ],
        "periods" : []
    }
}

Я хочу извлечь общее состояние buildStatus, т.е. ожидаемый результат был "ОШИБКА"

"buildStatus" : {
    "status" : "ERROR",
    ....
}

Я попробовал выражение sed ниже, но оно не работает, оно возвращает OK:

status= sed -E 's/.*\"buildStatus\":.*\"status\":\"([^\"]*)\",.*/\1/' jsonfile

Что я делаю неправильно?

user1876040
источник

Ответы:

16

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

Сначала вам нужно установить его:

sudo apt-get install jshon

Затем вы должны предоставить ему данные JSON для анализа через стандартный ввод, так что вы можете либо перенаправить туда вывод другой команды с помощью pipe ( |), либо перенаправить файл в него ( < filename).

Аргументы, необходимые для извлечения нужных вам данных, выглядят так:

jshon -e "buildStatus" -e "status" -u
  • -e "buildStatus" выбирает элемент с индексом "buildStatus" из словаря верхнего уровня.
  • -e "status" выбирает элемент с индексом «status» из словаря второго уровня, выбранного выше.
  • -u преобразует выбранные данные из JSON в простые данные (т. е. здесь он удаляет кавычки вокруг строки)

Таким образом, команда, которую вы запускаете, в зависимости от того, откуда вы получаете данные, выглядит так:

jshon -e "buildStatus" -e "status" -u < YOUR_INPUT_FILE
YOUR_JSON_PRODUCING_COMMAND | jshon -e "buildStatus" -e "status" -u

Чтобы узнать больше о jshon, вы можете прочитать его справочную страницу, доступную онлайн здесь или просто набрав man jshon.

Byte Commander
источник
6
Там также jq:jq -r .buildStatus.status
Муру
@HTNW Мне никогда не нравился этот ответ, потому что «один открытый тег XML» (который задает вопрос) - это обычный язык (и вы в принципе могли бы создать полный синтаксический анализатор XML, используя регулярные выражения для сопоставления тегов, комментариев, cdata разделы и использование простого стека для обработки вложенного контекста). Однако самый «интересный» обычный язык в JSON - это строковый литерал.
Random832
10

Работа для jq:

jq -r '.["buildStatus"]["status"]' file.json

Можно сократить до:

jq -r '.buildStatus.status' file.json

-r( --raw-output) выводит строку без jsonформатирования строки, т.е. без кавычек.

Пример:

% cat file.json                   
{
    "buildStatus" : {
        "status" : "ERROR",
        "conditions" : [{
                "status" : "OK",
                "metricKey" : "bugs"
            }, {
                "status" : "ERROR",
                "metricKey" : "test_success_density"
            }, {
                "status" : "OK",
                "metricKey" : "vulnerabilities"
            }
        ],
        "periods" : []
    }
}

% jq -r '.["buildStatus"]["status"]' file.json
ERROR

% jq -r '.buildStatus.status' file.json       
ERROR

Если он еще не установлен, установите его с помощью (доступно в репозитории Universe):

sudo apt-get install jq 
heemayl
источник
8

Как уже упоминалось, синтаксический анализ сложных структурированных данных предпочтительнее с соответствующим API. В Python есть jsonмодуль для этого, который я лично очень часто использую в своих скриптах, и довольно просто извлечь нужные поля, как вы:

$ python -c 'import sys,json;print json.load(sys.stdin)["buildStatus"]["status"]' <  input.txt
ERROR

Здесь происходит то, что мы перенаправляем входной файл в стандартный ввод python и читаем его с помощью json.load(). Это становится словарем Python с ключом «buildStatus», и он содержит другой словарь Python с ключом «status». Таким образом, мы просто распечатываем значение ключа в словаре, который хранится в другом словаре. Довольно просто.

Помимо простоты, еще одним преимуществом является то, что Python и этот API все предустановлены и поставляются с Ubuntu по умолчанию.

Сергей Колодяжный
источник
6

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

Теперь, в вашем простом примере, все, что вам нужно, это первое вхождение "status", так что вы можете сделать:

$ sed -nE '/status/{s/.*:\s*"(.*)",/\1/p;q}' file.json 
ERROR

Хитрость заключается в том, -nчтобы избежать печати, а затем, если строка соответствует status( /status/), вы удаляете все, кроме части, которую хотите s/.*:\s*"(.*)",/\1/, pнабираете строку и qудаляете.


Лично я нахожу эту эквивалентную команду grep намного проще:

$ grep -m1 -oP '"status"\s*:\s*"\K[^"]+' file.json 
ERROR

Или этот:

$ perl -ne 'if(s/.*"status"\s*:\s*"([^"]+).*/$1/){print;exit}' file.json 
ERROR

Если серьезно, если вы планируете анализ файлов JSON, не пытайтесь делать это вручную. Используйте правильный анализатор JSON.

terdon
источник
или этот:grep -m 1 status file.json | tr -cd '[[:alnum:]]:' | cut -f2 -d':'
slowko
1
@ user1876040 пожалуйста. Пожалуйста, не забудьте принять один из ответов (я рекомендую ByteCommander , он является лучшим решением), чтобы вопрос мог быть помечен как ответ).
тердон
6

Не говорю, что вы должны использовать sed(я думаю, что кто-то отказался от меня только за то, что я не написал обязательное предостережение), но, если вам нужно найти что-то в следующей строке, buildStatusкак вы, похоже, пытаетесь в своей собственной попытке, вам нужно сказать, sedчтобы прочитать следующая строка с Nкомандой

$ sed -rn '/buildStatus/N;s/.*buildStatus.*\n.*: "(.*)",/\1/p' file
ERROR

Примечания:

  • -n не печатайте ничего, пока мы не попросим об этом
  • -rиспользовать ERE (так же, как -E)
  • /buildStatus/N найдите этот шаблон и прочитайте следующую строку
  • s/old/new/заменить oldнаnew
  • .* любое количество любых символов в строке
  • \n новая линия
  • : "(.*)",сохранить любые символы между : "и",
  • \1 обратная ссылка на сохраненный шаблон
  • p распечатать часть, над которой мы работали
Занна
источник
0

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

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

Byte Commander, похоже, рекомендует jshonв другом ответе . Я не использовал этот инструмент, но он напоминает мне xmlstarletи его синтаксис, а также некоторые настраиваемые представления для вывода.

Pysis
источник
Вы, вероятно, говорите о stackoverflow.com/a/1732454/2072269
Muru
3
Подумайте над тем, как улучшить свой ответ, продемонстрировав пример того, как jsontoolего можно использовать для конкретного случая ОП
Сергей Колодяжный
Lol @muru, правильно, это один из постов, пытающихся воспрепятствовать использованию парсинга XML / JSON с помощью Regex! Я больше рекомендовал, jqчтобы Муру и Химэйл описали уже существующие примеры, и просто опубликовал обоснование этого: askubuntu.com/a/863948/230721
Пизис
0

Еще один инструмент Json под названием json ( https://github.com/trentm/json )

$ json buildStatus.status < file.json
ERROR

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

$ json -e 'this.buildStatus.status="not error"' < file.json > new.json

или даже...

$ json -e 'this.buildStatus.status="no errors"' < file.json | json -e 'this.buildStatus.status
no errors

документация в: http://trentm.com/json/


если не установлен:

  • установить узел
  • и судо npm install -g json

источник