awk / sed / perl one liner + jak wydrukować tylko linie właściwości z pliku json


10

jak wydrukować tylko linie właściwości z pliku json

przykład pliku 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"
      }
    }
  ]

oczekiwany wynik

    "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
Powiązane pytanie na temat SO: Analiza JSON za pomocą narzędzi uniksowych
hoefling

Odpowiedzi:


33

Jq jest właściwym narzędziem do przetwarzania danych JSON:

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

Wyjście:

"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"

Jeśli uzyskanie każdego klucza i wartości podwójnie zacytowane jest naprawdę obowiązkowe - użyj następującej modyfikacji:

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

Wyjście:

"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",

Opowiadasz się za użyciem narzędzia rozpoznającego składnię ( jq) zamiast naiwnych operacji na łańcuchach, co jest dobre, ale następnie używasz naiwnej operacji na łańcuchach, aby wykonać (ograniczone) przetwarzanie sekwencji specjalnej dla danych wyjściowych. To nie wydaje mi się dobrym pomysłem. jqmusi mieć sposób, aby właściwie uciec od wartości wyjściowej, prawda?
Daniel Pryden

@DanielPryden nr Choć jqma kilka sposobów, aby prawidłowo uciec wartość na wyjściu (jak @text, @shetc), ci nie pomoże w tym konkretnym przypadku.
RomanPerekhrest

Wariant, który pozostawia wartości właściwości jako obiekty JSON i używa sed do usuwania niechcianych nawiasów klamrowych i białych znaków:jq '.items[].properties' input.json | sed -n 's/^\s\+//p'
Joe Lee-Moyet

dlaczego „,” nie pojawia się w danych wyjściowych, ponieważ moje oczekiwane wyniki?
yael

czy widzisz proszę moją „oczekiwaną wydajność”, czy możesz edytować swoją odpowiedź zgodnie z moimi oczekiwanymi wynikami?
yael

27

Proszę, nie przyzwyczajaj się do analizowania danych strukturalnych za pomocą nieustrukturyzowanych narzędzi. Jeśli jesteś parsowania XML, JSON, YAML itp używać konkretnego parser, przynajmniej do konwersji ustrukturyzowanych danych w bardziej odpowiedniej formie AWK sed, grepetc.

W takim przypadku gronbardzo pomogłoby:

$ 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";

(Możesz to przetworzyć za pomocą, | cut -d. -f4- | gron --ungronaby uzyskać coś bardzo zbliżonego do pożądanego wyniku, aczkolwiek nadal jako prawidłowy JSON.)

jqjest również odpowiednie .


2

From Sed - Wprowadzenie i samouczek Bruce'a Barnetta :

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

Aby uzyskać dokładniejsze dopasowanie, a także zadbać o zamykanie linii nawiasów za pomocą dodatkowych białych znaków, których możesz użyć

sed -E -n '/"properties" : {/,/^[[:blank:]]*}[[:blank:]]$/ {
               /"properties" : {/n
               /^[[:blank:]]*}[[:blank:]]$/ !p
           }' FILE.json

Nie znam JSON, ale może /}/jest bezpieczniejszy niż /}$. To ostatnie wydaje się nie mieć żadnych zalet.
Hauke ​​Laging

1
@HaukeLaging Bez znacznika końca linii już pasuje do contentlinii, która zawiera }gdzieś.
nohillside

5
Chociaż jest to możliwe, najprawdopodobniej będzie działać tylko na pliku przykładowym . Jeśli chcesz przeanalizować dane strukturalne, powinieneś raczej użyć czegoś do tego przeznaczonego. Czy to jq, xpath, yq, xq itp. To dlatego, że parsowanie go za pomocą narzędzi zorientowanych liniowo ostatecznie ugryzie cię w plecy i debugowanie, które może nie być bardzo łatwe.
nert

Na przykład, co się stanie, jeśli jedno z pól „href” zawiera słowo „właściwości”?
Stig Hemmer

1
@StigHemmer Właśnie dlatego rozszerzyłem wzór w drugim przykładzie. Ale całkowicie się zgadzam, że zastosowanie gronlub jqjest lepszym podejściem.
nohillside

2

sedjedna wkładka. Drukuj linie między wyrażeniem regularnym properties(tj. Wiersz zawierający „właściwości”) a wyrażeniem regularnym ^ *}(tj. Linia rozpoczynająca się od zera lub więcej spacji, po której następuje „}” i koniec linii).

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

awk jedna wkładka.

awk '/^ *}/{s=0}/properties/{getline;s=1}s' file.json

może mógłbyś wyjaśnić, jak działa dopasowywanie wzorców.
vfbsilva

1
Chociaż działa to na podany przykładowy plik, ryzykowne jest parsowanie JSON za pomocą narzędzi, które go nie rozumieją. Na przykład, co się stanie, jeśli jedno z pól „href” zawiera słowo „właściwości”? Jest znacznie mniej podatny na błędy w narzędziu obsługującym JSON, takim jak najczęściej głosowane odpowiedzi.
Stig Hemmer

3
Zgadzam się, ryzykowne. Ale OP szczególnie chciał mieć jedno-liniowe rozwiązanie wykorzystujące sed / awk / perl. Podana przeze mnie odpowiedź spełnia wszystkie te kryteria.
steve

Co //!pznaczy Wydrukować, jeśli nie jedną z pasujących rzeczy?
David Conrad

1
Ach, rozumiem, //powtarza ostatnie wyrażenie, !nie pdrukuj. Miły.
David Conrad

1

Jest otagowany perli nie widzę jeszcze perlodpowiedzi, więc się włożę.

Nie używaj wyrażeń regularnych ani innych „nieustrukturyzowanych” parserów. perlma JSONz nim moduł. ( JSON::PPjest częścią rdzenia od 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"
      }
    }
  ]
}

Oczywiście czytałbyś z STDINlub nazwę pliku zamiast DATAw prawdziwym scenariuszu użytkowania.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.