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

Greetings, monks.

As several monks have kindly pointed out my question about sed was not clear, therefore I revise it here. Even though it is a very simple problem, bear in mind that

I am very new and clueless

and

I've benefited a great deal from the simple suggestions made to me thus far.

I want to be able to use perl interactively with vi to achieve the following. If the file has this:

Line1 Line2 Line3 Line ‘1’ Line ‘2’ Line ‘3’ Line4 Line5

I want it to ultimately look like this:

Line1 Line2 Line3 NewLine1 (+3) Line ’3’;fac=3 (+2) Line ‘2’;fac=2 (+1) Line ‘1’;fac=1 Line4 Line5 MeanLine

So far, I can get this if the file is already in the Order of the second file—what I want it to do is flip those 3 lines before applying the other changes. The script I have right now looks like this:

#!/usr/bin/perl -w while (<>) { if (/'3'/) { s/^L/(+3) L/g; s/'3'/'3';fac=3/g; s/^/Newline1\n/g } if (/'2'/) { s/^L/(+2) L/g; s/'2'/'2';fac=2/g; } if (/'1'/) { s/^L/(+1) L/g; s/'1'/'1';fac=1/g; } if (/Line5/) { s/Line5/Line5\nMeanLine/g } print "$_"; }

I had a sed script that would reverse the order of the lines based on their matching a pattern-- what I'm trying to find is a Simple way to achieve this same result. While substitution looks pretty much the same as sed in perl, I can't seem to find anything like sed's ability to put stuff in a hold space and retrieve it later, like this sed does:

/'1'/{ h d } /'2'/G /'2'/{ h d } /'3'/G /'3'/{ h d }

Putting this through s2p gave me 3 pages of code I won't understand until saint level.

I'm also trying to get it to do what the program above does-- work over a block of text in a file being edited with vi editor, and invoked thus:

:1,17! perlprogram

Thanks in advance for your help.

Replies are listed 'Best First'.
Re: sed to perl conversion revisted
by Roger (Parson) on Feb 20, 2004 at 22:33 UTC
    Another way to do this - read in entire file into list, then manipulate in memory, easier to work with.
    #!/usr/local/bin/perl -w use strict; chomp(my @lines = <DATA>); my @newlines = reverse grep m/'\d+'/, @lines; @lines = map { if (/'(\d+)'/) { my $c = scalar @newlines - $1 + 1; my $l = $1 == 1 ? "NewLine1\n" : ""; "$l(+$c) " . $newlines[$1-1] . ";fac=$c" } else { /Line5/ ? "$_\nMeanLine" : $_ } } @lines; print "$_\n" for @lines; __DATA__ Line1 Line2 Line3 Line4 Line '1' Line '2' Line '3' Line '4' Line5 Line6

    And the output -
    Line1 Line2 Line3 Line4 NewLine1 (+4) Line '4';fac=4 (+3) Line '3';fac=3 (+2) Line '2';fac=2 (+1) Line '1';fac=1 Line5 MeanLine Line6

Re: sed to perl conversion revisted
by Skeeve (Parson) on Feb 20, 2004 at 22:27 UTC
    Your code doesn't look like a real program to me but just as a bunch of editing commands to convert exactly one kind of input to exactly one kind of output. I don't know, whether or not my attempt fits your needs, but try for yourself...
    use strict; use warnings; # use these two always! my $pattern= qr/^(Line '(\d+)')$/; # This is the line you're searching + for my @holdspace; while (<DATA>) { #read the input if (my $hit= /$pattern/o .. not /$pattern/o) { # this will be executed for every Pattern-line # and the line after that. if ($hit==1) { # If it was the first: Clear the holdspace @holdspace= (); } elsif ($hit=~ /e0/i) { # If it's the last print everything print "NewLine1\n",@holdspace,$_; next; } # Store the pattern lines push @holdspace, "(+$2) $1;fac=$2\n"; next; } # Print everything else print; } # That's it print "MeanLine\n"; __END__ Line1 Line2 Line3 Line '1' Line '2' Line '3' Line4 Line5
Re: sed to perl conversion revisted
by fizbin (Chaplain) on Feb 20, 2004 at 22:48 UTC
    A completely perlish way of saying what you want:
    #!/usr/bin/perl -w @holdspace = (); while (<>) { if (/'(\d)'/) { s/^/(+$1) /g; s/$/;fac=$1/g; unshift @holdspace, $_; } else { if (@holdspace) { print "Newline1\n", @holdspace; @holdspace=(); } print; } if (/Line5/) { print "MeanLine\n"; } }
    Note that this does not depend on the specific digits "1", "2", or "3" - instead, it merely depends on lines containing a digit in single quotes, and that the lines to be reversed are sequential. (And that there's at least one line after the lines with single-quoted digits, though that's an easy restriction to lift) If you'd rather have something that more closely mirrored what the sed script was, (I'm guessing here, since I haven't seen the whole sed script) you could do:
    #!/usr/bin/perl -w $holdspace = ""; while (<>) { if (/'1'/) { s/^/(+1) /g; s/$/;fac=1/g; $holdspace = $_; $_ = ""; } elsif (/'2'/) { s/^/(+2) /g; s/$/;fac=2/g; $_ .= $holdspace; $holdspace = $_; $_ = ""; } elsif (/'3'/) { s/^/(+3) /g; s/$/;fac=3/g; $_ = "Newline1\n" . $_; $_ .= $holdspace; $holdspace = ""; } if (/Line5/) { $_ .= "MeanLine\n"; } print; }
    But I personally think that's both ugly and limited in what files it applies to.
Re: sed to perl conversion revisted
by ambrus (Abbot) on Feb 20, 2004 at 22:43 UTC

    Ok, let's try this again, this time with some explanation.

    #!/usr/local/bin/perl use warnings; while (<>) { if (/'3'/) { s/^L/(+3) L/g; s/'3'/'3';fac=3/g; s/^/Newline1\n/g; print $_, @h; } elsif (/'2'/) { s/^L/(+2) L/g; s/'2'/'2';fac=2/g; unshift @h, $_; } elsif (/'1'/) { s/^L/(+1) L/g; s/'1'/'1';fac=1/g; unshift @h, $_; } elsif (/Line5/) { print $_, "MeanLine\n"; } else { print; } } __END__

    Here, @h is an array variable global to the program. I use it instead of the hold space of sed. This array starts empty. When I meet the lines with '1' or '2', unshift inserts them to the beginning of the array. Note that I've moved the print statement in the else part of the if, so that it won't print these lines immediately. Now, when the script reads the line '3', it will first print the line itself (with Newline prepended), and then it will print the contents of the array @h. At that time @h has the lines with '2' as the first element and the one with '1' as the second.

    Update: it might have been better if I used next instead of moving the print in the conditional. That would not only make the code more similar to a sed script, but would avoid some repetitions too. I would not change it now, however.

Re: sed to perl conversion revisted
by ambrus (Abbot) on Feb 20, 2004 at 22:28 UTC

    For me, the sed script you enclosed does not seem to work. It gives this output given your example input:

    Line1 Line2 Line3 Line4 Line5

    I am using GNU sed 3.02.