awk / sed / perl one liner + как печатать только строки свойств из файла json

10

Как напечатать только строки свойств из файла JSON

пример файла JSON

{
  "href" : "http://master02:8080/api/v1/clusters/HDP/configurations?type=kafka-env&tag=version1527250007610",
  "items" : [
    {
      "href" : "http://master02:8080/api/v1/clusters/HDP/configurations?type=kafka-env&tag=version1527250007610",
      "tag" : "version1527250007610",
      "type" : "kafka-env",
      "version" : 8,
      "Config" : {
        "cluster_name" : "HDP",
        "stack_id" : "HDP-2.6"
      },
      "properties" : {
        "content" : "\n#!/bin/bash\n\n# Set KAFKA specific environment variables here.\n\n# The java implementation to use.\nexport JAVA_HOME={{java64_home}}\nexport PATH=$PATH:$JAVA_HOME/bin\nexport PID_DIR={{kafka_pid_dir}}\nexport LOG_DIR={{kafka_log_dir}}\nexport KAFKA_KERBEROS_PARAMS={{kafka_kerberos_params}}\nexport JMX_PORT=9997\n# Add kafka sink to classpath and related depenencies\nif [ -e \"/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\" ]; then\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/lib/*\nfi\n\nif [ -f /etc/kafka/conf/kafka-ranger-env.sh ]; then\n. /etc/kafka/conf/kafka-ranger-env.sh\nfi",
        "is_supported_kafka_ranger" : "true",
        "kafka_log_dir" : "/var/log/kafka",
        "kafka_pid_dir" : "/var/run/kafka",
        "kafka_user" : "kafka",
        "kafka_user_nofile_limit" : "128000",
        "kafka_user_nproc_limit" : "65536"
      }
    }
  ]

ожидаемый результат

    "content" : "\n#!/bin/bash\n\n# Set KAFKA specific environment variables here.\n\n# The java implementation to use.\nexport JAVA_HOME={{java64_home}}\nexport PATH=$PATH:$JAVA_HOME/bin\nexport PID_DIR={{kafka_pid_dir}}\nexport LOG_DIR={{kafka_log_dir}}\nexport KAFKA_KERBEROS_PARAMS={{kafka_kerberos_params}}\nexport JMX_PORT=9997\n# Add kafka sink to classpath and related depenencies\nif [ -e \"/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\" ]; then\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/lib/*\nfi\n\nif [ -f /etc/kafka/conf/kafka-ranger-env.sh ]; then\n. /etc/kafka/conf/kafka-ranger-env.sh\nfi",
    "is_supported_kafka_ranger" : "true",
    "kafka_log_dir" : "/var/log/kafka",
    "kafka_pid_dir" : "/var/run/kafka",
    "kafka_user" : "kafka",
    "kafka_user_nofile_limit" : "128000",
    "kafka_user_nproc_limit" : "65536"
Яэль
источник
3
Связанный вопрос по SO: Разбор JSON с инструментами Unix
hoefling
Также связано: stackoverflow.com/questions/1732348/…
Kartik

Ответы:

33

Jq правильный инструмент для обработки данных JSON:

jq '.items[].properties | to_entries[] | "\(.key) : \(.value)"' input.json

Выход:

"content : \n#!/bin/bash\n\n# Set KAFKA specific environment variables here.\n\n# The java implementation to use.\nexport JAVA_HOME={{java64_home}}\nexport PATH=$PATH:$JAVA_HOME/bin\nexport PID_DIR={{kafka_pid_dir}}\nexport LOG_DIR={{kafka_log_dir}}\nexport KAFKA_KERBEROS_PARAMS={{kafka_kerberos_params}}\nexport JMX_PORT=9997\n# Add kafka sink to classpath and related depenencies\nif [ -e \"/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\" ]; then\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/lib/*\nfi\n\nif [ -f /etc/kafka/conf/kafka-ranger-env.sh ]; then\n. /etc/kafka/conf/kafka-ranger-env.sh\nfi"
"is_supported_kafka_ranger : true"
"kafka_log_dir : /var/log/kafka"
"kafka_pid_dir : /var/run/kafka"
"kafka_user : kafka"
"kafka_user_nofile_limit : 128000"
"kafka_user_nproc_limit : 65536"

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

jq -r '.items[].properties | to_entries[]
       | "\"\(.key)\" : \"\(.value | gsub("\n";"\\n"))\","' input.json

Выход:

"content" : "\n#!/bin/bash\n\n# Set KAFKA specific environment variables here.\n\n# The java implementation to use.\nexport JAVA_HOME={{java64_home}}\nexport PATH=$PATH:$JAVA_HOME/bin\nexport PID_DIR={{kafka_pid_dir}}\nexport LOG_DIR={{kafka_log_dir}}\nexport KAFKA_KERBEROS_PARAMS={{kafka_kerberos_params}}\nexport JMX_PORT=9997\n# Add kafka sink to classpath and related depenencies\nif [ -e "/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar" ]; then\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/lib/*\nfi\n\nif [ -f /etc/kafka/conf/kafka-ranger-env.sh ]; then\n. /etc/kafka/conf/kafka-ranger-env.sh\nfi",
"is_supported_kafka_ranger" : "true",
"kafka_log_dir" : "/var/log/kafka",
"kafka_pid_dir" : "/var/run/kafka",
"kafka_user" : "kafka",
"kafka_user_nofile_limit" : "128000",
"kafka_user_nproc_limit" : "65536",
RomanPerekhrest
источник
Вы jqпредпочитаете использовать инструмент с поддержкой синтаксиса ( ), а не наивные строковые операции, что хорошо, но затем вы используете наивную строковую операцию для выполнения (ограниченной) обработки escape-последовательности для вывода. Это не кажется хорошей идеей для меня. jqдолжен быть способ правильно экранировать значение для вывода, верно?
Даниэль Приден
@DanielPryden, Нет. Хотя jqесть некоторые способы правильно экранировать значение для вывода (например @text, и @shт. Д.), Они не помогут в данном конкретном случае.
РоманПерехрест
Вариант, который оставляет значения свойств в виде объектов JSON и использует sed для удаления нежелательных скобок и пробелов:jq '.items[].properties' input.json | sed -n 's/^\s\+//p'
Джо Ли-Мойет,
почему "," не появляется в выводе, как мои ожидаемые результаты?
Яэль
Можете ли вы увидеть мой "ожидаемый результат", можете ли вы отредактировать свой ответ в соответствии с моими ожидаемыми результатами?
Яэль
27

Пожалуйста, пожалуйста, не занимайтесь анализом структурированных данных неструктурированными инструментами. Если вы разбираете XML, JSON, YAML и т.д., использовать конкретный анализатор, по крайней мере , для преобразования структурированных данных в более подходящую форму для AWK, sed, и grepт.д.

В этом случае gronочень поможет:

$ gron yourfile | grep -F .properties.
json.items[0].properties.content = "\n#!/bin/bash\n\n# Set KAFKA specific environment variables here.\n\n# The java implementation to use.\nexport JAVA_HOME={{java64_home}}\nexport PATH=/usr/lib/ccache:/home/steve/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/bin\nexport PID_DIR={{kafka_pid_dir}}\nexport LOG_DIR={{kafka_log_dir}}\nexport KAFKA_KERBEROS_PARAMS={{kafka_kerberos_params}}\nexport JMX_PORT=9997\n# Add kafka sink to classpath and related depenencies\nif [ -e \"/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\" ]; then\n  export CLASSPATH=:/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\n  export CLASSPATH=:/usr/lib/ambari-metrics-kafka-sink/lib/*\nfi\n\nif [ -f /etc/kafka/conf/kafka-ranger-env.sh ]; then\n. /etc/kafka/conf/kafka-ranger-env.sh\nfi";
json.items[0].properties.is_supported_kafka_ranger = "true";
json.items[0].properties.kafka_log_dir = "/var/log/kafka";
json.items[0].properties.kafka_pid_dir = "/var/run/kafka";
json.items[0].properties.kafka_user = "kafka";
json.items[0].properties.kafka_user_nofile_limit = "128000";
json.items[0].properties.kafka_user_nproc_limit = "65536";

(Вы можете постобработать это с помощью, | cut -d. -f4- | gron --ungronчтобы получить что-то очень близкое к желаемому результату, хотя все еще как действительный JSON.)

jqэто также подходит .

Стивен Китт
источник
2

От Седа - Введение и Учебник Брюса Барнетта :

sed -n '/properties/,/}$/ {
            /properties/n
            /}$/ !p
        }' FILE.json

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

sed -E -n '/"properties" : {/,/^[[:blank:]]*}[[:blank:]]$/ {
               /"properties" : {/n
               /^[[:blank:]]*}[[:blank:]]$/ !p
           }' FILE.json
nohillside
источник
Я не знаком с JSON, но, возможно /}/, безопаснее, чем /}$. Последнее, похоже, не имеет никаких преимуществ в любом случае.
Хауке Лагинг
1
@HaukeLaging Без маркера конца строки он уже соответствует contentстроке, которая содержит }где-то.
холме
5
Даже если это возможно, скорее всего, оно будет работать только с файлом примера . Если вы хотите анализировать структурированные данные, вы должны использовать что-то, предназначенное для этого. Будь то jq, xpath, yq, xq и т. Д. Это потому, что анализ его с помощью линейно-ориентированных инструментов в конечном итоге укусит вас в спину и отладки, что может быть не очень легко.
Nert
Например, что произойдет, если одно из полей 'href' содержит слово «свойства»?
Стиг Хеммер
1
@StigHemmer Вот почему я расширил шаблон во втором примере. Но я полностью согласен, что использование gronили jqявляется лучшим подходом.
Nohillside
2

sedодин лайнер. Вывести строки между регулярным выражением properties(т. Е. Строкой, содержащей «свойства») и регулярным выражением ^ *}(т. Е. Строкой, начинающейся с нуля или более пробелов, за которой следует «}» и конец строки).

sed -n '/properties/,/^ *}$/{//!p}' file.json

awk один лайнер.

awk '/^ *}/{s=0}/properties/{getline;s=1}s' file.json
Стив
источник
Может быть, вы могли бы объяснить, как работает ваше сопоставление с образцом.
vfbsilva
1
Хотя это работает для приведенного файла примера, рискованно пытаться проанализировать JSON с помощью инструментов, которые этого не понимают. Например, что произойдет, если одно из полей 'href' содержит слово «свойства»? Он гораздо менее подвержен ошибкам в JSON-ориентированном инструменте, таком как ответы с наибольшим количеством голосов.
Стиг Хеммер
3
Согласитесь, рискованно. Но OP специально хотел однострочное решение с использованием sed / awk / perl. Ответ, который я дал, отвечает всем этим критериям.
Стив
Что //!pзначит? Напечатайте, если не одну из вещей, которая соответствует?
Дэвид Конрад
1
Ах, понял, //повторяет последнее регулярное выражение, !не pпечатайте. Отлично.
Дэвид Конрад
1

Он помечен perl, и я perlпока не вижу ответа, поэтому я добавлю.

Не используйте регулярные выражения или другие неструктурированные парсеры. perlимеет JSONмодуль с ним. ( JSON::PPявляется частью ядра с 5.14 тоже)

#!/usr/bin/env perl

use strict;
use warnings;
use JSON;
use Data::Dumper;

my $str = do { local $/; <DATA> };

my $json = decode_json ( $str );

my $properties = $json -> {items} -> [0] -> {properties}; 

#dump the whole lot:
print Dumper $properties;


# or iterate
foreach my $key ( sort keys %$properties ) { 
   print "$key => ", $properties -> {$key},"\n";
}


__DATA__
{
  "href" : "http://master02:8080/api/v1/clusters/HDP/configurations?type=kafka-env&tag=version1527250007610",
  "items" : [
    {
      "href" : "http://master02:8080/api/v1/clusters/HDP/configurations?type=kafka-env&tag=version1527250007610",
      "tag" : "version1527250007610",
      "type" : "kafka-env",
      "version" : 8,
      "Config" : {
        "cluster_name" : "HDP",
        "stack_id" : "HDP-2.6"
      },
      "properties" : {
        "content" : "\n#!/bin/bash\n\n# Set KAFKA specific environment variables here.\n\n# The java implementation to use.\nexport JAVA_HOME={{java64_home}}\nexport PATH=$PATH:$JAVA_HOME/bin\nexport PID_DIR={{kafka_pid_dir}}\nexport LOG_DIR={{kafka_log_dir}}\nexport KAFKA_KERBEROS_PARAMS={{kafka_kerberos_params}}\nexport JMX_PORT=9997\n# Add kafka sink to classpath and related depenencies\nif [ -e \"/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\" ]; then\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/ambari-metrics-kafka-sink.jar\n  export CLASSPATH=$CLASSPATH:/usr/lib/ambari-metrics-kafka-sink/lib/*\nfi\n\nif [ -f /etc/kafka/conf/kafka-ranger-env.sh ]; then\n. /etc/kafka/conf/kafka-ranger-env.sh\nfi",
        "is_supported_kafka_ranger" : "true",
        "kafka_log_dir" : "/var/log/kafka",
        "kafka_pid_dir" : "/var/run/kafka",
        "kafka_user" : "kafka",
        "kafka_user_nofile_limit" : "128000",
        "kafka_user_nproc_limit" : "65536"
      }
    }
  ]
}

Естественно, вы будете читать из STDINили имя файла, а не DATAв вашем реальном сценарии использования.

Sobrique
источник