我有一些数据,我希望使用 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;
}