资料整理

资料整理

我有一些数据,我希望使用 shell/perl 脚本以以下格式输出。

数据:

Virtual Machine  Test status  Running.
Assigned Server  OVS001.local
Virtual Disk  Test_DISK1 (30) size  61GB
Physical Disk  HITACHI (110)
Physical Disk  HITACHI (113)
Physical Disk  HITACHI (111)

所需输出:

Virtual_Machine    Vdsks               size     Physical_Disks
Test               Test_DISK1          61GB        -
Test                 -                  -        HITACHI (110)
Test                 -                  -        HITACHI (113)
Test                 -                  -        HITACHI (111)

这需要为多个虚拟机拉取。

答案1

perl正是针对此类问题而设计和制造的。它的绰号是“病态的折衷垃圾列表”。

阅读有关的文档执行。您要做的就是读取数据(例如,一次一行),解析它,填充到变量中,然后发出命令,write根据format您定义的数据输出当前数据。

对于您的数据,您可能有一个标题格式:

format STDOUT_TOP = 
Virtual_Machine    Vdsks               size      Physical_Disks
-----------------  ------------------  --------- ---------------
.

每行输出还有另一个:

format STDOUT = 
@<<<<<<<<<<<<<<<<  @<<<<<<<<<<<<<<<<<  @>>>>>>>>>>> -
$virtualmachine,   @vdisk,             @vdisk_size
@<<<<<<<<<<<<<<<<  @<<<<<<<<<<<<<<<~~  @>>>>>>>>>~~ -
$#vdisk<0?'':$virtualmachine,   shift(@vdisk), shift(@vdisk_size)
@<<<<<<<<<<<<<<<<  -                                ^<<<<<<<<<<<<~~
$#disks<0?'':$virtualmachine,                       shift(@disks)

.

我不确定您如何获取数据,但假设您将其放在一个由两个换行符分隔的文件中。每个主机都是一个行块:

Virtual Machine  Test1 status  Running.
Assigned Server  OVS001.local
Virtual Disk  Test_DISK1 (30) size  61GB
Physical Disk  HITACHI (110)
Physical Disk  HITACHI (113)
Physical Disk  HITACHI (111)

Virtual Machine  Test status  Running.
Assigned Server  OVS002.local
Virtual Disk  Test_DISK1 (30) size  41GB
Physical Disk  HITACHI (110)
Physical Disk  HITACHI (113)
Physical Disk  HITACHI (111)

您可以像这样设置输入分隔符:

$/="\n\n";

每次阅读时,您都会得到一整块文本,即一个虚拟主机的所有行。

while (<>) { 
    # process one virtual machine
}

现在有趣的事情开始了:解析输入。在 while 循环内,您可以这样做:

my @lines=split("\n");
local @disks=(); # initialize
local $virtualmachine="unknown";
local $physicalserver="unknown";
local @vdisk=("unknown");
local @vdisk_size=("unknown");
foreach (@lines) { 
    $virtualmachine = $1 if /^Virtual Machine\s+(\S+)\s+status\s+/;
    $physicalserver = $1 if /^Assigned Server\s+(\S+)/;
    do { push @vdisk,$1; push @vdisk_size,$2; }
       if /^Virtual Disk\s+(\S+).* size\s+(\d+\w+)/;
    push @disks,$1 if /^Physical Disk\s+(.*)/;
}
write;

解析很粗糙,但通常是有效的。

答案2

取决于你想要的格式的精确程度 - 通常制表符分隔就足够了 - 但我会这样处理它:

#!/usr/bin/env perl
use strict;
use warnings;

#set record separator to double line feed.     
local $/ = "\n\n";

#print header row
print join "\t", "VM", "Virtual_Disk", "size", "Physical Disks", "\n";

#iterate stdin or files specified on command line
while ( <> ) { 
   #capture data from this 'chunk':
   my ( $vm ) = /Virtual Machine\s+(\w+)/;   
   my ( $status ) = /status\s*(\w+)/; 
   my @physical_disks = m/Physical Disk\s+(.*)/g; 
   my %virtual_disks = m/Virtual Disk\s+(\w+).*size\s+(\w+)/g;

   #output tab separated
   print join ("\t", $vm, $_, $virtual_disks{$_}, "-" ), "\n" for keys %virtual_disks;
   print join ("\t", $vm, "-", "-", $_ ), "\n" for @physical_disks;   
}

这与制表位对齐,而不是与固定宽度本身对齐。这可能更合适,具体取决于您的用例。否则 - 您可以format如上所述使用,或sprintf固定字段宽度:

#!/usr/bin/env perl
use strict;
use warnings;

local $/ = "\n\n";
my $field_format = "%8s";  #string, 8 chars wide

print join "\t", map { sprintf $field_format, $_ } "VM", "Virtual_Disk", "size", "Physical Disks", "\n";

while ( <> ) { 
   my ( $vm ) = /Virtual Machine\s+(\w+)/;   
   my ( $status ) = /status\s*(\w+)/; 
   my @physical_disks = m/Physical Disk\s+(.*)/g; 
   my %virtual_disks = m/Virtual Disk\s+(\w+).*size\s+(\w+)/g;
   print join "\t", map { sprintf $field_format, $_} ( $vm, $_, $virtual_disks{$_}, "-" ), "\n" for keys %virtual_disks;
   print join "\t", map { sprintf $field_format, $_} ( $vm, "-", "-", $_ ), "\n" for @physical_disks;   
}

相关内容