Question

I'm trying to parse the output of pvdisplay(8), which prints a separate "paragraph" for each physical volume:

  --- Physical volume ---
  PV Name               /dev/sdb
  VG Name               vg_virtual_01
  PV Size               16.37 TiB / not usable 2.25 MiB
  Allocatable           yes 
  PE Size               4.00 MiB
  Total PE              4291533
  Free PE               3830989
  Allocated PE          460544
  PV UUID               zqi1Q6-tIag-ghQy-MmdJ-kyOS-XkmY-HpyQ51

  --- Physical volume ---
  PV Name               /dev/sda
  VG Name               vg_virtual_02
  PV Size               16.37 TiB / not usable 2.25 MiB
  Allocatable           yes 
  PE Size               4.00 MiB
  Total PE              4291533
  Free PE               1525709
  Allocated PE          2765824
  PV UUID               BlYvJW-ieVx-AjRg-p3r6-e4oC-TkPc-u2lf7x

Simple enough, right?

#!/usr/bin/perl

use strict;
use warnings;

my @pvs = do {
    local $/ = '';
    `pvdisplay`;
};

As it turns out, not as simple as I thought. After half an hour of bashing head on keyboard because my array was only getting a single item, I realized that the blank lines between paragraphs actually contain additional whitespace before the EOL. According to perlvar, $/ treats empty lines as a terminator if set to the null string, with the caveat that empty lines cannot contain any spaces or tabs. And of course, $/ is a string, not a regex, so I can't set it to something like /^\s+$/.

The only way I can see around this is to first save the output to a file, strip the extraneous whitespace, and read it back in, but I would hate to create a temporary file for something so simple. Is there a more elegant way to do this?

Edit: I can do this using split(/^\s+$/m), but I'm just curious if there's a way to do it by changing $/

Was it helpful?

Solution

Huh? Just split the output yourself:

my @pvs = split /\n\s*\n/, scalar `pvdisplay`;

No, there is no way using $/ unless the exact separator is known character for character (you could try e.g. $/ = "\n \n" if there are two spaces`). Don't try to unnecessarily make your job harder.

OTHER TIPS

#!/usr/bin/env perl

use strict;
use warnings;

local $/ = '--- Physical volume ---';

while (my $chunk = <DATA>) {
    chomp $chunk;
    $chunk =~ s/\A \s+ //x;
    $chunk =~ s/\s+ \z //x;
    next unless $chunk =~ /\S/;
    my %obs = ($chunk =~  /^ (.+?[eED]) \s{2,} (\S .+)  $/gxm);
    use YAML;
    print Dump \%obs;
}



__DATA__
--- Physical volume ---
PV Name               /dev/sdb
VG Name               vg_virtual_01
PV Size               16.37 TiB / not usable 2.25 MiB
Allocatable           yes
PE Size               4.00 MiB
Total PE              4291533
Free PE               3830989
Allocated PE          460544
PV UUID               zqi1Q6-tIag-ghQy-MmdJ-kyOS-XkmY-HpyQ51

--- Physical volume ---
PV Name               /dev/sda
VG Name               vg_virtual_02
PV Size               16.37 TiB / not usable 2.25 MiB
Allocatable           yes
PE Size               4.00 MiB
Total PE              4291533
Free PE               1525709
Allocated PE          2765824
PV UUID               BlYvJW-ieVx-AjRg-p3r6-e4oC-TkPc-u2lf7x

Output:

$ ./gg.pl
---
Allocatable: yes
Allocated PE: 460544
Free PE: 3830989
PE Size: 4.00 MiB
PV Name: /dev/sdb
PV Size: 16.37 TiB / not usable 2.25 MiB
PV UUID: zqi1Q6-tIag-ghQy-MmdJ-kyOS-XkmY-HpyQ51
Total PE: 4291533
VG Name: vg_virtual_01
---
Allocatable: yes
Allocated PE: 2765824
Free PE: 1525709
PE Size: 4.00 MiB
PV Name: /dev/sda
PV Size: 16.37 TiB / not usable 2.25 MiB
PV UUID: BlYvJW-ieVx-AjRg-p3r6-e4oC-TkPc-u2lf7x
Total PE: 4291533
VG Name: vg_virtual_02
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top