http://qs321.pair.com?node_id=11121971

pritesh_ugrankar has asked for the wisdom of the Perl Monks concerning the following question:

Respected Monks,

This is regarding an issue I've been trying to fix the whole day, but have not succeeded.Will be thankful if you could help.

I have a text file that has the following content (command output).

Storage system address: 192.168.1.2 Storage system port: 443 HTTPS connection 1: ID = disk_dpe_0_0 Enclosure = DPE_0 Slot = 0 Name = DPE Disk 0 Health state = OK (5) Health details = "The component is operating normally. No ac +tion is required." Type = SAS Capacity = 288196762624 (268.4G) Rotational speed = 15000 rpm User capacity = 236420176896 (220.2G) Pool = performance Current speed = 6 Gbps Maximum speed = 6 Gbps Manufacturer = SEAGATE Model = STE30065 CLAR300 Vendor capacity = 322122547200 (300.0G) Part number = 005049273 Serial number = 6SJ2C6MV Firmware revision = ES0E WWN = 06:00:00:00:05:00:00:00:00:00:00:00:00:00:00 +:03 2: ID = disk_dpe_0_1 Enclosure = DPE_0 Slot = 1 Name = DPE Disk 1 Health state = OK (5) Health details = "The component is operating normally. No ac +tion is required." Type = SAS Capacity = 288196762624 (268.4G) Rotational speed = 15000 rpm User capacity = 236420176896 (220.2G) Pool = performance Current speed = 6 Gbps Maximum speed = 6 Gbps Manufacturer = SEAGATE Model = STE30065 CLAR300 Vendor capacity = 322122547200 (300.0G) Part number = 005049273 Serial number = 6SJ28QF3 Firmware revision = ES0E WWN = 06:00:00:00:05:00:00:00:01:00:00:00:01:00:00 +:03 .......................many more similar lines.

What I want to do is, create an anonymous array such that each value in the array is an anonymous hash that contains a paragraph from the text above. Paragraph meaning the content between the blank lines. What I am getting right now, is each line which becomes an anonymous hash.

######################## use strict; use warnings; use Data::Dumper; ######################## my $filename = "physical_disks.txt"; my $aoh_ref_physical_disks; open my $fh, "<", $filename or die "Cannot open file $filename: $!"; while (<$fh>) { #Do stuff here so that the following Array of Hashes is generated. #Try later to put all the seperate next ifs into a single statemen +t. next if $_=~ /^Storage/; next if $_=~ /^HTTPS/; next if $_=~ /^$/; my ($filekey, $filevalue) = (split /\s+=\s+/, $_); #print "$filekey => $filevalue\n"; push @$aoh_ref_physical_disks, {$filekey => $filevalue}; } foreach my $disk (1..$#$aoh_ref_physical_disks) { foreach my $key (sort keys $aoh_ref_physical_disks->[$disk]->%*) { #Add if loop here that checks if the key "Health status" #is anything but ok or if the key "Health details" does not #contain the word "normal", then print out the other details #such as #$aoh_ref_physical_disks->[$disk]->{'ID'} #$aoh_ref_physical_disks->[$disk]->{'Health status'} #$aoh_ref_physical_disks->[$disk]->{'Health details'} #$aoh_ref_physical_disks->[$disk]->{'Model'} } } #Use Data::Dumper output to verify. print Dumper($aoh_ref_physical_disks);

The output I get is:

C:\Users\pritesh\perlscripts>perl test.pl $VAR1 = [ { '1: ID' => 'disk_dpe_0_0 ' }, { ' Enclosure' => 'DPE_0 ' }, { ' Slot' => '0 ' }, { ' Name' => 'DPE Disk 0 ' }, { ' Health state' => 'OK (5) ' }, ]; #and so on............. C:\Users\pugrankar\perlscripts>

So basically, I am not able to tell perl that I want each hash to contain the lines from one paragraph. Kindly help me.

Replies are listed 'Best First'.
Re: Unable to get the paragraph in the list of hashes. Getting single lines instead.
by GrandFather (Saint) on Sep 20, 2020 at 21:14 UTC

    Perhaps:

    use strict; use warnings; use Data::Dumper; my %records; local $/ = "\n\n"; while (<DATA>) { next if !/^(\d+):(.*)/s; $records{$1} = $2; } print Dumper(\%records); __DATA__ Storage system address: 192.168.1.2 Storage system port: 443 HTTPS connection 1: ID = disk_dpe_0_0 Enclosure = DPE_0 Slot = 0 Name = DPE Disk 0 Health state = OK (5) Health details = "The component is operating normally. No ac +tion is require +d." Type = SAS Capacity = 288196762624 (268.4G) Rotational speed = 15000 rpm User capacity = 236420176896 (220.2G) Pool = performance Current speed = 6 Gbps Maximum speed = 6 Gbps Manufacturer = SEAGATE Model = STE30065 CLAR300 Vendor capacity = 322122547200 (300.0G) Part number = 005049273 Serial number = 6SJ2C6MV Firmware revision = ES0E WWN = 06:00:00:00:05:00:00:00:00:00:00:00:00:00:00 +:03 2: ID = disk_dpe_0_1 Enclosure = DPE_0 Slot = 1 Name = DPE Disk 1 Health state = OK (5) Health details = "The component is operating normally. No ac +tion is require +d." Type = SAS Capacity = 288196762624 (268.4G) Rotational speed = 15000 rpm User capacity = 236420176896 (220.2G) Pool = performance Current speed = 6 Gbps Maximum speed = 6 Gbps Manufacturer = SEAGATE Model = STE30065 CLAR300 Vendor capacity = 322122547200 (300.0G) Part number = 005049273 Serial number = 6SJ28QF3 Firmware revision = ES0E WWN = 06:00:00:00:05:00:00:00:01:00:00:00:01:00:00 +:03

    Prints:

    $VAR1 = { '1' => ' ID = disk_dpe_0_0 Enclosure = DPE_0 Slot = 0 Name = DPE Disk 0 Health state = OK (5) Health details = "The component is operating normally. No ac +tion is required." Type = SAS Capacity = 288196762624 (268.4G) Rotational speed = 15000 rpm User capacity = 236420176896 (220.2G) Pool = performance Current speed = 6 Gbps Maximum speed = 6 Gbps Manufacturer = SEAGATE Model = STE30065 CLAR300 Vendor capacity = 322122547200 (300.0G) Part number = 005049273 Serial number = 6SJ2C6MV Firmware revision = ES0E WWN = 06:00:00:00:05:00:00:00:00:00:00:00:00:00:00 +:03 ', '2' => ' ID = disk_dpe_0_1 Enclosure = DPE_0 Slot = 1 Name = DPE Disk 1 Health state = OK (5) Health details = "The component is operating normally. No ac +tion is required." Type = SAS Capacity = 288196762624 (268.4G) Rotational speed = 15000 rpm User capacity = 236420176896 (220.2G) Pool = performance Current speed = 6 Gbps Maximum speed = 6 Gbps Manufacturer = SEAGATE Model = STE30065 CLAR300 Vendor capacity = 322122547200 (300.0G) Part number = 005049273 Serial number = 6SJ28QF3 Firmware revision = ES0E WWN = 06:00:00:00:05:00:00:00:01:00:00:00:01:00:00 +:03 ' };

    Note the use of local $/ = "\n\n"; to turn the line break "character" (record separator) into two line breaks as an easy way of reading the input as a stream of records.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

      Hi GrandFather,

      Thank you very much for taking time to try this out and suggestion a truly genius solution. However, I was looking more for something that would let me access each field as a value so that I could get something like so (note that the output below does not :

      { '1: ID' => 'disk_dpe_0_0', 'Capacity' => '576393510912 (536.8G)', 'Rotational speed' => '15000 rpm', #more similar stuff............. 'User capacity' => '576359956480 (536.8G)', 'Pool' => 'Hot Spare (not in use)', }, #and so on....

      That way, I could only extract the values of a few keys and not the entire hash. But, you've already done a lot. Let me try something and come back here in case I cannot do it. Thank you once again Sir.

        You can break out the record fields by processing the record string as a list of lines:

        use strict; use warnings; use Data::Dumper; my %records; local $/ = "\n\n"; while (<DATA>) { next if !/^(\d+):(.*)/s; my ($id, $tail) = ($1, $2); local $/ = "\n"; open my $recIn, '<', \$tail; while (<$recIn>) { chomp; #next if !/(\w+)\s*=\s*(.*)/; # #$records{$id}{$1} = $2; next if !/^\s*?([^=]+)\s*=\s*(.*)/; my ($key, $value) = ($1, $2); s/^\s+|\s+$//g for $key, $value; $records{$id}{$key} = $value; } } print Dumper(\%records); __DATA__ ...

        Using the previous data prints:

        $VAR1 = { '1' => { 'Capacity' => '288196762624 (268.4G)', 'WWN' => '06:00:00:00:05:00:00:00:00:00:00:00:00:00 +:00:03', 'Pool' => 'performance', 'Model' => 'STE30065 CLAR300', 'Maximum speed' => '6 Gbps', 'Health details' => '"The component is operating no +rmally. No action is required."', 'Vendor capacity' => '322122547200 (300.0G)', 'Part number' => '005049273', 'Enclosure' => 'DPE_0', 'Health state' => 'OK (5)', 'Serial number' => '6SJ2C6MV', 'Slot' => '0', 'Type' => 'SAS', 'User capacity' => '236420176896 (220.2G)', 'ID' => 'disk_dpe_0_0', 'Manufacturer' => 'SEAGATE', 'Name' => 'DPE Disk 0', 'Current speed' => '6 Gbps', 'Rotational speed' => '15000 rpm', 'Firmware revision' => 'ES0E' }, '2' => { 'WWN' => '06:00:00:00:05:00:00:00:01:00:00:00:01:00 +:00:03', 'Pool' => 'performance', 'Capacity' => '288196762624 (268.4G)', 'Slot' => '1', 'Serial number' => '6SJ28QF3', 'Health state' => 'OK (5)', 'Part number' => '005049273', 'Enclosure' => 'DPE_0', 'Model' => 'STE30065 CLAR300', 'Maximum speed' => '6 Gbps', 'Health details' => '"The component is operating no +rmally. No action is required."', 'Vendor capacity' => '322122547200 (300.0G)', 'ID' => 'disk_dpe_0_1', 'User capacity' => '236420176896 (220.2G)', 'Manufacturer' => 'SEAGATE', 'Type' => 'SAS', 'Rotational speed' => '15000 rpm', 'Firmware revision' => 'ES0E', 'Current speed' => '6 Gbps', 'Name' => 'DPE Disk 1' } };

        Note that local overrides the variable's value just for the current block so changing $/ inside the main loop doesn't affect while (<DATA>). open my $recIn, '<', \$tail; treats $tail as a file.

        Update: Replaced commented out code to fix the single word matches for keys issue caught by tybalt89.

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re: Unable to get the paragraph in the list of hashes. Getting single lines instead.
by tybalt89 (Monsignor) on Sep 22, 2020 at 14:39 UTC

    It's really logically a one-liner

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11121971 use warnings; my @answer = grep %$_, # ignore empty hashs map +{ /^\h*(.*?)\h*= (.*)/gm }, # convert a paragraph to anon hash + by lines do { local $/ = ''; <DATA> }; # get list of paragraphs use Data::Dump 'dd'; dd \@answer; __DATA__ ..... data section same as my other example .....

    ( Shown on multiple lines with comments added to avoid annoying GrandFather :)

Re: Unable to get the paragraph in the list of hashes. Getting single lines instead.
by tybalt89 (Monsignor) on Sep 20, 2020 at 23:08 UTC
    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11121971 use warnings; my @answer; { local $/ = ''; while( <DATA> ) { /=/ and push @answer, { /^\h*(\S.*?\S)\h*= (.*)/gm }; } } use Data::Dump 'dd'; dd \@answer; __DATA__ Storage system address: 192.168.1.2 Storage system port: 443 HTTPS connection 1: ID = disk_dpe_0_0 Enclosure = DPE_0 Slot = 0 Name = DPE Disk 0 Health state = OK (5) Health details = "The component is operating normally. No ac +tion is required." Type = SAS Capacity = 288196762624 (268.4G) Rotational speed = 15000 rpm User capacity = 236420176896 (220.2G) Pool = performance Current speed = 6 Gbps Maximum speed = 6 Gbps Manufacturer = SEAGATE Model = STE30065 CLAR300 Vendor capacity = 322122547200 (300.0G) Part number = 005049273 Serial number = 6SJ2C6MV Firmware revision = ES0E WWN = 06:00:00:00:05:00:00:00:00:00:00:00:00:00:00 +:03 2: ID = disk_dpe_0_1 Enclosure = DPE_0 Slot = 1 Name = DPE Disk 1 Health state = OK (5) Health details = "The component is operating normally. No ac +tion is required." Type = SAS Capacity = 288196762624 (268.4G) Rotational speed = 15000 rpm User capacity = 236420176896 (220.2G) Pool = performance Current speed = 6 Gbps Maximum speed = 6 Gbps Manufacturer = SEAGATE Model = STE30065 CLAR300 Vendor capacity = 322122547200 (300.0G) Part number = 005049273 Serial number = 6SJ28QF3 Firmware revision = ES0E WWN = 06:00:00:00:05:00:00:00:01:00:00:00:01:00:00 +:03

      Golf and pedagogy are generally incompatible. To turn your node into something useful to the OP you should deconstruct your code and describe each facet.

      Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond

      Hi tybalt89,

      Thank you for this answer. There's a lot to learn from this for me.