Como converto a saída de ps (1) para JSON?

3

Eu quero converter a saída do comando ps em JSON para processá-lo como dados estruturados (com jq neste caso particular). Como eu faço isso?

A saída é semelhante à seguinte:

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

A linha de cabeçalho está sempre presente.

    
por nwk 17.11.2015 / 04:26

4 respostas

8

Existem duas maneiras óbvias de representar a saída de dados colunares em JSON: como uma matriz de matrizes e como uma matriz de objetos. No primeiro caso, você converte cada linha da entrada em uma matriz; no segundo, para um objeto.

Os comandos listados abaixo funcionam pelo menos com a saída de procps-ng no Linux para os comandos ps e ps -l .

Opção nº 1: matriz de matrizes

Usando o Perl

Você pode converter a saída usando Perl e o módulo CPAN 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"]]

Usando jq

Como alternativa, você pode usar o próprio jq para realizar a conversão.

# 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"
  ]
]

Opção nº 2: matriz de objetos

Você pode converter a entrada em uma matriz de objetos JSON com chaves nomeadas com significado, pegando os nomes das chaves da linha de cabeçalho.

Isso requer um pouco mais de esforço e é um pouco mais complicado em jq em particular. No entanto, o resultado é indiscutivelmente mais legível por humanos.

Usando o 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"}]

Observe que as chaves estão em ordem arbitrária para cada entrada. Este é um artefato de como os hashes de Perl funcionam.

Usando 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"
  }
]
    
por 17.11.2015 / 04:26
1

Eu sugiro definir explicitamente o que você deseja que a saída ps esteja com a opção -o. Além disso, use --no-header se você não quiser o cabeçalho na saída do json.

    
por 18.11.2015 / 08:10
1

Gostaria de sugerir como ponto de partida - não use ps e, em seguida, analise-o. Essa é uma boa maneira de se causar dor (como dizer - você quer estendê-lo para incluir argumentos de linha de comando, que são delimitados por espaço).

Então, um simples seria:

#!/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 } ); 

Isso lhe dará uma lista completa dos campos ps , que alguns podem ser redundantes.

E se você quiser fazer deste um forro:

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

Seguindo a resposta jq acima, criei uma versão mais elaborada nesta essência . Ele irá tokenizar a coluna CMD para que, para comandos confusos, como para java com um caminho de classe longo, você possa filtrar facilmente mais tarde.

    
por 22.08.2018 / 21:25