5

I'd like to convert the entire entry of a cat /proc/cpuinfo to a JSON of "key" : "value".

Is this possible? I tried to explode the string using \n in hopes that I could get each line and then I could explode each line using :.

But the newline character seems to produce inconsistent array.

3 Answers 3

3

Well, for a simple approach, you could just do:

sed 's/\(.*\)\t:\(.*\)/"\1" : "\2"/' /proc/cpuinfo 

That will match everything up to a tab followed by a colon and save it as \1, then everything after the colon and save it as \2. The replacement puts quotes around them.

That, however, results in cases like these:

"fpu    " : " yes"
power management:

Items with extra whitespace before the tab have their whitespace included and empty ones are ignored. This Perl version deals with those correctly:

perl -F: -alpe 's/.*/"$F[0]" : "$F[1]"/' /proc/cpuinfo 

This will split the line on : into the @F array (-F sets the character to split on and -a turns on automatic splitting into @F) and print each side quoted. It will break if you have more than one : on a line but I don't think that will ever happen in /proc/cpuinfo. However, it also prints any blank lines in the file. To avoid that, pipe it through grep first:

grep . /proc/cpuinfo | perl -F: -alpe 's/.*/"$F[0]" : "$F[1]"/' /proc/cpuinfo 

Or, only print if a line contains ::

perl -F: -alne 's/.*:.*/"$F[0]" : "$F[1]"/ && print' /proc/cpuinfo 
1
  • Just to note, this answer gives a lot of whitespace on my machine, e.g. ``` "cpu MHz " : " 1995.309" ``` Feb 5, 2022 at 0:00
3
sed -n '/./s/ *\(\( *[^:[:blank:]]\)*\)[^:]*\(:*\)/"\1"\3/gp' /proc/cpuinfo

...works in sed. With GNU shortcuts you can write practically the same statement like:

sed -En 's/ *(( ?[^ :\t])+)\s*(:?)/"\1"\3/gp' /proc/cpuinfo

This is kind of lame (because I could do better with a few keystrokes and jw), but I was playing around w/ sed and...

set '   ' $'\\\n' $'\n' '      '
sed -En "\$c$2$1}$2]${3}1ccpus$1=$1[$2$1{$3/^$/c$2$1},$2$1{
        s/ *(( ?[^ :\t])+)(\s*:\s*)?\s*/\"\1\"\3/g
        s/\"(([yn]).s?|([0-9]+))\"$/\3false\2true/
        s/falsey|ntrue|([0-9])f\w+$/\1/
        /^.flags./{
                s/\s*:\s*/$1=$1[/;h
                s/.*\t.//;x;s/(.*\t.).*/\1/;x
                s/ /\",$2\"/g
                s/^/pr -to24 -a4 <<''$2/e;H;x
                s/$/$2$1$1]/
};      s/.*/$1$4&,/p" /proc/cpuinfo

I think the output is something close to valid. I took a picture because I can never seem to to get tabs to line up in the browser window as they do in the terminal window.

Most of the tabulations are cheated anyway - pr does the flags reformatting via the GNU sed e command, and for the rest I preserve as much of the originally formatted output as I might. For example, because I had to insert two chars - the double-quotes - around the name members, when I also insert leading tabulation I make it 1 actual tab followed by six spaces in a crude attempt at retaining the current tab-stop position.

Here's the first hash:

enter image description here

2

My version:

#!/usr/bin/env perl

use strict;
use warnings;

use JSON;
use Storable qw(dclone);

my ( %h, @cpu );

while (<>) {
    chomp;

    if (m/^$/o) {
        push @cpu, dclone( \%h );
        undef %h;
        next;
    }

    my ( $k, $v ) = split /\s*:\s*/, $_, 2;

    if ( !defined $v or $v eq '' ) { $h{$k} = undef }
    elsif ( $k eq 'flags' )        { $h{$k} = [ split /\s/, $v ] }
    elsif ( $v =~ /^\d+$/o )       { $h{$k} = int ($v) }
    elsif ( $v eq 'yes' )          { $h{$k} = \1 }
    elsif ( $v eq 'no' )           { $h{$k} = \0 }
    else                           { $h{$k} = $v }
}

print JSON->new->pretty->encode( \@cpu );

This assumes there's a blank line between each CPU definition.

4
  • This does an array per cpu then? i just dropped the blanks myself because after looking at maybe doing , splitting for plural named attributes, i decided against it because i didn't know what do with the values - "flags":{"flagname":1,...} felt contrived. I stiil think it's interesting - but i just don't have a concrete enough understanding of JSON to make an informed decision. How you structure your output? (i'm also useless w/ perl, by the way)
    – mikeserv
    Jun 6, 2015 at 17:20
  • 1
    @mikeserv An array with the CPUs, each CPU is a hash, flags are an array of strings, integer values on the RHS are integer, empty values on RHS are null, everything else are strings, that's about all. Whether this makes sense depends on what you want to do with the result. The intention was to show you how to change things as you need (but this won't work if you don't know anything about Perl).
    – lcd047
    Jun 6, 2015 at 17:32
  • @mikeserv I just figured out yes and no on the RHS should be converted to true and false. Edited.
    – lcd047
    Jun 6, 2015 at 17:41
  • I tried to follow suit. I dunno if it's any good.
    – mikeserv
    Jun 7, 2015 at 4:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .