我希望能够将任何命令行程序的输出传递给将其转换为json的命令。

例如,我未知的程序可以接受目标列,定界符和输出字段名称

# select columns 1 and 3 from the output and convert it to simple json
netstat -a | grep CLOSE_WAIT | convert_to_json 1,3 name,other

并会生成如下内容:
[
  {"name": "tcp4", "other": "31"},
  {"name": "tcp4", "other": "0"}
...
]

我正在寻找适用于任何程序的东西,而不仅仅是netstat!

我愿意安装任何第三方工具/开源项目,并且倾向于在linux/osx上运行-不必是bash脚本解决方案,可以用node,perl,python等编写。

编辑:我当然愿意传递使它正常工作所需的更多信息,例如一个定界符或多个定界符-我只是想避免在命令行中进行显式解析,并拥有该工具去做。

最佳答案

过滤 STDIN 以构建 json 变量

介绍

由于终端是一种非常特殊的界面,具有等宽字体,
工具是为了在这个终端上监控而构建的,许多输出可能非常
难以解析:
netstat 输出是一个很好的示例:

Active UNIX domain sockets (servers and established)
Proto RefCnt Flags       Type       State         I-Node   Path
unix  2      [ ACC ]     STREAM     LISTENING     13947569 @/tmp/.X11-unix/X1
unix  2      [ ]         DGRAM                    8760     /run/systemd/notify
unix  2      [ ACC ]     SEQPACKET  LISTENING     8790     /run/udev/control

如果某些行包含空白字段,则不能简单地在空格上拆分。

因此,requestet 脚本 convert_to_json 将发布在此的最底部。

使用 awk 进行简单的基于空间的分割

通过使用 awk ,你可以使用很好的语法:
netstat -an |
    awk '/CLOSE_WAIT/{
        printf "  { \42%s\42:\42%s\42,\42%s\42:\42%s\42},\n","name",$1,"other",$3
    }' |
    sed '1s/^/[\n/;$s/,$/\n]/'

使用 perl 进行简单的基于空间的拆分,但使用 json 库

但是这种 perl 方式更灵活:
netstat -an | perl -MJSON::XS -ne 'push @out,{"name"=>,$1,"other"=>$2} if /^(\S+)\s+\d+\s+(\d+)\s.*CLOSE_WAIT/;END{print encode_json(\@out)."\n";}'

或相同但 split ;
netstat -an |
    perl -MJSON::XS -ne '
        push @out,{"name"=>,$1,"other"=>$2} if
                /^(\S+)\s+\d+\s+(\d+)\s.*CLOSE_WAIT/;
        END{print encode_json(\@out)."\n";
}'

或打印精美:
netstat -an | perl -MJSON::XS -ne '
    push @out,{"name"=>,$1,"other"=>$2} if /^(\S+)\s+\d+\s+(\d+)\s.*CLOSE_WAIT/;
    END{$coder = JSON::XS->new->ascii->pretty->allow_nonref;
        print $coder->encode(\@out);}'

最后,我喜欢这个不是基于 regex 的版本:
netstat -an | perl -MJSON::XS -ne '
    do {
        my @line=split(/\s+/);
        push @out,{"name"=>,$line[0],"other"=>$line[2]}
    } if /CLOSE_WAIT/;
    END{
        $coder = JSON::XS->new->ascii->pretty->allow_nonref;
        print $coder->encode(\@out);
    }'

但是你可以在 perl 脚本中运行命令:
perl -MJSON::XS -e '
    open STDIN,"netstat -an|";
    my @out;
    while (<>){
        push @out,{"name"=>,$1,"other"=>$2} if /^(\S+)\s+\d+\s+(\d+)\s.*CLOSE_WAIT/;
    };
    print encode_json \@out;'

这可能成为一个基本的原型(prototype):
#!/usr/bin/perl -w

use strict;
use JSON::XS;
my $coder = JSON::XS->new->ascii->pretty->allow_nonref;

$ENV{'LANG'}='C';
open STDIN,"netstat -naut|";
my @out;
my @fields;

my $searchre=":";
$searchre = shift @ARGV if @ARGV;

while (<>){
    map { s/_/ /g;push @fields,$_; } split(/\s+/) if
        /^Proto.*State/ && s/\sAddr/_Addr/g;
    do {
        my @line=split(/\s+/);
        my %entry;
        for my $i (0..$#fields) {
            $entry{$fields[$i]}=$line[$i];
        };
        push @out,\%entry;
    } if /$searchre/;
}

print $coder->encode(\@out);

(没有参数,这将转储整个 netstat -uta ,但您可以将任何搜索字符串作为参数,例如 CLOSE 或 IP。)

位置参数,netstat2json.pl
这种方法可以与 netcat 之外的许多其他工具一起使用,其中一些
更正:
#!/usr/bin/perl -w
use strict;
use JSON::XS;
my $coder = JSON::XS->new->ascii->pretty->allow_nonref;
$ENV{'LANG'}='C';
open STDIN,"netstat -nap|";
my ( $searchre ,@out,%fields)=( "[/:]" );
$searchre = shift @ARGV if @ARGV;
while (<>){
    next if /^Active\s.*\)$/;
    /^Proto.*State/ && do {
        s/\s(name|Addr)/_$1/g;
        my @head;
        map { s/_/ /g;push @head,$_; } split(/\s+/);
        s/_/ /g;
        %fields=();
        for my $i (0..$#head) {
            my $crt=index($_,$head[$i]);
            my $next=-1;
            $next=index($_,$head[$i+1])-$crt-1 if $i < $#head;
            $fields{$head[$i]}=[$crt,$next];
        }
        next;
    };
    do {
        my $line=$_;
        my %entry;
        for my $i (keys %fields) {
            my $crt=substr($line,$fields{$i}[0],$fields{$i}[1]);
            $crt=~s/^\s*(\S(|.*\S))\s*$/$1/;
            $entry{$i}=$crt;
        };
        push @out,\%entry;
    } if /$searchre/;
}
print $coder->encode(\@out);
  • 查找标题行 Proto.*State (特定于 netcat )
  • 存储具有位置和长度的字段名
  • 按字段长度分割线,然后修剪空格
  • 将变量转储为 json 字符串。

  • 这可以使用参数运行,就像以前一样:
    ./netstat2json.pl CLOS
    [
       {
          "Local Address" : "127.0.0.1:31001",
          "State" : "CLOSE_WAIT",
          "Recv-Q" : "18",
          "Proto" : "tcp",
          "Send-Q" : "0",
          "Foreign Address" : "127.0.0.1:55938",
          "PID/Program name" : "-"
       },
       {
          "Recv-Q" : "1",
          "Local Address" : "::1:53816",
          "State" : "CLOSE_WAIT",
          "Send-Q" : "0",
          "PID/Program name" : "-",
          "Foreign Address" : "::1:631",
          "Proto" : "tcp6"
       }
    ]
    

    空字段不会破坏变量赋值:
    ./netstat2json.pl 1000.*systemd/notify
    [
       {
          "Proto" : "unix",
          "I-Node" : "33378",
          "RefCnt" : "2",
          "Path" : "/run/user/1000/systemd/notify",
          "PID/Program name" : "-",
          "Type" : "DGRAM",
          "Flags" : "[ ]",
          "State" : ""
       }
    ]
    

    注意! 此修改版本使用 netstat 参数运行 -nap 以获取 PID/Program name 字段。

    如果不是由 super 用户 root 运行,您可以在 STDERR 上成为以下输出:
    (Not all processes could be identified, non-owned process info
    will not be shown, you would have to be root to see it all.)
    

    你可以避免它们
  • 通过运行 netstat2json.pl 2>/dev/null ,
  • 通过将其作为 root 或使用 sudo
  • 运行
  • 编辑行 #6 ,将 "netstat -nap|" 更改为 "netstat -na|"
  • convert_to_json.pl perl 将 STDIN 转换为 json 的脚本。

    convert_to_json.pl perl 脚本,严格按照要求:作为 netstat -an | grep CLOSE | ./convert_to_json.pl 1,3 name,other 运行
    #!/usr/bin/perl -w
    
    use strict;
    use JSON::XS;
    my $coder = JSON::XS->new->ascii->pretty->allow_nonref;
    
    my (@fields,@pos,@out);
    
    map {
        push @pos,1*$_-1
    } split ",",shift @ARGV;
    
    map {
        push @fields,$_
    } split ",",shift @ARGV;
    
    die "Number of fields don't match number of positions" if $#fields ne $#pos;
    
    while (<>) {
        my @line=split(/\s+/);
        my %entry;
        for my $i (0..$#fields) {
             $entry{$fields[$i]}=$line[$pos[$i]];
        };
        push @out,\%entry;
    }
    print $coder->encode(\@out);
    

    关于json - 通过终端中的列将任意输出转换为json?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40246134/

    10-12 20:23