perl 解析默认Salt高状态输出

fykwrbwg  于 2023-02-19  发布在  Perl
关注(0)|答案(3)|浏览(168)

我正在尝试解析Salt的高状态输出,这已经被证明是很困难的。没有将输出更改为json,因为我仍然希望它是人类可读的。
将摘要转换成机器可读的东西的最佳方法是什么?

Summary for app1.domain.com
--------------
Succeeded: 278 (unchanged=12, changed=6)
Failed:      0
--------------
Total states run:     278
Total run time:     7.383 s
--
Summary for app2.domain.com
--------------
Succeeded: 278 (unchanged=12, changed=6)
Failed:      0
--------------
Total states run:     278
Total run time:     7.448 s
--
Summary for app0.domain.com
--------------
Succeeded: 293 (unchanged=13, changed=6)
Failed:      0
--------------
Total states run:     293
Total run time:     7.510 s

没有更好的主意,我尝试grep和awk的输出,并插入到一个csv。
这两个工作:

cat ${_FILE} | grep Summary | awk '{ print $3} ' | \
    tr '\n' ',' | sed '$s/,$/\n/' >> /tmp/highstate.csv;

cat ${_FILE} | grep -oP '(?<=unchanged=)[0-9]+' | \
    tr '\n' ',' | sed '$s/,$/\n/' >> /tmp/highstate.csv;

但是这个失败了,但是在Reger中可以工作

cat ${_FILE} | grep -oP '(?<=\schanged=)[0-9]+' | \
    tr '\n' ',' | sed '$s/,$/\n/' >> /tmp/highstate.csv;

EDIT 1:@vintnes @ikegami我同意我更愿意使用json输出解析输出,但是Salt在输出到josn时没有提供更改摘要。到目前为止,这是我所拥有的,虽然非常丑陋,但它正在工作。

cat ${_FILE} | grep Summary | awk '{ print $3} ' | \
    tr '\n' ',' | sed '$s/,$/\n/' >> /tmp/highstate_tmp.csv;

cat ${_FILE} | grep -oP '(?<=unchanged=)[0-9]+' | \
    tr '\n' ',' | sed '$s/,$/\n/' >> /tmp/highstate_tmp.csv;

cat ${_FILE} | grep unchanged | awk -F' ' '{ print $4}' | \
    grep -oP '(?<=changed=)[0-9]+' | tr '\n' ',' | sed '$s/,$/\n/' >> /tmp/highstate_tmp.csv;

cat ${_FILE} | { grep "Warning" || true; } | awk -F: '{print $2+0} END { if (!NR) print "null" }' | \
    tr '\n' ',' | sed '$s/,$/\n/' >> /tmp/highstate_tmp.csv;

cat ${_FILE} | { grep "Failed" || true; } | awk -F: '{print $2+0} END { if (!NR) print "null" }' | \
    tr '\n' ',' | sed '$s/,$/\n/' >> /tmp/highstate_tmp.csv;

csvtool transpose /tmp/highstate_tmp.csv > /tmp/highstate.csv;

sed -i '1 i\instance,unchanged,changed,warning,failed' /tmp/highstate.csv;

输出:

instance,unchanged,changed,warning,failed
app1.domain.com,12,6,,0
app0.domain.com,13,6,,0
app2.domain.com,12,6,,0
qlzsbp2j

qlzsbp2j1#

开始。如果输出包含警告,此操作也会起作用。请注意,输出的顺序与您指定的顺序不同;它是每个记录在文件中出现的顺序。任何问题都不要犹豫。

$ awk -v OFS=, '
        BEGIN        { print "instance,unchanged,changed,warning,failed" }
        /^Summary/   { instance=$NF }
        /^Succeeded/ { split($3 $4 $5, S, /[^0-9]+/) }
        /^Failed/    { print instance, S[2], S[3], S[4], $2 }
' "$_FILE"
  • split($3 $4 $5, S, /[^0-9]+/)通过忽略前两个“单词”Succeeded: ###并使用任意数量的非数字作为分隔符来处理警告的可能性。
  • 编辑:* 在/^Fail/上打印,而不是使用/^Summ/END
u59ebvdq

u59ebvdq2#

perl -e'
   use strict;
   use warnings qw( all );

   use Text::CSV_XS qw( );

   my $csv = Text::CSV_XS->new({ auto_diag => 2, binary => 1 });
   $csv->say(select(), [qw( instance unchanged change warning failed )]);

   my ( $instance, $unchanged, $changed, $warning, $failed );
   while (<>) {
      if (/^Summary for (\S+)/) {
         ( $instance, $unchanged, $changed, $warning, $failed ) = $1;
      }
      elsif (/^Succeeded:\s+\d+ \(unchanged=(\d+), changed=(\d+)\)/) {
         ( $unchanged, $changed ) = ( $1, $2 );
      }
      elsif (/^Warning:\s+(\d+)/) {
         $warning = $1;
      }
      elsif (/^Failed:\s+(\d+)/) {
         $failed = $1;
         $csv->say(select(), [ $instance, $unchanged, $changed, $warning, $failed ]);
      }
   }
'

通过STDIN提供输入,或提供要作为参数读取的文件的路径。
简明版本:

perl -MText::CSV_XS -ne'
   BEGIN {
      $csv = Text::CSV_XS->new({ auto_diag => 2, binary => 1 });
      $csv->say(select(), [qw( instance unchanged change warning failed )]);
   }
   /^Summary for (\S+)/ and @row=$1;
   /^Succeeded:\s+\d+ \(unchanged=(\d+), changed=(\d+)\)/ and @row[1,2]=($1,$2);
   /^Warning:\s+(\d+)/ and $row[3]=$1;
   /^Failed:\s+(\d+)/ and ($row[4]=$1), $csv->say(select(), \@row);
'
t2a7ltrp

t2a7ltrp3#

改进@vintnes的答案。生成制表符分隔的CSV输出
编写awk脚本,按顺序从行中读取值。在读取时打印每个记录。

脚本.awk

BEGIN {print("computer","succeeded","unchanged","changed","failed","states run","run time");}
FNR%8 == 1 {arr[1] = $3}
FNR%8 == 3 {arr[2] = $2; arr[3] = extractNum($3); arr[4] = extractNum($4)}
FNR%8 == 4 {arr[5] = $2;}
FNR%8 == 6 {arr[6] = $4;}
FNR%8 == 7 {arr[7] = $4; print arr[1],arr[2],arr[3],arr[4],arr[5],arr[6],arr[7];}

function extractNum(str){match(str,/[[:digit:]]+/,m);return m[0];}

运行脚本

制表符分隔的CSV输出

awk -v OFS="\t" -f script.awk input-1.txt input-2.txt ...

逗号分隔CSV输出

awk -v OFS="," -f script.awk input-1.txt input-2.txt ...

输出

computer        succeeded       unchanged        changed failed  states run      run time
app1.domain.com 278     12      6       0       278     7.383
app2.domain.com 278     12      6       0       278     7.448
app0.domain.com 293     13      6       0       293     7.510

computer,succeeded,unchanged,changed,failed,states run,run time
app1.domain.com,278,12,6,0,278,7.383
app2.domain.com,278,12,6,0,278,7.448
app0.domain.com,293,13,6,0,293,7.510

解释

BEGIN {print("computer","succeeded","unchanged","changed","failed","states run","run time");}
打印标题CSV行
FNR%8 == 1 {arr[1] = $3}
从中的第3个字段提取arr[1]值(8行中的第1行)
FNR%8 == 3 {arr[2] = $2; arr[3] = extractNum($3); arr[4] = extractNum($4)}
从中的第2、3、4个字段提取arr[2,3,4]值(8行中的第3行)
FNR%8 == 4 {arr[5] = $2;}
从中的第2个字段提取arr[5]值(8行中的第4行)
FNR%8 == 6 {arr[6] = $4;}
从中的第4个字段提取arr[6]值(8行中的第6行)
FNR%8 == 7 {arr[7] = $4;
从中的第4个字段提取arr[7]值(8行中的第7行)
print arr[1],arr[2],arr[3],arr[4],arr[5],arr[6],arr[7];}
在读取完8行中的第7行时打印提取变量的数组元素。
function extractNum(str){match(str,/[[:digit:]]+/,m);return m[0];}
从文本字段中提取数字的实用程序函数。

相关问题