How to extract certain data using Perl from a file?

Viewed 332

I have data that needs to be extracted from a file, the lines I need for the moment are name,location and host. This is example of the extract. How would I go about getting these lines into a separate file? I have the Original file and the new file i want to create as the input/output file, there are thousands of devices contained within the output file and they are all the same formatting as in my example.

#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw(strftime);

#names of files to be input output

my $inputfile = "/home/nmis/nmis_export.csv";
my $outputfile = "/home/nmis/nmis_data.csv";
open(INPUT,'<',$inputfile) or die $!;
open(OUTPUT, '>',$outputfile) or die $!;
my @data = <INPUT>;
close INPUT;
my $line="";
foreach $line (@data)
{

======Sample Extract=======

    **"group" : "NMIS8",
         "host" : "1.2.3.4",
         "location" : "WATERLOO",
         "max_msg_size" : 1472,
         "max_repetitions" : 0,
         "model" : "automatic",
         "netType" : "lan",
         "ping" : 1,
         "polling_policy" : "default",
         "port" : 161,
         "rancid" : 0,
         "roleType" : "access",
         "serviceStatus" : "Production",
         "services" : null,
         "threshold" : 1,
         "timezone" : 0,
         "version" : "snmpv2c",
         "webserver" : 0
      },
      "lastupdate" : 1616690858,
      "name" : "test",
      "overrides" : {}
},
   {
      "activated" : {
         "NMIS" : 1
      },
      "addresses" : [],
      "aliases" : [],
      "configuration" : {
         "Stratum" : 3,
         "active" : 1,
         "businessService" : "",
         "calls" : 0,
         "cbqos" : "none",
         "collect" : 0,
         "community" : "public",
         "depend" : [
            "N/A"
         ],
         "group" : "NMIS8",
         "host" : "1.2.3.5",
         "location" : "WATERLOO",
         "max_msg_size" : 1472,
         "max_repetitions" : 0,
         "model" : "automatic",
         "netType" : "lan",
         "ping" : 1,
         "polling_policy" : "default",
         "port" : 161,
         "rancid" : 0,
         "roleType" : "access",
         "serviceStatus" : "Production",
         "services" : null,
         "threshold" : 1,
         "timezone" : 0,
         "version" : "snmpv2c",
         "webserver" : 0
      },
      "lastupdate" : 1616690858,
      "name" : "test2",
      "overrides" : {}
   },**
3 Answers

As what you have there is JSON, you should parse it with a JSON parser. JSON::PP is part of the standard Perl distribution. If you want something faster, you could install something else from CPAN.

Update: I included a link to JSON::PP in my answer. Did you follow that link? If you did, you would have seen the documentation for the module. That has more information about how to use the module than I could include in an answer on SO.

But it's possible that you need a little more high-level information. The documentation says this:

JSON::PP is a pure perl JSON decoder/encoder

But perhaps you don't know what that means. So here's a primer.

JSON is a text format for storing complex data structures. The format was initially used in Javascript (the acronym stands for "JavaScript Object Notation") but it is now a standard that is used across pretty much all programming languages.

You rarely want to actually deal with JSON in a program. A JSON document is just text and manipulating that would require some complex regular expressions. When dealing with JSON, the usual approach is to "decode" the JSON into a data structure inside your program. You can then manipulate the data structure however you want before (optionally) "encoding" the data structure back into JSON so you can write it to an output file (in your case, you don't need to do that as you want your output as CSV).

So there are pretty much only two things that a Perl JSON library needs to do:

  1. Take some JSON text and decode it into a Perl data structure
  2. Take a Perl data structure and encode it into JSON text

If you look at the JSON::PP documentation you'll see that it contains two functions, encode_json() and decode_json() which do what I describe above. There's also an OO interface, but let's not overcomplicate things too quickly.

So your program now needs to have the following steps:

  1. Read the JSON from the input file
  2. Decode the JSON into a Perl data structure
  3. Walk the Perl data structure to extract the items that you need
  4. Write the required items into your output file (for which Text::CSV will be useful

Having said all that, it really does seem to me that the jq solution suggested by user157251 is a much better idea.

I would use jq for this not Perl. You just need to query a JSON document. That's what jq is for. You can see an example here

The jq query I created is this one,

.[] | {name: .name, group: .configuration.group, location: .configuration.location}

This breaks down into

.[] # iterate over the array
  | # create a filter to send it to
  {  # that produces an object with the bellow key/values
    .name,
    group: .configuration.group,
    location: .configuration.location
  }

It provides an output like this,

{
  "name": "test2",
  "group": "NMIS8",
  "location": "WATERLOO"
}
{
  "name": "test2",
  "group": "NMIS8",
  "location": "WATERLOO"
}

You can use this to generate a csv

jq -R '.[] | [.name, .configuration.group, .configuration.location] | @csv' ./file.json

Or this to generate a csv with a header,

jq -R '["name","group","location"], (.[] | [.name, .configuration.group, .configuration.location]) | @csv' ./file.json

You can use the JSON distribution for this. Read the entire file in one fell swoop to put the entire JSON string into a scalar (as opposed to putting it into an array and iterating over it), then simply decode the string into a Perl data structure:

use warnings;
use strict;

use JSON;

my $file = 'file.json';

my $json_string;

{
    local $/;              # Locally reset line endings to nothing

    open my $fh, '<', $file or die "Can't open file $file!: $!";

    $json_string = <$fh>;  # Slurp in the entire file
}

my $perl_data_structure = decode_json $json_string;
Related