选项#1:数组的数组

选项#1:数组的数组

我想将命令的输出转换ps为 JSON,以便将其作为结构化数据进行处理(使用 杰克在这种特殊情况下)。我怎么做?

输出如下所示:

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

标题行始终存在。

答案1

在 JSON 中,有两种明显的方式来表示列式数据输出:作为数组的数组和作为对象的数组。在前一种情况下,您将输入的每一行转换为数组;在后者中,针对一个对象。

ps下面列出的命令至少适用于 Linux 上的 procps-ng 命令和 的输出ps -l

选项#1:数组的数组

使用 Perl

您可以使用 Perl 和 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"]]

使用jq

或者,您可以使用 jq 本身来执行转换。

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

选项#2:对象数组

您可以通过从标题行获取键名称,将输入转换为具有有意义命名键的 JSON 对象数组。

这需要更多的努力,尤其是在 jq 中稍微棘手。然而,结果可以说更具人类可读性。

使用 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"}]

请注意,每个条目的键顺序是任意的。这是 Perl 哈希工作原理的产物。

使用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

我写了一个名为 的命令行工具jc。它可以将许多命令行工具的输出转换为 JSON,包括ps.

$ jc ps -ef | jq
[
  {
    "uid": "root",
    "pid": 1,
    "ppid": 0,
    "c": 0,
    "stime": "Sep13",
    "tty": null,
    "time": "00:00:12",
    "cmd": "/usr/lib/systemd/systemd --switched-root --system --deserialize 22"
  },
  {
    "uid": "root",
    "pid": 2,
    "ppid": 0,
    "c": 0,
    "stime": "Sep13",
    "tty": null,
    "time": "00:00:00",
    "cmd": "[kthreadd]"
  },
  ...
]

https://github.com/kellyjonbrazil/jc

答案3

我建议您作为起点 - 不要使用ps然后解析它。这是给自己带来痛苦的好方法(比如 - 你想扩展它以包含命令行参数,这些参数是用空格分隔的)。

所以一个简单的就是:

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

这将为您提供完整的字段列表ps,其中有些字段可能是多余的。

如果你想把它做成单衬:

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

答案4

我建议使用 -o 选项明确设置您希望 ps 输出的内容。另外,如果您不想在 json 输出中包含标头,请使用 --no-header。

相关内容