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

Hi, I have data in a string with multiple lines opened for reading in a command line file handle . I want to join lines starting with a pattern until it hits the same pattern at the End . Below is my data . I want to combine the lines starting with array in to a single line until it hits the pattern starting with array and then when it finds the next array , It should combine all the lines and so on until End of String . The New O/P should look like this ...
array A physicaldrive 2C:1:1 port 2C:box 1:bay 1 SAS 1 TB OK physicald +rive 2C:1:2 port 2C:box 1:bay 2 SAS 1 TB OK array B physicaldrive 2C:1:3 port 2C:box 1:bay 3 SAS 1 TB OK physicald +rive 2C:1:4 port 2C:box 1:bay 4 SAS 1 TB OK

data array A physicaldrive 2C:1:1 port 2C:box 1:bay 1 SAS 1 TB OK physicaldrive 2C:1:2 port 2C:box 1:bay 2 SAS 1 TB OK array B physicaldrive 2C:1:3 port 2C:box 1:bay 3 SAS 1 TB OK physicaldrive 2C:1:4 port 2C:box 1:bay 4 SAS 1 TB OK array C physicaldrive 3C:1:5 port 3C:box 1:bay 5 SAS 1 TB OK physicaldrive 3C:1:6 port 3C:box 1:bay 6 SAS 1 TB OK array D physicaldrive 3C:1:7 port 3C:box 1:bay 7 SAS 1 TB OK physicaldrive 3C:1:8 port 3C:box 1:bay 8 SAS 1 TB OK array E physicaldrive 4C:2:1 port 4C:box 2:bay 1 SAS 1 TB OK physicaldrive 4C:2:2 port 4C:box 2:bay 2 SAS 1 TB OK array F physicaldrive 4C:2:3 port 4C:box 2:bay 3 SAS 1 TB OK physicaldrive 4C:2:4 port 4C:box 2:bay 4 SAS 1 TB OK array G physicaldrive 5C:2:5 port 5C:box 2:bay 5 SAS 1 TB OK physicaldrive 5C:2:6 port 5C:box 2:bay 6 SAS 1 TB OK array H physicaldrive 5C:2:7 port 5C:box 2:bay 7 SAS 1 TB OK physicaldrive 5C:2:8 port 5C:box 2:bay 8 SAS 1 TB OK array I physicaldrive 6C:3:1 port 6C:box 3:bay 1 SAS 1 TB OK physicaldrive 6C:3:2 port 6C:box 3:bay 2 SAS 1 TB OK array J physicaldrive 6C:3:3 port 6C:box 3:bay 3 SAS 1 TB OK physicaldrive 6C:3:4 port 6C:box 3:bay 4 SAS 1 TB OK array K physicaldrive 7C:3:5 port 7C:box 3:bay 5 SAS 1 TB OK physicaldrive 7C:3:6 port 7C:box 3:bay 6 SAS 1 TB OK unassigned physicaldrive 7C:3:7 port 7C:box 3:bay 7 SAS 1 TB OK physicaldrive 7C:3:8 port 7C:box 3:bay 8 SAS 1 TB OK
The Code that I tried to implement didn't work.
while (my $row =~ <$fd>) { $row =~ /^$/ and next; $row =~ s/[,|)|(]//g; chomp $row; next if $row =~ /^Smart/g; $row =~ s/^\s+//; my @temp; my $counter; if($row =~ /^array/) { push @temp,$_; next unless ($row =~ /^array/); } ## Aim is to have the resulting O/P Captured in $row .

Replies are listed 'Best First'.
Re: Join multiple lines in a string based on regex. (Updated)
by BrowserUk (Patriarch) on Dec 16, 2014 at 04:26 UTC

    Try (Updated:simplified code):

    #! perl -sw use strict; while( <DATA> ) { chomp; print "$_ "; unless( $. % 3 ) { print "\n"; } } __DATA__ array A physicaldrive 2C:1:1 port 2C:box 1:bay 1 SAS 1 TB OK physicaldrive 2C:1:2 port 2C:box 1:bay 2 SAS 1 TB OK array B physicaldrive 2C:1:3 port 2C:box 1:bay 3 SAS 1 TB OK physicaldrive 2C:1:4 port 2C:box 1:bay 4 SAS 1 TB OK array C physicaldrive 3C:1:5 port 3C:box 1:bay 5 SAS 1 TB OK physicaldrive 3C:1:6 port 3C:box 1:bay 6 SAS 1 TB OK array D physicaldrive 3C:1:7 port 3C:box 1:bay 7 SAS 1 TB OK physicaldrive 3C:1:8 port 3C:box 1:bay 8 SAS 1 TB OK

    Ouput:

    C:\test>junk array A physicaldrive 2C:1:1 port 2C:box 1:bay 1 SAS 1 TB OK physical +drive 2C:1:2 port 2C:box 1:bay 2 SAS 1 TB OK array B physicaldrive 2C:1:3 port 2C:box 1:bay 3 SAS 1 TB OK physical +drive 2C:1:4 port 2C:box 1:bay 4 SAS 1 TB OK array C physicaldrive 3C:1:5 port 3C:box 1:bay 5 SAS 1 TB OK physical +drive 3C:1:6 port 3C:box 1:bay 6 SAS 1 TB OK

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Thanks for the reply . It seems we are combining every 3 lines in to a single line , If I am not wrong . The O/P could even stretch to more than 3 lines after matching array and until it hits the next array string .
        The O/P could even stretch to more than 3 lines after matching array and until it hits the next array string

        Then it is a good idea to present sample data that shows that.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Join multiple lines in a string based on regex.
by NetWallah (Canon) on Dec 16, 2014 at 05:07 UTC
    Short code, using a (relatively) complex regex with negative look-ahead for the special case "unassigned":
    use strict; use warnings; local $/="\narray "; while (<DATA>){ s/\n(?!unassigned)(:?array)?/ /g; print my $row=(m/^array/?"":"array "). "$_\n"; } __DATA__ array A physicaldrive 2C:1:1 port 2C:box 1:bay 1 SAS 1 TB OK physicaldrive 2C:1:2 port 2C:box 1:bay 2 SAS 1 TB OK array B ... etc ... unassigned physicaldrive 7C:3:7 port 7C:box 3:bay 7 SAS 1 TB OK ...
    Output :
    array A physicaldrive 2C:1:1 port 2C:box 1:bay 1 SAS 1 TB OK physicald +rive 2C:1:2 port 2C:box 1:bay 2 SAS 1 TB OK array B physicaldrive 2C:1:3 port 2C:box 1:bay 3 SAS 1 TB OK physicald +rive 2C:1:4 port 2C:box 1:bay 4 SAS 1 TB OK ... array J physicaldrive 6C:3:3 port 6C:box 3:bay 3 SAS 1 TB OK physicald +rive 6C:3:4 port 6C:box 3:bay 4 SAS 1 TB OK array K physicaldrive 7C:3:5 port 7C:box 3:bay 5 SAS 1 TB OK physicald +rive 7C:3:6 port 7C:box 3:bay 6 SAS 1 TB OK unassigned physicaldrive 7C:3:7 port 7C:box 3:bay 7 SAS 1 TB OK physic +aldrive 7C:3:8 port 7C:box 3:bay 8 SAS 1 TB OK
    Separating the "unassigned" in $row is left as an exercise.

            "You're only given one little spark of madness. You mustn't lose it."         - Robin Williams

      Hi, Here is the O/P that I needed. I probably skipped few lines in my code during my first post .
      use strict; use warnings; use Data::Dumper; my %unitMap; my $skipNext; my $count = 0; my $cmd = "somecommand";; open my $fd, "$cmd|" or return \%unitMap; while (my $row = <$fd>) { $row =~ /^$/ and next; $row =~ s/[,|)|(]//g; chomp $row; next if $row =~ /^Smart/g; $row =~ s/^\s+//; if( $row =~ /^unassigned/ ){ $skipNext = 2; next; } if( $skipNext ){ $skipNext--; next; } print "$ow\n";
      Below is my Data exactly .
      data array A physicaldrive 2C:1:1 port 2C:box 1:bay 1 SAS 1 TB OK physicaldrive 2C:1:2 port 2C:box 1:bay 2 SAS 1 TB OK array B physicaldrive 2C:1:3 port 2C:box 1:bay 3 SAS 1 TB OK physicaldrive 2C:1:4 port 2C:box 1:bay 4 SAS 1 TB OK array C physicaldrive 3C:1:5 port 3C:box 1:bay 5 SAS 1 TB OK physicaldrive 3C:1:6 port 3C:box 1:bay 6 SAS 1 TB OK array D physicaldrive 3C:1:7 port 3C:box 1:bay 7 SAS 1 TB OK physicaldrive 3C:1:8 port 3C:box 1:bay 8 SAS 1 TB OK array E physicaldrive 4C:2:1 port 4C:box 2:bay 1 SAS 1 TB OK physicaldrive 4C:2:2 port 4C:box 2:bay 2 SAS 1 TB OK array F physicaldrive 4C:2:3 port 4C:box 2:bay 3 SAS 1 TB OK physicaldrive 4C:2:4 port 4C:box 2:bay 4 SAS 1 TB OK array G physicaldrive 5C:2:5 port 5C:box 2:bay 5 SAS 1 TB OK physicaldrive 5C:2:6 port 5C:box 2:bay 6 SAS 1 TB OK array H physicaldrive 5C:2:7 port 5C:box 2:bay 7 SAS 1 TB OK physicaldrive 5C:2:8 port 5C:box 2:bay 8 SAS 1 TB OK array I physicaldrive 6C:3:1 port 6C:box 3:bay 1 SAS 1 TB OK physicaldrive 6C:3:2 port 6C:box 3:bay 2 SAS 1 TB OK array J physicaldrive 6C:3:3 port 6C:box 3:bay 3 SAS 1 TB OK physicaldrive 6C:3:4 port 6C:box 3:bay 4 SAS 1 TB OK array K physicaldrive 7C:3:5 port 7C:box 3:bay 5 SAS 1 TB OK physicaldrive 7C:3:6 port 7C:box 3:bay 6 SAS 1 TB OK unassigned physicaldrive 7C:3:7 port 7C:box 3:bay 7 SAS 1 TB OK physicaldrive 7C:3:8 port 7C:box 3:bay 8 SAS 1 TB OK
      a) The Aim here is to join lines starting with string array and until it hits the pattern starting with the string array again and join the lines and repeat the same for all the lines in the string . b) The Line containing unassigned and the lines after it should be removed until it hits a line containing the string array at the start of line . c) In my code , I have removed the line containing unassigned and 2 lines after it , But it should remove any number of lines after unassigned until it matches array at the start of the string .
      data unassinged... physicaldrive ......... physical dirve ..... physicaldrive...... array
        Slightly simpler logic..
        use strict; use warnings; my $row =""; my $inarray=0; while (<DATA>) { chomp; if (/^array|^unassigned/){ print "$row\n" if $inarray; $row=$_; $inarray=m/^array/; next; } next unless $inarray; $row .= " $_"; } print "$row\n" if $inarray; __DATA__ ....

                "You're only given one little spark of madness. You mustn't lose it."         - Robin Williams

Re: Join multiple lines in a string based on regex.
by trippledubs (Deacon) on Dec 16, 2014 at 04:34 UTC
    #!/usr/bin/env perl use strict; use warnings; use Data::Dump; my $hashRef; # See Perldoc perldsc my $lastSeen; for (<DATA>) { length or next; chomp; if (!/^physicaldrive/) { $lastSeen=$_; } else { push @{$hashRef->{$lastSeen}},$_; } } my @arrayA = @{$hashRef->{'array A'}}; dd @arrayA; my $secondDiskArrayA = $hashRef->{'array A'}[1]; dd $secondDiskArrayA; my $unassignedDisks = $hashRef->{unassigned}; dd $unassignedDisks; dd $unassignedDisks->[0];
    Recommend reading perldsc
Re: Join multiple lines in a string based on regex.
by hdb (Monsignor) on Dec 16, 2014 at 09:03 UTC

    I like the following way:

    use strict; use warnings; use Data::Dumper; my @data; while( <DATA> ) { chop; if ( /^(array.*|unassigned)$/ ) { push @data, $1; } else { $data[-1] .= " $_"; } } print Dumper \@data; __DATA__ array A physicaldrive 2C:1:1 port 2C:box 1:bay 1 SAS 1 TB OK physicaldrive 2C:1:2 port 2C:box 1:bay 2 SAS 1 TB OK array B physicaldrive 2C:1:3 port 2C:box 1:bay 3 SAS 1 TB OK physicaldrive 2C:1:4 port 2C:box 1:bay 4 SAS 1 TB OK unassigned physicaldrive 7C:3:7 port 7C:box 3:bay 7 SAS 1 TB OK physicaldrive 7C:3:8 port 7C:box 3:bay 8 SAS 1 TB OK

      Nice! Except the groups beginning with "unassigned" should not be printed.

      1 Peter 4:10
      Thanks for the reply . $row is the variable where my Command output file handle is for reading . How do I go about capturing the output in the $row variable as I have trimmed lot of lines in the O/P using the scalar variable .
      Thanks for the reply . Tried the same code , But it fails with the Error Below . Modification of non-creatable array value attempted, subscript -1 at ./file2.pl line 15, <$fd> line 1.

        This can happen if the data does not start with an "array" line (I am guessing).

Re: Join multiple lines in a string based on regex.
by Anonymous Monk on Dec 16, 2014 at 03:43 UTC
    Could you explain in plain english what steps you as a human would take to solve this problem? If you as a human had to read one line, do something, then read next line...