awk / sed / perl一线+如何仅打印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上的相关问题:使用Unix工具解析JSON
hoefling

Answers:


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

您主张使用语法感知工具(jq)而不是朴素的字符串操作,这很好,但是随后您使用朴素的字符串操作对输出进行(有限)转义序列处理。对我来说,这似乎不是一个好主意。jq必须有一种方法可以正确地转义输出值,对吗?
丹尼尔·普赖顿

@DanielPryden,否。尽管jq确实有一些方法可以正确地转义输出值(例如@text@sh等),但是在这种特殊情况下,这些方法无济于事。
RomanPerekhrest

一种变体,将属性值保留为JSON对象,并使用sed去除不需要的花括号和空格:jq '.items[].properties' input.json | sed -n 's/^\s\+//p'
Joe Lee-Moyet

为什么“,”未如预期的结果那样出现在输出中?
yael

您可以看到我的“预期输出”吗,可以根据我的预期结果编辑答案吗?
Yael

27

请,请不要养成使用非结构化工具解析结构化数据的习惯。如果你解析XML,JSON,YAML等,使用特定的解析器,至少在结构化的数据转换成AWK,一个更合适的形式sedgrep等等。

在这种情况下,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-Bruce Barnett的简介和教程

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

为了更精确地匹配,并且还要注意用额外的空格封闭方括号,您可以使用

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

我不熟悉JSON,但可能/}/比JSON 更安全/}$。后者似乎没有任何优势。
Hauke Laging,

1
@HaukeLaging没有行尾标记,它已经与content包含}某处的行匹配。
nohillside

5
即使有可能,它也很可能仅适用于示例文件。如果要解析结构化数据,则应使用为此设计的内容。可以是jq,xpath,yq,xq等。这是因为使用面向行的工具对其进行解析最终将使您陷入困境,并且调试起来可能并不容易。
书呆子

例如,如果“ href”字段之一包含单词“ properties”,会发生什么?
斯蒂格·海默

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的工具来解析JSON是有风险的。例如,如果“ href”字段之一包含单词“ properties”,会发生什么?像投票最多的答案这样的JSON感知工具更不容易出错。
Stig Hemmer '18

3
同意,冒险。但是OP特别希望使用sed / awk / perl的单线解决方案。我给出的答案符合所有这些条件。
史蒂夫

什么//!p意思 打印是否符合条件之一?
大卫·康拉德

1
嗯,知道了,//重复最后一个正则表达式,!而不是p打印。好。
大卫·康拉德

1

它被标记为perlperl但我还没有答案,所以我会插手。

不要使用正则表达式或其他“非结构化”解析器。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实际使用情况。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.