4

I want to convert the output of the command ps to JSON in order to process it as structured data (with jq in this particular case). How do I do that?

The output looks like the following:

  PID TTY          TIME CMD
20162 pts/2    00:00:00 ps
28280 pts/2    00:00:02 zsh

The header row is always present.

  • Following the jq answer above, I created a more elaborate version in this gist. It will tokenize the CMD column so that for messy commands, like for java with a long classpath, you can easily filter out later. – Alan Berezin Aug 22 '18 at 19:25
11

There are two obvious ways to represent columnar data output in JSON: as an array of arrays and as an array of objects. In the former case you convert each line of the input to an array; in the latter, to an object.

The commands listed bellow work at least with the output of procps-ng on Linux for the commands ps and ps -l.

Option #1: array of arrays

Using Perl

You can convert the output using Perl and the CPAN module JSON::XS.

# ps | perl -MJSON -lane 'my @a = @F; push @data, \@a; END { print encode_json \@data }'
[["PID","TTY","TIME","CMD"],["12921","pts/2","00:00:00","ps"],["12922","pts/2","00:00:00","perl"],["28280","pts/2","00:00:01","zsh"]]

Using jq

Alternatively, you can use jq itself to perform the conversion.

# ps | jq -sR '[sub("\n$";"") | splits("\n") | sub("^ +";"") | [splits(" +")]]' 
[
  [
    "PID",
    "TTY",
    "TIME",
    "CMD"
  ],
  [
    "16694",
    "pts/2",
    "00:00:00",
    "ps"
  ],
  [
    "16695",
    "pts/2",
    "00:00:00",
    "jq"
  ],
  [
    "28280",
    "pts/2",
    "00:00:02",
    "zsh"
  ]
]

Option #2: array of objects

You can convert the input to an array of JSON objects with meaningfully named keys by taking the key names from the header row.

This requires a little more effort and is slightly trickier in jq in particular. However, the result is arguably more human-readable.

Using Perl

# ps | perl -MJSON -lane 'if (!@keys) { @keys = @F } else { my %h = map {($keys[$_], $F[$_])} 0..$#keys; push @data, \%h } END { print encode_json \@data }'
[{"TTY":"pts/2","CMD":"ps","TIME":"00:00:00","PID":"11030"},{"CMD":"perl","TIME":"00:00:00","PID":"11031","TTY":"pts/2"},{"TTY":"pts/2","CMD":"zsh","TIME":"00:00:01","PID":"28280"}]

Note that the keys are in arbitrary order for each entry. This is an artifact of how Perl's hashes work.

Using jq

# ps | jq -sR '[sub("\n$";"") | splits("\n") | sub("^ +";"") | [splits(" +")]] | .[0] as $header | .[1:] | [.[] | [. as $x | range($header | length) | {"key": $header[.], "value": $x[.]}] | from_entries]'
[
  {
    "PID": "19978",
    "TTY": "pts/2",
    "TIME": "00:00:00",
    "CMD": "ps"
  },
  {
    "PID": "19979",
    "TTY": "pts/2",
    "TIME": "00:00:00",
    "CMD": "jq"
  },
  {
    "PID": "28280",
    "TTY": "pts/2",
    "TIME": "00:00:02",
    "CMD": "zsh"
  }
]
2

I would suggest as your starting point - don't use ps and then parse it. That's a good way of causing yourself pain (like say - you want to extend it to include command line args, which are space delimited).

So a simple one would be:

#!/usr/bin/env perl
use strict;
use warnings;
use JSON;
use Proc::ProcessTable;

my $json;
foreach my $proc ( @{ Proc::ProcessTable -> new -> table } ) { 
    push ( @$json, { %$proc } ); 
}

print to_json ( $json, { pretty => 1 } ); 

This will give you a full list of ps fields, which some may be redundant.

And if you want to make this a one liner:

perl -MJSON -MProc::ProcessTable -e 'print to_json ( [ map { %$_ } } @{ Proc::ProcessTable->new->table } ], { pretty => 1 } );'
1

I would suggest explicitly setting what you want the ps output to be with the -o option. Also, use --no-header if you didn't want the header in your json output.

-1

jc can convert the output of many command line tools to JSON.

https://github.com/kellyjonbrazil/jc

ps -ef | jc --ps | jq...

  • 2
    If this tool is yours, you should disclose that. – muru Oct 30 at 1:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.