从shell更改JSON数据文件中的值

时间:2014-11-28 13:36:48

标签: json bash perl shell awk

我创建了一个JSON文件,在这种情况下包含:

{"ipaddr":"10.1.1.2","hostname":"host2","role":"http","status":"active"},
{"ipaddr":"10.1.1.3","hostname":"host3","role":"sql","status":"active"},
{"ipaddr":"10.1.1.4","hostname":"host4","role":"quad","status":"active"},

另一方面,我有一个带有值的变量,例如:

arr="10.1.1.2 10.1.1.3"

来自后续检查服务器状态,例如。对于这些值,我想将状态字段更改为"inactive"。换句话说,grep主机并更改其"status"值。

预期产出:

{"ipaddr":"10.1.1.2","hostname":"host2","role":"http","status":"inactive"},
{"ipaddr":"10.1.1.3","hostname":"host3","role":"sql","status":"inactive"},
{"ipaddr":"10.1.1.4","hostname":"host4","role":"quad","status":"active"},

5 个答案:

答案 0 :(得分:2)

$ arr="10.1.1.2 10.1.1.3"
$ awk -v arr="$arr" -F, 'BEGIN { gsub(/\./,"\\.",arr); gsub(/ /,"|",arr) }
    $1 ~ "\"(" arr ")\"" { sub(/active/,"in&") } 1' file
{"ipaddr":"10.1.1.2","hostname":"host2","role":"http","status":"inactive"},
{"ipaddr":"10.1.1.3","hostname":"host3","role":"sql","status":"inactive"},
{"ipaddr":"10.1.1.4","hostname":"host4","role":"quad","status":"active"},

答案 1 :(得分:2)

版本1 : 使用简单的基于正则表达式的转换。这可以通过几种方式完成。从最初的问题来看,ipaddr列表在arr中是变量。使用Bash env变量的示例:

$ export var="... ..."

通过命令行参数提供此信息是一种可能的解决方案。

#!/usr/bin/perl
my %inact;                    # ipaddr to inactivate
my $arr=$ENV{arr} ;           # from external var (export arr=...)
## $arr=shift;                # from command line arg

for( split(/\s+/, $arr)){ $inact{$_}=1 }

while(<>){                    # one "json" line at the time
   if(/"ipaddr":"(.*?)"/ and $inact{$1}){
       s/"active"/"inactive"/}
   print $_;
}

版本2

使用Json解析器,我们可以进行更复杂的转换;由于输入不是真正的JSON,我们当时会处理一行“几乎是json”:

use JSON;

use strict;
my ($line, %inact);
my $arr=$ENV{arr} ; 

for( split(/\s+/, $arr)){ $inact{$_}=1 }

while(<>){              # one "json" line at the time
   if(/^\{.*\},/){
      s/,\n//;
      $line = from_json( $_);
      if($inact{$line->{ipaddr}}){
         $line->{status} = "inactive" ;}
      print to_json($line), ",\n"; }
   else { print $_;}
}

答案 2 :(得分:2)

这是一个快速perl“环绕式单行”:使用JSON模块和slurps with the -0 switch

perl -MJSON -n0E '$j = decode_json($_); 
   for (@{$j->{hosts}}){$_->{status}=inactive if $_->{ipaddr}=~/2|3/} ; 
   say to_json( $j->{hosts}, {pretty=>1} )' status_data.json

可能更好或可能违反map的PBP建议:

perl -MJSON -n0E '$j = decode_json($_); 
   map { $_->{status}=inactive if $_->{ipaddr}=~/2|3/ } @{ $j->{hosts} }  ;
   say to_json( $j->{hosts} )' status_data.json

使用jq重置状态的shell脚本也是可能的。这是使用jq解析和输出JSON更改的快速方法:

cat status_data.json| jq -r '.hosts |.[] |
select(.ipaddr == "10.1.1.2"//.ipaddr == "10.1.1.3" )' |jq '.status = "inactive"'

编辑在之前的评论中,我不确定OP是否对应用程序更感兴趣,而不是快速搜索和替换(关于短语“另一方...... “和”检查服务器状态“)。以下是脚本形式的(仍然很简单的)perl方法:

use v5.16; #strict, warnings, say
use JSON ;
use IO::All;

my $status_data < io 'status_data.json';
my $network = JSON->new->utf8->decode($status_data) ;
my @changed_hosts= qw/10.1.1.2 10.1.1.3/;

sub status_report {
  foreach my $host ( @{ $network->{hosts} }) {
     say "$host->{hostname} is $host->{status}";
  }
}

sub change_status {
  foreach my $host ( @{ $network->{hosts} }){
    foreach (@changed_hosts) {
      $host->{status} = "inactive" if $host->{ipaddr} eq $_ ;
    }
  }
  status_report;
}

defined $ENV{CHANGE_HAPPENED} ? change_status : status_report ;

该脚本读取JSON文件status_data.json(使用IO::All非常有趣),然后使用JSON将其解码为哈希。很难判断这是否是一个完整的解决方案,因为如果您正在“监控”主机状态,那么我们应该定期检查JSON数据文件并将其与我们的哈希进行比较,然后在发生更改时运行脚本的主体

要模拟发生的更改,您可以使用CHANGE_HAPPENED(或export CHANGE_HAPPENED=1,如果在setenv中)和tcsh以及unset CHANGE_HAPPENED在您的环境中定义/取消定义status_report()然后,脚本将更新消息和散列或“报告”。为了完成此操作,我们应更新哈希值中的数据,以定期或在事件发生时匹配数据文件。可以更改@inactive_hosts子例程,以便在@active_hosts告知它时执行update_status()if ( something_happened() ) { update_status() }的数组:status_data.json等。

希望有所帮助。

<强> { "hosts":[ {"ipaddr":"10.1.1.2","hostname":"host2","role":"http","status":"active"}, {"ipaddr":"10.1.1.3","hostname":"host3","role":"sql","status":"active"}, {"ipaddr":"10.1.1.4","hostname":"host4","role":"quad","status":"active"} ] }

~/ % perl network_status_json.pl
host2 is active
host3 is active
host4 is active
~/ % export CHANGE_HAPPENED=1   
~/ % perl network_status_json.pl
host2 is inactive
host3 is inactive
host4 is active

<强>输出:

{{1}}

答案 3 :(得分:1)

#!/bin/ksh

# your "array" of IP
arr="10.1.1.2 10.1.1.3"


# create and prepare temporary file for sed action
SedAction=/tmp/Action.sed

# --- for/do generating SedAction --------
echo "#sed action" > ${SedAction}

#take each IP from the arr variable one by one
for IP in ${arr}
 do
   # prepare for a psearch pattern use
   IP_RE="$( echo "${IP}" | sed 's/\./\\./g' )"

   # generate sed action in temporary file.
   # final action will be like: 
   #   s/\("ipaddr":"10\.1\.1\.2".*\)"active"}/\1"inactive"}/;t
   # escape(double) \ for in_file espace, escape(simple) " for this line interpretation
   echo "s/\\\(\"ipaddr\":\"${IP_RE}\".*\\\)\"active\"}/\\\1\"inactive\"}/;t" >> ${SedAction}
 done

# --- sed generating sed action ---------------
echo "${arr}" \
 | tr " " "\n" \
 | sed 's/\./\\./g
        s#.*#s/\\("ipaddr":"&".*\\)"active"}/\\1"inactive"}/;t#
        ' \
 > ${SedAction}


# core of the process (use -i for inline editing or "double" redirection for non GNU sed)
sed -f ${SedAction} YourFile

# clean temporary file
rm ${SedAction}

自我评论,在ksh / AIX中测试过。 2根据你想要做的动作生成SedAction的方法(如果有的话)。你只需要一个工作,我更喜欢第二个

答案 4 :(得分:1)

在Perl中使用JSON模块确实非常简单。

use strict;
use warnings;

use JSON qw/ from_json to_json /;

my $json = JSON->new;

my $data = from_json(do { local $/; <DATA> });

my $arr = "10.1.1.2 10.1.1.3";
my %arr = map { $_ => 1 } split ' ', $arr;

for my $item (@$data) {
  $item->{status} = 'inactive' if $arr{$item->{ipaddr}};
}

print to_json($data, { pretty => 1 }), "\n";

__DATA__
[
{"ipaddr":"10.1.1.2","hostname":"host2","role":"http","status":"active"},
{"ipaddr":"10.1.1.3","hostname":"host3","role":"sql","status":"active"},
{"ipaddr":"10.1.1.4","hostname":"host4","role":"quad","status":"active"}
]

<强>输出

[
   {
      "role" : "http",
      "hostname" : "host2",
      "status" : "inactive",
      "ipaddr" : "10.1.1.2"
   },
   {
      "hostname" : "host3",
      "role" : "sql",
      "ipaddr" : "10.1.1.3",
      "status" : "inactive"
   },
   {
      "ipaddr" : "10.1.1.4",
      "status" : "active",
      "hostname" : "host4",
      "role" : "quad"
   }
]