awk / sed / perl one liner + comment imprimer uniquement les lignes de propriétés du fichier json

10

comment imprimer uniquement les lignes de propriétés du fichier json

exemple de fichier 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"
      }
    }
  ]

production attendue

    "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"
Yael
la source
3
Question connexe sur SO: analyser JSON avec les outils Unix
hoefling
Également lié: stackoverflow.com/questions/1732348/…
Kartik

Réponses:

33

Jq est le bon outil pour traiter les données JSON:

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

Le résultat:

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

Dans le cas où il est vraiment obligatoire d'obtenir chaque clé et valeur entre guillemets - utilisez la modification suivante:

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

Le résultat:

"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
la source
Vous préconisez d'utiliser un outil compatible avec la syntaxe ( jq) plutôt que des opérations de chaîne naïves, ce qui est bien, mais vous utilisez ensuite une opération de chaîne naïve pour effectuer un traitement de séquence d'échappement (limité) pour la sortie. Cela ne me semble pas une bonne idée. jqdoit avoir un moyen d'échapper correctement la valeur de sortie, non?
Daniel Pryden
@DanielPryden, Non. Bien jqqu'il existe des moyens d'échapper correctement la valeur de sortie (comme @text, @shetc.), cela n'aidera pas dans ce cas particulier.
RomanPerekhrest
Une variante qui laisse les valeurs des propriétés en tant qu'objets JSON et utilise sed pour supprimer les accolades et les espaces indésirables:jq '.items[].properties' input.json | sed -n 's/^\s\+//p'
Joe Lee-Moyet
pourquoi "," n'apparaît pas dans la sortie, comme mes résultats attendus?
yael
pouvez-vous voir s'il vous plaît ma "sortie attendue", pouvez-vous modifier votre réponse en fonction de mes résultats attendus?
yael
27

Veuillez ne pas prendre l'habitude d'analyser des données structurées avec des outils non structurés. Si vous analysez XML, JSON, YAML etc., utilisez un analyseur spécifique, au moins pour convertir les données structurées sous une forme plus appropriée pour AWK sed, grepetc.

Dans ce cas, groncela aiderait grandement:

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

(Vous pouvez post-traiter cela avec | cut -d. -f4- | gron --ungronpour obtenir quelque chose de très proche de la sortie souhaitée, bien que toujours en JSON valide.)

jqest également approprié .

Stephen Kitt
la source
2

De Sed - Une introduction et un tutoriel de Bruce Barnett :

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

Pour une correspondance plus exacte et pour prendre également soin de fermer les lignes de support avec des espaces supplémentaires, vous pouvez utiliser

sed -E -n '/"properties" : {/,/^[[:blank:]]*}[[:blank:]]$/ {
               /"properties" : {/n
               /^[[:blank:]]*}[[:blank:]]$/ !p
           }' FILE.json
nohillside
la source
Je ne connais pas JSON mais peut /}/- être est plus sûr que /}$. Ce dernier ne semble de toute façon pas avoir d'avantages.
Hauke ​​Laging
1
@HaukeLaging Sans le marqueur de fin de ligne, il correspond déjà à la contentligne qui contient un }quelque part.
nohillside
5
Même si cela est possible, il ne fonctionnera probablement que sur l' exemple de fichier. Si vous souhaitez analyser des données structurées, vous devriez plutôt utiliser quelque chose de conçu pour cela. Que ce soit jq, xpath, yq, xq, etc. C'est parce que l'analyser avec des outils orientés ligne finira par vous mordre dans le dos et déboguer ce qui pourrait ne pas être très facile.
nert
Par exemple, que se passe-t-il si l'un des champs «href» contient le mot «propriétés»?
Stig Hemmer
1
@StigHemmer C'est pourquoi j'ai étendu le modèle dans le deuxième exemple. Mais je suis tout à fait d'accord pour dire que l'utilisation de gronou jqest la meilleure approche.
nohillside
2

sedbon mot. Affiche les lignes entre l'expression régulière properties(c'est-à-dire la ligne contenant "propriétés") et l'expression régulière ^ *}(c'est-à-dire la ligne commençant par zéro ou plusieurs espaces suivis de "}" et de fin de ligne).

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

awk bon mot.

awk '/^ *}/{s=0}/properties/{getline;s=1}s' file.json
Steve
la source
peut-être pourriez-vous expliquer comment fonctionne votre correspondance de motifs.
vfbsilva
1
Bien que cela fonctionne pour l'exemple de fichier donné, il est risqué d'essayer d'analyser JSON avec des outils qui ne le comprennent pas. Par exemple, que se passe-t-il si l'un des champs «href» contient le mot «propriétés»? Il est beaucoup moins sujet aux bogues d'un outil compatible JSON comme les réponses les mieux notées.
Stig Hemmer
3
D'accord, risqué. Mais OP souhaitait spécifiquement une solution monoplace utilisant sed / awk / perl. La réponse que j'ai donnée répond à tous ces critères.
steve
Que veut //!pdire? Imprimer sinon une des choses qui correspondent?
David Conrad
1
Ah, j'ai compris, //répète le dernier regex, !pas, pimprime. Agréable.
David Conrad
1

C'est tagué perl, et je ne vois pas perlencore de réponse, alors je vais intervenir.

N'utilisez pas d'expressions régulières ou d'autres analyseurs «non structurés». perla le JSONmodule avec lui. ( JSON::PPfait également partie du noyau depuis 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"
      }
    }
  ]
}

Naturellement, vous devriez lire STDINou un nom de fichier plutôt que DATAdans votre scénario d'utilisation réel.

Sobrique
la source